README

Welcome to the 2nd generation of Gizmo servers 🚀!

Gizmo's intentions from the beginning were to eventually join forces with the wonders of the go-kit toolkit. This package is meant to embody that goal.

The kit server is composed of multiple kit/transport/http.Servers that are tied together with a common HTTP mux, HTTP options and middlewares. By default all HTTP endpoints will be encoded as JSON, but developers may override each HTTPEndpoint to use whatever encoding they need. If users need to use gRPC, they can can register the same endpoints to serve both HTTP and gRPC requests on two different ports.

This server expects to be configured via environment variables. The available variables can be found by inspecting the Config struct within this package. If no health check or warm up endpoints are defined, this server will automatically register basic endpoints there to return a simple "200 OK" response.

Since NYT uses Google Cloud, deploying this server to that environment provides additional perks:

  • If running in the App Engine 2nd Generation runtime (Go >=1.11), servers will:
    • Automatically catch any panics and send them to Stackdriver Error reporting
    • Automatically use Stackdriver logging and, if kit.LogXXX functions are used, logs will be trace enabled and will be combined with their parent access log in the Stackdriver logging console.
    • Automatically register Stackdriver exporters for Open Census trace and monitoring. Most Google Cloud clients (like Cloud Spanner) will detect this and emit the traces. Users can also add their own trace and monitoring spans via the Open Census clients.
    • Monitoring, traces and metrics are automatically registered if running within App Engine, Kubernetes Engine, Compute Engine or AWS EC2 Instances. To change the name and version for Error reporting and Traces use SERVICE_NAME and SERVICE_VERSION environment variables.

For an example of how to build a server that utilizes this package, see the Reading List example.

Documentation

