Documentation ¶
Overview ¶
Package webserver provides a HTTP server: - Gracefully handles shutdown - Applies best practices such as setting up timeouts - Routing powered by Gorilla Mux - HTTP server powered by Go built-in HTTP server - Observability is first-class:
- Logging powered Sypl
- Telemetry powered by Open Telemetry
- Metrics powered by ExpVar
- Built-in useful handlers such as liveness, and readiness.
Index ¶
- Variables
- type IServer
- type Logging
- type Option
- func WithLoggingOptions(console, request, filepath string) Option
- func WithMetrics(name string, v interface{}) Option
- func WithMetricsRaw(name string, metrics expvar.Var) Option
- func WithPreLoadedHandlers(handlers ...handler.Handler) Option
- func WithReadiness(readinessFunc handler.ReadinessFunc) Option
- func WithTelemetry(t *telemetry.Telemetry) Option
- func WithTimeout(read, request, inflight, tasks, write time.Duration) Option
- func WithoutLogging() Option
- func WithoutMetrics() Option
- func WithoutPreLoadedHandlers() Option
- func WithoutTelemetry() Option
- type Server
- type Timeout
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrRequesTimeout = customerror.NewFailedToError( "finish request, timed out", customerror.WithStatusCode(http.StatusRequestTimeout), )
Functions ¶
This section is empty.
Types ¶
type IServer ¶
type IServer interface { // GetLogger returns the server logger. GetLogger() sypl.ISypl // GetRouter returns the server router. GetRouter() *mux.Router GetTelemetry() telemetry.ITelemetry // Start the server. Start() error }
IServer defines what a server does.
func New ¶
New is the web server factory. It returns a web server with observability: - Metrics: `cmdline`, `memstats`, and `server`. - Telemetry: `stdout` exporter. - Logging: `error`, no file. - Pre-loaded handlers (Liveness, OK, and Stop).
Example ¶
Example showing how to use the Web Server.
package main import ( "errors" "fmt" "io/ioutil" "net/http" "os" "strings" "sync" "time" "github.com/saucelabs/randomness" "github.com/saucelabs/webserver" ) const serverName = "test-server" // Logs, and exit. func logAndExit(msg string) { fmt.Println(msg) os.Exit(1) } // Call. // //nolint:noctx func callAndExpect(port int, url string, sc int, expectedBodyContains string) (int, string) { c := http.Client{Timeout: time.Duration(10) * time.Second} resp, err := c.Get(fmt.Sprintf("http://0.0.0.0:%d/%s", port, url)) if err != nil { logAndExit(err.Error()) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { logAndExit(err.Error()) } if sc != 0 { if resp.StatusCode != sc { logAndExit(fmt.Sprintf("Expect %v got %v\n", sc, resp.StatusCode)) } } bodyS := string(body) if expectedBodyContains != "" { if !strings.Contains(bodyS, expectedBodyContains) { logAndExit(fmt.Sprintf("Expect %v got %v\n", expectedBodyContains, bodyS)) } } return resp.StatusCode, bodyS } ////// // Examples. ////// // Example showing how to use the Web Server. func main() { // Probe results accumulator. Golang's example requires some output to be // tested against. This accumulator will serve it. probeResults := []string{} // Part of the readiness simulation. readinessFlag := false // Allows to safely modify `probeResults` and `readinessFlag` from // concurrent routines. var readinessFlagLocker sync.Mutex var probeResultsLocker sync.Mutex // Golang's example are like tests, it's a bad practice to have a hardcoded // port because of the possibility of collision. Generate a random port. r, err := randomness.New(3000, 7000, 10, true) if err != nil { logAndExit(err.Error()) } port := r.MustGenerate() // Setup server settings some options. testServer, err := webserver.New(serverName, fmt.Sprintf("0.0.0.0:%d", port), webserver.WithoutMetrics(), webserver.WithoutTelemetry(), // Sets server readiness. webserver.WithReadiness(func() error { readinessFlagLocker.Lock() defer readinessFlagLocker.Unlock() if !readinessFlag { // Returning any error means server isn't ready. return errors.New("Not ready") } return nil }), ) if err != nil { logAndExit(err.Error()) } // Start server, non-blocking way. go func() { if err := testServer.Start(); err != nil { if errors.Is(err, http.ErrServerClosed) { fmt.Println("server stopped") } else { logAndExit(err.Error()) } } }() // Ensures enough time for the server to be up, and ready - just for testing. time.Sleep(3 * time.Second) // Simulates a Readiness probe, for example, Kubernetes. go func() { for { _, body := callAndExpect(int(port), "/readiness", 0, "") probeResultsLocker.Lock() probeResults = append(probeResults, body) probeResultsLocker.Unlock() // Probe wait time. time.Sleep(500 * time.Millisecond) } }() // Simulates some action which indicates server is ready, example data was // loaded from DB, or got updated data from another service. go func() { time.Sleep(2 * time.Second) readinessFlagLocker.Lock() defer readinessFlagLocker.Unlock() readinessFlag = true }() // Hold the server online for testing. time.Sleep(5 * time.Second) // Satisfies Golang example output need. probeResultsLocker.Lock() fmt.Println(strings.Contains(strings.Join(probeResults, ","), "OK")) probeResultsLocker.Unlock() }
Output: true
type Logging ¶
type Logging struct { // ConsoleLevel defines the level for the `Console` output. ConsoleLevel string `json:"console_level" validate:"required,gte=3,oneof=none fatal error info warn debug trace"` // RequestLevel defines the level for logging requests. RequestLevel string `json:"request_level" validate:"required,gte=3,oneof=none fatal error info warn debug trace"` // Filepath is the file path to optionally write logs. Filepath string `json:"filepath" validate:"omitempty,gte=3"` }
Logging definition.
type Option ¶
type Option func(s *Server)
Option allows to define options for the Server.
func WithLoggingOptions ¶
WithLoggingOptions sets logging configuration.
NOTE: Set filepath to "" to disabled that.
func WithMetrics ¶
WithMetrics provides a quick way to publish static metric values.
func WithMetricsRaw ¶
WithMetricsRaw allows to publishes metrics based on exp vars. It's useful for cases such as counters. It gives full control over what's being exposed.
func WithPreLoadedHandlers ¶
WithPreLoadedHandlers adds handlers to the list of pre-loaded handlers.
NOTE: Use `handler.New` to bring your own handler.
func WithReadiness ¶
func WithReadiness(readinessFunc handler.ReadinessFunc) Option
WithReadiness sets server readiness. Returning any non-nil error means server isn't ready.
func WithTelemetry ¶
WithTelemetry sets telemetry.
NOTE: Use `telemetry.New` to bring your own telemetry.
func WithTimeout ¶
WithTimeout sets the maximum duration for each individual timeouts.
func WithoutPreLoadedHandlers ¶
func WithoutPreLoadedHandlers() Option
WithoutPreLoadedHandlers disable the default pre-loaded handlers: - OK handler (`GET /`) - Liveness handler (`GET /liveness`) - Readiness handler (`GET /readiness`) - Stop handler (`GET /stop`) - Metrics handler (`GET /debug/vars`).
type Server ¶
type Server struct { // Address is a TCP address to listen on, default: ":4446". Address string `json:"address" validate:"tcp_addr"` // Name of the server. Name string `json:"name" validate:"required,gte=3"` // EnableMetrics controls whether metrics are enable, or not, default: true. EnableMetrics bool `json:"enable_metrics"` // EnableTelemetry controls whether telemetry are enable, or not, // default: true. EnableTelemetry bool `json:"enable_telemetry"` // Logging fine-control. *Logging `json:"logging" validate:"required"` // Timeouts fine-control. *Timeout `json:"timeout" validate:"required"` // contains filtered or unexported fields }
Server definition.
func (*Server) GetTelemetry ¶
func (s *Server) GetTelemetry() telemetry.ITelemetry
GetTelemetry returns telemetry.
type Timeout ¶
type Timeout struct { // ReadTimeout max duration for READING the entire request, including the // body, default: 3s. ReadTimeout time.Duration `json:"read_timeout"` // RequestTimeout max duration to WAIT BEFORE CANCELING A REQUEST, // default: 1s. // // NOTE: It's automatically validated against other timeouts, and needs to // be smaller. RequestTimeout time.Duration `json:"request_timeout" validate:"ltfield=ReadTimeout"` // ShutdownInFlightTimeout max duration to WAIT IN-FLIGHT REQUESTS, // default: 3s. ShutdownInFlightTimeout time.Duration `json:"shutdown_in_flight_timeout"` // ShutdownTaskTimeout max duration TO WAIT for tasks such as flush cache, // files, and telemetry, default: 10s. ShutdownTaskTimeout time.Duration `json:"shutdown_task_timeout"` // ShutdownTimeout max duration for WRITING the response, default: 3s. WriteTimeout time.Duration `json:"write_timeout"` }
Timeout definition.
Directories ¶
Path | Synopsis |
---|---|
package handler provides a collection of useful/built-in handlers.
|
package handler provides a collection of useful/built-in handlers. |
internal
|
|
expvar
Package expvar is the standard Golang's expvar package with one modification: it removes the bad practice of using `init()`, instead exposes: - `Start`: does everything the old `init` does - `PublishCmdLine`: publishes command line information - `PublishMemStats`: publishes memory statistics - `RegisterHandler`: registers the standard endpoint: `GET /debug/vars`.
|
Package expvar is the standard Golang's expvar package with one modification: it removes the bad practice of using `init()`, instead exposes: - `Start`: does everything the old `init` does - `PublishCmdLine`: publishes command line information - `PublishMemStats`: publishes memory statistics - `RegisterHandler`: registers the standard endpoint: `GET /debug/vars`. |
logger
Package logger provides server's logging powered by Sypl.
|
Package logger provides server's logging powered by Sypl. |
middleware
Package middleware offers common, and usual middlewares.
|
Package middleware offers common, and usual middlewares. |
validation
Package validation provides data validation.
|
Package validation provides data validation. |
Package telemetry provides server's telemetry powered by Open Telemetry.
|
Package telemetry provides server's telemetry powered by Open Telemetry. |