ion

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2025 License: MIT Imports: 16 Imported by: 0

README ΒΆ

Ion

Ion is an enterprise-grade, structured logging library designed for high-performance blockchain applications at JupiterMeta Labs.

It combines the raw speed of Zap with seamless OpenTelemetry (OTEL) integration, ensuring your logs are both fast and observable.

Features

  • πŸš€ High Performance: Built on Uber's Zap with a custom Zero-Allocation core for hot paths.
  • πŸ”­ OpenTelemetry Native: Seamless integration with OTEL for distributed tracing (Logs + Traces).
  • πŸ›‘οΈ Enterprise Grade: Reliable Shutdown hooks, internal pooling, and safe concurrency patterns.
  • πŸ”— Blockchain Ready: Specialized field helpers for TxHash, Slot, ShardID, and more.
  • πŸ“ Developer Friendly: Pretty console output for local dev, JSON for production.
  • πŸ”„ Lumberjack: Built-in file rotation and compression.

Installation

go get github.com/JupiterMetaLabs/ion

Quick Start

Basic Usage
package main

import (
	"github.com/JupiterMetaLabs/ion"
)

func main() {
	// Initialize with default settings
	logger := ion.New(ion.Default())
	defer logger.Sync()

	logger.Info("service started", ion.F("port", 8080))
}
With Context (Tracing)

Ion automatically correlates logs with distributed traces if a context is provided.

func HandleRequest(ctx context.Context) {
    // If ctx contains OTEL trace info, it will be added to the log
    logger.WithContext(ctx).Info("processing transaction",
        ion.F("user_id", "u_12345"),
    )
}
Configuration

Ion uses a strongly typed Config struct. You can load it from code or environment variables.

cfg := ion.Default()

// Override with env vars or manual settings
cfg.Level = "debug"
cfg.ServiceName = "payment-service"
cfg.OTEL.Enabled = true
cfg.OTEL.Endpoint = "otel-collector:4317"

logger := ion.New(cfg)
Production Configuration

For enterprise deployments, you should utilize the full configuration power (Files, Rotation, OTEL):

cfg := ion.Config{
    Level:       "info",
    Development: false,      // JSON format
    ServiceName: "payment-service",
    
    // File Logging with Rotation (Lumberjack)
    File: &ion.FileConfig{
        Enabled:    true,
        Path:       "/var/log/app/service.log",
        MaxSize:    100, // MB
        MaxBackups: 10,
        Compress:   true,
    },

    // OpenTelemetry Export
    OTEL: &ion.OTELConfig{
        Enabled:  true,
        Endpoint: "otel-collector:4317", // gRPC by default
        Protocol: "grpc",
        BatchSize: 1000,
        Attributes: map[string]string{
            "env": "production",
        },
    },
}
logger, _ := ion.NewWithOTEL(cfg)
defer logger.Shutdown(ctx)
1. Dependency Injection (Preferred)

Pass the ion.Logger explicitly to your components. This makes testing easier and dependencies clear.

type Server struct {
    log ion.Logger
}

func NewServer(l ion.Logger) *Server {
    return &Server{
        log: l.With("component", "server"), // Attach context once
    }
}
2. Global Singleton (Refactoring / Scripts)

For legacy codebases or simple scripts where passing the logger is difficult, use the global singleton.

// In main.go
ion.SetGlobal(logger)

// In package foo
func Bar() {
    // Uses the globally configured logger
    ion.L().Info("something happened")
}

πŸš€ Performance Tips

Ion is designed for high-performance zero-allocation logging. To maximize speed:

  1. Use Typed Constructors: Always use ion.Int, ion.String, ion.Bool instead of ion.F.
    • ion.String("key", "val") -> 0 allocations
    • ion.F("key", "val") -> 1 allocation (boxing to any)
3. Lifecycle & Runtime Configuration
Graceful Shutdown

Always ensure you flush logs before your application exits to prevent data loss (especially for OTEL traces).

// In main()
logger, _ := ion.NewWithOTEL(cfg)
// Use a timeout context to ensure we don't hang if OTEL collector is down
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
defer logger.Shutdown(ctx)
Runtime Configuration (Dynamic Level)

Ion supports changing the log level at runtime without restarting the application. This is thread-safe.

