olog

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Sep 26, 2025 License: MIT Imports: 10 Imported by: 0

README

olog - OpenTelemetry Logging Facade

Go Reference Keep a Changelog go.mod LICENSE Go Report Card Codecov

Star this repository if you find it valuable and worth maintaining.

👁 Watch this repository to get notified about new releases, issues, etc.

Description

The olog package provides an ergonomic frontend API for OpenTelemetry structured logging.

It is designed to provide a more user-friendly interface while using the OpenTelemetry Logs API as the backend.

It addresses the concerns raised in opentelemetry-specification#4661.

Features
  1. Simple API: Easy-to-use methods like Debug(), Info(), Warn(), Error() similar to popular logging libraries
  2. Level-specific enabled checks: Built-in support for common log levels with DebugEnabled(), InfoEnabled(), etc. for performance
  3. Structured logging: Support for key-value pairs using the alternating syntax (similar to slog)
  4. Event logging: Dedicated Event() and EventAttr() methods for semantic events
  5. Context support: All methods accept context.Context for trace correlation
  6. Logger composition: With() and WithAttr() methods for attribute composition
  7. Performance oriented: Level-specific enabled checks to avoid expensive operations
  8. Automatic package detection: Auto-detects caller's package name when logger name is not specified
  9. Type safety: Support for both argument-based and strongly-typed attribute APIs

Contributing

Feel free to create an issue, join the discussions, or propose a pull request.

Please follow the Code of Conduct.

This module follows several key design principles:

  1. Ergonomic API: Provides simple methods that are easy to use and understand
  2. Performance First: Includes Enabled() checks and optimizations to minimize overhead
  3. Structured Logging: Emphasizes key-value pairs over string formatting
  4. Compatibility: Uses OpenTelemetry Logs API as the backend for full compatibility
  5. Composability: Supports logger composition through With()
  6. Familiar Patterns: Similar to slog design patterns that Go developers already know

License

olog is licensed under the terms of the MIT license.

Documentation

Overview

Package olog provides an ergonomic OpenTelemetry Logging Facade.

This package addresses the usability concerns with the OpenTelemetry Logs API by providing a user-friendly frontend interface while using the OpenTelemetry Logs API as the backend. It offers simple methods similar to popular logging libraries while maintaining full compatibility with OpenTelemetry's structured logging capabilities.

Basic Usage

The simplest way to use olog is by creating a logger instance:

import (
	"context"
	"go.opentelemetry.io/otel/log"
	"go.opentelemetry.io/otel/log/global"
	"github.com/pellared/olog"
)

ctx := context.Background()
logger := olog.New(olog.Options{
	Provider: global.GetLoggerProvider(),
	Name:     "myapp",
})

logger.TraceAttr(ctx, "detailed tracing", log.String("trace_id", "abc123"))
logger.InfoAttr(ctx, "application started",
	log.String("version", "1.0.0"),
	log.Int("port", 8080))
logger.WarnAttr(ctx, "deprecated feature used", log.String("feature", "old-api"))
logger.ErrorAttr(ctx, "failed to connect", log.String("host", "db.example.com"))
logger.DebugAttr(ctx, "processing request",
	log.String("method", "GET"),
	log.String("path", "/api/users"))

// Check if logging is enabled before expensive operations
if logger.DebugEnabled(ctx) {
	expensiveData := computeExpensiveDebugInfo()
	logger.DebugAttr(ctx, "debug info", log.String("data", expensiveData))
}

Logger Composition

Use WithAttr to create loggers with common attributes:

serviceLogger := logger.WithAttr(
	log.String("service", "user-service"),
	log.String("version", "2.1.0"))
serviceLogger.InfoAttr(ctx, "user created", log.Int("user_id", 12345))

requestLogger := serviceLogger.WithAttr(log.String("request_id", "req-789"))
requestLogger.InfoAttr(ctx, "processing request", log.String("endpoint", "/api/users"))

Use structured attributes to organize your logs:

httpLogger := logger.WithAttr(log.String("component", "http"))
httpLogger.InfoAttr(ctx, "request",
	log.String("method", "POST"),
	log.Int("status", 201))
// Logs with component="http" and the specified attributes

Event Logging

Log structured events following semantic conventions:

logger.EventAttr(ctx, "user.login",
	log.String("user.id", "12345"),
	log.String("user.email", "user@example.com"),
	log.String("session.id", "sess-abc123"))

Performance

