Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgattiluxor committed Jul 13, 2022
0 parents commit 3818062
Show file tree
Hide file tree
Showing 26 changed files with 2,873 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/prometheus-luxor-exporter.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
ARG APP_VERSION=0.0.0-SNAPSHOT
ARG APP_GID=5000
ARG APP_UID=5000

## Build stage
FROM golang:1.16-alpine AS build
ENV CGO_ENABLED=0
WORKDIR /app

# Download deps
COPY go.mod ./
COPY go.sum ./
RUN go mod download

# Build app
ARG APP_VERSION
COPY cmd cmd
COPY util util
RUN go build -v \
-ldflags="-X 'main.appVersion=${APP_VERSION}'" \
-o prometheus-luxor-exporter \
cmd/prometheus-luxor-exporter/*.go

# Test
#RUN go test -v .

## Runtime stage
FROM alpine:3 AS runtime
WORKDIR /app

ARG APP_GID
ARG APP_UID
RUN addgroup -g $APP_GID -S app && adduser -G app -u $APP_UID -S app

COPY --from=build /app/prometheus-luxor-exporter ./
RUN chown app:app prometheus-luxor-exporter

USER app
ENTRYPOINT ["./prometheus-luxor-exporter"]
CMD [""]
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Prometheus Luxor Exporter

![](examples/luxordash.png)
![](examples/luxordash2.png)
Fork of this repo [prometheus-ethermine-exporter](https://github.com/HON95/prometheus-ethermine-exporter).

The original is an exporter for the following cryptocurrency mining pools:
Ethermine, Ethermine ETC, Ethpool, Zcash Flypool, Ravencoin Flypool, BEAM Flypool.
The exporter uses a unified API structure for all the listed pools, so we create a fork
to be used with our graphql api.

## Usage
Information on how to use it can be seen [here](dev/how-to.md).

## Grafana

Example dashboards:

[Miner dashboard](dashboard.json)

## Configuration
Docker Image can be found [here](https://hub.docker.com/repository/docker/luxorlabs/prometheus-luxor-exporter)

## Metrics
See the miner example output [here](examples/output-miner.txt)

## Development
Build: go build -o prometheus-luxor-exporter cmd/prometheus-luxor-exporter/*.go
Lint: golint ./...

# Changelog

## [1.0.0] - 2022-06-24

Initial release.
16 changes: 16 additions & 0 deletions cmd/prometheus-luxor-exporter/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

// CurrencySymbolEthereum is a list of currency symbols.
const (
CurrencySymbolEthereum = "ETH"
)

const namespace = "luxor"

const defaultDebug = false
const defaultEndpoint = ":8080"

const appVersion = "0.1.0-alabama"

var enableDebug = false
var endpoint = defaultEndpoint
76 changes: 76 additions & 0 deletions cmd/prometheus-luxor-exporter/luxor_graphql_requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"gitlab.com/luxorlabs/prometheus-luxor-exporter/util"
"io/ioutil"
"net/http"
"time"
)

func getMinersInfo(response http.ResponseWriter, apikey string) MinerInfo {
jsonData := map[string]string{
"query": `
{
miners (first: 20000 filter: {miningProfileName: {equalTo: ETH}} orderBy: ID_ASC) {
nodes {
workerName
user { username }
details1H { badShares duplicateShares efficiency hashrate lowDiffShares invalidShares revenue staleShares status updatedAt validShares }
details24H { hashrate }
}
}
}
`,
}
jsonValue, _ := json.Marshal(jsonData)
request, err := http.NewRequest("POST", "https://api.beta.luxor.tech/graphql", bytes.NewBuffer(jsonValue))
request.Header.Set("X-Lux-Api-Key", apikey)
request.Header.Set("Content-Type", "application/json")

client := &http.Client{Timeout: time.Second * 100}
resp, err := client.Do(request)
defer resp.Body.Close()
if err != nil {
fmt.Printf("The HTTP request failed with error %s\n", err)
}
rawdata, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(rawdata))

var data MinerInfo
util.ParseJSON(&data, response, rawdata, false, enableDebug)

return data
}

func getPendingBalance(response http.ResponseWriter, user, apikey string) Wallet {
jsonData := map[string]string{
"query": fmt.Sprintf(`
{
getWallets(first: 100 uname:"%s" filter: {currencyProfileName: {equalTo: ETH}}) {
nodes {
pendingBalance
}
}
}`, user),
}
jsonValue, _ := json.Marshal(jsonData)
request, err := http.NewRequest("POST", "https://api.beta.luxor.tech/graphql", bytes.NewBuffer(jsonValue))
request.Header.Set("X-Lux-Api-Key", apikey)
request.Header.Set("Content-Type", "application/json")

client := &http.Client{Timeout: time.Second * 100}
resp, err := client.Do(request)
defer resp.Body.Close()
if err != nil {
fmt.Printf("The HTTP request failed with error %s\n", err)
}
rawdata, _ := ioutil.ReadAll(resp.Body)

var wallet Wallet
util.ParseJSON(&wallet, response, rawdata, false, enableDebug)

return wallet
}
27 changes: 27 additions & 0 deletions cmd/prometheus-luxor-exporter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"flag"
"fmt"
"os"
)

func main() {
parseCliArgs()
if enableDebug {
fmt.Printf("[DEBUG] Debug mode enabled.\n")
}

if err := runServer(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
return
}
}

func parseCliArgs() {
flag.BoolVar(&enableDebug, "debug", defaultDebug, "Show debug messages.")
flag.StringVar(&endpoint, "endpoint", defaultEndpoint, "The address-port endpoint to bind to.")

// Exits on error
flag.Parse()
}
68 changes: 68 additions & 0 deletions cmd/prometheus-luxor-exporter/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"github.com/prometheus/client_golang/prometheus"
"gitlab.com/luxorlabs/prometheus-luxor-exporter/util"
"net/http"
)

// Builds a new registry for the miner endpoint, adds scraped data to it and returns it if successful or nil if not.
func buildMinerRegistry(response http.ResponseWriter, pool *Pool, username string, statsData *minerStatsAPIData, workersData *minerWorkersAPIData) *prometheus.Registry {
registry := prometheus.NewRegistry()
registry.MustRegister(prometheus.NewGoCollector())

util.NewExporterMetric(registry, namespace, appVersion)

// Note: Miner address isn't needed as it's the instance/target of the scrape.
constLabels := prometheus.Labels{
"pool": pool.ID,
"subaccount": username,
}
constLabelsWithCurrency := util.MergeLabels(constLabels, prometheus.Labels{
"currency": string(pool.Currency),
})

// Miner info
util.NewGauge(registry, namespace, "miner", "info", "Metadata about the miner.", util.MergeLabels(constLabels, prometheus.Labels{
"pool_name": pool.Name,
"pool_currency": string(pool.Currency),
})).Set(1)

// Miner stats
util.NewGauge(registry, namespace, "miner", "last_seen_seconds", "Delta between time of last statistics entry and when any workers from the miner was last seen (s).", constLabels).Set(statsData.Data.Timestamp - statsData.Data.LastSeenTimestamp)
util.NewGauge(registry, namespace, "miner", "hashrate_current_hps", "Total current hash rate for a miner (H/s).", constLabels).Set(statsData.Data.CurrentHashRate)
util.NewGauge(registry, namespace, "miner", "hashrate_average_hps", "Total average hash rate for a miner (H/s).", constLabels).Set(statsData.Data.AverageHashRate)
util.NewGauge(registry, namespace, "miner", "shares_valid", "Total number of valid shares for a miner.", constLabels).Set(statsData.Data.ValidShares)
util.NewGauge(registry, namespace, "miner", "shares_invalid", "Total number of invalid shares for a miner.", constLabels).Set(statsData.Data.InvalidShares)
util.NewGauge(registry, namespace, "miner", "shares_stale", "Total number of stale shares for a miner.", constLabels).Set(statsData.Data.StaleShares)
util.NewGauge(registry, namespace, "miner", "workers_active", "Number of active workers.", constLabels).Set(statsData.Data.ActiveWorkers)
util.NewGauge(registry, namespace, "miner", "income_coins", "Mined coins per second.", constLabelsWithCurrency).Set(statsData.Data.CoinsPerMinute / 60)
util.NewGauge(registry, namespace, "miner", "income_usd", "Mined coins per second (converted to USD).", constLabels).Set(statsData.Data.USDPerMinute / 60)
util.NewGauge(registry, namespace, "miner", "income_btc", "Mined coins per second (converted to BTC).", constLabels).Set(statsData.Data.BTCPerMinute / 60)
util.NewGauge(registry, namespace, "miner", "balance_unpaid_coins", "Unpaid balance for a miner.", constLabelsWithCurrency).Set(statsData.Data.UnpaidBalanceBaseUnits)
util.NewGauge(registry, namespace, "miner", "balance_unpaid_usd", "Unpaid balance for a miner.", constLabels).Set(statsData.Data.UnpaidBalanceBaseUnits)
// Deprecated
util.NewGauge(registry, namespace, "miner", "income_minute_coins", "(Deprecated) Mined coins per minute.", constLabelsWithCurrency).Set(statsData.Data.CoinsPerMinute)
util.NewGauge(registry, namespace, "miner", "income_minute_usd", "(Deprecated) Mined coins per minute (converted to USD).", constLabels).Set(statsData.Data.USDPerMinute)
util.NewGauge(registry, namespace, "miner", "income_minute_btc", "(Deprecated) Mined coins per minute (converted to BTC).", constLabels).Set(statsData.Data.BTCPerMinute)

// Worker stats
workerLabels := make(prometheus.Labels)
workerLabels["worker"] = ""
workerLastSeenMetric := util.NewGaugeVec(registry, namespace, "worker", "last_seen_seconds", "Delta between time of last statistics entry and when the miner was last seen (s).", constLabels, workerLabels)
workerCurrentHashRateMetric := util.NewGaugeVec(registry, namespace, "worker", "hashrate_current_hps", "Current hash rate for a worker (H/s).", constLabels, workerLabels)
workerValidSharesMetric := util.NewGaugeVec(registry, namespace, "worker", "shares_valid", "Number of valid shared for a worker.", constLabels, workerLabels)
workerInvalidSharesMetric := util.NewGaugeVec(registry, namespace, "worker", "shares_invalid", "Number of invalid shared for a worker.", constLabels, workerLabels)
workerStaleSharesMetric := util.NewGaugeVec(registry, namespace, "worker", "shares_stale", "Number of stale shared for a worker.", constLabels, workerLabels)
for _, element := range workersData.Data {
labels := make(prometheus.Labels)
labels["worker"] = element.Name
workerLastSeenMetric.With(labels).Set(element.Timestamp - element.LastSeenTimestamp)
workerCurrentHashRateMetric.With(labels).Set(element.CurrentHashRate)
workerValidSharesMetric.With(labels).Set(element.ValidShares)
workerInvalidSharesMetric.With(labels).Set(element.InvalidShares)
workerStaleSharesMetric.With(labels).Set(element.StaleShares)
}

return registry
}
Loading

0 comments on commit 3818062

Please sign in to comment.