Documentation
¶
Overview ¶
Package ion provides enterprise-grade structured logging for JupiterMeta blockchain services.
Ion wraps the high-performance Zap logger with OpenTelemetry integration, offering a simple yet powerful API for consistent, observable logging across microservices.
Key Features ¶
- Pool-optimized hot paths with minimal allocations
- Built-in OpenTelemetry (OTEL) integration for log export
- Automatic context propagation (trace_id, span_id)
- Specialized field helpers for blockchain primitives (TxHash, Slot, ShardID)
- Configurable output formats (JSON for production, Pretty for development)
- Log rotation and compression via lumberjack
Quick Start ¶
Global logger pattern (recommended for applications):
package main
import (
"context"
"github.com/JupiterMetaLabs/ion"
)
func main() {
ctx := context.Background()
ion.SetGlobal(ion.InitFromEnv())
defer ion.Sync()
ion.Info(ctx, "application started", ion.String("version", "1.0.0"))
}
Dependency Injection ¶
For libraries or explicit dependencies, pass Logger directly:
func NewServer(logger ion.Logger) *Server {
return &Server{log: logger.Named("server")}
}
func (s *Server) Start(ctx context.Context) {
s.log.Info(ctx, "server started", ion.Int("port", 8080))
}
Context-First Logging ¶
Context is always the first parameter. Trace IDs are extracted automatically:
func HandleRequest(ctx context.Context) {
// trace_id and span_id are added to logs if present in ctx
logger.Info(ctx, "processing request")
}
For startup and shutdown logs where no trace context exists:
ion.Info(context.Background(), "service starting")
Configuration ¶
Ion supports configuration via code or environment variables:
cfg := ion.Default() cfg.Level = "debug" cfg.OTEL.Enabled = true cfg.OTEL.Endpoint = "otel-collector:4317" logger := ion.New(cfg)
Environment variables supported by InitFromEnv:
LOG_LEVEL - debug, info, warn, error (default: info) LOG_DEVELOPMENT - "true" for pretty console output SERVICE_NAME - service name for logs and OTEL SERVICE_VERSION - service version for OTEL resources OTEL_ENDPOINT - collector address, enables OTEL if set OTEL_INSECURE - "true" to disable TLS OTEL_USERNAME - Basic Auth username OTEL_PASSWORD - Basic Auth password
Package ion provides enterprise-grade structured logging for JupiterMeta blockchain applications.
Ion is designed for distributed systems where trace correlation is critical. All log methods require a context.Context as the first parameter to ensure trace information is never forgotten.
Features:
- High-performance Zap core with pool-optimized hot paths
- Multi-destination output (Console, File, OTEL)
- Blockchain-specific field helpers (TxHash, ShardID, Slot, etc.)
- Automatic trace context propagation from context.Context
- Pretty console output for development
- File rotation via lumberjack
- OpenTelemetry integration for observability
Basic usage:
ctx := context.Background()
logger := ion.New(ion.Default())
defer logger.Sync()
logger.Info(ctx, "server started", ion.Int("port", 8080))
With blockchain fields:
import "github.com/JupiterMetaLabs/ion/fields"
logger.Info(ctx, "transaction routed",
fields.TxHash("abc123"),
fields.ShardID(5),
fields.LatencyMs(12.5),
)
Context-first design ensures trace_id and span_id are automatically extracted:
func HandleRequest(ctx context.Context) {
// trace_id and span_id from ctx are added to logs automatically
logger.Info(ctx, "processing request")
}
Index ¶
- func Debug(ctx context.Context, msg string, fields ...Field)
- func Error(ctx context.Context, msg string, err error, fields ...Field)
- func Fatal(ctx context.Context, msg string, err error, fields ...Field)
- func Info(ctx context.Context, msg string, fields ...Field)
- func NewFileWriter(cfg FileConfig) io.Writer
- func RequestIDFromContext(ctx context.Context) string
- func SetGlobal(l Logger)
- func Shutdown(ctx context.Context) error
- func Sync() error
- func UserIDFromContext(ctx context.Context) string
- func Warn(ctx context.Context, msg string, fields ...Field)
- func WithRequestID(ctx context.Context, requestID string) context.Context
- func WithTraceID(ctx context.Context, traceID string) context.Context
- func WithUserID(ctx context.Context, userID string) context.Context
- type Config
- type ConsoleConfig
- type Field
- type FieldType
- type FileConfig
- type Logger
- type OTELConfig
- type TelemetryLog
- func (t *TelemetryLog) Component(name string) *TelemetryLog
- func (t *TelemetryLog) Debug(msg string, fields ...Field)
- func (t *TelemetryLog) Error(msg string, err error, fields ...Field)
- func (t *TelemetryLog) Info(msg string, fields ...Field)
- func (t *TelemetryLog) Instrument(name string) *TelemetryLog
- func (t *TelemetryLog) Level(level zapcore.Level) *TelemetryLog
- func (t *TelemetryLog) Module(name string) *TelemetryLog
- func (t *TelemetryLog) Printf(format string, args ...any)
- func (t *TelemetryLog) Trace(spanName string) *TelemetryLog
- func (t *TelemetryLog) Warn(msg string, fields ...Field)
- func (t *TelemetryLog) WithContext(ctx context.Context) *TelemetryLog
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewFileWriter ¶
func NewFileWriter(cfg FileConfig) io.Writer
NewFileWriter creates a file writer with rotation using lumberjack. Returns nil if the path is empty.
func RequestIDFromContext ¶
RequestIDFromContext extracts the request ID from context.
func SetGlobal ¶
func SetGlobal(l Logger)
SetGlobal sets the global logger instance. Applications should call this during initialization.
func UserIDFromContext ¶
UserIDFromContext extracts the user ID from context.
func WithRequestID ¶
WithRequestID adds a request ID to the context. This ID will be automatically included in logs.
func WithTraceID ¶
WithTraceID adds a trace ID to the context (for non-OTEL scenarios).
Types ¶
type Config ¶
type Config struct {
// Level sets the minimum log level: debug, info, warn, error.
// Default: "info"
Level string `yaml:"level" json:"level" env:"LOG_LEVEL"`
// Development enables development mode with:
// - Pretty console output by default
// - Caller information in logs
// - Stack traces on error/fatal
Development bool `yaml:"development" json:"development" env:"LOG_DEVELOPMENT"`
// ServiceName identifies this service in logs and OTEL.
// Default: "unknown"
ServiceName string `yaml:"service_name" json:"service_name" env:"SERVICE_NAME"`
// Version is the application version, included in logs.
Version string `yaml:"version" json:"version" env:"SERVICE_VERSION"`
// Console output configuration.
Console ConsoleConfig `yaml:"console" json:"console"`
// File output configuration (with rotation).
File FileConfig `yaml:"file" json:"file"`
// OTEL (OpenTelemetry) exporter configuration.
OTEL OTELConfig `yaml:"otel" json:"otel"`
}
Config holds the complete logger configuration.
func Development ¶
func Development() Config
Development returns a Config optimized for development.
func (Config) WithService ¶
WithService returns a copy of the config with the specified service name.
type ConsoleConfig ¶
type ConsoleConfig struct {
// Enabled controls whether console output is active.
// Default: true
Enabled bool `yaml:"enabled" json:"enabled"`
// Format: "json" for structured JSON, "pretty" for human-readable.
// Default: "json" (production), "pretty" (development)
Format string `yaml:"format" json:"format"`
// Color enables ANSI colors in pretty format.
// Default: true
Color bool `yaml:"color" json:"color"`
// ErrorsToStderr sends warn/error/fatal to stderr, others to stdout.
// Default: true
ErrorsToStderr bool `yaml:"errors_to_stderr" json:"errors_to_stderr"`
}
ConsoleConfig configures console (stdout/stderr) output.
type Field ¶
type Field struct {
Key string
Type FieldType
Integer int64
StringVal string
Float float64
Interface any
}
Field represents a structured logging field (key-value pair). Field construction is zero-allocation for primitive types (String, Int, etc).
type FileConfig ¶
type FileConfig struct {
// Enabled controls whether file output is active.
// Default: false
Enabled bool `yaml:"enabled" json:"enabled"`
// Path is the log file path.
// Example: "/var/log/app/app.log"
Path string `yaml:"path" json:"path"`
// MaxSizeMB is the maximum size in MB before rotation.
// Default: 100
MaxSizeMB int `yaml:"max_size_mb" json:"max_size_mb"`
// MaxAgeDays is the maximum age in days to retain old logs.
// Default: 7
MaxAgeDays int `yaml:"max_age_days" json:"max_age_days"`
// MaxBackups is the maximum number of old log files to keep.
// Default: 5
MaxBackups int `yaml:"max_backups" json:"max_backups"`
// Compress enables gzip compression of rotated log files.
// Default: true
Compress bool `yaml:"compress" json:"compress"`
}
FileConfig configures file output with rotation.
type Logger ¶
type Logger interface {
// Debug logs a message at debug level.
Debug(ctx context.Context, msg string, fields ...Field)
// Info logs a message at info level.
Info(ctx context.Context, msg string, fields ...Field)
// Warn logs a message at warn level.
Warn(ctx context.Context, msg string, fields ...Field)
// Error logs a message at error level with an error.
Error(ctx context.Context, msg string, err error, fields ...Field)
// Fatal logs a message at fatal level and calls os.Exit(1).
Fatal(ctx context.Context, msg string, err error, fields ...Field)
// With returns a child logger with additional fields attached.
// Fields are included in all subsequent log entries.
With(fields ...Field) Logger
// Named returns a named sub-logger.
// The name appears in logs as the "component" field.
Named(name string) Logger
// Sync flushes any buffered log entries.
// Applications should call Sync before exiting.
Sync() error
// Shutdown gracefully shuts down the logger, flushing any buffered logs
// and closing background resources (like OTEL exporters).
Shutdown(ctx context.Context) error
// SetLevel changes the log level at runtime.
// Valid levels: debug, info, warn, error, fatal.
SetLevel(level string)
// GetLevel returns the current log level as a string.
GetLevel() string
}
Logger is the primary logging interface. All methods are safe for concurrent use. All log methods require a context.Context as the first parameter for trace correlation.
Example ¶
ctx := context.Background()
// 1. Initialize the logger
logger := New(Development())
defer func() { _ = logger.Sync() }()
// 2. Log a simple message (context-first)
logger.Info(ctx, "Hello, World!")
// 3. Log with structured fields
logger.Info(ctx, "User logged in",
F("user_id", 42),
F("ip", "192.168.1.1"),
)
Example (ContextIntegration) ¶
// Initialize logger
logger := New(Default())
defer func() { _ = logger.Sync() }()
// Create a context (in a real app, this comes from the request)
ctx := context.Background()
ctx = WithRequestID(ctx, "req-123")
// Context is ALWAYS the first parameter
// Trace IDs are extracted automatically
logger.Info(ctx, "Processing request")
func InitFromEnv ¶ added in v0.1.1
func InitFromEnv() Logger
InitFromEnv initializes a logger using environment variables. This is the recommended way to configure ion in production deployments.
Supported variables:
- LOG_LEVEL: debug, info, warn, error (default: info)
- LOG_DEVELOPMENT: "true" for pretty console output
- SERVICE_NAME: name of the service (default: unknown)
- SERVICE_VERSION: version of the service
- OTEL_ENDPOINT: collector address, enables OTEL if set (e.g., "localhost:4317")
- OTEL_INSECURE: "true" to disable TLS for OTEL connection
- OTEL_USERNAME: Basic Auth username for OTEL collector
- OTEL_PASSWORD: Basic Auth password for OTEL collector
func New ¶
New creates a new Logger from the provided configuration. This is the main constructor for ion.
func NewWithOTEL ¶
NewWithOTEL creates a logger with OTEL export enabled. This wires Zap logs to the OpenTelemetry log pipeline.
type OTELConfig ¶
type OTELConfig struct {
// Enabled controls whether OTEL export is active.
// Default: false
Enabled bool `yaml:"enabled" json:"enabled"`
// Protocol: "grpc" or "http". gRPC is recommended for performance.
// Default: "grpc"
Protocol string `yaml:"protocol" json:"protocol"`
// Endpoint is the OTEL collector endpoint.
// Examples: "localhost:4317" (gRPC), "localhost:4318/v1/logs" (HTTP)
Endpoint string `yaml:"endpoint" json:"endpoint"`
// Insecure disables TLS for the connection.
// Default: false
Insecure bool `yaml:"insecure" json:"insecure"`
// Username for Basic Authentication (optional).
Username string `yaml:"username" json:"username" env:"OTEL_USERNAME"`
// Password for Basic Authentication (optional).
Password string `yaml:"password" json:"password" env:"OTEL_PASSWORD"`
// Headers are additional headers to send (e.g., auth tokens).
Headers map[string]string `yaml:"headers" json:"headers"`
// Timeout is the export timeout.
// Default: 10s
Timeout time.Duration `yaml:"timeout" json:"timeout"`
// BatchSize is the number of logs per export batch.
// Default: 512
BatchSize int `yaml:"batch_size" json:"batch_size"`
// ExportInterval is how often to export batched logs.
// Default: 5s
ExportInterval time.Duration `yaml:"export_interval" json:"export_interval"`
// Attributes are additional resource attributes for OTEL.
// Example: {"environment": "production", "chain": "solana"}
Attributes map[string]string `yaml:"attributes" json:"attributes"`
}
OTELConfig configures OpenTelemetry log export.
type TelemetryLog ¶
type TelemetryLog struct {
// contains filtered or unexported fields
}
TelemetryLog provides a fluent API for trace-coupled logging. It creates both a log entry AND a trace span in a single call.
Usage:
NewTelemetryLog(logger).
Instrument("example.routing").
Component("pool").
Trace("ROUTE_TX").
Info("transaction routed", fields.TxHash("abc123"))
The log will automatically include trace_id and span_id. Errors automatically mark the span as failed.
func NewTelemetryLog ¶
func NewTelemetryLog(logger Logger) *TelemetryLog
NewTelemetryLog creates a new fluent telemetry logger. Pass the base logger to use for output.
func NewTelemetryLogFromGlobal ¶
func NewTelemetryLogFromGlobal() *TelemetryLog
NewTelemetryLogFromGlobal creates a TelemetryLog using the global logger.
func T ¶
func T() *TelemetryLog
T is a shorthand for creating a TelemetryLog from the global logger.
func (*TelemetryLog) Component ¶
func (t *TelemetryLog) Component(name string) *TelemetryLog
Component sets the component name field.
func (*TelemetryLog) Debug ¶
func (t *TelemetryLog) Debug(msg string, fields ...Field)
Debug logs at debug level with optional trace span.
func (*TelemetryLog) Error ¶
func (t *TelemetryLog) Error(msg string, err error, fields ...Field)
Error logs at error level and marks span as error.
func (*TelemetryLog) Info ¶
func (t *TelemetryLog) Info(msg string, fields ...Field)
Info logs at info level with optional trace span.
func (*TelemetryLog) Instrument ¶
func (t *TelemetryLog) Instrument(name string) *TelemetryLog
Instrument sets the OTEL instrumentation scope name. Example: "my-service.routing", "consensus.engine"
func (*TelemetryLog) Level ¶
func (t *TelemetryLog) Level(level zapcore.Level) *TelemetryLog
Level sets the log level.
func (*TelemetryLog) Module ¶
func (t *TelemetryLog) Module(name string) *TelemetryLog
Module sets the module name field.
func (*TelemetryLog) Printf ¶
func (t *TelemetryLog) Printf(format string, args ...any)
Printf is a convenience method for formatted logging.
func (*TelemetryLog) Trace ¶
func (t *TelemetryLog) Trace(spanName string) *TelemetryLog
Trace sets the span name to create. Example: "ROUTE_TX", "VALIDATE_BLOCK", "CONNECT_NODE"
func (*TelemetryLog) Warn ¶
func (t *TelemetryLog) Warn(msg string, fields ...Field)
Warn logs at warn level with optional trace span.
func (*TelemetryLog) WithContext ¶
func (t *TelemetryLog) WithContext(ctx context.Context) *TelemetryLog
WithContext sets the context for trace propagation. If the context already has a span, the new span will be a child.