From c7ce4675084313cb4e0932ac7233953a4d19d55d Mon Sep 17 00:00:00 2001 From: Russell Troxel Date: Wed, 7 Jun 2023 14:21:35 -0700 Subject: [PATCH] Add AppInfo metric & automatic build time app info population (#178) --- .github/workflows/ci.yml | 24 +++++++++++++++------ .goreleaser.yml | 4 ++++ Dockerfile | 7 +++++- cmd/exportarr/main.go | 14 +++++++++++- internal/commands/arr.go | 10 ++++----- internal/commands/root.go | 42 +++++++++++++++++++++++++++++++----- internal/commands/sabnzbd.go | 2 +- 7 files changed, 84 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17f4677..4d9dfd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,10 +39,18 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Prepare - id: prep - run: | - echo ::set-output name=version::${GITHUB_REF##*/} + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ghcr.io/${{ github.repository }} + flavor: | + latest=true + prefix=v + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -73,5 +81,9 @@ jobs: file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true - tags: | - ghcr.io/${{ github.repository_owner }}/exportarr:${{ steps.prep.outputs.version }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ steps.meta.outputs.version }} + BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} + REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }} diff --git a/.goreleaser.yml b/.goreleaser.yml index 0c0c898..3b25967 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -7,6 +7,10 @@ archives: builds: - main: "./cmd/exportarr" binary: "exportarr" + + ldflags: + - -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} -X main.buildTime={{.CommitTimestamp}} + goarch: - "386" - "amd64" diff --git a/Dockerfile b/Dockerfile index 8b0a9bb..eddf7a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,11 @@ FROM golang:1.20.5-alpine as builder ARG TARGETOS ARG TARGETARCH ARG TARGETVARIANT="" + +ARG VERSION="development" +ARG BUILDTIME="" +ARG REVISION="" + ENV GO111MODULE=on \ CGO_ENABLED=0 \ GOOS=${TARGETOS} \ @@ -11,7 +16,7 @@ RUN apk add --no-cache ca-certificates tini-static \ && update-ca-certificates WORKDIR /build COPY . . -RUN go build -a -tags netgo -ldflags '-w -extldflags "-static"' -o exportarr /build/cmd/exportarr/. +RUN go build -a -tags netgo -ldflags "-w -extldflags '-static' -X main.version=${VERSION} -X main.buildTime=${BUILDTIME} -X main.revision=${REVISION}" -o exportarr /build/cmd/exportarr/. FROM gcr.io/distroless/static:nonroot ENV PORT="9707" diff --git a/cmd/exportarr/main.go b/cmd/exportarr/main.go index ee4608e..04cfa29 100644 --- a/cmd/exportarr/main.go +++ b/cmd/exportarr/main.go @@ -2,6 +2,18 @@ package main import "github.com/onedr0p/exportarr/internal/commands" +var ( + appName = "exportarr" + version = "development" + buildTime = "" + revision = "" +) + func main() { - commands.Execute() + commands.Execute(commands.AppInfo{ + Name: appName, + Version: version, + BuildTime: buildTime, + Revision: revision, + }) } diff --git a/internal/commands/arr.go b/internal/commands/arr.go index fd306de..9d2fbb9 100644 --- a/internal/commands/arr.go +++ b/internal/commands/arr.go @@ -49,7 +49,7 @@ var radarrCmd = &cobra.Command{ c.ApiVersion = "v3" UsageOnError(cmd, c.Validate()) - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister( collector.NewRadarrCollector(c), collector.NewQueueCollector(c), @@ -76,7 +76,7 @@ var sonarrCmd = &cobra.Command{ c.ApiVersion = "v3" UsageOnError(cmd, c.Validate()) - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister( collector.NewSonarrCollector(c), collector.NewQueueCollector(c), @@ -102,7 +102,7 @@ var lidarrCmd = &cobra.Command{ c.ApiVersion = "v1" UsageOnError(cmd, c.Validate()) - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister( collector.NewLidarrCollector(c), collector.NewQueueCollector(c), @@ -129,7 +129,7 @@ var readarrCmd = &cobra.Command{ c.ApiVersion = "v1" UsageOnError(cmd, c.Validate()) - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister( collector.NewReadarrCollector(c), collector.NewQueueCollector(c), @@ -161,7 +161,7 @@ var prowlarrCmd = &cobra.Command{ UsageOnError(cmd, c.Validate()) UsageOnError(cmd, c.Prowlarr.Validate()) - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister( collector.NewProwlarrCollector(c), collector.NewHistoryCollector(c), diff --git a/internal/commands/root.go b/internal/commands/root.go index 2b305ed..79a6309 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -22,8 +22,8 @@ import ( var GRACEFUL_TIMEOUT = 5 * time.Second var ( - conf = &config.Config{} - + conf = &config.Config{} + appInfo = &AppInfo{} rootCmd = &cobra.Command{ Use: "exportarr", Short: "exportarr is a AIO Prometheus exporter for *arr applications", @@ -36,7 +36,15 @@ More information available at the Github Repo (https://github.com/onedr0p/export } ) -func Execute() error { +type AppInfo struct { + Name string + Version string + BuildTime string + Revision string +} + +func Execute(a AppInfo) error { + appInfo = &a return rootCmd.Execute() } @@ -83,7 +91,13 @@ func initLogger() { } atom.SetLevel(lvl) - zap.S().Debug("Logger initialized") + zap.S().Infow( + fmt.Sprintf("Starting %s", appInfo.Name), + "app_name", appInfo.Name, + "version", appInfo.Version, + "buildTime", appInfo.BuildTime, + "revision", appInfo.Revision, + ) } func finalizeLogger() { @@ -91,7 +105,7 @@ func finalizeLogger() { zap.S().Sync() } -type registerFunc func(registry *prometheus.Registry) +type registerFunc func(registry prometheus.Registerer) func serveHttp(fn registerFunc) { var srv http.Server @@ -116,6 +130,7 @@ func serveHttp(fn registerFunc) { }() registry := prometheus.NewRegistry() + registerAppInfoMetric(registry) fn(registry) mux := http.NewServeMux() @@ -140,3 +155,20 @@ func serveHttp(fn registerFunc) { } <-idleConnsClosed } + +func registerAppInfoMetric(registry prometheus.Registerer) { + registry.MustRegister(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Namespace: appInfo.Name, + Name: "app_info", + Help: "A metric with a constant '1' value labeled by app name, version, build time, and revision.", + ConstLabels: prometheus.Labels{ + "app_name": appInfo.Name, + "version": appInfo.Version, + "build_time": appInfo.BuildTime, + "revision": appInfo.Revision, + }, + }, + func() float64 { return 1 }, + )) +} diff --git a/internal/commands/sabnzbd.go b/internal/commands/sabnzbd.go index d5dd3e1..232852d 100644 --- a/internal/commands/sabnzbd.go +++ b/internal/commands/sabnzbd.go @@ -29,7 +29,7 @@ var sabnzbdCmd = &cobra.Command{ if err != nil { return err } - serveHttp(func(r *prometheus.Registry) { + serveHttp(func(r prometheus.Registerer) { r.MustRegister(collector) }) return nil