Português | English
Axio is a structured logger for Go, focused on observability, audit, and data governance. It standardizes fields, reduces the risk of sensitive data leakage, and enables correlation with distributed tracing, without coupling your application to the internal logging engine.
Direct dependency on logging libraries (Zap, Logrus, zerolog) couples the entire application to a specific implementation. Changes to the logging engine require refactoring dozens of files.
Axio functions as an abstraction layer with a stable interface (Logger). Business code depends only on the Axio interface, not on the internal engine.
| Advantage | Description |
|---|---|
| Decoupling | Business code doesn't know about Zap |
| Facilitated migration | Change internal engine without refactoring apps |
| Consistency | Same API for all teams/services |
| Extensibility | Hooks, metrics, tracing via composition |
| Testability | Interface facilitates mocks in tests |
| Centralized governance | PII, audit, formats in one place |
┌─────────────────────────────────────────────────┝
│ Application (business code) │
│ ↓ │
│ axio.Logger (interface) │
│ ↓ │
│ ┌─────────────────────────────────────────┝ │
│ │ Axio Core │ │
│ │ ┌─────┝ ┌─────┝ ┌───────┝ ┌─────────┝ │ │
│ │ │ PII │ │Audit│ │Tracing│ │ Metrics │ │ │
│ │ └─────┘ └─────┘ └───────┘ └─────────┘ │ │
│ │ ↓ │ │
│ │ Logging Engine │ │
│ │ (Zap - replaceable) │ │
│ └─────────────────────────────────────────┘ │
│ ↓ │
│ Outputs (Console/File/Stdout) │
└─────────────────────────────────────────────────┘
- Installation
- Quick Example
- Configuration
- Features
- Logging Best Practices
- Guide by Service Type
- Examples and Anti-patterns
- Troubleshooting
go get github.com/pragmabits/axioimport "github.com/pragmabits/axio"Complete HTTP handler with context, annotations, and cleanup:
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/pragmabits/axio"
)
var logger axio.Logger
func main() {
var err error
logger, err = axio.New(axio.Config{
ServiceName: "sales-api",
ServiceVersion: "1.0.0",
Environment: axio.Production,
Level: axio.LevelInfo,
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()
http.HandleFunc("/api/orders", handleOrder)
http.ListenAndServe(":8080", nil)
}
func handleOrder(w http.ResponseWriter, r *http.Request) {
start := time.Now()
ctx := r.Context()
// ... business logic ...
logger.With(
axio.Annotate("http", axio.HTTP{
Method: r.Method,
URL: r.URL.Path,
StatusCode: 201,
LatencyMS: time.Since(start).Milliseconds(),
ClientIP: r.RemoteAddr,
}),
axio.Annotate("user_id", "usr_123"),
).Info(ctx, "order created")
w.WriteHeader(http.StatusCreated)
}| Field | Type | Required | Default | Values | Validation |
|---|---|---|---|---|---|
ServiceName |
string |
No | "" |
any | - |
ServiceVersion |
string |
No | "" |
any | - |
Environment |
Environment |
No | development |
production, staging, development |
ErrInvalidEnvironment if invalid |
InstanceID |
string |
No | "" |
any | - |
Level |
Level |
No | info |
debug, info, warn, error |
ErrInvalidLevel if invalid |
CallerSkip |
int |
No | 0 |
>= 0 |
- |
DisableSample |
bool |
No | false |
true, false |
- |
AgentMode |
bool |
No | false |
true, false |
If true, outputs must be stdout+json |
Outputs |
[]OutputConfig |
No | auto | see OutputConfig | Validated individually |
PIIEnabled |
bool |
No | false |
true, false |
- |
PIIPatterns |
[]PIIPattern |
No | [cpf, cnpj, credit_card] |
see PII table | - |
PIIFields |
[]string |
No | DefaultSensitiveFields() |
any | - |
PIICustomPatterns |
[]CustomPII |
No | [] |
see CustomPII | Regex must be valid |
TracerType |
string |
No | noop |
otel, noop |
ErrInvalidTracer if invalid |
Audit |
AuditConfig |
No | disabled | see AuditConfig | - |
Metrics |
MetricsConfig |
No | disabled | see MetricsConfig | - |
| Field | Type | Required | Default | Values | Validation |
|---|---|---|---|---|---|
Type |
OutputType |
Yes | - | console, stdout, file |
ErrInvalidOutputType if invalid |
Format |
Format |
Yes | - | json, text |
ErrInvalidFormat if invalid |
Path |
string |
Conditional | "" |
file path | ErrFileOutputNoPath if Type=file and empty |
Rotation |
RotationConfig |
No | disabled | see RotationConfig | Only used when Type=file |
| Field | Type | Required | Default | Values | Validation |
|---|---|---|---|---|---|
MaxSize |
int |
No | 0 |
megabytes (0 = no size limit) | - |
MaxAge |
int |
No | 0 |
days (0 = no age limit) | - |
MaxBackups |
int |
No | 0 |
count (0 = retain all) | - |
Compress |
bool |
No | false |
true, false |
- |
LocalTime |
bool |
No | false |
true, false |
- |
Interval |
Duration |
No | 0 |
e.g., 24h, 1h30m, 500ms |
- |
| Field | Type | Required | Default | Values | Validation |
|---|---|---|---|---|---|
Enabled |
bool |
No | false |
true, false |
- |
StorePath |
string |
Conditional | "" |
file path | ErrAuditWithoutPath if Enabled=true and empty |
| Field | Type | Required | Default | Values | Validation |
|---|---|---|---|---|---|
Enabled |
bool |
No | false |
true, false |
- |
MeterName |
string |
No | axio |
any | - |
MeterVersion |
string |
No | 1.0.0 |
any | - |
Axio supports configuration via YAML, JSON, or TOML:
// Load from file (detects format by extension)
config, err := axio.LoadConfig("/etc/axio/config.yaml")
// Load from io.Reader (specify format)
config, err := axio.LoadConfigFrom(reader, "yaml")
// Panic version (useful in main)
config := axio.MustLoadConfig("/etc/axio/config.yaml")Complete YAML example:
serviceName: sales-api
serviceVersion: 2.1.0
environment: production
instanceId: pod-abc123
level: info
callerSkip: 0
agentMode: false
outputs:
- type: stdout
format: json
- type: file
format: json
path: /var/log/app.log
rotation:
maxSize: 100
maxAge: 30
maxBackups: 10
compress: true
interval: 24h
piiEnabled: true
piiPatterns:
- cpf
- cnpj
- email
- credit_card
piiFields:
- password
- token
- secret
piiCustomPatterns:
- name: employee_id
pattern: "EMP-\\d{6}"
mask: "EMP-******"
audit:
enabled: true
storePath: /var/lib/axio/chain.json
tracer: otel
metrics:
enabled: true
meterName: axio
meterVersion: 1.0.0| Type | Destination | Typical use |
|---|---|---|
console |
stderr | Local development |
stdout |
stdout | Containers with collection agents |
file |
file | Environments without agents, auditing |
| Format | Description | Use |
|---|---|---|
json |
Structured JSON | Production, aggregation systems |
text |
Colored text | Local development |
| Environment | Default Output | Format | Stack Trace |
|---|---|---|---|
development |
Console | Text | No |
staging |
Stdout | JSON | On errors |
production |
Stdout | JSON | On errors |
// Multiple outputs
logger, _ := axio.New(config,
axio.WithOutputs(
axio.Console(axio.FormatText),
axio.Stdout(axio.FormatJSON),
axio.MustFile("/var/log/app.log", axio.FormatJSON),
),
)
// Agent mode (stdout + JSON, optimized for Promtail, Fluent Bit, etc.)
logger, _ := axio.New(config, axio.WithAgentMode())File outputs support automatic rotation by size, time interval, or both:
// Size-based rotation (rotate when file exceeds 100 MB)
out, _ := axio.RotatingFile("/var/log/app.log", axio.FormatJSON, axio.RotationConfig{
MaxSize: 100,
MaxBackups: 5,
Compress: true,
})
// Time-based rotation (rotate every 24 hours)
out, _ := axio.RotatingFile("/var/log/app.log", axio.FormatJSON, axio.RotationConfig{
Interval: axio.Duration(24 * time.Hour),
MaxAge: 30,
})
// Combined (whichever triggers first)
out, _ := axio.RotatingFile("/var/log/app.log", axio.FormatJSON, axio.RotationConfig{
MaxSize: 100,
Interval: axio.Duration(24 * time.Hour),
MaxBackups: 10,
MaxAge: 30,
Compress: true,
})
logger, _ := axio.New(config, axio.WithOutputs(out))
defer logger.Close()MustRotatingFile is available for initialization where failure should be fatal.
| Level | Constant | Semantics | When to use |
|---|---|---|---|
| Debug | LevelDebug |
Technical details | Development, troubleshooting |
| Info | LevelInfo |
Normal events | Start/end of operations, milestones |
| Warn | LevelWarn |
Non-critical anomalies | Timeouts, fallbacks, degradation |
| Error | LevelError |
Real failures | Operation failed, requires attention |
Methods:
logger.Debug(ctx, "debug details")
logger.Info(ctx, "processed %d items", count)
logger.Warn(ctx, err, "timeout querying supplier")
logger.Error(ctx, err, "failed to persist order")Adds key-value fields to the log:
logger.With(
axio.Annotate("user_id", "usr_123"),
axio.Annotate("order_id", "ord_456"),
axio.Annotate("amount_cents", 15000),
).Info(ctx, "order created")Struct for HTTP request metadata:
logger.With(axio.Annotate("http", axio.HTTP{
Method: "POST",
URL: "/api/v1/orders",
StatusCode: 201,
LatencyMS: 45,
UserAgent: r.UserAgent(),
ClientIP: r.RemoteAddr,
})).Info(ctx, "request processed")| Field | Type | Description |
|---|---|---|
Method |
string |
HTTP method (GET, POST, etc.) |
URL |
string |
Request path |
StatusCode |
int |
Response code |
LatencyMS |
int64 |
Latency in milliseconds |
UserAgent |
string |
Client User-Agent |
ClientIP |
string |
Client IP |
Implement Annotable for types that produce multiple fields:
type Order struct {
ID string
Items []Item
secret string // will not be logged
}
func (o Order) Append(target []axio.Annotation) []axio.Annotation {
return append(target,
axio.Annotate("order_id", o.ID),
axio.Annotate("item_count", len(o.Items)),
)
}
// Usage — fields are expanded individually in the log output
logger.With(axio.Annotate("order", order)).Info(ctx, "order processed")Creates loggers with namespace:
httpLogger := logger.Named("http")
dbLogger := logger.Named("db")
cacheLogger := logger.Named("cache")
httpLogger.Info(ctx, "request received") // logger: "http"
dbLogger.Info(ctx, "query executed") // logger: "db"Hooks process log entries before writing. Executed in fixed order:
- PIIHook - masks sensitive data
- AuditHook - calculates hash chain
- Custom hooks - in the order passed to
WithHooks
type Hook interface {
Name() string
Process(ctx context.Context, entry *Entry) error
}type TenantHook struct {
tenantID string
}
func (h TenantHook) Name() string { return "tenant" }
func (h TenantHook) Process(ctx context.Context, entry *axio.Entry) error {
entry.Annotations = append(entry.Annotations,
axio.Annotate("tenant_id", h.tenantID))
return nil
}
// Usage
logger, _ := axio.New(config, axio.WithHooks(TenantHook{tenantID: "acme"}))PII (Personally Identifiable Information) is any data that can identify a person, directly or indirectly. Examples: CPF, CNPJ, email, phone, IP address, card numbers.
In environments with centralized logs, exposed PII represents risk of:
- Data leakage
- Non-compliance with LGPD/GDPR
- Exposure in security incidents
References:
| Pattern | Constant | Detected formats | Mask |
|---|---|---|---|
| CPF | PatternCPF |
123.456.789-01, 12345678901 |
***.***.***-** |
| CNPJ | PatternCNPJ |
12.345.678/0001-90 |
**.***.***/****-** |
| Credit Card | PatternCreditCard |
1234-5678-9012-3456 |
****-****-****-**** |
PatternEmail |
user@domain.com |
***@***.*** |
|
| Phone | PatternPhone |
(11) 99999-9999 |
(**) *****-**** |
| Phone (no area) | PatternPhoneNoDDD |
99999-9999 |
*****-**** |
Fields whose names contain these terms are automatically redacted to [REDACTED]:
password, senha, token, api_key, apikey, secret, credential, authorization, bearer, private_key, privatekey, access_key, secret_key, client_secret, clientsecret
// Via Options (recommended)
logger, _ := axio.New(config,
axio.WithPII(
[]axio.PIIPattern{axio.PatternCPF, axio.PatternEmail},
axio.DefaultSensitiveFields(),
),
)
// Via Hook directly
hook := axio.MustPIIHook(axio.DefaultPIIConfig())
logger, _ := axio.New(config, axio.WithHooks(hook))
// Via Config (YAML file)
// piiEnabled: true
// piiPatterns: [cpf, cnpj, email]config := axio.PIIConfig{
Patterns: []axio.PIIPattern{axio.PatternCPF},
CustomPatterns: []axio.CustomPII{
{
Name: "employee_id",
Pattern: `EMP-\d{6}`,
Mask: "EMP-******",
},
},
Fields: axio.DefaultSensitiveFields(),
}PII masking applies to:
- String annotation values — scanned for configured patterns (CPF, CNPJ, etc.) and replaced where matched.
- Annotation names matching
PIIConfig.Fields— the full value is replaced with[REDACTED]regardless of type. map[string]anyannotation values — recursively masked up toPIIConfig.MaxDepthlevels (default2). At each level, keys are checked againstFieldsand string values are pattern-scanned. Values at or beyond the depth cap are passed through unchanged. SetMaxDepth: 1to mask only top-level map keys.
Struct-typed annotation values are NOT recursively scanned. If you log a struct whose fields contain sensitive data, axio cannot see those fields from the annotation hook. To make a struct's fields visible to masking, implement Annotable on the type — Annotable values are flattened to top-level annotations before the PII hook runs, so each resulting field is subject to the same name/value checks.
type User struct {
Email string
Password string
}
// Without Annotable: the whole struct ships to the wire intact.
// logger.With(axio.Annotate("user", User{Email: "a@b.com", Password: "x"})).Info(ctx, "...")
// -> {"user": {"Email": "a@b.com", "Password": "x"}}
// With Annotable: each field becomes a top-level annotation, masked individually.
func (u User) Append(target []axio.Annotation) []axio.Annotation {
return append(target,
axio.Annotate("user_email", u.Email),
axio.Annotate("user_password", u.Password),
)
}
// -> {"user_email": "***@***.***", "user_password": "[REDACTED]"}A hash chain is a structure where each record contains the cryptographic hash of the previous record. Any modification to a record breaks the entire subsequent chain, allowing tampering detection.
Useful for:
- Regulatory compliance (LGPD, SOX, PCI-DSS)
- Tamper-proof audit logs
- Integrity evidence in investigations
Important: Hash chain detects tampering, it doesn't prevent it. Immutability depends on the storage backend.
| Field | Description |
|---|---|
hash |
SHA256 hash of this entry |
previous_hash |
Hash of previous entry |
// Via Options
logger, _ := axio.New(config,
axio.WithAudit("/var/lib/axio/chain.json"),
)
// Via Hook directly
store := axio.NewFileStore("/var/lib/axio/chain.json")
hook, _ := axio.NewAuditHook(store)
logger, _ := axio.New(config, axio.WithHooks(hook))Implement ChainStore for custom backends (Redis, PostgreSQL, etc.):
type ChainStore interface {
Save(sequence uint64, lastHash string) error
Load() (sequence uint64, lastHash string, err error)
}Distributed tracing allows tracking a request through multiple services. Each operation receives a span identified by:
- trace_id: unique identifier of the complete request
- span_id: unique identifier of this specific operation
With these IDs in logs, it's possible to correlate logs and traces in tools like Jaeger, Tempo, or Zipkin.
Axio uses OpenTelemetry (OTel) as the standard for tracing for the following reasons:
| Factor | OpenTelemetry |
|---|---|
| Standardization | Official CNCF project, industry standard |
| Vendor-neutral | Works with any backend (Jaeger, Zipkin, Datadog, AWS X-Ray) |
| Unification | Traces, metrics, and logs in a single API |
| Adoption | AWS, GCP, Azure, Datadog, Grafana, all support it |
| Community | Active development, extensive documentation |
| Future | Official successor to OpenTracing and OpenCensus |
Considered alternatives:
- Jaeger client: specific to Jaeger, discontinued in favor of OTel
- Zipkin: less flexible, no signal unification
- Proprietary: vendor lock-in
References:
// Via Options (recommended)
logger, _ := axio.New(config, axio.WithTracer(axio.Otel()))
// Via Config (YAML file)
// tracer: otel
// Disable (default)
logger, _ := axio.New(config, axio.WithTracer(axio.NoopTracing()))func handleRequest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // contains span from OTel middleware
logger.Info(ctx, "request received")
// Log will include: {"trace_id": "abc123...", "span_id": "def456..."}
}Metrics are numerical values that represent the system state over time. Common types:
- Counters: values that only increase (e.g., total logs)
- Histograms: distribution of values (e.g., hook duration)
Axio emits metrics about the logging process itself, allowing monitoring of volume, errors, and performance.
References:
| Metric | Type | Labels | Description |
|---|---|---|---|
logs.total |
Counter | level |
Total logs emitted |
pii.masked |
Counter | pattern |
PII occurrences masked |
audit.records |
Counter | - | Audit records created |
hook.duration |
Histogram | hook.name, error |
Hook execution duration |
// Via Options with MeterProvider
provider := otel.GetMeterProvider()
logger, _ := axio.New(config, axio.WithMetrics(provider))
// Via Config (uses global provider with warning)
// metrics:
// enabled: true
// meterName: axio
// meterVersion: 1.0.0type Metrics interface {
LogsTotal(ctx context.Context, level Level)
PIIMasked(ctx context.Context, pattern PIIPattern)
AuditRecords(ctx context.Context)
HookDuration(ctx context.Context, hookName string, duration time.Duration, hasError bool)
}Wide events (also called canonical log lines) replace scattered per-step log lines with a single, richly-annotated entry emitted at the end of a unit of work (e.g., an HTTP request). Instead of 5–10 lines per request, one event captures the complete context of what happened.
Wide events omit the log level field — severity is expressed through the event's own fields (status_code, error, etc.), not through traditional log levels.
event, err := axio.NewEvent("checkout", config)
if err != nil {
return err
}
defer event.Close()
event.Add("user_id", userID)
event.Add("cart_total", 15999)
event.Add("item_count", 3)
event.Emit(ctx)
// Output: {"timestamp":"...","event":"checkout","user_id":"usr_456","cart_total":15999,"item_count":3,"duration_ms":42}| Method | Description |
|---|---|
NewEvent(name, config, ...Option) |
Creates a new event with the same Config/Option used by New |
Add(key, value) |
Adds a key-value field (thread-safe) |
With(...Annotation) |
Adds annotations, including Annotable types like HTTP |
SetError(err, ...Annotation) |
Records an error with optional detail annotations |
Emit(ctx) |
Writes the event as a single log entry (computes duration_ms, runs hooks) |
Close() |
Releases output resources (call after Emit) |
Store the event in the context so downstream handlers can enrich it:
// Middleware: create and store
event, _ := axio.NewEvent("http_request", config)
defer event.Close()
ctx = axio.WithEvent(ctx, event)
// Handler: enrich from context
event := axio.EventFromContext(ctx)
event.Add("user_id", userID)
event.With(axio.Annotate("http", axio.HTTP{
Method: r.Method,
URL: r.URL.Path,
StatusCode: 201,
LatencyMS: latencyMS,
}))
// Middleware: emit at end of request
event.Emit(ctx)// Simple error
event.SetError(err)
// Error with structured details
event.SetError(err,
axio.Annotate("error_code", "card_declined"),
axio.Annotate("error_retriable", false),
)Wide events support the same options as the standard logger:
// With PII masking
event, _ := axio.NewEvent("user_registration", config,
axio.WithPII(nil, nil),
)
// With audit hash chain
event, _ := axio.NewEvent("access_grant", config,
axio.WithAudit("/var/lib/axio/chain.json"),
)
// With tracing
event, _ := axio.NewEvent("http_request", config,
axio.WithTracer(axio.Otel()),
)- Use structured fields for data; message is human summary
- Prefer stable keys:
user_id,order_id,tenant_id - Avoid dynamic keys:
field_123,user_email_john@...
| Level | Use when |
|---|---|
| Debug | Technical details, temporary |
| Info | Normal events, flow milestones |
| Warn | Anomalies that don't interrupt |
| Error | Real operation failure |
Rule: Log error once, at the system boundary (handler, job, consumer).
Always pass context.Context and add identifiers:
request_id/correlation_iduser_id,tenant_idtrace_id,span_id(via tracing)
- Use
PIIHookas default defense - Never log: password, token, secret, private key
- If you need the payload, log hash or ID, not the content
- Avoid logs in hot loops; prefer aggregation
- Don't build large strings/maps unnecessarily
- In production: JSON + agent collection
Fields with unlimited values (email, payloads) explode indexes. Maintain:
- Stable IDs (user, order, tenant)
- Status codes, methods, endpoints
- Latency in milliseconds
For critical operations, use AuditHook and combine with reliable storage.
logger.With(
axio.Annotate("http", axio.HTTP{
Method: r.Method,
URL: r.URL.Path,
StatusCode: statusCode,
LatencyMS: latencyMS,
ClientIP: r.RemoteAddr,
}),
axio.Annotate("request_id", requestID),
axio.Annotate("user_id", userID),
).Info(ctx, "request completed")- Does the message summarize the event?
- Are fields consistent and stable?
- Is the level correct?
- Is there exposed PII?
- Was the error logged only once?
Goal: Measure latency, success/error, track requests.
| Event | Level | Suggested fields |
|---|---|---|
| Request completed | Info | http.*, request_id, user_id, trace_id |
| Domain error | Warn/Error | +operation, +entity, +error |
logger.With(axio.Annotate("http", axio.HTTP{...}), axio.Annotate("request_id", id)).Info(ctx, "request completed")Goal: Know when it started, finished, how much it processed.
| Event | Level | Suggested fields |
|---|---|---|
| Job started | Info | job_name, job_id |
| Job completed | Info | +items_total, +items_ok, +items_failed, +duration_ms |
| Item error | Warn | +item_id, +error (sampled) |
logger.With(
axio.Annotate("job_name", "reconcile_payments"),
axio.Annotate("items_ok", okCount),
axio.Annotate("items_failed", failedCount),
).Info(ctx, "job completed")Goal: Track consumption, retries, failures per message.
| Event | Level | Suggested fields |
|---|---|---|
| Message processed | Info/Debug | queue, message_id, latency_ms |
| Message failure | Warn/Error | +retry_count, +error |
Goal: Visibility of latency and failures in third parties.
| Event | Level | Suggested fields |
|---|---|---|
| External call | Info/Debug | provider, operation, status_code, latency_ms |
| Timeout/error | Warn | +attempt, +timeout_ms |
Goal: Audit execution and result.
| Event | Level | Suggested fields |
|---|---|---|
| Start | Info | command, args_redacted |
| End | Info | +exit_code, +duration_ms, +output_count |
Wrong:
logger.Info(ctx, "user=%s status=%d", userID, statusCode)Correct:
logger.With(
axio.Annotate("user_id", userID),
axio.Annotate("status_code", statusCode),
).Info(ctx, "request completed")Wrong:
logger.Info(ctx, "payload=%+v", payload)Correct:
logger.With(
axio.Annotate("payload_id", payload.ID),
axio.Annotate("payload_size", len(payload.Data)),
).Info(ctx, "payload received")Wrong:
// repository
if err != nil {
logger.Error(ctx, err, "failed to insert")
return err
}Correct:
// repository
if err != nil {
return fmt.Errorf("insert order: %w", err)
}
// handler (system boundary)
if err != nil {
logger.Error(ctx, err, "failed to create order")
}Wrong:
for _, item := range items {
logger.Debug(ctx, "processing item %s", item.ID)
}Correct:
logger.With(
axio.Annotate("items_total", len(items)),
axio.Annotate("items_ok", okCount),
axio.Annotate("items_failed", failedCount),
).Info(ctx, "batch processed")Wrong:
logger.With(axio.Annotate("email", user.Email)).Info(ctx, "login")Correct:
logger.With(axio.Annotate("user_id", user.ID)).Info(ctx, "login")Wrong:
logger.Error(ctx, err, "error")Correct:
logger.With(
axio.Annotate("order_id", order.ID),
).Error(ctx, err, "failed to confirm payment")| Error | Cause | Solution |
|---|---|---|
ErrInvalidEnvironment |
Invalid Environment value | Use production, staging, or development |
ErrInvalidLevel |
Invalid Level value | Use debug, info, warn, or error |
ErrInvalidFormat |
Invalid Format value | Use json or text |
ErrInvalidOutputType |
Invalid OutputType value | Use console, stdout, or file |
ErrIncompatibleOutputs |
AgentMode with non-stdout/json output | In AgentMode, use only stdout + json |
ErrFileOutputNoPath |
File output type without path | Specify path in OutputConfig |
ErrAuditWithoutPath |
Audit enabled without storePath | Specify storePath in AuditConfig |
ErrInvalidTracer |
Invalid TracerType value | Use otel or noop |
ErrLoadConfig |
Failed to read config file | Check path and permissions |
ErrUnknownFormat |
Unknown file extension | Use .yaml, .yml, .json, or .toml |
ErrUnmarshalConfig |
Failed to parse config | Check file syntax |
ErrApplyOption |
Failed to apply Option | Check Option parameters |
ErrValidateConfig |
Invalid configuration after Options | Check value combination |
ErrBuildOutputs |
Failed to create outputs | Check file paths |
ErrBuildHooks |
Failed to create hooks | Check PIICustomPatterns regex |
ErrBuildMetrics |
Failed to build metrics | Check MeterProvider configuration |
ErrBuildEngine |
Failed to build logging engine | Check output and config combination |
ErrOpenFile |
Failed to open log file | Check path and permissions |
ErrLoadChainState |
Failed to load chain state | Check chain file |
ErrSaveChainState |
Failed to save chain state | Check write permissions |
ErrMarshalChainState |
Failed to marshal chain state | Internal serialization error |
ErrUnmarshalChainState |
Failed to unmarshal chain state | Chain file corrupted or invalid format |
ErrHashMismatch |
Calculated hash doesn't match | Audit chain corrupted |
ErrChainBroken |
Chain integrity compromised | Records have been tampered with |
ErrSerializeEntry |
Failed to serialize audit entry | Entry contains non-serializable data |
ErrCreateAuditHook |
Failed to create audit hook | Check chain store configuration |
ErrNilMetricsProvider |
Metrics provider is nil | Pass a valid MeterProvider |
ErrCreateMetric |
Failed to create OTel instrument | Check provider configuration |
ErrNilTracer |
Tracer passed to WithTracer is nil | Pass a non-nil Tracer or omit the option |
ErrLoggerClosed |
Logger has already been closed | Idempotent guard; check with errors.Is |
ErrLoggerNotRoot |
Close called on a forked Logger | Only the root from New can be closed |