Documentation
¶
Overview ¶
Package logger is the project-wide slog wrapper. All logging goes through context.Context — services and handlers must NOT embed a *slog.Logger field. Use Load(ctx) to fetch the logger and WithService(ctx, name) at the service boundary so every record carries the same "service" attribute.
Output format is fixed: text handler to stderr with RFC3339 timestamps and stable attribute ordering. This guarantees a single consistent format across the entire process; do not construct ad-hoc slog.New(...) instances in service code — call NewLogger or NewTestLogger here so format and handler options stay aligned.
Index ¶
- Constants
- Variables
- func APIConsoleMiddleware() echo.MiddlewareFunc
- func AddAttrs(ctx context.Context, attrs ...slog.Attr) context.Context
- func EchoMiddleware(logger *slog.Logger) echo.MiddlewareFunc
- func ExtractErrorFields(body []byte) (string, string)
- func HasService(ctx context.Context, name string) bool
- func Load(ctx context.Context) *slog.Logger
- func Middleware(logger *slog.Logger) func(http.Handler) http.Handler
- func NewLogger(level slog.Level) *slog.Logger
- func NewTestLogger() *slog.Logger
- func Save(ctx context.Context, logger *slog.Logger) context.Context
- func ServiceTag(ctx context.Context) string
- func WithService(ctx context.Context, name string) context.Context
- func WithWorker(ctx context.Context, service, job string) context.Context
- type CapturedRequest
- type RequestRingBuffer
- func (r *RequestRingBuffer) Add(req *CapturedRequest)
- func (r *RequestRingBuffer) GetAll() []*CapturedRequest
- func (r *RequestRingBuffer) MaxSize() int
- func (r *RequestRingBuffer) Recent(n int, match func(*CapturedRequest) bool) []*CapturedRequest
- func (r *RequestRingBuffer) Subscribe() chan *CapturedRequest
- func (r *RequestRingBuffer) Unsubscribe(ch chan *CapturedRequest)
Constants ¶
const (
// RequestsEndpointPath is the request-log read endpoint; it is not itself logged.
RequestsEndpointPath = "/_gopherstack/requests"
)
Variables ¶
var GlobalRingBuffer = NewRequestRingBuffer(defaultBufferSize)
GlobalRingBuffer is the global buffer for the Live API Console. It stores the last 100 requests.
var Key = ctxval.NewKey[*slog.Logger]("logger") //nolint:gochecknoglobals // existing issue.
Key is the typed context key under which the logger is stored. Exported so middleware that wants raw ctxval access can reuse it.
Functions ¶
func APIConsoleMiddleware ¶
func APIConsoleMiddleware() echo.MiddlewareFunc
APIConsoleMiddleware captures incoming API requests and stores them in the ring buffer. It should be injected after standard loggers but before request processing.
func AddAttrs ¶
AddAttrs returns a child context whose logger has attrs pre-attached to every record. slog.Logger.With does not mutate the parent so this is safe for concurrent use across requests.
func EchoMiddleware ¶
func EchoMiddleware(logger *slog.Logger) echo.MiddlewareFunc
EchoMiddleware creates an Echo middleware that injects a logger into the request context.
func ExtractErrorFields ¶
ExtractErrorFields pulls the AWS error code and message from a (possibly truncated) JSON or XML error body.
func HasService ¶
HasService reports whether WithService already tagged ctx's logger with name.
func Load ¶
Load returns the logger carried on ctx, or slog.Default() when none is set. Always returns non-nil so callers can chain methods without a guard.
func Middleware ¶
Middleware creates an HTTP middleware that injects a logger into the request context.
func NewTestLogger ¶
NewTestLogger returns a logger with debug level enabled, useful in tests.
func ServiceTag ¶
ServiceTag returns the service name WithService attached to ctx, or "".
func WithService ¶
WithService returns a child context whose logger carries service=<name>. Call this once at the entry of every service handler so downstream records emitted via Load(ctx) are uniformly tagged. The service tag persists across AddAttrs calls because slog.Logger.With layers attributes.
func WithWorker ¶
WithWorker returns a child context whose logger carries service=<service> and worker=<service>-<job>, so records emitted by a background routine are attributable to the specific job that produced them.
Safety rules (the logger is a *slog.Logger pointer carried in ctx):
- Call this ONCE at the entry of the worker goroutine, never inside a loop. Each call layers attributes onto a new logger; repeating it on a long-lived context would grow the attribute chain (and memory) without bound.
- Derive ctx from the process/lifecycle context (e.g. the janitor context), never from a per-request context, so worker logs don't inherit request_id and don't pin a finished request alive.
Enrichment is copy-on-write: slog.Logger.With does not mutate the parent, so concurrent workers and requests never corrupt each other's logger.
Types ¶
type CapturedRequest ¶
type CapturedRequest struct {
Timestamp time.Time `json:"timestamp"`
Headers map[string]string `json:"headers"`
ID string `json:"id"`
Method string `json:"method"`
Path string `json:"path"`
Query string `json:"query,omitempty"`
Service string `json:"service,omitempty"`
Body string `json:"body,omitempty"`
ErrorCode string `json:"error_code,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
Status int `json:"status"`
Duration time.Duration `json:"duration_ms"`
}
CapturedRequest represents a single HTTP request captured by the console middleware.
func (*CapturedRequest) Clone ¶
func (r *CapturedRequest) Clone() *CapturedRequest
Clone returns a copy that shares no mutable state with r.
type RequestRingBuffer ¶
type RequestRingBuffer struct {
// contains filtered or unexported fields
}
RequestRingBuffer holds the last N captured requests.
func NewRequestRingBuffer ¶
func NewRequestRingBuffer(maxSize int) *RequestRingBuffer
NewRequestRingBuffer creates a new ring buffer for captured requests.
func (*RequestRingBuffer) Add ¶
func (r *RequestRingBuffer) Add(req *CapturedRequest)
Add appends a new request into the ring buffer and notifies subscribers.
func (*RequestRingBuffer) GetAll ¶
func (r *RequestRingBuffer) GetAll() []*CapturedRequest
GetAll returns copies of all captured requests in chronological order.
func (*RequestRingBuffer) MaxSize ¶
func (r *RequestRingBuffer) MaxSize() int
MaxSize returns the buffer capacity.
func (*RequestRingBuffer) Recent ¶
func (r *RequestRingBuffer) Recent(n int, match func(*CapturedRequest) bool) []*CapturedRequest
Recent returns copies of up to n captured requests accepted by match (nil matches all), newest first.
func (*RequestRingBuffer) Subscribe ¶
func (r *RequestRingBuffer) Subscribe() chan *CapturedRequest
Subscribe adds a channel to receive incoming requests.
func (*RequestRingBuffer) Unsubscribe ¶
func (r *RequestRingBuffer) Unsubscribe(ch chan *CapturedRequest)
Unsubscribe removes a channel from receiving requests.