Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
go-version-file: go.mod
- uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: v2.5.0
version: v2.11.3

test:
runs-on: ubuntu-latest
Expand Down
48 changes: 48 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Makefile for github.com/zakame/speedtest-go-exporter

BINARY := speedtest-go-exporter
CMD := ./cmd/$(BINARY)
BIN_DIR := ./bin
CGO_ENABLED ?= 0

.DEFAULT_GOAL := help

.PHONY: help build test cover lint vet fmt run clean

help: ## Show this help message
@echo "Usage: make <target>"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| sort \
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-10s\033[0m %s\n", $$1, $$2}'

build: ## Compile the binary to ./bin/speedtest-go-exporter
@mkdir -p $(BIN_DIR)
CGO_ENABLED=$(CGO_ENABLED) go build -o $(BIN_DIR)/$(BINARY) $(CMD)

test: ## Run all tests
go test -v -count=1 ./...

cover: ## Run tests with coverage report
go test -cover -coverpkg=./... -coverprofile=coverage.txt -covermode=atomic ./...
go tool cover -func=coverage.txt

lint: ## Run golangci-lint if available, otherwise fall back to go vet
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run; \
else \
echo "golangci-lint not found, falling back to go vet"; \
go vet ./...; \
fi

vet: ## Run go vet
go vet ./...

fmt: ## Check formatting (exits non-zero if files need formatting)
@test -z "$$(gofmt -l .)" || (gofmt -l . && exit 1)

run: build ## Build and run the exporter (env: SPEEDTEST_PORT, SPEEDTEST_SERVER, SPEEDTEST_EXPORTER_DEBUG)
$(BIN_DIR)/$(BINARY)

clean: ## Remove build artifacts (./bin/, coverage.txt)
rm -rf $(BIN_DIR) coverage.txt
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Download the latest release from the [releases page](https://github.com/zakame/s
### Building from Source

Requirements:
- Go 1.25 or later
- Go 1.26 or later

```bash
git clone https://github.com/zakame/speedtest-go-exporter.git
Expand All @@ -61,7 +61,7 @@ Configuration is done via environment variables:
|----------|-------------|---------|
| `SPEEDTEST_PORT` | Port to listen on | `9798` |
| `SPEEDTEST_SERVER` | Speedtest server ID to use (optional, auto-selects if not set) | `` |
| `SPEEDTEST_EXPORTER_DEBUG` | Enable debug mode (adds Go runtime and process metrics) | `false` |
| `SPEEDTEST_EXPORTER_DEBUG` | Enable debug mode — any non-empty value enables it (adds Go runtime and process metrics) | `` |

### Example

Expand All @@ -74,22 +74,48 @@ export SPEEDTEST_EXPORTER_DEBUG=1

## Metrics

The exporter provides the following Prometheus metrics:
The exporter provides the following Prometheus metrics. None carry labels.

| Metric | Type | Description |
|--------|------|-------------|
| `speedtest_server_id` | Gauge | Speedtest server ID used for the test |
| `speedtest_jitter_latency_milliseconds` | Gauge | Jitter latency in milliseconds |
| `speedtest_ping_latency_milliseconds` | Gauge | Ping latency in milliseconds |
| `speedtest_download_bits_per_second` | Gauge | Download speed in bits per second |
| `speedtest_upload_bits_per_second` | Gauge | Upload speed in bits per second |
| `speedtest_up` | Gauge | Speedtest up status (`1` = successful, `0` = failed) |
| Metric | Type | Unit | Description |
|--------|------|------|-------------|
| `speedtest_server_id` | Gauge | — | Numeric ID of the speedtest server used for the test |
| `speedtest_jitter_latency_milliseconds` | Gauge | milliseconds | Jitter latency |
| `speedtest_ping_latency_milliseconds` | Gauge | milliseconds | Ping (round-trip) latency |
| `speedtest_download_bits_per_second` | Gauge | bits/second | Download speed |
| `speedtest_upload_bits_per_second` | Gauge | bits/second | Upload speed |
| `speedtest_up` | Gauge | — | `1` if the last speedtest succeeded, `0` if it failed |

### Example output

```
# HELP speedtest_download_bits_per_second Speedtest download speed in bits per second.
# TYPE speedtest_download_bits_per_second gauge
speedtest_download_bits_per_second 9.4e+08
# HELP speedtest_jitter_latency_milliseconds Speedtest jitter latency in milliseconds.
# TYPE speedtest_jitter_latency_milliseconds gauge
speedtest_jitter_latency_milliseconds 0.512
# HELP speedtest_ping_latency_milliseconds Speedtest ping latency in milliseconds.
# TYPE speedtest_ping_latency_milliseconds gauge
speedtest_ping_latency_milliseconds 7.25
# HELP speedtest_server_id Speedtest server ID.
# TYPE speedtest_server_id gauge
speedtest_server_id 12345
# HELP speedtest_up Speedtest up status.
# TYPE speedtest_up gauge
speedtest_up 1
# HELP speedtest_upload_bits_per_second Speedtest upload speed in bits per second.
# TYPE speedtest_upload_bits_per_second gauge
speedtest_upload_bits_per_second 5.2e+08
```

### Failure behaviour

When a speedtest fails (network error, server unreachable, timeout), `speedtest_up` is set to `0`
and all other metrics are set to `0`. Use `speedtest_up == 0` as the signal in alerts — the
zeroed values for speed/latency metrics on failure should be ignored.

When `SPEEDTEST_EXPORTER_DEBUG` is enabled, additional Go runtime metrics are also exposed.
When `SPEEDTEST_EXPORTER_DEBUG` is set, additional Go runtime metrics (`go_*`) and process
metrics (`process_*`) from the standard Prometheus Go client collectors are also exposed.

## Kubernetes Deployment

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/zakame/speedtest-go-exporter

go 1.25
go 1.26

require (
github.com/prometheus/client_golang v1.23.2
Expand Down
17 changes: 16 additions & 1 deletion internal/exporter/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package exporter

import (
"context"
"errors"
"time"

"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -71,7 +72,21 @@ func (se SpeedtestCollector) Collect(ch chan<- prometheus.Metric) {

s, err := se.runner.Run(ctx)
if err != nil {
log.WithError(err).Error("Speedtest failed")
if errors.Is(err, context.DeadlineExceeded) {
log.WithError(err).Error("Speedtest timed out")
} else {
log.WithError(err).Error("Speedtest failed")
}
ch <- prometheus.MustNewConstMetric(server, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(jitter, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(ping, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(downloadSpeed, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(uploadSpeed, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, 0)
return
}
if s == nil {
log.Error("Speedtest returned no result")
ch <- prometheus.MustNewConstMetric(server, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(jitter, prometheus.GaugeValue, 0)
ch <- prometheus.MustNewConstMetric(ping, prometheus.GaugeValue, 0)
Expand Down
Loading