olog is designed with performance in mind:

  • Use TraceEnabled, DebugEnabled, InfoEnabled, WarnEnabled, and ErrorEnabled checks to avoid expensive operations when logging is disabled
  • Logger composition with WithAttr pre-processes common attributes
  • Direct integration with OpenTelemetry Logs API avoids unnecessary conversions

Design Goals

This package is designed to provide:

  1. Simple, ergonomic API similar to popular logging libraries
  2. Performance-oriented design with efficient enabled checks
  3. Full compatibility with OpenTelemetry Logs API and ecosystem
  4. Support for structured logging with key-value pairs
  5. Logger composition for better code organization and performance
  6. Event logging capabilities for semantic events

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Logger

type Logger struct {
	log.Logger
	// contains filtered or unexported fields
}

Logger provides an ergonomic frontend API for OpenTelemetry structured logging. It provides convenience methods for common logging patterns while using the OpenTelemetry Logs API as the backend.

The Logger offers two styles of API:

  • Argument-based methods (Trace, Debug, Info, Warn, Error, Log, Event, With) that accept alternating key-value pairs as ...any arguments
  • Attribute-based methods (TraceAttr, DebugAttr, InfoAttr, WarnAttr, ErrorAttr, LogAttr, EventAttr, WithAttr) that accept strongly-typed log.KeyValue attributes

The attribute-based methods provide better type safety and can offer better performance in some scenarios, particularly when used with WithAttr for pre-configured loggers.

Example (Basic)
package main

import (
	"context"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger instance
	logger := olog.New(olog.Options{})

	// Use the logger for basic logging
	logger.Info(ctx, "application started", "version", "1.0.0", "port", 8080)
	logger.Warn(ctx, "deprecated feature used", "feature", "old-api")
	logger.Error(ctx, "failed to connect", "host", "db.example.com", "error", "connection timeout")
	logger.Debug(ctx, "processing request", "method", "GET", "path", "/api/users")

	// Check if logging is enabled before expensive operations
	if logger.DebugEnabled(ctx) {
		expensiveData := computeExpensiveDebugInfo()
		logger.Debug(ctx, "debug info", "data", expensiveData)
	}
}

func computeExpensiveDebugInfo() string {

	return "expensive debug data"
}
Example (EventAttr)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log"
	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger for structured events
	logger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})

	// Log events using the attribute-based method for better type safety
	logger.EventAttr(ctx, "user.signup",
		log.String("user.id", "user-789"),
		log.String("user.email", "newuser@example.com"),
		log.String("signup.method", "email"),
		log.Bool("email.verified", false))

	logger.EventAttr(ctx, "payment.processed",
		log.String("payment.id", "pay-abc123"),
		log.Float64("payment.amount", 49.99),
		log.String("payment.currency", "USD"),
		log.String("payment.method", "credit_card"))

	logger.EventAttr(ctx, "file.uploaded",
		log.String("file.id", "file-456"),
		log.String("file.name", "document.pdf"),
		log.Int64("file.size_bytes", 2048576),
		log.String("user.id", "user-123"))
}
Example (Events)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger for events
	logger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})

	// Log structured events following semantic conventions
	logger.Event(ctx, "user.login",
		"user.id", "12345",
		"user.email", "user@example.com",
		"session.id", "sess-abc123",
		"client.ip", "192.168.1.100")

	logger.Event(ctx, "payment.processed",
		"payment.id", "pay-xyz789",
		"payment.amount", 99.99,
		"payment.currency", "USD",
		"user.id", "12345")

	// Events with additional attributes
	logger.Event(ctx, "order.created",
		"order.id", "order-456",
		"order.total", 149.99,
		"customer.id", "cust-789",
		"domain", "ecommerce")
}
Example (Performance)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a base logger
	logger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})

	// Check if logging is enabled to avoid expensive operations
	if logger.DebugEnabled(ctx) {
		// Only compute expensive debug information if debug logging is enabled
		debugData := computeExpensiveDebugInfo()
		logger.Debug(ctx, "detailed debug information", "data", debugData)
	}

	// Pre-configure logger with common attributes for better performance
	requestLogger := logger.With(
		"service", "api-server",
		"version", "1.2.3",
		"request_id", generateRequestID(),
	)

	// Use the pre-configured logger for all request-scoped logging
	requestLogger.Info(ctx, "request started", "method", "GET", "path", "/api/users")
	requestLogger.Info(ctx, "database query", "table", "users", "duration_ms", 23)
	requestLogger.Info(ctx, "request completed", "status", 200, "total_duration_ms", 145)
}

