Documentation
¶
Index ¶
- Variables
- func GetSpanID(ctx context.Context) string
- func GetTraceFlags(ctx context.Context) string
- func GetTraceID(ctx context.Context) string
- func LiveHandlerFunc() http.HandlerFunc
- func NewHandlerFromConfig(cfg LogConfig, opts ...ContextHandlerOption) (slog.Handler, error)
- func NewHealthHandler(opts ...HealthHandlerOption) http.Handler
- func ReadyHandlerFunc(version string, environment string, checkers []Checker, opts ...ReadyOption) http.HandlerFunc
- func RespondProblem(ctx context.Context, w http.ResponseWriter, problem *ProblemDetail)
- type CheckResponse
- type Checker
- type ContextHandler
- func (h *ContextHandler) Enabled(ctx context.Context, level slog.Level) bool
- func (h *ContextHandler) Handle(ctx context.Context, record slog.Record) error
- func (h *ContextHandler) Registry() *Registry
- func (h *ContextHandler) Unwrap() slog.Handler
- func (h *ContextHandler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *ContextHandler) WithGroup(name string) slog.Handler
- type ContextHandlerOption
- type ContextKey
- type HealthHandlerOption
- type LiveResponse
- type LogConfig
- type Middleware
- type OTelOption
- type ProblemDetail
- func BadRequest(detail string, opts ...ProblemOption) *ProblemDetail
- func Conflict(detail string, opts ...ProblemOption) *ProblemDetail
- func Forbidden(detail string, opts ...ProblemOption) *ProblemDetail
- func Gone(detail string, opts ...ProblemOption) *ProblemDetail
- func InternalServerError(detail string, opts ...ProblemOption) *ProblemDetail
- func MethodNotAllowed(detail string, opts ...ProblemOption) *ProblemDetail
- func NewProblemDetail(status int, title string, opts ...ProblemOption) *ProblemDetail
- func NotFound(detail string, opts ...ProblemOption) *ProblemDetail
- func ServiceUnavailable(detail string, opts ...ProblemOption) *ProblemDetail
- func TooManyRequests(detail string, opts ...ProblemOption) *ProblemDetail
- func Unauthorized(detail string, opts ...ProblemOption) *ProblemDetail
- func UnprocessableEntity(detail string, opts ...ProblemOption) *ProblemDetail
- type ProblemOption
- type ReadyOption
- type ReadyResponse
- type Registry
- type Server
- type ServerOption
- func WithIdleTimeout(timeout time.Duration) ServerOption
- func WithLogger(logger *slog.Logger) ServerOption
- func WithPort(port int) ServerOption
- func WithReadTimeout(timeout time.Duration) ServerOption
- func WithShutdownTimeout(timeout time.Duration) ServerOption
- func WithTLS(certPath, keyPath string) ServerOption
- func WithWriteTimeout(timeout time.Duration) ServerOption
- type Status
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidLogLevel is returned when an invalid log level is provided. ErrInvalidLogLevel = errors.New("invalid log level") // ErrInvalidLogFormat is returned when an invalid log format is provided. ErrInvalidLogFormat = errors.New("invalid log format") )
var ErrReservedExtensionKey = errors.New("extension key conflicts with reserved RFC 9457 field")
ErrReservedExtensionKey is returned when an extension key conflicts with a reserved RFC 9457 field.
var SpanIDKey = ContextKey{Name: "span_id"}
SpanIDKey is the context key for W3C span ID.
var TraceFlagsKey = ContextKey{Name: "trace_flags"}
TraceFlagsKey is the context key for W3C trace flags.
var TraceIDKey = ContextKey{Name: "trace_id"}
TraceIDKey is the context key for W3C trace ID.
Functions ¶
func GetTraceFlags ¶
GetTraceFlags retrieves the trace flags from the request context.
func GetTraceID ¶
GetTraceID retrieves the trace ID from the request context.
func LiveHandlerFunc ¶
func LiveHandlerFunc() http.HandlerFunc
LiveHandlerFunc returns an HTTP handler function for liveness health checks.
func NewHandlerFromConfig ¶
func NewHandlerFromConfig(cfg LogConfig, opts ...ContextHandlerOption) (slog.Handler, error)
NewHandlerFromConfig creates a new slog.Handler based on the provided configuration. Returns an error if level or format are invalid.
func NewHealthHandler ¶
func NewHealthHandler(opts ...HealthHandlerOption) http.Handler
NewHealthHandler creates an HTTP handler that provides health check endpoints at /health/live and /health/ready.
Example ¶
ExampleNewHealthHandler demonstrates creating health check endpoints.
package main
import (
"fmt"
"net/http"
"github.com/monkescience/vital"
)
func main() {
// Create health handler with version and environment
healthHandler := vital.NewHealthHandler(
vital.WithVersion("1.0.0"),
vital.WithEnvironment("production"),
)
// Mount on router
mux := http.NewServeMux()
mux.Handle("/", healthHandler)
fmt.Println("Health endpoints configured")
fmt.Println("GET /health/live - liveness probe")
fmt.Println("GET /health/ready - readiness probe")
// Cleanup
_ = mux
}
Output: Health endpoints configured GET /health/live - liveness probe GET /health/ready - readiness probe
func ReadyHandlerFunc ¶
func ReadyHandlerFunc( version string, environment string, checkers []Checker, opts ...ReadyOption, ) http.HandlerFunc
ReadyHandlerFunc returns an HTTP handler function for readiness health checks that executes the provided checkers and includes version and environment metadata in the response.
func RespondProblem ¶
func RespondProblem(ctx context.Context, w http.ResponseWriter, problem *ProblemDetail)
RespondProblem writes a ProblemDetail as an HTTP response. It sets the appropriate content type and status code.
Example ¶
ExampleRespondProblem demonstrates returning RFC 9457 problem details.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/monkescience/vital"
)
func main() {
// Handler that returns a problem detail
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Return 404 Not Found with problem detail
problem := vital.NotFound("user not found",
vital.WithType("https://api.example.com/errors/not-found"),
vital.WithInstance(r.URL.Path),
)
vital.RespondProblem(r.Context(), w, problem)
})
// Simulate request
req := httptest.NewRequest(http.MethodGet, "/users/123", nil)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
fmt.Printf("Status: %d\n", rec.Code)
fmt.Printf("Content-Type: %s\n", rec.Header().Get("Content-Type"))
}
Output: Status: 404 Content-Type: application/problem+json
Types ¶
type CheckResponse ¶
type CheckResponse struct {
Name string `json:"name"`
Status Status `json:"status"`
Message string `json:"message,omitempty"`
Duration string `json:"duration,omitempty"`
}
CheckResponse represents the result of a single health check.
type ContextHandler ¶
type ContextHandler struct {
// contains filtered or unexported fields
}
ContextHandler is a slog.Handler that automatically extracts registered context values and adds them as log attributes.
func NewContextHandler ¶
func NewContextHandler(handler slog.Handler, opts ...ContextHandlerOption) *ContextHandler
NewContextHandler creates a new ContextHandler wrapping the provided handler. If the provided handler is already a ContextHandler, it unwraps it first to avoid nesting. Options can be provided to configure which context keys are extracted.
Example usage:
handler := vital.NewContextHandler(
slog.NewJSONHandler(os.Stdout, nil),
vital.WithBuiltinKeys(), // Include CorrelationIDKey
vital.WithContextKeys(UserIDKey), // Add custom keys
)
func (*ContextHandler) Enabled ¶
Enabled reports whether the handler handles records at the given level.
func (*ContextHandler) Handle ¶
Handle processes the log record, extracting registered context values and adding them as attributes.
func (*ContextHandler) Registry ¶
func (h *ContextHandler) Registry() *Registry
Registry returns the handler's registry for inspection.
func (*ContextHandler) Unwrap ¶
func (h *ContextHandler) Unwrap() slog.Handler
Unwrap returns the underlying handler wrapped by this ContextHandler.
type ContextHandlerOption ¶
type ContextHandlerOption func(*ContextHandler)
ContextHandlerOption is a functional option for configuring a ContextHandler.
func WithBuiltinKeys ¶
func WithBuiltinKeys() ContextHandlerOption
WithBuiltinKeys registers all built-in context keys from the vital library. This includes keys used by vital's middleware (e.g., CorrelationIDKey).
func WithContextKeys ¶
func WithContextKeys(keys ...ContextKey) ContextHandlerOption
WithContextKeys registers specific context keys to be extracted and logged. This is useful for adding custom application-specific keys.
func WithRegistry ¶
func WithRegistry(registry *Registry) ContextHandlerOption
WithRegistry provides a custom registry for the ContextHandler. Use this when you want full control over the registry instance.
type ContextKey ¶
type ContextKey struct {
Name string
}
ContextKey is a strongly-typed key for storing values in context that should be logged.
func BuiltinKeys ¶
func BuiltinKeys() []ContextKey
BuiltinKeys returns all built-in context keys provided by the vital library. These are keys used by vital's middleware (e.g., TraceIDKey, SpanIDKey, TraceFlagsKey).
type HealthHandlerOption ¶
type HealthHandlerOption func(*handlerConfig)
HealthHandlerOption configures the health check handler.
func WithCheckers ¶
func WithCheckers(checkers ...Checker) HealthHandlerOption
WithCheckers adds health checkers to be executed during readiness checks.
func WithEnvironment ¶
func WithEnvironment(env string) HealthHandlerOption
WithEnvironment sets the environment string to include in readiness responses.
func WithReadyOptions ¶
func WithReadyOptions(opts ...ReadyOption) HealthHandlerOption
WithReadyOptions configures readiness-specific options such as timeouts.
func WithVersion ¶
func WithVersion(v string) HealthHandlerOption
WithVersion sets the version string to include in readiness responses.
type LiveResponse ¶
type LiveResponse struct {
Status Status `json:"status"`
}
LiveResponse represents the response payload for the liveness health check endpoint.
type LogConfig ¶
type LogConfig struct {
// Level is the log level (debug, info, warn, error).
Level string `json:"level" yaml:"level"`
// Format is the log format (json, text).
Format string `json:"format" yaml:"format"`
// AddSource includes the source file and line number in the log.
AddSource bool `json:"add_source" yaml:"add_source"`
}
LogConfig holds configuration for the logger.
type Middleware ¶
Middleware is a function that wraps an http.Handler.
func BasicAuth ¶
func BasicAuth(username, password string, realm string) Middleware
BasicAuth returns a middleware that requires HTTP Basic Authentication. It uses constant-time comparison to prevent timing attacks.
func OTel ¶
func OTel(opts ...OTelOption) (Middleware, error)
OTel returns a middleware that instruments HTTP requests with OpenTelemetry traces and metrics. Returns an error if required metric instruments cannot be created.
Features:
- Creates a span for each HTTP request with standard HTTP semantic conventions
- Propagates W3C traceparent headers (incoming and outgoing)
- Records HTTP metrics: http.server.request.duration histogram
- Adds trace_id and span_id to request context for log correlation
- Returns an error if the duration histogram instrument cannot be created
Example:
tp := sdktrace.NewTracerProvider(...)
mp := sdkmetric.NewMeterProvider(...)
middleware, err := vital.OTel(
vital.WithTracerProvider(tp),
vital.WithMeterProvider(mp),
)
if err != nil {
return err
}
handler := middleware(myHandler)
Example ¶
ExampleOTel demonstrates using OpenTelemetry middleware.
package main
import (
"fmt"
"net/http"
"github.com/monkescience/vital"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
func main() {
// Create tracer and meter providers
tp := sdktrace.NewTracerProvider()
mp := sdkmetric.NewMeterProvider()
// Create handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Wrap with OTel middleware
otelMiddleware, err := vital.OTel(
vital.WithTracerProvider(tp),
vital.WithMeterProvider(mp),
)
if err != nil {
panic(err)
}
otelHandler := otelMiddleware(handler)
fmt.Println("Handler instrumented with OpenTelemetry")
// Cleanup
_ = otelHandler
}
Output: Handler instrumented with OpenTelemetry
func Recovery ¶
func Recovery(logger *slog.Logger) Middleware
Recovery returns a middleware that recovers from panics and returns a 500 error.
func RequestLogger ¶
func RequestLogger(logger *slog.Logger) Middleware
RequestLogger returns a middleware that logs HTTP requests and responses. It logs the method, path, status code, duration, and remote address.
func Timeout ¶
func Timeout(duration time.Duration) Middleware
Timeout returns a middleware that enforces a timeout on request processing. If the handler does not complete within the specified duration, it returns a 503 Service Unavailable response with a ProblemDetail JSON body.
A timeout of 0 or negative duration disables the timeout (passthrough).
The middleware wraps the ResponseWriter to detect if headers have already been sent. If the timeout fires after WriteHeader has been called, the middleware cannot change the response status and will not write the timeout error response.
Example ¶
ExampleTimeout demonstrates using the timeout middleware.
package main
import (
"fmt"
"net/http"
"time"
"github.com/monkescience/vital"
)
func main() {
// Create a handler
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("success"))
})
// Wrap with timeout middleware (30 second timeout)
timeoutHandler := vital.Timeout(30 * time.Second)(handler)
fmt.Println("Handler wrapped with 30s timeout")
// Cleanup
_ = timeoutHandler
}
Output: Handler wrapped with 30s timeout
type OTelOption ¶
type OTelOption func(*otelConfig)
OTelOption configures the OTel middleware.
func WithMeterProvider ¶
func WithMeterProvider(mp metric.MeterProvider) OTelOption
WithMeterProvider sets a custom meter provider.
func WithPropagator ¶
func WithPropagator(p propagation.TextMapPropagator) OTelOption
WithPropagator sets a custom propagator (default W3C TraceContext).
func WithTracerProvider ¶
func WithTracerProvider(tp trace.TracerProvider) OTelOption
WithTracerProvider sets a custom tracer provider.
type ProblemDetail ¶
type ProblemDetail struct {
// Type is a URI reference that identifies the problem type.
// When dereferenced, it should provide human-readable documentation.
// Defaults to "about:blank" when not specified.
Type string `json:"type,omitempty"`
// Title is a short, human-readable summary of the problem type.
Title string `json:"title"`
// Status is the HTTP status code for this occurrence of the problem.
Status int `json:"status"`
// Detail is a human-readable explanation specific to this occurrence.
Detail string `json:"detail,omitempty"`
// Instance is a URI reference identifying the specific occurrence.
// It may or may not yield further information if dereferenced.
Instance string `json:"instance,omitempty"`
// Extensions holds any additional members for extensibility.
// Use this for problem-type-specific information.
// Reserved keys (type, title, status, detail, instance) are rejected during marshaling.
Extensions map[string]any `json:"-"`
}
ProblemDetail represents an RFC 9457 problem details response. See https://datatracker.ietf.org/doc/html/rfc9457 for specification.
func BadRequest ¶
func BadRequest(detail string, opts ...ProblemOption) *ProblemDetail
BadRequest creates a 400 Bad Request problem detail.
func Conflict ¶
func Conflict(detail string, opts ...ProblemOption) *ProblemDetail
Conflict creates a 409 Conflict problem detail.
func Forbidden ¶
func Forbidden(detail string, opts ...ProblemOption) *ProblemDetail
Forbidden creates a 403 Forbidden problem detail.
func Gone ¶
func Gone(detail string, opts ...ProblemOption) *ProblemDetail
Gone creates a 410 Gone problem detail.
func InternalServerError ¶
func InternalServerError(detail string, opts ...ProblemOption) *ProblemDetail
InternalServerError creates a 500 Internal Server Error problem detail.
func MethodNotAllowed ¶
func MethodNotAllowed(detail string, opts ...ProblemOption) *ProblemDetail
MethodNotAllowed creates a 405 Method Not Allowed problem detail.
func NewProblemDetail ¶
func NewProblemDetail(status int, title string, opts ...ProblemOption) *ProblemDetail
NewProblemDetail creates a new ProblemDetail with the specified status, title, and options.
func NotFound ¶
func NotFound(detail string, opts ...ProblemOption) *ProblemDetail
NotFound creates a 404 Not Found problem detail.
func ServiceUnavailable ¶
func ServiceUnavailable(detail string, opts ...ProblemOption) *ProblemDetail
ServiceUnavailable creates a 503 Service Unavailable problem detail.
func TooManyRequests ¶
func TooManyRequests(detail string, opts ...ProblemOption) *ProblemDetail
TooManyRequests creates a 429 Too Many Requests problem detail.
func Unauthorized ¶
func Unauthorized(detail string, opts ...ProblemOption) *ProblemDetail
Unauthorized creates a 401 Unauthorized problem detail.
func UnprocessableEntity ¶
func UnprocessableEntity(detail string, opts ...ProblemOption) *ProblemDetail
UnprocessableEntity creates a 422 Unprocessable Entity problem detail.
func (ProblemDetail) MarshalJSON ¶
func (p ProblemDetail) MarshalJSON() ([]byte, error)
MarshalJSON implements custom JSON marshaling to include extensions. It returns an error if any extension key conflicts with a reserved RFC 9457 field name.
type ProblemOption ¶
type ProblemOption func(*ProblemDetail)
ProblemOption configures a ProblemDetail.
func WithDetail ¶
func WithDetail(detail string) ProblemOption
WithDetail sets the detail message for the problem detail.
func WithExtension ¶
func WithExtension(key string, value any) ProblemOption
WithExtension adds a custom extension field to the problem detail. Reserved keys (type, title, status, detail, instance) will cause MarshalJSON to return an error.
func WithInstance ¶
func WithInstance(instance string) ProblemOption
WithInstance sets the instance URI for the problem detail.
func WithType ¶
func WithType(typeURI string) ProblemOption
WithType sets the type URI for the problem detail.
type ReadyOption ¶
type ReadyOption func(*readyConfig)
ReadyOption configures the readiness handler behavior.
func WithOverallReadyTimeout ¶
func WithOverallReadyTimeout(d time.Duration) ReadyOption
WithOverallReadyTimeout sets the maximum time allowed for all readiness checks to complete.
type ReadyResponse ¶
type ReadyResponse struct {
Status Status `json:"status"`
Checks []CheckResponse `json:"checks"`
Version string `json:"version,omitempty"`
Environment string `json:"environment,omitempty"`
}
ReadyResponse represents the response payload for the readiness health check endpoint.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages a collection of context keys to extract and log. Each ContextHandler can have its own Registry for isolation.
func (*Registry) Keys ¶
func (r *Registry) Keys() []ContextKey
Keys returns all registered keys as a slice for iteration.
func (*Registry) Register ¶
func (r *Registry) Register(key ContextKey)
Register adds a context key to this registry.
type Server ¶
func NewServer ¶
func NewServer(handler http.Handler, opts ...ServerOption) *Server
NewServer creates a new Server with the provided handler and options.
Example ¶
ExampleNewServer demonstrates creating a basic HTTP server with options.
package main
import (
"fmt"
"net/http"
"time"
"github.com/monkescience/vital"
)
func main() {
// Create a simple handler
mux := http.NewServeMux()
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("Hello, World!"))
})
// Create server with options
server := vital.NewServer(mux,
vital.WithPort(8080),
vital.WithShutdownTimeout(30*time.Second),
)
// Server is ready to use
fmt.Printf("Server configured on port %d\n", 8080)
// Cleanup
_ = server
}
Output: Server configured on port 8080
func (*Server) Run ¶
func (server *Server) Run()
Run starts the server and blocks until a termination signal is received.
type ServerOption ¶
type ServerOption func(*Server)
ServerOption is a functional option for configuring a Server.
func WithIdleTimeout ¶
func WithIdleTimeout(timeout time.Duration) ServerOption
WithIdleTimeout sets the maximum amount of time to wait for the next request.
func WithLogger ¶
func WithLogger(logger *slog.Logger) ServerOption
WithLogger sets the structured logger for the server.
func WithReadTimeout ¶
func WithReadTimeout(timeout time.Duration) ServerOption
WithReadTimeout sets the maximum duration for reading the entire request.
func WithShutdownTimeout ¶
func WithShutdownTimeout(timeout time.Duration) ServerOption
WithShutdownTimeout sets the graceful shutdown timeout.
func WithTLS ¶
func WithTLS(certPath, keyPath string) ServerOption
WithTLS sets the TLS certificate and key paths.
func WithWriteTimeout ¶
func WithWriteTimeout(timeout time.Duration) ServerOption
WithWriteTimeout sets the maximum duration before timing out writes.