logger

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 9, 2026 License: MIT Imports: 14 Imported by: 0

README

Logging package (logger)

This package provides a structured logging facility for an event-driven microservice architecture based on zerolog.

  • Outputs: console/stderr (always), optional file logs (LogDir/app.log when EnableFile: true), and optional Seq over HTTP.
  • Format: JSON structured logs only (JSON lines for file output).
  • Levels: info, warn, error, critical.
  • Structured fields (always available): service, environment, service_version, trace_id, message, level, timestamp.
  • Common contextual fields: component, event, function, error_path, duration_ms, retry_count, metadata, exception.

Strategy alignment (Saga-based microservices)

This section highlights how to use this package in a saga (orchestrator-based) architecture and how it maps to the standardized logging strategy.

For the cross-language schema contract (Go + C# + Python examples), see CONTRACT.md.

0. Compliance checklist (package + service integration)

  • Structured logging only: All production logs emitted via this package are JSON structured logs.
  • Trace is mandatory:
    • Required in every log: trace_id
    • Strongly recommended in every log (strategy requirement): span_id when available
  • Consistent log levels:
    • Supported: info, warn, error, critical
  • Contextual data:
    • Package-provided baseline: service, environment, service_version, timestamp, level, message, plus function and error_path
    • Supported: component (Orchestrator/Participant/Consumer/API) and metadata object
  • Propagation:
    • Package supports: trace ID propagation via context.Context (WithTraceID)
    • Integration required: HTTP/message header propagation must be implemented in each service (gateway, orchestrator, participants, consumers).
  • Observability integration:
    • Package supports: emitting trace_id / span_id as optional fields if the service provides them
    • Optional helper: WithTraceFromContext(ctx) (build with -tags=otel) extracts IDs from OpenTelemetry context automatically.
  • Sensitive data masking:
    • Supported: key-based redaction via Config.RedactKeys (applies to metadata and Seq properties).
  • Saga events:
    • Supported pattern: use event for saga events like SagaStarted, SagaStepCompleted, SagaStepFailed, CompensationTriggered, CompensationFailed
    • Integration required: orchestrator/participants must emit these events consistently.
  • Centralized aggregation / retention / alerts / dashboards / runbooks:
    • Partially supported: Seq output
    • Integration required: log aggregation deployment, retention policy, alert rules, dashboards, and runbooks are outside this package.

1. Standard schema mapping and gaps

Your strategy's canonical fields vs this package's current fields:

Strategy field Strategy meaning Package field today Status
timestamp Event time timestamp Supported
level Info/Warn/Error/Critical level Supported
service Service name service Supported
serviceVersion Deployed version service_version Supported
environment prod/stage/dev environment Supported
component Orchestrator/Participant/Consumer/API component Supported
traceId End-to-end trace correlation trace_id Required
spanId OTel span id span_id Supported (optional)
event Event name event Supported
message Human-readable description message Supported
metadata Structured business context metadata Supported
performance.durationMs Duration in ms duration_ms Supported
performance.retryCount Retries attempted retry_count Supported (optional)
exception Error details exception Supported

Important: the package uses snake_case keys (e.g., trace_id, span_id). If your org standard mandates camelCase (traceId, etc.), either:

  • Standardize on snake_case in the strategy for Go services, or
  • Add a compatibility layer/field-aliasing in this package so emitted keys match the strategy exactly.

1. Configuration

Call InitConfig once at startup in each application that uses this package.

import (
    "os"
    "time"

    "github.com/PACRAKora/logger/logger"
)

func main() {
    logging.InitConfig(logging.Config{
        // REQUIRED FIELD: unique per service
        Service: os.Getenv("SERVICE_NAME"),

        // CONFIGURABLE
        Env:         getenvDefault("APP_ENV", "production"),
        ServiceVersion: os.Getenv("SERVICE_VERSION"),
        ConsoleJSON: getenvDefault("LOG_CONSOLE_JSON", "true") == "true",

        // Leave LogDir empty to defer to LOG_DIR env var (recommended)
        LogDir: "",

        // EnableFile: true to write logs to LogDir/app.log (local dev / VM only)
        // Seq is auto-enabled from SEQ_ENABLE, SEQ_URL, SEQ_API_KEY env vars

        // Masking/redaction (recommended defaults)
        RedactKeys: []string{"password", "token", "authorization", "api_key"},

        TimeFormat: time.RFC3339,
    })

    // ...
}

Recommended environment variables (set per deployment):

  • SERVICE_NAME – logical service name (e.g. orders-service).
  • APP_ENVdevelopment, staging, or production.
  • SERVICE_VERSION – deployed version identifier (e.g. 1.2.3 or git SHA).
  • LOG_DIR – absolute or project-root-relative path for logs (e.g. /app/logs).
  • LOG_CONSOLE_JSON"true" for JSON console, "false" for pretty output in development.
  • SEQ_ENABLE"true"/"false" to toggle Seq.
  • SEQ_URL – e.g. http://seq:5341.
  • SEQ_API_KEY – optional Seq API key.

If LogDir is empty in config:

  • The package will first look at LOG_DIR.
  • If LOG_DIR is not set, it falls back to "logs" in the current working directory.

2. Using trace IDs

Use contexts to propagate a trace_id across calls.

import "github.com/PACRAKora/logger/logger"

// New request / message entry-point:
func handler(ctx context.Context) {
    // Generate or attach a trace id
    ctx = logging.WithTraceID(ctx, "")

    logging.Info(ctx, "handler", "started handling request")
}

If the context already has a trace id (e.g. from an HTTP gateway), use:

ctx, traceID := logging.TraceIDFromContext(ctx)
_ = traceID // use for headers, etc.

2.1 Strategy-required propagation (what services must do)

This package propagates trace ID via context.Context, but your strategy also requires propagating identifiers across boundaries.

HTTP headers (sync calls):

  • X-Trace-Id: propagate into trace_id (required)

Message headers (async):

  • trace_id (name depends on your broker conventions)

Services should:

  • Extract these IDs at entry points (HTTP middleware, message consumer wrapper)
  • Attach them to context
  • Ensure every log line includes them (either as first-class fields in this package or via additional options you add/extend)

3. Emitting logs

ctx := logging.WithTraceID(context.Background(), "")

// Info
logging.Info(ctx, "main", "application started")

// Warn (function and error_path are REQUIRED)
logging.Warn(ctx, "processEvent", "/handler/processEvent", "unexpected payload",
    logging.WithEvent("SagaStepRetrying"),
    logging.WithComponent("Participant"),
    logging.WithMetadata(map[string]any{"order_id": "order-2024-001"}),
    logging.WithTopic("user-events"),
)

// Error (function and error_path are REQUIRED)
logging.Error(ctx, "processEvent", "/handler/processEvent", "failed to persist event",
    logging.WithRetryCount(3),
    logging.WithMessageID("msg-123"),
    logging.WithException(errors.New("example failure")),
)

All three outputs (file, console, Seq if enabled) receive the same structured event.

3.1 Saga event naming (strategy alignment)

Use event consistently for saga lifecycle events so production incident queries and business analytics are possible:

  • SagaStarted
  • SagaStepCompleted
  • SagaStepFailed
  • SagaStepRetrying
  • SagaAborted
  • SagaCompleted
  • CompensationTriggered
  • CompensationFailed

4. Where logs go

  • File: app.log inside LogDir (JSON lines).
  • Console:
    • Pretty human-readable in development when Env == "development" and ConsoleJSON == false.
    • JSON in all other cases.
  • Seq:
    • Enabled when EnableSeq == true and SeqURL is non-empty.
    • Failures to reach Seq never crash the app; they are ignored.

5. Extension points

  • Config (in config.go) – add new configurable knobs.
  • Option and With… helpers (in logger.go) – add new optional fields.
  • seqWriter (in seq.go) – customize Seq payloads, HTTP client, retries.
  • initLogger (in writer.go) – add hooks, sampling, or additional writers.

Build tags

OpenTelemetry trace/span extraction

If you want WithTraceFromContext(ctx) support, build with:

go test -tags=otel ./...

This intentionally keeps OpenTelemetry as an optional dependency.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CorrelationIDFromContext added in v0.1.0

func CorrelationIDFromContext(ctx context.Context) (context.Context, string)

CorrelationIDFromContext extracts the correlation ID from the context. Returns "" if not found.

func Critical

func Critical(ctx context.Context, fnName, errorPath, msg string, opts ...Option)

Critical logs a critical event. REQUIRED FIELD: fnName and errorPath must both be provided and non-empty.

func Error

func Error(ctx context.Context, fnName, errorPath, msg string, opts ...Option)

Error logs an error event. REQUIRED FIELD: fnName and errorPath must both be provided and non-empty.

func Info

func Info(ctx context.Context, fnName string, msg string, opts ...Option)

Info logs an informational event.

func InitConfig

func InitConfig(cfg Config)

InitConfig initializes global logging configuration. This must be called once during application startup before using the logging package.

func Logger

func Logger() zerolog.Logger

Logger returns the configured root logger.

func SpanIDFromContext added in v0.0.9

func SpanIDFromContext(ctx context.Context) (context.Context, string)

SpanIDFromContext extracts the span ID from the context. Returns "" if not found.

func TraceIDFromContext

func TraceIDFromContext(ctx context.Context) (context.Context, string)

TraceIDFromContext extracts the trace ID from the context. Returns "" if not found.

func Warn

func Warn(ctx context.Context, fnName, errorPath, msg string, opts ...Option)

Warn logs a warning event. REQUIRED FIELD: fnName and errorPath must both be provided and non-empty.

func WithCorrelationID added in v0.1.0

func WithCorrelationID(ctx context.Context, id string) context.Context

WithCorrelationID attaches a business transaction correlation ID to the context.

func WithSpanID added in v0.0.9

func WithSpanID(ctx context.Context, spanID string) context.Context

WithSpanID attaches the given span ID to the context.

func WithTraceID

func WithTraceID(ctx context.Context, traceID string) context.Context

WithTraceID attaches the given trace ID to the context. REQUIRED FIELD: Trace ID is used to tie together related log events.

Types

type Config

type Config struct {
	// Service is the logical service name for this application.
	// REQUIRED FIELD: Must be provided and non-empty.
	Service string

	// Env represents the deployment environment (e.g. "development", "staging", "production").
	// CONFIGURABLE
	Env string

	// RedactKeys are case-insensitive field names that should be redacted from metadata/properties.
	// Example: ["password", "token", "authorization", "ssn"].
	// CONFIGURABLE
	RedactKeys []string

	// LogDir is the directory where log files will be written.
	// Only used when EnableFile is true.
	// CONFIGURABLE
	LogDir string

	// EnableFile writes logs to a file at LogDir/app.log.
	// Disabled by default; suitable for containerised deployments where stderr is collected.
	EnableFile bool

	// ConsoleJSON determines whether console output should be JSON (true)
	// or a human-friendly pretty format (false).
	// CONFIGURABLE
	ConsoleJSON bool

	// EnableSeq toggles sending logs to Seq over HTTP.
	// Can also be enabled by setting SEQ_ENABLE=true in the environment.
	// CONFIGURABLE
	EnableSeq bool

	// SeqURL is the GELF UDP address for Seq (e.g. "localhost:12201").
	// Requires a GELF UDP input to be enabled in Seq.
	// Can also be set via the SEQ_URL environment variable.
	// CONFIGURABLE
	SeqURL string

	// SeqAPIKey is unused with GELF UDP transport and kept for backwards compatibility.
	// Can also be set via the SEQ_API_KEY environment variable.
	// CONFIGURABLE
	SeqAPIKey string

	// TimeFormat is the layout used for timestamps when pretty-printing.
	// CONFIGURABLE
	TimeFormat string
}

Config holds all logging configuration. CONFIGURABLE: All fields in this struct are intended to be set from application configuration.

func ConfigOrDefault

func ConfigOrDefault() Config

ConfigOrDefault returns the current globalConfig. It panics if InitConfig has not been called.

type Event

type Event struct {
	// Required / baseline fields
	Service     string `json:"service"`
	Environment string `json:"environment,omitempty"`

	TraceID  string `json:"trace_id"`
	SpanID   string `json:"span_id,omitempty"`
	ParentID string `json:"parent_id,omitempty"`

	// event is the canonical field name for business/saga event names.
	Event string `json:"event,omitempty"`

	// Location/context fields
	Function         string `json:"function,omitempty"`
	ErrorPath        string `json:"error_path,omitempty"`
	RetryCount       int    `json:"retry_count,omitempty"`
	DurationMs       int64  `json:"duration_ms,omitempty"`
	SubscribeSubject string `json:"subscribe_subject,omitempty"`
	PublishSubject   string `json:"publish_subject,omitempty"`
	ReceivedPayload  any    `json:"received_payload,omitempty"`
	ResponsePayload  any    `json:"response_payload,omitempty"`

	// Who — actor identity (flat for direct filterability)
	UserID   string `json:"user_id,omitempty"`   // user ID performing the action
	UserRole string `json:"user_role,omitempty"` // "user" | "service" | "scheduler" | "system"
	ActorIP  string `json:"actor_ip,omitempty"`  // originating IP

	// What — action on a resource (flat for direct filterability)
	Action  string `json:"action,omitempty"`  // "create" | "update" | "delete" | "read"
	Outcome string `json:"outcome,omitempty"` // "success" | "failure" | "partial"

	// Correlation — ties events across services/traces for one business transaction
	CorrelationID string `json:"correlation_id,omitempty"`

	// Additional structured context
	Metadata  map[string]any `json:"metadata,omitempty"`
	Exception *Exception     `json:"exception,omitempty"`
}

Event represents a structured log event with required and optional fields.

Note: This struct is primarily used as an internal carrier for field values. The logger writes fields using zerolog; not all struct fields are serialized directly.

type Exception

type Exception struct {
	Type    string `json:"type,omitempty"`
	Message string `json:"message,omitempty"`
	Stack   string `json:"stack,omitempty"`
}

Exception represents a structured error payload for logs.

type Option

type Option func(*Event)

Option allows adding optional metadata to an Event. EXTENSION POINT: Add more option helpers for additional fields.

func WithAction added in v0.1.0

func WithAction(action string) Option

WithAction sets the action performed ("create" | "update" | "delete" | "read").

func WithActorIP added in v0.1.0

func WithActorIP(ip string) Option

WithActorIP sets the originating IP address of the actor.

func WithCorrelationIDOption added in v0.1.0

func WithCorrelationIDOption(id string) Option

WithCorrelationIDOption sets the correlation ID as an option, overriding any value from context.

func WithDurationMs

func WithDurationMs(ms int64) Option

WithDurationMs records the operation duration in milliseconds.

func WithEnvironment

func WithEnvironment(env string) Option

WithEnvironment sets the deployment environment, overriding the config default.

func WithEvent

func WithEvent(evName string) Option

WithEvent sets the canonical event field.

func WithException

func WithException(err error) Option

WithException populates the structured exception field. If err is nil, this option does nothing.

func WithMetadata

func WithMetadata(metadata map[string]any) Option

WithMetadata attaches arbitrary business-context key/value pairs; sensitive keys are redacted.

func WithOutcome added in v0.1.0

func WithOutcome(outcome string) Option

WithOutcome sets the outcome of the action ("success" | "failure" | "partial").

func WithParentID added in v0.0.9

func WithParentID(id string) Option

WithParentID sets the parent span ID for distributed tracing context.

func WithPublishSubject added in v0.0.4

func WithPublishSubject(subject string) Option

WithPublishSubject sets the NATS subject the service is publishing this event to.

func WithReceivedPayload added in v0.0.6

func WithReceivedPayload(payload []byte) Option

WithReceivedPayload attaches the raw NATS inbound message body to the log entry. If payload is valid JSON it is stored as a structured value; otherwise as a string.

func WithResponsePayload added in v0.0.6

func WithResponsePayload(payload []byte) Option

WithResponsePayload attaches the outbound NATS response body to the log entry. If payload is valid JSON it is stored as a structured value; otherwise as a string.

func WithRetryCount

func WithRetryCount(count int) Option

WithRetryCount records the number of retry attempts made.

func WithSubscribeSubject added in v0.0.4

func WithSubscribeSubject(subject string) Option

WithSubscribeSubject sets the NATS subject the service consumed this event from.

func WithTraceFromContext

func WithTraceFromContext(ctx context.Context) Option

WithTraceFromContext is a no-op unless built with -tags=otel.

func WithUserID added in v0.1.1

func WithUserID(id string) Option

WithUserID sets the user ID performing the action.

func WithUserRole added in v0.1.1

func WithUserRole(t string) Option

WithUserRole sets the user role ("user" | "service" | "scheduler" | "system").

Jump to

Keyboard shortcuts

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