Last active
January 28, 2022 08:33
-
-
Save pedro-stanaka/571ca857743b033c6d6de2a64268cd42 to your computer and use it in GitHub Desktop.
Golang example of Prometheus usage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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