Skip to content

Commit

Permalink
Provide adapters for mailgun and modernprogram.
Browse files Browse the repository at this point in the history
  • Loading branch information
udhos committed Jan 15, 2024
1 parent 27e24c1 commit 8ea8193
Show file tree
Hide file tree
Showing 11 changed files with 725 additions and 29 deletions.
119 changes: 118 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,118 @@
# Prometheus Groupcache exporter
[![license](http:https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/udhos/groupcache_exporter/blob/main/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/udhos/groupcache_exporter)](https://goreportcard.com/report/github.com/udhos/groupcache_exporter)
[![Go Reference](https://pkg.go.dev/badge/github.com/udhos/groupcache_exporter.svg)](https://pkg.go.dev/github.com/udhos/groupcache_exporter)

# Prometheus Groupcache exporter

This exporter implements the prometheus.Collector interface in order to expose Prometheus metrics for [groupcache](https://github.com/golang/groupcache).

# Example for mailgun groupcache

```golang
import "github.com/grafana/groupcache_exporter/groupcache/mailgun"

// ...

appName := filepath.Base(os.Args[0])

cache := startGroupcache()

//
// expose prometheus metrics
//
{
metricsRoute := "/metrics"
metricsPort := ":3000"

log.Printf("starting metrics server at: %s %s", metricsPort, metricsRoute)

mailgun := mailgun.New(cache)
labels := map[string]string{
"app": appName,
}
namespace := ""
collector := groupcache_exporter.NewExporter(namespace, labels, mailgun)

prometheus.MustRegister(collector)

go func() {
http.Handle(metricsRoute, promhttp.Handler())
log.Fatal(http.ListenAndServe(metricsPort, nil))
}()
}
```

Full example: [./examples/groupcache/mailgun](./examples/groupcache/mailgun)

# Example for modernprogram groupcache

```golang
import "github.com/grafana/groupcache_exporter/groupcache/modernprogram"

// ...

appName := filepath.Base(os.Args[0])

cache := startGroupcache()

//
// expose prometheus metrics
//
{
metricsRoute := "/metrics"
metricsPort := ":3000"

log.Printf("starting metrics server at: %s %s", metricsPort, metricsRoute)

modernprogram := modernprogram.New(cache)
labels := map[string]string{
"app": appName,
}
namespace := ""
collector := groupcache_exporter.NewExporter(namespace, labels, modernprogram)

prometheus.MustRegister(collector)

go func() {
http.Handle(metricsRoute, promhttp.Handler())
log.Fatal(http.ListenAndServe(metricsPort, nil))
}()
}
```

Full example: [./examples/groupcache/modernprogram](./examples/groupcache/modernprogram)

# Testing

## Build

go install ./...

## Run example application

groupcache-exporter-mailgun

## Query the metrics endpoint

```bash
curl -s localhost:3000/metrics | grep -E ^groupcache
groupcache_cache_bytes{app="groupcache-exporter-mailgun",group="files",type="hot"} 0
groupcache_cache_bytes{app="groupcache-exporter-mailgun",group="files",type="main"} 2954
groupcache_cache_evictions_total{app="groupcache-exporter-mailgun",group="files",type="hot"} 0
groupcache_cache_evictions_total{app="groupcache-exporter-mailgun",group="files",type="main"} 1
groupcache_cache_gets_total{app="groupcache-exporter-mailgun",group="files",type="hot"} 4
groupcache_cache_gets_total{app="groupcache-exporter-mailgun",group="files",type="main"} 16
groupcache_cache_hits_total{app="groupcache-exporter-mailgun",group="files",type="hot"} 0
groupcache_cache_hits_total{app="groupcache-exporter-mailgun",group="files",type="main"} 12
groupcache_cache_items{app="groupcache-exporter-mailgun",group="files",type="hot"} 0
groupcache_cache_items{app="groupcache-exporter-mailgun",group="files",type="main"} 1
groupcache_gets_total{app="groupcache-exporter-mailgun",group="files"} 14
groupcache_hits_total{app="groupcache-exporter-mailgun",group="files"} 12
groupcache_loads_deduped_total{app="groupcache-exporter-mailgun",group="files"} 2
groupcache_loads_total{app="groupcache-exporter-mailgun",group="files"} 2
groupcache_local_load_errs_total{app="groupcache-exporter-mailgun",group="files"} 0
groupcache_local_load_total{app="groupcache-exporter-mailgun",group="files"} 2
groupcache_peer_errors_total{app="groupcache-exporter-mailgun",group="files"} 0
groupcache_peer_loads_total{app="groupcache-exporter-mailgun",group="files"} 0
groupcache_server_requests_total{app="groupcache-exporter-mailgun",group="files"} 0
```
23 changes: 23 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

go install golang.org/x/vuln/cmd/govulncheck@latest

gofmt -s -w .

revive ./...

gocyclo -over 15 .

go mod tidy

govulncheck ./...

go env -w CGO_ENABLED=1

go test -race ./...

go env -w CGO_ENABLED=0

go install ./...

go env -u CGO_ENABLED
71 changes: 71 additions & 0 deletions examples/groupcache-exporter-mailgun/groupcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"context"
"log"
"net/http"
"os"
"time"

"github.com/mailgun/groupcache/v2"
)

func startGroupcache() *groupcache.Group {

ttl := time.Minute

log.Printf("groupcache ttl: %v", ttl)

//
// create groupcache pool
//

groupcachePort := ":5000"

myURL := "http:https://127.0.0.1" + groupcachePort

log.Printf("groupcache my URL: %s", myURL)

pool := groupcache.NewHTTPPoolOpts(myURL, &groupcache.HTTPPoolOptions{})

//
// start groupcache server
//

serverGroupCache := &http.Server{Addr: groupcachePort, Handler: pool}

go func() {
log.Printf("groupcache server: listening on %s", groupcachePort)
err := serverGroupCache.ListenAndServe()
log.Printf("groupcache server: exited: %v", err)
}()

pool.Set(myURL)

//
// create cache
//

var groupcacheSizeBytes int64 = 1_000_000

// https://talks.golang.org/2013/oscon-dl.slide#46
//
// 64 MB max per-node memory usage
cache := groupcache.NewGroup("files", groupcacheSizeBytes, groupcache.GetterFunc(
func(ctx context.Context, key string, dest groupcache.Sink) error {

log.Printf("getter: loading: key:%s, ttl:%v", key, ttl)

data, errFile := os.ReadFile(key)
if errFile != nil {
return errFile
}

expire := time.Now().Add(ttl)
dest.SetBytes(data, expire)

return nil
}))

return cache
}
62 changes: 62 additions & 0 deletions examples/groupcache-exporter-mailgun/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Package main implements the example.
package main

import (
"context"
"log"
"net/http"
"os"
"path/filepath"
"time"

"github.com/grafana/groupcache_exporter"
"github.com/grafana/groupcache_exporter/groupcache/mailgun"
"github.com/mailgun/groupcache/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {

appName := filepath.Base(os.Args[0])

cache := startGroupcache()

//
// expose prometheus metrics
//
{
metricsRoute := "/metrics"
metricsPort := ":3000"

log.Printf("starting metrics server at: %s %s", metricsPort, metricsRoute)

mailgun := mailgun.New(cache)
labels := map[string]string{
"app": appName,
}
namespace := ""
collector := groupcache_exporter.NewExporter(namespace, labels, mailgun)

prometheus.MustRegister(collector)

go func() {
http.Handle(metricsRoute, promhttp.Handler())
log.Fatal(http.ListenAndServe(metricsPort, nil))
}()
}

//
// query cache periodically
//

const interval = 5 * time.Second

for {
var dst []byte
cache.Get(context.TODO(), "/etc/passwd", groupcache.AllocatingByteSliceSink(&dst))
log.Printf("cache answer: %d bytes, sleeping %v", len(dst), interval)
time.Sleep(interval)
}

}
71 changes: 71 additions & 0 deletions examples/groupcache-exporter-modernprogram/groupcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"context"
"log"
"net/http"
"os"
"time"

"github.com/modernprogram/groupcache/v2"
)

func startGroupcache() *groupcache.Group {

ttl := time.Minute

log.Printf("groupcache ttl: %v", ttl)

//
// create groupcache pool
//

groupcachePort := ":5000"

myURL := "http:https://127.0.0.1" + groupcachePort

log.Printf("groupcache my URL: %s", myURL)

pool := groupcache.NewHTTPPoolOpts(myURL, &groupcache.HTTPPoolOptions{})

//
// start groupcache server
//

serverGroupCache := &http.Server{Addr: groupcachePort, Handler: pool}

go func() {
log.Printf("groupcache server: listening on %s", groupcachePort)
err := serverGroupCache.ListenAndServe()
log.Printf("groupcache server: exited: %v", err)
}()

pool.Set(myURL)

//
// create cache
//

var groupcacheSizeBytes int64 = 1_000_000

// https://talks.golang.org/2013/oscon-dl.slide#46
//
// 64 MB max per-node memory usage
cache := groupcache.NewGroup("files", groupcacheSizeBytes, groupcache.GetterFunc(
func(ctx context.Context, key string, dest groupcache.Sink) error {

log.Printf("getter: loading: key:%s, ttl:%v", key, ttl)

data, errFile := os.ReadFile(key)
if errFile != nil {
return errFile
}

expire := time.Now().Add(ttl)
dest.SetBytes(data, expire)

return nil
}))

return cache
}
Loading

0 comments on commit 8ea8193

Please sign in to comment.