Skip to content

Instantly share code, notes, and snippets.

@pedro-stanaka
Last active January 28, 2022 08:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pedro-stanaka/571ca857743b033c6d6de2a64268cd42 to your computer and use it in GitHub Desktop.
Save pedro-stanaka/571ca857743b033c6d6de2a64268cd42 to your computer and use it in GitHub Desktop.
Golang example of Prometheus usage
package main
import (
"context"
"fmt"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promauto"
"log"
"net/http"
"os"
"os/signal"
"sync/atomic"
"syscall"
"time"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type key int
const (
requestIDKey key = 0
)
var (
listenAddr string
healthy int32
)
func main() {
// All apps in prod platform must obey to PORT env var.
port := os.Getenv("PORT")
if port == "" {
port = "3001"
}
logger := log.New(os.Stdout, "http: ", log.LstdFlags)
logger.Println("Server is starting...")
// Prometheus metrics registry
re := prometheus.NewRegistry()
// Registerer with prefix enabled, use this one to register all your metrics.
reg := prometheus.WrapRegistererWithPrefix("my_app_name_", re)
// Register default golang collectors (optional, only if needed)
reg.MustRegister(
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
)
reqCount := promauto.With(reg).NewCounter(
prometheus.CounterOpts{
Name: "request_count",
Help: "Total number of requests served.",
},
)
router := http.NewServeMux()
router.Handle("/", index())
router.Handle("/healthz", healthz())
router.Handle("/metrics", promhttp.HandlerFor(re, promhttp.HandlerOpts{Registry: re}))
server := &http.Server{
Addr: fmt.Sprintf(":%s", port),
Handler: requestCounter(reqCount)(logging(logger)(router)),
ErrorLog: logger,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
done := make(chan bool)
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-quit
logger.Println("Server is shutting down...")
atomic.StoreInt32(&healthy, 0)
const gracefulShutdownTimeout = 10 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), gracefulShutdownTimeout)
defer cancel()
server.SetKeepAlivesEnabled(false)
if err := server.Shutdown(ctx); err != nil {
logger.Fatalf("Could not gracefully shutdown the server: %v\n", err)
}
close(done)
}()
logger.Println("Server is ready to handle requests at", listenAddr)
atomic.StoreInt32(&healthy, 1)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Fatalf("Could not listen on %s: %v\n", listenAddr, err)
}
<-done
logger.Println("Server stopped")
}
func index() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello, World!")
})
}
func healthz() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&healthy) == 1 {
w.WriteHeader(http.StatusNoContent)
return
}
w.WriteHeader(http.StatusServiceUnavailable)
})
}
func logging(logger *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
requestID, ok := r.Context().Value(requestIDKey).(string)
if !ok {
requestID = "unknown"
}
logger.Println(requestID, r.Method, r.URL.Path, r.RemoteAddr, r.UserAgent())
}()
next.ServeHTTP(w, r)
})
}
}
func requestCounter(reqCount prometheus.Counter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqCount.Inc()
next.ServeHTTP(w, r)
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment