Lumograph is a Go command-line tool that fetches time-series data from a Percona Monitoring and Management (PMM) / VictoriaMetrics endpoint and renders it into high-quality PNG charts. It can also list available services and graph groups, and package/upload rendered charts to Dipper.
- Language: Go (see
go.mod, currently 1.25+) - Logging:
go.uber.org/zap(structured, colored dev logger) - Plotting:
gonum.org/v1/plot - Fonts: Inter (Medium/Bold) TTFs embedded via
//go:embed(resources/fonts/) - Config generation:
gopkg.in/yaml.v3 - Data source: VictoriaMetrics / any Prometheus-compatible
query_rangeAPI
Single main package split across focused files (not a monolith):
main.go— entry point; dispatches to a subcommand'sexecute*function.config.go—LumoConfig, flag sets, flag/env/positional parsing, token & time resolution.errors.go— all sentinel (static) errors, grouped by concern.logger.go—zaplogger setup (debug toggles level/caller/stacktrace).httpclient.go— sharedhttpClient;-insecure-tlsdisables TLS verification.services.go— PMM inventory API (list services, look up a service by name).get_graphs.go—get-graphsorchestration, flag validation, embedded fonts, output paths.graph_fetch.go— builds and executes the VictoriaMetricsquery_rangerequest.graph_render.go— coordinates fetching + plotting into a PNG.graph_plot.go— plot primitives (palette, grids, tickers, series parsing).graph_legend.go— value formatting and the legend table.dipper.go—dipper-sync: tar.gz the PNGs and upload them to Dipper.utils.go— value formatting, snake_case, PromQL variable interpolation, config validation.types.go—VMResponse,GraphConfig,SeriesConfig,TableRow.rebuild-config.go—//go:build ignoregenerator (run viago generate).lumographs.go— generated (// DO NOT EDIT);LumoGraphs map[string][]GraphConfig.
Run lumograph <command> -h for the full flag list.
get-graphs— renders charts for a service. Key flags:-endpoint,-service(required),-groups(comma-separated, required), plus optional-node,-cluster-name,-database,-replset,-outdir,-interval(default5m),-start,-end,-token,-debug,-insecure-tls.list-groups— prints available graph groups to stdout.list-services— lists PMM services (grouped by type, sorted) to stdout. Flags:-endpoint,-token,-debug,-insecure-tls.dipper-sync— compresses a directory of PNGs into a.tar.gzand uploads it to Dipper via multipart POST (X-Dipper-Authheader,project_id+hostnamefields). Flags:-token,-projectid,-hostname(all required) and a single positional argument: the image directory.
-token may be supplied via environment variable instead of the flag; providing
both is an error:
get-graphs/list-services:PMM_TOKENdipper-sync:DIPPER_TOKEN
lumographs.go is generated from the YAML files in resources/graphs/
(os, mysql, pgsql, mongo, valkey). Each YAML dashboard declares a
groups: list and the graph titles to pull; the generator downloads the pinned
upstream PMM Grafana dashboard JSON, extracts each graph's PromQL, and writes the
native Go structs. A graph may belong to multiple groups and is emitted under each
group key in LumoGraphs.
Regenerate after editing YAML (requires network to reach GitHub):
go generate ./...
gofmt -w lumographs.goPromQL expressions use placeholders interpolated at query time (see
interpolateGraphConfig in utils.go): $service_name, $ns_service_name,
$interval, $node_name, $cluster, $replication_set, $set, $rs_nm,
$database.
- Errors: define static sentinel errors in
errors.go; wrap with%w(fmt.Errorf("%w: %w", ErrX, err)). No dynamicerrors.New/fmt.Errorfwithout a wrapped sentinel (enforced byerr113). - Fatal exits: return errors up the stack; let the top-level
execute*/mainpath callzap.S().Fatal*. Avoid fatal calls deep in the stack. - Logging vs. output: diagnostics/logs go to stderr via
zap; machine- readable command output (e.g.list-*) goes to stdout so it can be piped. - HTTP: use the shared
httpClient, always with a context timeout andhttp.NewRequestWithContext. - Formatting:
gofmt/goimportsclean. - Linting: must pass
golangci-lint run ./...with zero issues (config in.golangci.yaml; notecyclopmax complexity 15 andgocriticenable-all). - Simplicity: keep files focused; prefer idiomatic, minimal-dependency code.
go build -o lumograph . # build
golangci-lint run ./... # lint (must be clean)
go generate ./... # regenerate lumographs.go from upstream PMM