// Safe to call concurrently
logger.SetLevel("debug") 
// Later...
logger.SetLevel("info")
Hot Reloading (Initialization Pattern)

A common pattern for applications is to start with a basic logger, load configuration, and then upgrade to a production logger.

func main() {
    // 1. Start with a safe default (Console, Info)
    ion.SetGlobal(ion.New(ion.Default()))

    // 2. Load Configuration (e.g., from YAML or Env)
    appConfig := LoadConfig()

    // 3. Initialize Production Logger (File, OTEL, etc.)
    prodLogger, err := ion.NewWithOTEL(appConfig.Log)
    if err != nil {
        ion.L().Fatal("failed to init logger", err)
    }

    // 4. Replace Global Logger
    ion.SetGlobal(prodLogger)
    
    // 5. Run App
    RunApp(ion.L())
}

πŸš€ Performance Tips

Ion is designed for high-performance zero-allocation logging. To maximize speed:

  1. Use Typed Constructors: Always use ion.Int, ion.String, ion.Bool instead of ion.F.
    • ion.String("key", "val") -> 0 allocations
    • ion.F("key", "val") -> 1 allocation (boxing to any)
  2. Sync vs Shutdown: logger.Sync() only flushes the local buffer. logger.Shutdown(ctx) flushes everything (including OTEL) and closes connections. Use Shutdown on exit.

The Config struct maps to YAML/JSON and Environment Variables.

Field Env Var Default Description
level LOG_LEVEL info Minimum log level (debug, info, warn, error).
development LOG_DEVELOPMENT false Enables pretty printing, stack traces, and caller info.
service_name SERVICE_NAME unknown Name of the service for OTEL traces.
version SERVICE_VERSION "" Service version for OTEL.
Console
console.enabled - true Enable stdout/stderr output.
console.format - json json or pretty.
console.color - true Enable ANSI colors in pretty mode.
File
file.enabled - false Enable writing to a log file.
file.path - "" Absolute path to the log file.
file.max_size_mb - 100 Rotate after N megabytes.
file.max_backups - 5 Keep N old files.
file.max_age_days - 7 Keep files for N days.
file.compress - true Gzip rotated files.
OTEL
otel.enabled - false Enable OpenTelemetry export.
otel.endpoint - "" Collector address (e.g., localhost:4317).
otel.protocol - grpc grpc or http.
otel.insecure - false Disable TLS (use for local collectors).
otel.timeout - 10s Export timeout.
otel.batch_size - 512 Max logs per batch.
otel.export_interval - 5s Flush interval.

Architecture

Ion is designed as a wrapper around zap.Logger. It injects a custom Core for OTEL integration that does not impede the performance of local logging.

  • Console/File: Handled directly by Zap.
  • OTEL: Handled by an asynchronous batch processor to avoid blocking the application.

License

MIT Β© 2025 JupiterMeta Labs

Documentation ΒΆ

Overview ΒΆ

Package ion provides an enterprise-grade structured logger tailored for JupiterMeta blockchain services.

It wraps the high-performance Zap logger and OpenTelemetry (OTEL) for observability, offering a simple yet powerful API for consistent logging across microservices.

Key Features ΒΆ

  • Zero-allocation hot paths using a custom Zap core.
  • 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.

Basic Usage ΒΆ

Initialize the logger with a configuration:

import "github.com/JupiterMetaLabs/ion"

func main() {
    logger := ion.New(ion.Default())
    defer logger.Sync()

    logger.Info("application started", ion.F("version", "1.0.0"))
}

Context Support ΒΆ

Use FromContext or WithContext to automatically attach trace identifiers:

func HandleRequest(ctx context.Context) {
    // Extracts trace_id and span_id if present in ctx
    logger.WithContext(ctx).Info("processing request", ion.F("user_id", 123))
}

Package ion provides enterprise-grade structured logging for JupiterMeta blockchain applications.

Features:

  • High-performance Zap core with zero-allocation hot paths
  • Multi-destination output (Console, File, OTEL)
  • Blockchain-specific field helpers (TxHash, ShardID, Slot, etc.)
  • Automatic trace context propagation
  • Pretty console output for development
  • File rotation via lumberjack
  • OpenTelemetry integration for observability

Basic usage:

logger := ion.New(ion.Default())
defer logger.Sync()

logger.Info("server started", ion.F("port", 8080))