func computeExpensiveDebugInfo() string {

	return "expensive debug data"
}

func generateRequestID() string {
	return "req-12345"
}
Example (With)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger and add common attributes
	baseLogger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})
	logger := baseLogger.With("service", "user-service", "version", "2.1.0")

	// All log records from this logger will include the common attributes
	logger.Info(ctx, "user created", "user_id", 12345, "email", "user@example.com")
	logger.Warn(ctx, "user login failed", "user_id", 12345, "reason", "invalid password")

	// Chain with additional attributes
	requestLogger := logger.With("request_id", "req-789", "ip", "192.168.1.100")
	requestLogger.Info(ctx, "processing request", "endpoint", "/api/users/12345")
	requestLogger.Error(ctx, "request failed", "status", 500, "duration_ms", 1234)
}
Example (WithAttr)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log"
	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a base logger
	baseLogger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})

	// Create a logger with common attributes using WithAttr
	serviceLogger := baseLogger.WithAttr(
		log.String("service.name", "user-service"),
		log.String("service.version", "2.1.0"),
		log.String("deployment.environment", "production"))

	// All subsequent logs will include the service attributes
	serviceLogger.InfoAttr(ctx, "service started",
		log.Int64("port", 8080),
		log.String("build", "abc1234"))

	// Chain additional attributes for request-scoped logging
	requestLogger := serviceLogger.WithAttr(
		log.String("request.id", "req-789"),
		log.String("user.id", "user-456"))

	requestLogger.InfoAttr(ctx, "processing request",
		log.String("http.method", "POST"),
		log.String("http.route", "/api/users"))

	requestLogger.ErrorAttr(ctx, "validation failed",
		log.String("field", "email"),
		log.String("error", "invalid format"))

	// Mix WithAttr and With methods
	mixedLogger := requestLogger.With("trace.id", "trace-xyz").WithAttr(log.Bool("debug.enabled", true))
	mixedLogger.DebugAttr(ctx, "detailed processing info",
		log.Int64("processing.step", 3),
		log.Float64("processing.duration_ms", 12.5))
}
Example (WithAttributes)
package main

import (
	"context"

	"go.opentelemetry.io/otel/log"
	"go.opentelemetry.io/otel/log/global"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Import the log package for KeyValue construction
	// import "go.opentelemetry.io/otel/log"

	// Create a logger instance
	logger := olog.New(olog.Options{Provider: global.GetLoggerProvider(), Name: "example"})

	// Using the new attribute-based methods for type-safe logging
	logger.InfoAttr(ctx, "user logged in",
		log.String("user.id", "12345"),
		log.String("user.email", "user@example.com"),
		log.Int64("session.duration", 3600),
		log.Bool("first_login", false))

	logger.WarnAttr(ctx, "rate limit exceeded",
		log.String("client.ip", "192.168.1.100"),
		log.Int64("requests_per_minute", 150),
		log.Int64("limit", 100))

	logger.ErrorAttr(ctx, "database connection failed",
		log.String("database.host", "db.example.com"),
		log.Int64("database.port", 5432),
		log.String("error.type", "connection_timeout"))

	// Use LogAttr for custom severity levels
	logger.LogAttr(ctx, log.SeverityWarn2, "custom warning",
		log.String("component", "cache"),
		log.Float64("memory_usage_percent", 85.5))
}

func New

func New(options Options) *Logger

New creates a new Logger with the provided options. If options.Provider is nil, the global LoggerProvider is used. If options.Name is empty, the caller's full package name is automatically detected.

Example (Minimal)
package main

import (
	"context"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Minimal configuration - only name is required
	logger := olog.New(olog.Options{
		Name: "minimal-logger",
	})

	logger.Info(ctx, "minimal logger example")
}
Example (WithGlobalProvider)
package main

import (
	"context"

	"go.opentelemetry.io/otel/attribute"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger that uses the global provider (provider is nil)
	logger := olog.New(olog.Options{
		Name: "global-logger",
		Attributes: attribute.NewSet(
			attribute.String("component", "authentication"),
			attribute.String("version", "2.0.0"),
		),
	})

	logger.Info(ctx, "using global logger provider", "initialized", true)
}
Example (WithOptions)
package main

import (
	"context"

	"go.opentelemetry.io/otel/attribute"

	"github.com/pellared/olog"
)

func main() {
	ctx := context.Background()

	// Create a logger using the new Options API
	logger := olog.New(olog.Options{
		Name:    "my-service",
		Version: "1.2.3",
		Attributes: attribute.NewSet(
			attribute.String("service.name", "user-service"),
			attribute.String("deployment.environment", "production"),
			attribute.Int("service.port", 8080),
		),
	})

	// All log records will include the pre-configured attributes
	logger.Info(ctx, "service started", "status", "ready")
	logger.Warn(ctx, "high memory usage", "memory_percent", 85.5)
	logger.Error(ctx, "database connection failed", "retry_count", 3)
}

func (*Logger) Debug

func (l *Logger) Debug(ctx context.Context, msg string, args ...any)

Debug logs a debug message with optional key-value pairs.

func (*Logger) DebugAttr

func (l *Logger) DebugAttr(ctx context.Context, msg string, attrs ...log.KeyValue)

DebugAttr logs a debug message with the provided attributes.

func (*Logger) DebugEnabled

func (l *Logger) DebugEnabled(ctx context.Context) bool

DebugEnabled reports whether the logger emits debug-level log records.

func (*Logger) Error

func (l *Logger) Error(ctx context.Context, msg string, args ...any)

Error logs an error message with optional key-value pairs.

func (*Logger) ErrorAttr

func (l *Logger) ErrorAttr(ctx context.Context, msg string, attrs ...log.KeyValue)

ErrorAttr logs an error message with the provided attributes.

func (*Logger) ErrorEnabled

func (l *Logger) ErrorEnabled(ctx context.Context) bool

ErrorEnabled reports whether the logger emits error-level log records.

func (*Logger) Event

func (l *Logger) Event(ctx context.Context, name string, args ...any)

Event logs an event with the specified name and optional key-value pairs.

func (*Logger) EventAttr

func (l *Logger) EventAttr(ctx context.Context, name string, attrs ...log.KeyValue)

EventAttr logs an event with the specified name and the provided attributes.

func (*Logger) Info

func (l *Logger) Info(ctx context.Context, msg string, args ...any)

Info logs an info message with optional key-value pairs.

func (*Logger) InfoAttr

func (l *Logger) InfoAttr(ctx context.Context, msg string, attrs ...log.KeyValue)

InfoAttr logs an info message with the provided attributes.

func (*Logger) InfoEnabled

func (l *Logger) InfoEnabled(ctx context.Context) bool

InfoEnabled reports whether the logger emits info-level log records.

func (*Logger) Log

func (l *Logger) Log(ctx context.Context, level log.Severity, msg string, args ...any)

Log logs a message at the specified level with optional key-value pairs.

func (*Logger) LogAttr

func (l *Logger) LogAttr(ctx context.Context, level log.Severity, msg string, attrs ...log.KeyValue)

LogAttr logs a message at the specified level with the provided attributes.

func (*Logger) Trace

func (l *Logger) Trace(ctx context.Context, msg string, args ...any)

Trace logs a trace message with the provided attributes.

func (*Logger) TraceAttr

func (l *Logger) TraceAttr(ctx context.Context, msg string, attrs ...log.KeyValue)

TraceAttr logs a trace message with the provided attributes.

func (*Logger) TraceEnabled

func (l *Logger) TraceEnabled(ctx context.Context) bool

TraceEnabled reports whether the logger emits trace-level log records.

func (*Logger) Warn

func (l *Logger) Warn(ctx context.Context, msg string, args ...any)

Warn logs a warning message with optional key-value pairs.

func (*Logger) WarnAttr

func (l *Logger) WarnAttr(ctx context.Context, msg string, attrs ...log.KeyValue)

WarnAttr logs a warning message with the provided attributes.

func (*Logger) WarnEnabled

func (l *Logger) WarnEnabled(ctx context.Context) bool

WarnEnabled reports whether the logger emits warn-level log records.

func (*Logger) With

func (l *Logger) With(args ...any) *Logger

With returns a new Logger that includes the given attributes in all log records.

func (*Logger) WithAttr

func (l *Logger) WithAttr(attrs ...log.KeyValue) *Logger

WithAttr returns a new Logger that includes the given attributes in all log records.

type Options

type Options struct {
	// Provider is the LoggerProvider to use. If nil, the global LoggerProvider is used.
	Provider log.LoggerProvider

	// Name is the name of the logger, typically the package or component name.
	// If empty, the caller's full package name is automatically detected.
	Name string

	// Version is the version of the logger, typically the package or component version.
	Version string

	// Attributes are pre-configured attributes that will be included in all log records.
	Attributes attribute.Set
}

Options contains configuration options for creating a Logger.

Jump to

Keyboard shortcuts

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