logcore

package module
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package logcore is the framework-agnostic logging engine behind the logfiber (Fiber v2) and logfiberv3 (Fiber v3) adapters.

It builds a zap logger pre-wired for APM:

  • Error/Fatal log calls are auto-emitted as APM error events (via apmcore.WrapZapCore on top of apmzap.Core).
  • logcore.LogCtx(ctx) returns a logger decorated with trace.id / transaction.id / span.id when ctx has an active APM transaction.

The package keeps a process-global logger (logcore.Log / Logy / LogCtx) so handlers can call it without plumbing — and exposes logcore.New + SetGlobal for tests and apps that want to inject their own.

It also exports HTTPClientHook(), an adapter that plugs straight into httpclient.SetHook(...) and produces the same "outgoing" structured-log schema the Fiber incoming middlewares use, so requests are searchable in Kibana by req/res/responseTime regardless of direction.

Index

Constants

View Source
const RedactMask = "[REDACTED]"

RedactMask is the placeholder substituted for any value identified as sensitive. It is intentionally short and constant so Kibana queries can match it (e.g. `incoming.req.headers.authorization: "[REDACTED]"`).

Variables

View Source
var DefaultSensitiveKeys = []string{

	"authorization",
	"proxy-authorization",
	"cookie",
	"set-cookie",
	"x-api-key",
	"x-auth-token",
	"x-access-token",
	"x-csrf-token",
	"x-xsrf-token",
	"x-amz-security-token",

	"password",
	"passwd",
	"pwd",
	"secret",
	"client_secret",
	"clientsecret",
	"token",
	"access_token",
	"accesstoken",
	"refresh_token",
	"refreshtoken",
	"id_token",
	"idtoken",
	"api_key",
	"apikey",
	"private_key",
	"privatekey",

	"card",
	"card_number",
	"cardnumber",
	"pan",
	"cvv",
	"cvc",
	"cvv2",
	"security_code",
	"securitycode",
	"ssn",
	"taxid",
	"tax_id",
}

DefaultSensitiveKeys is the case-insensitive set of map keys whose values are masked by the default Redactor. It covers the headers and body fields that most commonly carry credentials, session material, or PII in a payments context. Matching is exact (case-insensitive) against the key; for substring matching see DefaultSensitivePatterns.

The list is deliberately conservative-but-broad: a value is only ever replaced with RedactMask, so over-matching costs a masked log field, never a leaked secret. Callers that need a different policy build their own Redactor via NewRedactor.

View Source
var DefaultSensitivePatterns = []string{
	"password",
	"secret",
	"passwd",
	"authorization",
}

DefaultSensitivePatterns matches keys by substring (case-insensitive). It catches families of fields whose exact name is unpredictable but whose name contains a sensitive token — e.g. "user_password", "stripeSecretKey", "x-vault-token". Patterns are checked in addition to DefaultSensitiveKeys.

Functions

func AutobatchLogger added in v0.7.0

func AutobatchLogger(l *Logger) func(level autobatch.LogLevel, msg string, args ...any)

AutobatchLogger returns a function compatible with autobatch.Config.Logger that routes log entries to l (or the global logger when l is nil). The autobatch package documents its Logger args as slog/zap-sugared key-value pairs, which maps directly to zap's SugaredLogger.Infow etc.

db.Use(autobatch.New(autobatch.Config{
    Logger: logcore.AutobatchLogger(nil), // uses global logger
}))

func DecodeJSONBody

func DecodeJSONBody(raw []byte) any

DecodeJSONBody attempts to unmarshal raw as JSON. On failure (non-JSON payload, empty, malformed) it returns the raw bytes as a string so the log entry still carries something useful. nil bytes → nil.

func FlattenHeaders

func FlattenHeaders(h map[string][]string) any

FlattenHeaders converts a map[string][]string headers shape into a flat map[string]string by joining multi-valued entries with ",". Returns nil when the input is empty so the JSON encoder drops it.