With blockchain fields:

import "github.com/JupiterMetaLabs/ion/fields"

logger.Info("transaction routed",
    fields.TxHash("abc123"),
    fields.ShardID(5),
    fields.LatencyMs(12.5),
)

With context (auto-extracts trace_id):

logger.WithContext(ctx).Info("handling request")

Index ΒΆ

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 ΒΆ

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext extracts the request ID from context.

func SetGlobal ΒΆ

func SetGlobal(l Logger)

SetGlobal sets the global logger instance. Call this early in application startup.

func UserIDFromContext ΒΆ

func UserIDFromContext(ctx context.Context) string

UserIDFromContext extracts the user ID from context.

func WithRequestID ΒΆ

func WithRequestID(ctx context.Context, requestID string) context.Context

WithRequestID adds a request ID to the context. This ID will be automatically included in logs via WithContext().

func WithTraceID ΒΆ

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

WithTraceID adds a trace ID to the context (for non-OTEL scenarios).

func WithUserID ΒΆ

func WithUserID(ctx context.Context, userID string) context.Context

WithUserID adds a user ID to the context.

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 Default ΒΆ

func Default() Config

Default returns a Config with sensible production defaults.

func Development ΒΆ

func Development() Config

Development returns a Config optimized for development.

func (Config) WithFile ΒΆ

func (c Config) WithFile(path string) Config

WithFile returns a copy of the config with file logging enabled.

func (Config) WithLevel ΒΆ

func (c Config) WithLevel(level string) Config

WithLevel returns a copy of the config with the specified level.

func (Config) WithOTEL ΒΆ

func (c Config) WithOTEL(endpoint string) Config

WithOTEL returns a copy of the config with OTEL enabled.

func (Config) WithService ΒΆ

func (c Config) WithService(name string) Config

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). It is designed to be zero-allocation for common types.

func Bool ΒΆ

func Bool(key string, value bool) Field

Bool creates a boolean field.

func Err ΒΆ

func Err(err error) Field

Err creates an error field with the standard key "error".

func F ΒΆ

func F(key string, value any) Field

F is a convenience constructor for Field. It detects the type and creates the appropriate Field.

func Float64 ΒΆ

func Float64(key string, value float64) Field

Float64 creates a float64 field.

func Int ΒΆ

func Int(key string, value int) Field

Int creates an integer field.

func Int64 ΒΆ

func Int64(key string, value int64) Field

Int64 creates an int64 field.

func String ΒΆ

func String(key, value string) Field

String creates a string field.

type FieldType ΒΆ

type FieldType uint8

FieldType roughly mirrors zapcore.FieldType

const (
	UnknownType FieldType = iota
	StringType
	Int64Type
	Float64Type
	BoolType
	ErrorType
	AnyType
)

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(msg string, fields ...Field)

	// Info logs a message at info level.
	Info(msg string, fields ...Field)

	// Warn logs a message at warn level.
	Warn(msg string, fields ...Field)

	// Error logs a message at error level with an error.
	Error(msg string, err error, fields ...Field)

	// Fatal logs a message at fatal level and calls os.Exit(1).
	Fatal(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

	// WithContext returns a child logger that extracts trace_id, span_id,
	// and other context values (request_id, user_id) from the context.
	WithContext(ctx context.Context) 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.

Example ΒΆ
// 1. Initialize the logger
logger := New(Development())
defer logger.Sync()

// 2. Log a simple message
logger.Info("Hello, World!")

// 3. Log with structured fields
logger.Info("User logged in",
	F("user_id", 42),
	F("ip", "192.168.1.1"),
)

func GetGlobal ΒΆ

func GetGlobal() Logger

GetGlobal returns the global logger instance. Returns a no-op logger if SetGlobal was never called.

func L ΒΆ

func L() Logger

L is a shorthand for GetGlobal().

func New ΒΆ

func New(cfg Config) Logger

New creates a new Logger from the provided configuration. This is the main constructor for ion.

func NewWithOTEL ΒΆ

func NewWithOTEL(cfg Config) (Logger, error)

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"`

	// 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.

Directories ΒΆ

Path Synopsis
examples
basic command
Package fields provides blockchain-specific logging field helpers.
Package fields provides blockchain-specific logging field helpers.
internal

Jump to

Keyboard shortcuts

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