first commit

This commit is contained in:
Simon Rieger 2025-03-09 23:25:18 +01:00
commit 1136ac0b01
7 changed files with 212 additions and 0 deletions

1
.env.example Normal file
View file

@ -0,0 +1 @@
TRAEWELLING_TOKEN=

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.env

20
docker-compose.yml Normal file
View file

@ -0,0 +1,20 @@
version: '3.8'
services:
traewelling-metrics:
build:
context: go/.
dockerfile: Dockerfile
#ports:
# - "8080:8080" # Exponiere Port 8080 für Prometheus-Scraping
restart: always # Neustart bei Fehlern oder Updates
environment:
- TRAEWELLING_TOKEN=${TRAEWELLING_TOKEN}
networks:
default:
dns:
ipv4_address: 172.28.0.245
networks:
dns:
external: true

28
go/Dockerfile Normal file
View file

@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1
# Build the application from source
FROM golang:1.21 AS build-stage
WORKDIR /app
COPY * ./
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -o /main
# Run the tests in the container
FROM build-stage AS run-test-stage
RUN go test -v ./...
# Deploy the application binary into a lean image
FROM gcr.io/distroless/base-debian11 AS build-release-stage
WORKDIR /
COPY --from=build-stage /main /main
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/main"]

17
go/go.mod Normal file
View file

@ -0,0 +1,17 @@
module traewelling-exporter
go 1.19
require github.com/prometheus/client_golang v1.21.1
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.28.0 // indirect
google.golang.org/protobuf v1.36.1 // indirect
)

26
go/go.sum Normal file
View file

@ -0,0 +1,26 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

119
go/main.go Normal file
View file

@ -0,0 +1,119 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
totalTrips = promauto.NewGauge(prometheus.GaugeOpts{
Name: "traewelling_total_trips",
Help: "Gesamtanzahl der Fahrten",
})
totalDistance = promauto.NewGauge(prometheus.GaugeOpts{
Name: "traewelling_total_distance_km",
Help: "Gesamtdistanz aller Fahrten in Kilometern",
})
averageDuration = promauto.NewGauge(prometheus.GaugeOpts{
Name: "traewelling_average_duration_minutes",
Help: "Durchschnittliche Dauer der Fahrten in Minuten",
})
)
type DashboardResponse struct {
Data []struct {
ID int `json:"id"`
Train struct {
Trip int `json:"trip"`
Distance float64 `json:"distance"`
Duration int `json:"duration"`
Category string `json:"category"`
LineName string `json:"lineName"`
JourneyNumber int `json:"journeyNumber"`
Origin struct {
Name string `json:"name"`
} `json:"origin"`
Destination struct {
Name string `json:"name"`
} `json:"destination"`
} `json:"train"`
} `json:"data"`
}
func fetchTraewellingData() (*DashboardResponse, error) {
url := "https://traewelling.de/api/v1/dashboard"
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("Fehler beim Erstellen der Anfrage: %v", err)
}
token := os.Getenv("TRAEWELLING_TOKEN")
if token == "" {
return nil, fmt.Errorf("TRAEWELLING_TOKEN ist nicht gesetzt")
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Fehler beim Senden der Anfrage: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Fehlerhafte Antwort: %d", resp.StatusCode)
}
var apiResponse DashboardResponse
err = json.NewDecoder(resp.Body).Decode(&apiResponse)
if err != nil {
return nil, fmt.Errorf("Fehler beim Parsen der JSON-Daten: %v", err)
}
return &apiResponse, nil
}
func updateMetrics() {
data, err := fetchTraewellingData()
if err != nil {
fmt.Printf("Fehler beim Abrufen der Daten: %v\n", err)
return
}
var totalTripsCount int
var totalDistanceSum float64
var totalDurationSum int
for _, trip := range data.Data {
totalTripsCount++
totalDistanceSum += trip.Train.Distance / 1000
totalDurationSum += trip.Train.Duration
}
totalTrips.Set(float64(totalTripsCount))
totalDistance.Set(totalDistanceSum)
if totalTripsCount > 0 {
averageDuration.Set(float64(totalDurationSum) / float64(totalTripsCount))
}
}
func main() {
go func() {
for {
updateMetrics()
time.Sleep(30 * time.Second)
}
}()
http.Handle("/metrics", promhttp.Handler())
fmt.Println("Server läuft auf Port 8080...")
http.ListenAndServe(":8080", nil)
}