func GSCoreGlobalLogger added in v0.7.0

func GSCoreGlobalLogger() gscoreLoggerIface

GSCoreGlobalLogger is a convenience wrapper that returns GSCoreLogger(nil), i.e. a gscore.Logger backed by the global logger.

func GSCoreLogger added in v0.7.0

func GSCoreLogger(l *Logger) gscoreLoggerIface

GSCoreLogger returns a gscore.Logger-compatible value backed by l. If l is nil the global logger is used. The returned value satisfies the real gscore.Logger interface via structural typing — no import of gscore is needed inside logcore.

func HTTPClientHook

func HTTPClientHook() httpclient.Hook

HTTPClientHook returns a httpclient.Hook that emits one structured log per attempt with the same shape the Fiber middlewares produce for incoming requests — Kibana queries like `outgoing.req.body.id` match regardless of direction.

Use it during bootstrap:

httpclient.SetHook(logcore.HTTPClientHook())

The log line uses the global logger decorated with apmcore trace fields (via LogCtx). Sensitive headers and body fields are masked with DefaultRedactor before logging. For a custom logger, see HookFor; for a custom redaction policy, see HookForRedacting.

func HookFor

func HookFor(l *Logger) httpclient.Hook

HookFor returns the same hook bound to l, applying DefaultRedactor. Nil falls back to the global logger.

func HookForRedacting added in v0.9.1

func HookForRedacting(l *Logger, r *Redactor) httpclient.Hook

HookForRedacting returns the hook bound to l with redaction policy r. A nil l falls back to the global logger; a nil r disables redaction (the request and response are logged verbatim — use only when the data is known safe).

func InstallHTTPClientHook added in v0.7.0

func InstallHTTPClientHook()

InstallHTTPClientHook adds the global-logger hook to the httpclient hook chain. Uses AddHook (not SetHook) so it composes with any previously installed hook. Call once at boot.

func InstallHTTPClientHookFor added in v0.7.0

func InstallHTTPClientHookFor(l *Logger)

InstallHTTPClientHookFor is InstallHTTPClientHook bound to l.

func Log

func Log() *zap.Logger

Log returns the global logger, lazily constructing a production one the first time it's called. Safe for concurrent use.

func LogCtx

func LogCtx(ctx context.Context) *zap.Logger

LogCtx returns the global logger decorated with APM trace fields.

func Logy

func Logy() *zap.SugaredLogger

Logy returns the global sugared logger.

func NewRedactCore added in v0.9.1

func NewRedactCore(c zapcore.Core, r *Redactor) zapcore.Core

NewRedactCore wraps c so sensitive field values are masked before encoding. When r is nil the package DefaultRedactor is used. Plug it in via zap.WrapCore, or let Options.RedactFields do it for you:

logger, _ := logcore.New(logcore.Options{RedactFields: true})

func RedactString added in v0.9.1

func RedactString(s string) string

RedactString masks "Bearer <token>" sequences inside a free-form string, leaving the rest intact. Useful for error messages or URLs that may embed a credential. Returns s unchanged when nothing matches.

func RegisterGlobalWithManager added in v0.7.0

func RegisterGlobalWithManager(mgr CloserRegistrar, phase int, timeout time.Duration)

RegisterGlobalWithManager is RegisterWithManager applied to the global logger.

func SetGlobal

func SetGlobal(l *Logger)

SetGlobal replaces the process-wide logger. Passing nil restores the lazy default.

Types

type CloserRegistrar added in v0.7.0

type CloserRegistrar = gscore.CloserRegistrar

CloserRegistrar is the subset of gscore.Manager used by logcore helpers. It is a type alias for gscore.CloserRegistrar; *gscore.Manager satisfies it directly.

type Incoming

type Incoming struct {
	Req          *Req    `json:"req"`
	Res          *Res    `json:"res"`
	Error        *string `json:"error,omitempty"`
	ResponseTime string  `json:"responseTime"`
}

