Skip to content

Instantly share code, notes, and snippets.

@maisnamraju
Forked from creack/main.go
Created June 27, 2019 10:53
Show Gist options
  • Select an option

  • Save maisnamraju/34a88f307105b01b43324b61b74e15fa to your computer and use it in GitHub Desktop.

Select an option

Save maisnamraju/34a88f307105b01b43324b61b74e15fa to your computer and use it in GitHub Desktop.
A simple golang web server with basic logging, tracing, health check, graceful shutdown and zero dependencies
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"sync/atomic"
"syscall"
"time"
)
var healthy int64
func shutdown(quit <-chan os.Signal, server *http.Server) {
<-quit
server.ErrorLog.Printf("Server is shutting down...\n")
atomic.StoreInt64(&healthy, 0)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.SetKeepAlivesEnabled(false)
if err := server.Shutdown(ctx); err != nil {
server.ErrorLog.Fatalf("Could not gracefully shutdown the server: %s\n", err)
}
}
func main() {
listenAddr := ":5000"
if len(os.Args) == 2 {
listenAddr = os.Args[1]
}
logger := log.New(os.Stdout, "http: ", log.LstdFlags)
logger.Printf("Server is starting...")
router := http.NewServeMux()
router.HandleFunc("/", index)
router.HandleFunc("/healthz", healthz)
server := &http.Server{
Addr: listenAddr,
Handler: tracing(logging(router, logger), func() string { return strconv.FormatInt(time.Now().UnixNano(), 36) }),
ErrorLog: logger,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go shutdown(quit, server)
logger.Printf("Server is ready to handle requests at %q\n", listenAddr)
atomic.StoreInt64(&healthy, time.Now().UnixNano())
if err := server.ListenAndServe(); err != http.ErrServerClosed {
logger.Fatalf("Could not listen on %q: %s\n", listenAddr, err)
}
logger.Printf("Server stopped\n")
}
func index(w http.ResponseWriter, req *http.Request) {
if req.URL.Path != "/" {
http.NotFound(w, req)
return
}
fmt.Fprintf(w, "Hello, World!\n")
}
func healthz(w http.ResponseWriter, req *http.Request) {
h := atomic.LoadInt64(&healthy)
if h == 0 {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
fmt.Fprintf(w, "uptime: %s\n", time.Since(time.Unix(0, h)))
}
func logging(hdlr http.Handler, logger *log.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func(start time.Time) {
requestID := w.Header().Get("X-Request-Id")
if requestID == "" {
requestID = "unknown"
}
logger.Println(requestID, req.Method, req.URL.Path, req.RemoteAddr, req.UserAgent(), time.Since(start))
}(time.Now())
hdlr.ServeHTTP(w, req)
})
}
func tracing(hdlr http.Handler, nextRequestID func() string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
requestID := req.Header.Get("X-Request-Id")
if requestID == "" {
requestID = nextRequestID()
}
w.Header().Set("X-Request-Id", requestID)
hdlr.ServeHTTP(w, req)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment