olog

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 10 Imported by: 0

README

olog - OpenTelemetry Ergonomic Logs API

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 offer a more user-friendly interface while using the OpenTelemetry Logs API as the backend.

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

Contributing

Feel free to create an issue or propose a pull request.

Please follow the Code of Conduct.

License

olog is licensed under the terms of the MIT license.

Documentation

Overview

Package olog provides OpenTelemetry Ergonomic Logs API.

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.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, "connection.failed", log.String("host", "db.example.com"))
logger.DebugAttr(ctx, "request.processing",
	log.String("method", "GET"),
	log.String("path", "/api/users"))

// Check if logging is enabled before expensive operations
if logger.DebugEnabled(ctx, "debug.info") {
	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, "request.processing", log.String("endpoint", "/api/users"))

Use structured attributes to organize your logs:

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

Event Logging

Log structured events at different severity levels following semantic conventions:

// Log events at different levels
logger.InfoAttr(ctx, "user.login",
	log.String("user.id", "12345"),
	log.String("user.email", "user@example.com"),
	log.String("session.id", "sess-abc123"))

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

API Variants

olog provides two variants for emitting logs and events:

  • Variadic methods (Trace, Debug, Info, Warn, Error, Log) accept alternating key-value pairs as interface{} arguments for convenience
  • Attr methods (TraceAttr, DebugAttr, InfoAttr, WarnAttr, ErrorAttr, LogAttr) accept typed log.KeyValue attributes

The Attr variants are recommended for production use as they provide:

  • Better performance: no reflection or runtime type conversion overhead

  • Type safety: compile-time validation of attribute types

  • Better IDE support: autocompletion and type checking

    // Variadic - convenient but less performant logger.Info(ctx, "user.created", "user_id", 12345, "email", "user@example.com")

    // Attr - more performant and type-safe logger.InfoAttr(ctx, "user.created", log.Int("user_id", 12345), log.String("email", "user@example.com"))

Performance

olog is designed with performance in mind:

  • Use TraceEnabled, DebugEnabled, InfoEnabled, WarnEnabled, 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
  • Prefer Attr variants (TraceAttr, InfoAttr, etc.) over variadic methods for better performance and type safety

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. Event logging capabilities for OpenTelemetry semantic events
  4. Support for structured logging with key-value pairs
  5. Logger composition for better code organization and performance

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 event 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, With) that accept alternating key-value pairs as ...any arguments
  • Attribute-based methods (TraceAttr, DebugAttr, InfoAttr, WarnAttr, ErrorAttr, LogAttr, 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
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 at different severity levels
	logger.Info(ctx, "user.login",
		"user.id", "12345",
		"user.email", "user@example.com",
		"session.id", "sess-abc123",
		"client.ip", "192.168.1.100")

	logger.Warn(ctx, "rate.limit.approached",
		"client.ip", "192.168.1.100",
		"requests_per_minute", 85,
		"limit", 100)

	logger.Error(ctx, "payment.failed",
		"payment.id", "pay-xyz789",
		"payment.amount", 99.99,
		"payment.currency", "USD",
		"user.id", "12345",
		"error", "insufficient_funds")

	// Check if debug event logging is enabled before expensive operations
	if logger.DebugEnabled(ctx, "debug.session.details") {
		logger.Debug(ctx, "debug.session.details",
			"session.data", computeSessionDebugInfo(),
			"trace.id", "trace-abc123")
	}
}

func computeSessionDebugInfo() string {

	return "session debug information"
}
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 events 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, "request.processing",
		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, "processing.detail",
		log.Int64("processing.step", 3),
		log.Float64("processing.duration_ms", 12.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.
	// The logger name is the caller's full package name.
	logger := olog.New(olog.Options{})

	logger.Info(ctx, "minimal.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, "logger.initialized", "uses_global_provider", 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 event records will include the pre-configured attributes
	logger.Info(ctx, "service.started", "status", "ready")
	logger.Warn(ctx, "resource.high_usage", "memory_percent", 85.5)
	logger.Error(ctx, "database.connection_failed", "retry_count", 3)
}

func (*Logger) Debug

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

Debug logs a debug-level event with the specified name and optional key-value pairs.

func (*Logger) DebugAttr

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

DebugAttr logs a debug-level event with the specified name and the provided attributes.

func (*Logger) DebugEnabled

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

DebugEnabled reports whether the logger emits debug-level event log records for the specified event name.

func (*Logger) Error

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

Error logs an error-level event with the specified name and optional key-value pairs.

func (*Logger) ErrorAttr

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

ErrorAttr logs an error-level event with the specified name and the provided attributes.

func (*Logger) ErrorEnabled

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

ErrorEnabled reports whether the logger emits error-level event log records for the specified event name.

func (*Logger) Info

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

Info logs an info-level event with the specified name and optional key-value pairs.

func (*Logger) InfoAttr

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

InfoAttr logs an info-level event with the specified name and the provided attributes.

Example
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 at different severity levels using the attribute-based methods
	logger.InfoAttr(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.WarnAttr(ctx, "api.deprecated.usage",
		log.String("api.endpoint", "/v1/users"),
		log.String("client.id", "client-123"),
		log.String("replacement", "/v2/users"))

	logger.ErrorAttr(ctx, "payment.failed",
		log.String("payment.id", "pay-abc123"),
		log.Float64("payment.amount", 49.99),
		log.String("payment.currency", "USD"),
		log.String("payment.method", "credit_card"),
		log.String("error.code", "card_declined"))

	logger.DebugAttr(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"))
}

func (*Logger) InfoEnabled

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

InfoEnabled reports whether the logger emits info-level event log records for the specified event name.

func (*Logger) Log

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

Log logs an event at the specified level with the specified name and optional key-value pairs.

func (*Logger) LogAttr

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

LogAttr logs an event at the specified level with the specified name and the provided attributes.

func (*Logger) Trace

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

Trace logs a trace-level event with the specified name and optional key-value pairs.

func (*Logger) TraceAttr

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

TraceAttr logs a trace-level event with the specified name and the provided attributes.

func (*Logger) TraceEnabled

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

TraceEnabled reports whether the logger emits trace-level event log records for the specified event name.

func (*Logger) Warn

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

Warn logs a warn-level event with the specified name and optional key-value pairs.

func (*Logger) WarnAttr

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

WarnAttr logs a warn-level event with the specified name and the provided attributes.

func (*Logger) WarnEnabled

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

WarnEnabled reports whether the logger emits warn-level event log records for the specified event name.

func (*Logger) With

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

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

Example
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"})

	// 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 events
	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 generateRequestID() string {
	return "req-12345"
}

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