Incoming is the payload published under the "incoming" key by the Fiber middleware.

type Logger

type Logger struct{ *zap.Logger }

Logger wraps *zap.Logger so the package can attach helpers without shadowing zap's API.

func New

func New(opts Options) (*Logger, error)

New constructs a Logger configured per opts. The returned Logger is independent — call SetGlobal to make it the process-wide default.

func (*Logger) LogCtx

func (l *Logger) LogCtx(ctx context.Context) *zap.Logger

LogCtx returns a child logger decorated with the APM trace fields pulled from ctx — useful for any handler/service log call so the resulting line jumps back to its trace in Kibana.

func (*Logger) RegisterWithManager added in v0.7.0

func (l *Logger) RegisterWithManager(mgr CloserRegistrar, phase int, timeout time.Duration)

RegisterWithManager registers logger.Sync() as a closer on mgr so the zap OS write buffer is flushed before the process exits. Must be called after all other closers so log lines from shutdown itself are not lost.

phase must be gscore.PhasePostDB (value 4). Pass 0 to use PhasePostDB. timeout=0 defaults to 5s.

func (*Logger) Sugar

func (l *Logger) Sugar() *zap.SugaredLogger

Sugar returns the sugared variant of the underlying logger.

type Options

type Options struct {
	// Level is the minimum log level. Default: InfoLevel.
	Level zapcore.Level

	// Encoding selects the zap encoder. "json" (default) or "console".
	Encoding string

	// DisableAPMCore turns off the apmzap.Core wrap. By default the
	// logger's core is wrapped so .Error/.Fatal calls are auto-emitted
	// as APM error events in Kibana → APM → Errors. Set to true to
	// disable (e.g. in tests where you don't want APM noise).
	DisableAPMCore bool

	// Service / Version / Environment are added as permanent fields on
	// every log line — useful as Kibana filters when many services
	// share an index.
	Service     string
	Version     string
	Environment string

	// RedactFields wraps the logger core with NewRedactCore so the values
	// of sensitive fields (Authorization, Cookie, password, token, card
	// data, …) are masked globally before encoding — for every log call,
	// not just the HTTP request/response logs. Defaults to false to stay
	// backwards-compatible; new services should enable it.
	RedactFields bool

	// Redactor overrides the policy used when RedactFields is true. Nil
	// uses DefaultRedactor (the package default key/pattern set).
	Redactor *Redactor

	// Extra is appended to the zap.New options list. Use it for hooks,
	// custom AddCallerSkip, ReplaceCore, etc.
	Extra []zap.Option
}

Options tunes New. All fields are optional; sensible production defaults are applied when zero.

type Outgoing

type Outgoing struct {
	Req          *Req    `json:"req"`
	Res          *Res    `json:"res"`
	Error        *string `json:"error,omitempty"`
	ResponseTime string  `json:"responseTime"`
}

Outgoing is the payload published under the "outgoing" key by the httpclient hook.

type Redactor added in v0.9.1

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

Redactor masks sensitive values in the structured shapes logcore logs (headers as map[string]string, bodies/queries as map[string]any with nested maps and slices). It never mutates its input — redaction always returns a freshly allocated copy, so the caller's request/response data is untouched.

The zero Redactor is not ready for use; build one with NewRedactor or DefaultRedactor.

func DefaultRedactor added in v0.9.1

func DefaultRedactor() *Redactor

DefaultRedactor returns a Redactor using the package defaults (DefaultSensitiveKeys + DefaultSensitivePatterns + RedactMask). It is what the HTTP hook and Fiber adapters apply when no custom Redactor is set.

func NewRedactor added in v0.9.1

func NewRedactor(opts RedactorOptions) *Redactor

NewRedactor builds a Redactor from opts. See RedactorOptions for defaults.

func (*Redactor) Incoming added in v0.9.1

func (r *Redactor) Incoming(inc Incoming) Incoming

Incoming returns a redacted copy of inc with Req and Res masked. The Error and ResponseTime fields are copied through unchanged.

func (*Redactor) Outgoing added in v0.9.1

func (r *Redactor) Outgoing(out Outgoing) Outgoing

Outgoing returns a redacted copy of out with Req and Res masked.

func (*Redactor) Req added in v0.9.1

func (r *Redactor) Req(req *Req) *Req

Req returns a redacted copy of req, or nil when req is nil. Each field is run through Value so sensitive headers and body keys are masked.

func (*Redactor) Res added in v0.9.1

func (r *Redactor) Res(res *Res) *Res

Res returns a redacted copy of res, or nil when res is nil.

func (*Redactor) Value added in v0.9.1

func (r *Redactor) Value(v any) any

Value redacts an arbitrary decoded value (the shape stored in Req/Res fields). Maps with sensitive keys have those values masked; nested maps and slices are walked recursively. Non-map values are returned unchanged — a bare string body cannot be keyed, so there is nothing to mask. The input is never mutated; a copy is returned for any container that is walked.

type RedactorOptions added in v0.9.1

type RedactorOptions struct {
	// Keys is the set of exact (case-insensitive) key names to mask. When
	// nil, DefaultSensitiveKeys is used. Pass a non-nil empty slice to opt
	// out of exact matching entirely.
	Keys []string

	// Patterns is the set of case-insensitive substrings; any key that
	// contains one is masked. When nil, DefaultSensitivePatterns is used.
	// Pass a non-nil empty slice to disable substring matching.
	Patterns []string

	// Extra is merged into the resolved Keys set. Use it to add app-specific
	// fields without restating the defaults.
	Extra []string

	// RemoveKeys drops keys from the resolved exact set (case-insensitive),
	// applied after Keys and Extra. Use it to un-redact a default field
	// without restating the whole Keys list — e.g. keep "x-request-id"
	// visible while everything else in the defaults stays masked.
	//
	// Note: RemoveKeys only affects exact-key matching. A key that is also
	// caught by a substring Pattern (e.g. "secret") stays masked unless you
	// also override Patterns.
	RemoveKeys []string

	// Mask overrides the replacement string. Default: RedactMask.
	Mask string

	// MaxDepth bounds recursion into nested maps/slices, guarding against
	// pathological or cyclic-looking decoded payloads. Default: 32.
	MaxDepth int

	// PartialReveal opts specific sensitive keys into partial redaction:
	// instead of replacing the whole value with Mask, the last N characters
	// are kept and the rest is masked, producing "[REDACTED]…1111". The map
	// is keyed by field name (case-insensitive) → N, the count of trailing
	// characters to reveal.
	//
	// Only keys already considered sensitive (via Keys/Patterns/Extra) are
	// eligible — listing a non-sensitive key here has no effect. Keys not
	// listed here stay fully masked, so the default policy is unchanged.
	//
	// Partial reveal only applies to string values long enough that the
	// revealed tail is at most half the value (so a short secret is never
	// mostly exposed); otherwise the value falls back to a full mask. This
	// guards against, e.g., a 5-char password leaking 4 of its characters.
	PartialReveal map[string]int
}

RedactorOptions configures NewRedactor. All fields are optional.

type Req

type Req struct {
	Params      any `json:"params,omitempty"`
	QueryString any `json:"queryString,omitempty"`
	Headers     any `json:"headers,omitempty"`
	Body        any `json:"body,omitempty"`
}

Req is the request side of an incoming or outgoing log entry. Fields stay omitempty so the JSON in Kibana is compact.

type Res

type Res struct {
	Headers    any    `json:"headers,omitempty"`
	Body       any    `json:"body,omitempty"`
	StatusCode string `json:"statusCode"`
}

Res is the response side. StatusCode is a string ("Ø" when unknown) to match the pattern used in existing dashboards.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL