Skip to content

Commit

Permalink
Merge pull request #62 from tgeoghegan/prometheus
Browse files Browse the repository at this point in the history
Add Prometheus metric scrapes
  • Loading branch information
bwesterb authored May 29, 2024
2 parents 1d8081f + 1fc437f commit 954b26e
Show file tree
Hide file tree
Showing 495 changed files with 223,687 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.pem
app-gateway-go
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ require (
google.golang.org/protobuf v1.33.0
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
)

require (
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/prometheus/client_golang v1.19.1
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/sys v0.17.0 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ github.com/DataDog/datadog-go/v5 v5.1.1 h1:JLZ6s2K1pG2h9GkvEvMdEGqMDyVLEAccdX5Tl
github.com/DataDog/datadog-go/v5 v5.1.1/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
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/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chris-wood/ohttp-go v0.0.0-20230523152405-45fb0d05eb13 h1:6KPUTuaINL/GlEf3Fd08p/JVVoVRX4Mh4GtsAJUKv7o=
github.com/chris-wood/ohttp-go v0.0.0-20230523152405-45fb0d05eb13/go.mod h1:P/sVWl8F9KHJ1esPj/g1A5h8vfA3Ps9n6JOMNf6TszU=
github.com/cloudflare/circl v1.3.3-0.20230418220640-795540340d5c/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
Expand All @@ -18,6 +22,14 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -64,6 +76,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
Expand Down
50 changes: 32 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const (
gatewayVerboseEnvironmentVariable = "VERBOSE"
logSecretsEnvironmentVariable = "LOG_SECRETS"
targetRewritesVariables = "TARGET_REWRITES"
prometheusConfigVariable = "PROMETHEUS_CONFIG"
)

var versionFlag = flag.Bool("version", false, "print name and version to stdout")
Expand Down Expand Up @@ -198,8 +199,6 @@ func main() {
debugResponse := getBoolEnv(gatewayDebugEnvironmentVariable, false)
verbose := getBoolEnv(gatewayVerboseEnvironmentVariable, false)

monitoringServiceName := getStringEnv(monitoringServiceNameEnvironmentVariable, defaultMonitoringServiceName)

configID := uint8(getUintEnv(configurationIdEnvironmentVariable, 0))
config, err := ohttp.NewConfigFromSeed(configID, hpke.KEM_X25519_KYBER768_DRAFT00, hpke.KDF_HKDF_SHA256, hpke.AEAD_AES128GCM, seed)
if err != nil {
Expand Down Expand Up @@ -263,23 +262,38 @@ func main() {
}

// Configure metrics
metricsHost := os.Getenv(statsdHostVariable)
metricsPort := os.Getenv(statsdPortVariable)
metricsTimeout, err := strconv.ParseInt(os.Getenv(statsdTimeoutVariable), 10, 64)
if err != nil {
log.Printf("Failed parsing metrics timeout: %s", err)
metricsTimeout = 100
}
client, err := createStatsDClient(metricsHost, metricsPort, int(metricsTimeout))
if err != nil {
log.Fatalf("Failed to create statsd client: %s", err)
}
defer client.Close()
var metricsFactory MetricsFactory

if prometheusConfigJSON := os.Getenv(prometheusConfigVariable); prometheusConfigJSON != "" {
var prometheusConfig PrometheusConfig
if err := json.Unmarshal([]byte(prometheusConfigJSON), &prometheusConfig); err != nil {
log.Fatalf("Failed to parse Prometheus config: %s", err)
}

metricsFactory, err = NewPrometheusMetricsFactory(prometheusConfig)
if err != nil {
log.Fatalf("Failed to configure Prometheus metrics: %s", err)
}
} else {
// Default to StatsD metrics
monitoringServiceName := getStringEnv(monitoringServiceNameEnvironmentVariable, defaultMonitoringServiceName)
metricsHost := os.Getenv(statsdHostVariable)
metricsPort := os.Getenv(statsdPortVariable)
metricsTimeout, err := strconv.ParseInt(getStringEnv(statsdTimeoutVariable, "100"), 10, 64)
if err != nil {
log.Fatalf("Failed parsing metrics timeout: %s", err)
}
client, err := createStatsDClient(metricsHost, metricsPort, int(metricsTimeout))
if err != nil {
log.Fatalf("Failed to create statsd client: %s", err)
}
defer client.Close()

metricsFactory := &StatsDMetricsFactory{
serviceName: monitoringServiceName,
metricsName: "ohttp_gateway_duration",
client: client,
metricsFactory = &StatsDMetricsFactory{
serviceName: monitoringServiceName,
metricsName: "ohttp_gateway_duration",
client: client,
}
}

// Load endpoint configuration defaults
Expand Down
94 changes: 94 additions & 0 deletions prometheus_metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) 2024 Cloudflare, Inc. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause

package main

import (
"errors"
"fmt"
"log"
"net"
"net/http"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

type PrometheusConfig struct {
Host string
Port string
ScrapePath string
MetricName string
}

type PrometheusMetrics struct {
startedAt time.Time
histogram prometheus.ObserverVec
}

func (p *PrometheusMetrics) Fire(result string) {
observer := p.histogram.With(prometheus.Labels{
"method": "unknown",
"status": "unknown",
"result": result,
})
p.observe(observer)
}

func (p *PrometheusMetrics) ResponseStatus(method string, status int) {
observer := p.histogram.With(prometheus.Labels{
"method": method,
"status": fmt.Sprint(status),
"result": "unknown",
})
p.observe(observer)
}

func (p *PrometheusMetrics) observe(observer prometheus.Observer) {
elapsed := time.Now().Sub(p.startedAt)
observer.Observe(float64(elapsed.Milliseconds()))
}

type PrometheusMetricsFactory struct {
metricName string
}

func NewPrometheusMetricsFactory(config PrometheusConfig) (MetricsFactory, error) {
serveMux := http.NewServeMux()
serveMux.Handle(config.ScrapePath, promhttp.Handler())
server := http.Server{
Addr: net.JoinHostPort(config.Host, config.Port),
Handler: serveMux,
}

go func() {
log.Printf("Listening for Prometheus scrapes on %s:%s\n", config.Host, config.Port)
log.Fatal(server.ListenAndServe())
}()

return &PrometheusMetricsFactory{metricName: config.MetricName}, nil
}

func (p PrometheusMetricsFactory) Create(eventName string) Metrics {
histogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: p.metricName,
}, []string{"eventName", "status", "method", "result"})

if err := prometheus.Register(histogram); err != nil {
are := &prometheus.AlreadyRegisteredError{}
if errors.As(err, are) {
// Use previously registered metric collector
histogram = are.ExistingCollector.(*prometheus.HistogramVec)
} else {
// There's no other reason prometheus.Register should fail and the interface won't let
// us return an error.
panic(err)
}
}

return &PrometheusMetrics{
startedAt: time.Now(),
histogram: histogram.MustCurryWith(prometheus.Labels{"eventName": eventName}),
}
}
3 changes: 2 additions & 1 deletion statsd_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"net"
"time"

"github.com/DataDog/datadog-go/v5/statsd"
Expand Down Expand Up @@ -34,7 +35,7 @@ func createStatsDClient(host, port string, timeout int) (statsd.ClientInterface,
return &statsd.NoOpClient{}, nil
}

return statsd.New(host+":"+port, statsd.WithWriteTimeout(time.Duration(timeout)*time.Millisecond), statsd.WithoutTelemetry())
return statsd.New(net.JoinHostPort(host, port), statsd.WithWriteTimeout(time.Duration(timeout)*time.Millisecond), statsd.WithoutTelemetry())
}

type StatsDMetricsFactory struct {
Expand Down
20 changes: 20 additions & 0 deletions vendor/github.com/beorn7/perks/LICENSE

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

Loading

0 comments on commit 954b26e

Please sign in to comment.