Overview

    Package kit implements an opinionated server based on go-kit primitives.

    Index

    Constants

    View Source
    const (
    
    	// ContextKeyCloudTraceContext is a context key for storing and retrieving the
    	// inbound 'x-cloud-trace-context' header. This server will automatically look for
    	// and inject the value into the request context. If in the App Engine environment
    	// this will be used to enable combined access and application logs.
    	ContextKeyCloudTraceContext contextKey
    )

    Variables

    This section is empty.

    Functions

    func AddLogKeyVals

    func AddLogKeyVals(ctx context.Context, l log.Logger) log.Logger

      AddLogKeyVals will add any common HTTP headers or gRPC metadata from the given context to the given logger as fields. This is used by the server to initialize the request scopes logger.

      func EncodeProtoResponse

      func EncodeProtoResponse(ctx context.Context, w http.ResponseWriter, pres interface{}) error

        EncodeProtoResponse is an httptransport.EncodeResponseFunc that serializes the response as Protobuf. Many Proto-over-HTTP services can use it as a sensible default. If the response implements Headerer, the provided headers will be applied to the response. If the response implements StatusCoder, the provided StatusCode will be used instead of 200.

        func Log

        func Log(ctx context.Context, keyvals ...interface{}) error

          Log will pull a request scoped log.Logger from the context and log the given keyvals with it.

          func LogDebug

          func LogDebug(ctx context.Context, message string) error

            LogDebug will log the given message to the server logger with the key "message" along with all the common request headers or gRPC metadata. This message will have a "debug" log level associated with it.

            func LogDebugf

            func LogDebugf(ctx context.Context, format string, v ...interface{}) error

              LogDebugf will format the given string with the arguments then log to the server logger with the key "message" along with all the common request headers or gRPC metadata. Arguments are handlered in the manner of fmt.Printf. This message will have a "debug" log level associated with it.

              func LogErrorMsg

              func LogErrorMsg(ctx context.Context, err error, message string) error

                LogErrorMsg will log the given error under the key "error", the given message under the key "message" along with all the common request headers or gRPC metadata. This message will have an "error" log level associated with it.

                func LogErrorf

                func LogErrorf(ctx context.Context, format string, v ...interface{}) error

                  LogErrorf will format the given string with the arguments then log to the server logger with the key "message" along with all the common request headers or gRPC metadata. Arguments are handlered in the manner of fmt.Printf. This message will have a "warn" log level associated with it.

                  func LogMsg

                  func LogMsg(ctx context.Context, message string) error

                    LogMsg will log the given message to the server logger with the key "message" along with all the common request headers or gRPC metadata. This message will have an "info" log level associated with it.

                    func LogWarning

                    func LogWarning(ctx context.Context, message string) error

                      LogWarning will log the given message to the server logger with the key "message" along with all the common request headers or gRPC metadata. This message will have a "warn" log level associated with it.

                      func LogWarningf

                      func LogWarningf(ctx context.Context, format string, v ...interface{}) error

                        LogWarningf will the format given string with the arguments then log to the server logger with the key "message" along with all the common request headers or gRPC metadata. Arguments are handlered in the manner of fmt.Printf. This message will have a "warn" log level associated with it.

                        func Logf

                        func Logf(ctx context.Context, format string, v ...interface{}) error

                          Logf will format the given string with the arguments then log to the server logger with the key "message" along with all the common request headers or gRPC metadata. Arguments are handlered in the manner of fmt.Printf. This message will have an "info" log level associated with it.

                          func Logger

                          func Logger(ctx context.Context) log.Logger

                            Logger will return a kit/log.Logger that has been injected into the context by the kit server. This logger has had request headers and metadata added as key values. This function will only work within the scope of a request initiated by the server.

                            func NewLogger

                            func NewLogger(ctx context.Context, logID string) (log.Logger, func() error, error)

                              NewLogger will inspect the environment and, if running in the Google App Engine, Google Kubernetes Engine, Google Compute Engine or AWS EC2 environment, it will return a new Stackdriver logger annotated with the current server's project ID, service ID and version and other environment specific values. If not in App Engine, GKE, GCE or AWS EC2 - a normal JSON logger pointing to stdout will be returned. This function can be used for services that need to log information outside the context of an inbound request. When using the Stackdriver logger, any go-kit/log/levels will be translated to Stackdriver severity levels. The logID field is used when the server is deployed in a Stackdriver enabled environment. If an empty string is provided, "gae_log" will be used in App Engine and "stdout" elsewhere. For more information about to use of logID see the documentation here: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#FIELDS.log_name To speed up start up time in non-GCP enabled environments, this function also checks the observe.SkipObserve() function and will use a basic JSON logger writing to stdout if set.

                              func Run

                              func Run(service Service) error

                                Run will use environment variables to configure the server then register the given Service and start up the server(s). This will block until the server shuts down.

                                func SetLogger

                                func SetLogger(ctx context.Context, logger log.Logger) context.Context

                                  SetLogger sets log.Logger to the context and returns new context with logger.

                                  func SetRouteVars

                                  func SetRouteVars(r *http.Request, val interface{}) *http.Request

                                    SetRouteVars will set the given value into into the request context with the shared 'vars' storage key.

                                    func Vars

                                    func Vars(r *http.Request) map[string]string

                                      Vars is a helper function for accessing route parameters from any server.Router implementation. This is the equivalent of using `mux.Vars(r)` with the Gorilla mux.Router.

                                      Types

                                      type Config

                                      type Config struct {
                                      	// HealthCheckPath is used by server to init the proper HealthCheckHandler.
                                      	// If empty, this will default to '/healthz'.
                                      	HealthCheckPath string `envconfig:"GIZMO_HEALTH_CHECK_PATH"`
                                      
                                      	// MaxHeaderBytes can be used to override the default of 1<<20.
                                      	MaxHeaderBytes int `envconfig:"GIZMO_MAX_HEADER_BYTES"`
                                      
                                      	// ReadTimeout can be used to override the default http server timeout of 10s.
                                      	// The string should be formatted like a time.Duration string.
                                      	ReadTimeout time.Duration `envconfig:"GIZMO_READ_TIMEOUT"`
                                      
                                      	// WriteTimeout can be used to override the default http server timeout of 10s.
                                      	// The string should be formatted like a time.Duration string.
                                      	WriteTimeout time.Duration `envconfig:"GIZMO_WRITE_TIMEOUT"`
                                      
                                      	// IdleTimeout can be used to override the default http server timeout of 120s.
                                      	// The string should be formatted like a time.Duration string.
                                      	IdleTimeout time.Duration `envconfig:"GIZMO_IDLE_TIMEOUT"`
                                      
                                      	// ShutdownTimeout can be used to override the default http server shutdown timeout
                                      	// of 5m.
                                      	ShutdownTimeout time.Duration `envconfig:"GIZMO_SHUTDOWN_TIMEOUT"`
                                      
                                      	// GOMAXPROCS can be used to override the default GOMAXPROCS.
                                      	GOMAXPROCS int `envconfig:"GIZMO_GOMAXPROCS"`
                                      
                                      	// HTTPAddr is the address the server implementation will bind to.
                                      	// The default is "" (bind to all interfaces)
                                      	HTTPAddr string `envconfig:"HTTP_ADDR"`
                                      	// HTTPPort is the port the server implementation will serve HTTP over.
                                      	// The default is 8080
                                      	HTTPPort int `envconfig:"HTTP_PORT"`
                                      	// RPCPort is the port the server implementation will serve RPC over.
                                      	// The default is 8081.
                                      	RPCPort int `envconfig:"RPC_PORT"`
                                      
                                      	// Enable pprof Profiling. Off by default.
                                      	EnablePProf bool `envconfig:"ENABLE_PPROF"`
                                      }

                                        Config holds info required to configure a gizmo kit.Server.

                                        This struct is loaded from the environment at Run and only made public to expose for documentation.

                                        type HTTPEndpoint

                                        type HTTPEndpoint struct {
                                        	Endpoint endpoint.Endpoint
                                        	Decoder  httptransport.DecodeRequestFunc
                                        	Encoder  httptransport.EncodeResponseFunc
                                        	Options  []httptransport.ServerOption
                                        }

                                          HTTPEndpoint encapsulates everything required to build an endpoint hosted on a kit server.

                                          type JSONStatusResponse

                                          type JSONStatusResponse struct {
                                          	// contains filtered or unexported fields
                                          }

                                            JSONStatusResponse implements: `httptransport.StatusCoder` to allow users to respond with the given response with a non-200 status code. `json.Marshaler` so it can wrap JSON Endpoint responses. `error` so it can be used to respond as an error within the go-kit stack.

                                            func NewJSONStatusResponse

                                            func NewJSONStatusResponse(res interface{}, code int) *JSONStatusResponse

                                              NewJSONStatusResponse allows users to respond with a specific HTTP status code and a JSON serialized response.

                                              func (*JSONStatusResponse) Error

                                              func (c *JSONStatusResponse) Error() string

                                                Error is to implement error

                                                func (*JSONStatusResponse) MarshalJSON

                                                func (c *JSONStatusResponse) MarshalJSON() ([]byte, error)

                                                  MarshalJSON is to implement json.Marshaler

                                                  func (*JSONStatusResponse) StatusCode

                                                  func (c *JSONStatusResponse) StatusCode() int

                                                    StatusCode is to implement httptransport.StatusCoder

                                                    type ProtoStatusResponse

                                                    type ProtoStatusResponse struct {
                                                    	// contains filtered or unexported fields
                                                    }

                                                      ProtoStatusResponse implements: `httptransport.StatusCoder` to allow users to respond with the given response with a non-200 status code. `proto.Marshaler` and proto.Message so it can wrap a proto Endpoint responses. `json.Marshaler` so it can wrap JSON Endpoint responses. `error` so it can be used to respond as an error within the go-kit stack.

                                                      func NewProtoStatusResponse

                                                      func NewProtoStatusResponse(res proto.Message, code int) *ProtoStatusResponse

                                                        NewProtoStatusResponse allows users to respond with a specific HTTP status code and a Protobuf or JSON serialized response.

                                                        func (*ProtoStatusResponse) Error

                                                        func (c *ProtoStatusResponse) Error() string

                                                          to implement error

                                                          func (*ProtoStatusResponse) Marshal

                                                          func (c *ProtoStatusResponse) Marshal() ([]byte, error)

                                                            Marshal is to implement proto.Marshaler. It will marshal the given message, not this struct.

                                                            func (*ProtoStatusResponse) MarshalJSON

                                                            func (c *ProtoStatusResponse) MarshalJSON() ([]byte, error)

                                                              MarshalJSON is to implement json.Marshaler. It will marshal the given message, not this struct.

                                                              func (*ProtoStatusResponse) ProtoMessage

                                                              func (c *ProtoStatusResponse) ProtoMessage()

                                                                ProtoMessage is to implement proto.Message. It uses the given message's ProtoMessage method.

                                                                func (*ProtoStatusResponse) Reset

                                                                func (c *ProtoStatusResponse) Reset()

                                                                  Reset is to implement proto.Message. It uses the given message's Reset method.

                                                                  func (*ProtoStatusResponse) StatusCode

                                                                  func (c *ProtoStatusResponse) StatusCode() int

                                                                    StatusCode implements httptransport.StatusCoder and will return the given HTTP code.

                                                                    func (*ProtoStatusResponse) String

                                                                    func (c *ProtoStatusResponse) String() string

                                                                      String is to implement proto.Message. It uses the given message's String method.

                                                                      type Router

                                                                      type Router interface {
                                                                      	Handle(method string, path string, handler http.Handler)
                                                                      	HandleFunc(method string, path string, handlerFunc func(http.ResponseWriter, *http.Request))
                                                                      	ServeHTTP(w http.ResponseWriter, r *http.Request)
                                                                      	SetNotFoundHandler(handler http.Handler)
                                                                      }

                                                                        Router is an interface to wrap different router implementations.

                                                                        type RouterOption

                                                                        type RouterOption func(Router) Router

                                                                          RouterOption sets optional Router overrides.

                                                                          func CustomRouter

                                                                          func CustomRouter(r Router) RouterOption

                                                                            CustomRouter allows users to inject an alternate Router implementation.

                                                                            func RouterNotFound

                                                                            func RouterNotFound(h http.Handler) RouterOption

                                                                              RouterNotFound will set the not found handler of the router.

                                                                              func RouterSelect

                                                                              func RouterSelect(name string) RouterOption

                                                                                RouterSelect allows users to override the default use of the Gorilla Router. TODO add type and constants for names + docs

                                                                                type Server

                                                                                type Server struct {
                                                                                	// contains filtered or unexported fields
                                                                                }

                                                                                  Server encapsulates all logic for registering and running a gizmo kit server.

                                                                                  func NewServer

                                                                                  func NewServer(svc Service) *Server

                                                                                    NewServer will create a new kit server for the given Service.

                                                                                    Generally, users should only use the 'Run' function to start a server and use this function within tests so they may call ServeHTTP.

                                                                                    func (*Server) ServeHTTP

                                                                                    func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

                                                                                    type Service

                                                                                    type Service interface {
                                                                                    	// Middleware is for any service-wide go-kit middlewares. This middleware
                                                                                    	// is applied to both HTTP and gRPC services.
                                                                                    	Middleware(endpoint.Endpoint) endpoint.Endpoint
                                                                                    
                                                                                    	// HTTPMiddleware is for service-wide http specific middleware
                                                                                    	// for easy integration with 3rd party http.Handlers like nytimes/gziphandler.
                                                                                    	HTTPMiddleware(http.Handler) http.Handler
                                                                                    
                                                                                    	// HTTPOptions are service-wide go-kit HTTP server options
                                                                                    	HTTPOptions() []httptransport.ServerOption
                                                                                    
                                                                                    	// HTTPRouterOptions allows users to override the default
                                                                                    	// behavior and use of the GorillaRouter.
                                                                                    	HTTPRouterOptions() []RouterOption
                                                                                    
                                                                                    	// HTTPEndpoints default to using a JSON serializer if no encoder is provided.
                                                                                    	// For example:
                                                                                    	//
                                                                                    	//    return map[string]map[string]kit.HTTPEndpoint{
                                                                                    	//        "/cat/{id}": {
                                                                                    	//            "GET": {
                                                                                    	//                Endpoint: s.GetCatByID,
                                                                                    	//                Decoder:  decodeGetCatRequest,
                                                                                    	//            },
                                                                                    	//        },
                                                                                    	//        "/cats": {
                                                                                    	//            "PUT": {
                                                                                    	//                Endpoint: s.PutCats,
                                                                                    	//                HTTPDecoder:  decodePutCatsProtoRequest,
                                                                                    	//            },
                                                                                    	//            "GET": {
                                                                                    	//                Endpoint: s.GetCats,
                                                                                    	//                HTTPDecoder:  decodeGetCatsRequest,
                                                                                    	//            },
                                                                                    	//        },
                                                                                    	//  }
                                                                                    	HTTPEndpoints() map[string]map[string]HTTPEndpoint
                                                                                    
                                                                                    	// RPCMiddleware is for any service-wide gRPC specific middleware
                                                                                    	// for easy integration with 3rd party grpc.UnaryServerInterceptors like
                                                                                    	// http://godoc.org/cloud.google.com/go/trace#Client.GRPCServerInterceptor
                                                                                    	//
                                                                                    	// The underlying kit server already uses the one available grpc.UnaryInterceptor
                                                                                    	// grpc.ServerOption so attempting to pass your own in this Service's RPCOptions()
                                                                                    	// will cause a panic at startup.
                                                                                    	//
                                                                                    	// If you want to apply multiple RPC middlewares,
                                                                                    	// we recommend using:
                                                                                    	// http://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware#ChainUnaryServer
                                                                                    	RPCMiddleware() grpc.UnaryServerInterceptor
                                                                                    
                                                                                    	// RPCServiceDesc allows services to declare an alternate gRPC
                                                                                    	// representation of themselves to be hosted on the RPC_PORT (8081 by default).
                                                                                    	RPCServiceDesc() *grpc.ServiceDesc
                                                                                    
                                                                                    	// RPCOptions are for service-wide gRPC server options.
                                                                                    	//
                                                                                    	// The underlying kit server already uses the one available grpc.UnaryInterceptor
                                                                                    	// grpc.ServerOption so attempting to pass your own in this method will cause a panic
                                                                                    	// at startup. We recommend using RPCMiddleware() to fill this need.
                                                                                    	RPCOptions() []grpc.ServerOption
                                                                                    }

                                                                                      Service is the interface of mixed HTTP/gRPC that can be registered and hosted by a gizmo/server/kit server. Services provide hooks for service-wide options and middlewares and can be used as a means of dependency injection. In general, a Service should just contain the logic for deserializing and authorizing requests, passing the request to a business logic interface abstraction, handling errors and serializing the apprioriate response.

                                                                                      In other words, each Endpoint is similar to a 'controller' and the Service a container for injecting dependencies (business services, repositories, etc.) into each request handler.

                                                                                      type Shutdowner

                                                                                      type Shutdowner interface {
                                                                                      	Shutdown()
                                                                                      }

                                                                                        Shutdowner allows your service to shutdown gracefully when http server stops. This may used when service has any background task which needs to be completed gracefully.