Server

The Go CDK’s server package provides a pre-configured HTTP server with diagnostic hooks for request logging, health checks, and trace exporting via OpenCensus. This guide will show you how to start up and shut down the server, as well as how to work with the request logging and health checks.

Starting up the server🔗

The Go CDK Server constructor takes an http.Handler and an Options struct. The simplest way to start the server is to use http.DefaultServeMux and pass nil for the options.

import (
	"fmt"
	"net/http"

	"gocloud.dev/server"
)

// Use the constructor function to create the server.
srv := server.New(http.DefaultServeMux, nil)

// Register a route.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!")
})

// Start the server. If ListenAndServe returns an error, print it and exit.
if err := srv.ListenAndServe(":8080"); err != nil {
	log.Fatalf("%v", err)
}

Adding a request logger🔗

You can use the server.Options struct to specify a request logger.

The example is shown with the Go CDK requestlog package’s NCSALogger. To get logs in the Stackdriver JSON format, use NewStackdriverLogger in place of NewNCSALogger.

import (
	"fmt"
	"net/http"
	"os"

	"gocloud.dev/server"
	"gocloud.dev/server/requestlog"
)

// Create a logger, and assign it to the RequestLogger field of a
// server.Options struct.
srvOptions := &server.Options{
	RequestLogger: requestlog.NewNCSALogger(os.Stdout, func(error) {}),
}

// Pass the options to the Server constructor.
srv := server.New(http.DefaultServeMux, srvOptions)

// Register a route.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!")
})

// Start the server. You will see requests logged to STDOUT.
if err := srv.ListenAndServe(":8080"); err != nil {
	log.Fatalf("%v", err)
}

Adding health checks🔗

The Go CDK server package affords a hook for you to define health checks for your application and see the results at /healthz/readiness. The server also runs an endpoint at /healthz/liveness, which is a conventional name for a liveness check and is where Kubernetes, if you are using it, will look.

Health checks are an important part of application monitoring, and readiness checks are subtly different than liveness checks. The liveness check will return 200 OK if the server can serve requests. But because each application may have a different definition of what it means to be “healthy” (perhaps your application has a dependency on a back end service), you will need to define a concrete type to implement the health.Checker interface and define a CheckHealth method specific to your application for readiness checks.

// customHealthCheck is an example health check. It implements the
// health.Checker interface and reports the server is healthy when the healthy
// field is set to true.
type customHealthCheck struct {
	mu      sync.RWMutex
	healthy bool
}

// customHealthCheck implements the health.Checker interface because it has a
// CheckHealth method.
func (h *customHealthCheck) CheckHealth() error {
	h.mu.RLock()
	defer h.mu.RUnlock()
	if !h.healthy {
		return errors.New("not ready yet!")
	}
	return nil
}
import (
	"fmt"
	"net/http"
	"time"

	"gocloud.dev/server"
	"gocloud.dev/server/health"
)

// Create a health.Checker from the type we defined for our application.
// In this example, healthCheck will report the server is unhealthy for 10 seconds
// after startup, and as healthy henceforth. Check the /healthz/readiness
// HTTP path to see readiness.
healthCheck := new(customHealthCheck)
time.AfterFunc(10*time.Second, func() {
	healthCheck.mu.Lock()
	defer healthCheck.mu.Unlock()
	healthCheck.healthy = true
})

// The server.Options struct takes a slice of health checks, because you
// may need to check several things.
srvOptions := &server.Options{
	HealthChecks: []health.Checker{healthCheck},
}

// Pass the options to the Server constructor.
srv := server.New(http.DefaultServeMux, srvOptions)

// Register a route.
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!")
})

// Start the server. You will see requests logged to STDOUT.
if err := srv.ListenAndServe(":8080"); err != nil {
	log.Fatalf("%v", err)
}