ion

package module
v0.2.7 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2026 License: MIT Imports: 13 Imported by: 0

README ΒΆ

Ion

Ion is an enterprise-grade observability client for Go services. It unifies structured logging (Zap) and distributed tracing (OpenTelemetry) into a single, cohesive API designed for high-throughput, long-running infrastructure.

Status: v0.2 Release Candidate
Target: Microservices, Blockchain Nodes, Distributed Systems


Guarantees & Design Invariants

Ion is built on strict operational guarantees. Operators can rely on these invariants in production:

  1. No Process Termination: Ion will never call os.Exit, panic, or log.Fatal. Even Critical level logs are strictly informational (mapped to FATAL severity) and guarantee control flow returns to the caller.
  2. Thread Safety: All public APIs on Logger and Tracer are safe for concurrent use by multiple goroutines.
  3. Non-Blocking Telemetry: Trace export is asynchronous and decoupled from application logic. A slow OTEL collector will never block your business logic (logs are synchronous to properly handle crash reporting, but rely on high-performance buffered writes).
  4. Failure Isolation: Telemetry backend failures (e.g., Collector down) are isolated. They may result in data loss (dropped spans) but will never crash the service.

Non-Goals

To maintain focus and stability, Ion explicitly avoids:

  • Metrics: Use the Prometheus or OpenTelemetry Metrics SDKs directly.
  • Alerting: Ion emits signals; it does not manage thresholds or paging.
  • Framework Magic: Ion does not auto-inject into HTTP handlers without explicit middleware usage.

Operational Model

How Ion Works
  • Logs: Emitted synchronously to the configured cores (Console/File/Memory). This ensures that if your application crashes immediately after a log statement, the log is persisted (up to OS buffering).
  • Traces: Buffered and exported asynchronously. Spans are batched in memory and sent to the configured OTEL, endpoint on a timer or size threshold.
  • Correlation: trace_id and span_id are extracted from context.Context at the moment of logging and injected as fields.
When to Use Logs vs Traces
  • Logs: Use for state changes, errors, and high-cardinality events (e.g., specific transaction failure reasons). Logs must be reliable and available immediately.
  • Traces: Use for latency analysis, causality (who called whom), and request flows. Traces are sampled and statistically significant, but individual traces may be dropped under load.

Installation

go get github.com/JupiterMetaLabs/ion

Requires Go 1.21+.


Quick Start

A minimal, correct example for a production service.

package main

import (
    "context"
    "log"
    "time"

    "github.com/JupiterMetaLabs/ion"
)

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

    // 1. Initialize with Service Identity
    // Returns warning slice for non-fatal config issues (e.g. invalid OTEL url)
    app, warnings, err := ion.New(ion.Default().WithService("payment-node"))
    if err != nil {
        log.Fatalf("Fatal: failed to init observability: %v", err)
    }
    for _, w := range warnings {
        log.Printf("Ion Startup Warning: %v", w)
    }

    // 2. Establishing the Lifecycle Contract
    // Ensure logs/traces flush before exit.
    defer func() {
        shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
        defer cancel()
        // Errors here mean data loss, not application failure.
        if err := app.Shutdown(shutdownCtx); err != nil {
            log.Printf("Shutdown data loss: %v", err)
        }
    }()

    // 3. Application Logic
    app.Info(ctx, "node started", ion.String("version", "1.0.0"))
    
    // Simulate work
    doWork(ctx, app)
}

func doWork(ctx context.Context, logger ion.Logger) {
    // Context is mandatory for correlation
    logger.Info(ctx, "processing block", ion.Uint64("height", 100))
}

Configuration Reference

Ion uses a comprehensive configuration struct for behavior control. This maps 1:1 with ion.Config.

Root Configuration (ion.Config)
Field Type Default Description
Level string "info" Minimum log level (debug, info, warn, error, fatal).
Development bool false Enables development mode (pretty output, caller location, stack traces).
ServiceName string "unknown" Identity of the service (vital for trace attribution).
Version string "" Service version (e.g., commit hash or semver).
Console ConsoleConfig Enabled: true configuration for stdout/stderr.
File FileConfig Enabled: false configuration for file logging (with rotation).
OTEL OTELConfig Enabled: false configuration for remote OpenTelemetry logging.
Tracing TracingConfig Enabled: false configuration for Distributed Tracing.
Console Configuration (ion.ConsoleConfig)
Field Type Default Description
Enabled bool true If false, stdout/stderr is silenced.
Format string "json" "json" (production) or "pretty" (human-readable).
Color bool true Enables ANSI colors (only references pretty format).
ErrorsToStderr bool true Writes warn/error/fatal to stderr, others to stdout.
File Configuration (ion.FileConfig)
Field Type Default Description
Enabled bool false Enables file writing.
Path string "" Absolute path to the log file (e.g., /var/log/app.log).
MaxSizeMB int 100 Max size per file before rotation.
MaxBackups int 5 Number of old files to keep.
MaxAgeDays int 7 Max age of files to keep.
Compress bool true Gzip old log files.
OTEL Configuration (ion.OTELConfig)

Controls the OpenTelemetry Logs Exporter.

Field Type Default Description
Enabled bool false Enables log export to Collector.
Level string Config.Level (Internal) Respects the main Level config (e.g. debug or info).
Endpoint string "" host:port (e.g., localhost:4317).
Protocol string "grpc" "grpc" (recommended) or "http".
Insecure bool false If true, disables TLS (dev only).
Username string "" Basic Auth Username.
Password string "" Basic Auth Password.
BatchSize int 512 Max logs per export batch.
ExportInterval Duration 5s flush interval.
Tracing Configuration (ion.TracingConfig)

Controls the OpenTelemetry Trace Provider.

Field Type Default Description
Enabled bool false Enables trace generation and export.
Endpoint string "" host:port. Inherits OTEL.Endpoint if empty.
Sampler string "always" "always", "never", or "ratio:0.X" (e.g., ratio:0.1 for 10%).
Protocol string "grpc" "grpc" or "http". Inherits OTEL.Protocol if empty.
Username string "" Inherits OTEL.Username if empty.
Password string "" Inherits OTEL.Password if empty.

Detailed Initialization

For full control, initialize the struct directly rather than using builders.

cfg := ion.Config{
    Level:       "info",
    ServiceName: "payment-service",
    Version:     "v1.2.3",
    
    Console: ion.ConsoleConfig{
        Enabled:        true,
        Format:         "json",
        ErrorsToStderr: true,
    },
    
    // File rotation
    File: ion.FileConfig{
        Enabled:    true,
        Path:       "/var/log/payment-service.log",
        MaxSizeMB:  500,
        MaxBackups: 3,
        Compress:   true,
    },
    
    // Remote Telemetry (OTEL)
    OTEL: ion.OTELConfig{
        Enabled:  true,
        Endpoint: "otel-collector.prod:4317",
        Protocol: "grpc",
        // Attributes added to every log
        Attributes: map[string]string{
            "cluster": "us-east-1",
            "env":     "production",
        },
    },
    
    Tracing: ion.TracingConfig{
        Enabled: true,
        // Fallback: If Endpoint/Auth/Protocol are empty, they are inherited from OTEL config above.
        // This is safe to leave empty if using the same collector for logs and traces.
        Sampler: "ratio:0.05", // Sample 5% of traces
    },
}

// Initialize
app, warnings, err := ion.New(cfg)
if err != nil {
    panic(err)
}
// Handle warnings (e.g., invalid sampler string fallback)
for _, w := range warnings {
    log.Println("Ion config warning:", w)
}
defer app.Shutdown(context.Background())

πŸ“˜ Deep Dive: For enterprise patterns, advanced tracing, and blockchain-specific examples, see the Enterprise User Guide.

Proper Usage Guide

1. The Logger

Use the Logger for human-readable events, state changes, and errors.

  • Always pass context.Context (even if generic).
  • Prefer typed fields (ion.String) over generic ion.F.
  • Do not log sensitive data (PII/Secrets).
// INFO: Operational state changes
app.Info(ctx, "transaction processed", 
    ion.String("tx_id", "0x123"),
    ion.Duration("latency", 50 * time.Millisecond),
)

// ERROR: Actionable failures. Does not interrupt flow.
if err != nil {
    // Automatically adds "error": err.Error() field
    app.Error(ctx, "database connection failed", err, ion.String("db_host", "primary"))
}

// CRITICAL: Invariant violations (e.g. data corruption).
// Use this for "wake up the on-call" events.
// GUARANTEE: Does NOT call os.Exit(). Safe to use in libraries.
app.Critical(ctx, "memory corruption detected", nil)

### 3. Child Loggers (Scopes)
Use `With` and `Named` to create context-aware sub-loggers. This is often better than passing raw `ion` fields everywhere.

*   **`Named(name)`**: Appends to the logger name. Good for components.
    *   `app` -> `app.http` -> `app.http.client`
*   **`With(fields...)`**: Permanently attaches fields to all logs from this logger.

```go
// In your constructor
func NewPaymentService(root ion.Logger) *PaymentService {
    // All logs from this service will have "service": "payment" and "component": "core"
    // AND be named "main.payment"
    log := root.Named("payment").With(ion.String("component", "core"))
    
    return &PaymentService{log: log}
}

func (s *PaymentService) Process(ctx context.Context) {
    // Log comes out as:
    // logger="main.payment" component="core" msg="processing"
    s.log.Info(ctx, "processing") 
}
4. The Tracer

Use the Tracer for latency measurement and causal chains.

  • Start/End: Every Start MUST have a corresponding End().
  • Defer: Use defer span.End() immediately after checking err isn't nil (or just immediately if function is simple).
  • Attributes: Add attributes to spans only if they are valuable for querying latency/filtering (e.g., "http.status_code"). High cardinality data belongs in Logs, not Spans attributes (usually).
func ProcessOrder(ctx context.Context, orderID string) error {
    // 1. Get Named Tracer
    tracer := app.Tracer("order.processor")
    
    // 2. Start Span
    ctx, span := tracer.Start(ctx, "ProcessOrder")
    // 3. Ensure End
    defer span.End()
    
    // 4. Enrich Span
    span.SetAttributes(attribute.String("order.id", orderID))
    
    // ... work ...
    
    if err := validate(ctx); err != nil {
        // 5. Record Errors in Span
        span.RecordError(err)
        span.SetStatus(codes.Error, "validation failed")
        return err
    }
    
    return nil
}

Common Configurations

Recipes for standard deployment scenarios.


Initialization Scenarios

Ion is flexible. Here are the core patterns for different environments.

1. The "Standard" (Console Only)

Best for: CLI tools, scripts, local testing.

cfg := ion.Default()
cfg.Level = "info" 
// cfg.Development = true // Optional: enables callers / stacktraces
2. The "Boxed Service" (Console + File)

Best for: Systemd services, VM-based deployments, legacy nodes.

cfg := ion.Default()
// Console for live tailing (kubectl logs)
cfg.Console.Enabled = true 
// File for long-term retention
cfg.File.Enabled = true
cfg.File.Path = "/var/log/app/app.log"
cfg.File.MaxSizeMB = 500
cfg.File.MaxBackups = 5
3. The "Silent Agent" (OTEL Only)

Best for: High-traffic sidecars where local IO is expensive.

cfg := ion.Default()
cfg.Console.Enabled = false // Disable local IO
cfg.OTEL.Enabled = true
cfg.OTEL.Endpoint = "localhost:4317"
cfg.OTEL.Protocol = "grpc"
4. The "Full Stack" (Console + OTEL + Tracing)

Best for: Kubernetes microservices.

cfg := ion.Default()
cfg.Console.Enabled = true       // For pod logs
cfg.OTEL.Enabled = true          // For log aggregation (Loki/Elastic)
cfg.OTEL.Endpoint = "otu-col:4317"

cfg.Tracing.Enabled = true       // For distributed traces (Tempo/Jaeger)
cfg.Tracing.Sampler = "ratio:0.1" // 10% sampling

πŸ” Tracing Guide for Developers

Distributed tracing is powerful but requires discipline. Follow these rules to get useful traces.

1. Span Lifecycle
  • Root Spans: Created by middleware (HTTP/gRPC). You rarely start these manually unless writing a background worker.
  • Child Spans: Created by Start(ctx, name). Always inherit parent ID from ctx.
// 1. Start (creates child if ctx has parent)
ctx, span := tracer.Start(ctx, "CalculateHash")
// 2. Defer End (Critical!)
defer span.End() 
2. Attributes vs Events vs Logs
  • Attributes: "Search Tags". Low cardinality. Use for filtering.
    • Good: user_id, http.status, region, retry_count
    • Bad: error_message (too variable), payload_dump (too big)
  • Events: "Timestamped Markers". Significant moments inside a span.
    • Example: span.AddEvent("cache_miss")
  • Logs: "Detailed Context". High cardinality. Use app.Info(ctx, ...) instead.
    • Why: Logs are cheaper and searchable by full text.
3. Handling Errors in Spans

The Problem: By default, a span finishes as OK even if your function returns an error. This results in 0% Error Rate on your dashboard despite complete failure.

The Fix: You must explicitly "taint" the span.

if err != nil {
    // 1. Record stacktrace and error type in the span
    span.RecordError(err)
    // 2. Flip the span status to Error (turns red in Jaeger/Tempo)
    span.SetStatus(codes.Error, "failed to insert order")
    
    // 3. Log it for humans (Logs = Detail, Traces = Signals)
    app.Error(ctx, "failed to insert order", err)
    return err
}
// Optional: Explicitly mark success if needed, though default is Unset/OK
span.SetStatus(codes.Ok, "success")
4. Spawning Goroutines

The Problem: Spans are bound to context. If the parent request finishes, ctx is canceled and the parent span Ends. A background goroutine using that same ctx becomes a "Ghost Span" β€” it might log errors, but it has no parent in the trace visualization (disconnected).

The Fix: Create a Link. A Link connects a new Root Span to the old Parent Trace, saying "This background job was caused by that request", without being killed by it.

// Fire-and-Forget Background Task
go func(parentCtx context.Context) {
    // 1. Create a FRESH context (so we don't die when request finishes)
    newCtx := context.Background()
    tracer := app.Tracer("background_worker")
    
    // 2. Create a Link from the parent (Preserves causality)
    link := trace.LinkFromContext(parentCtx) 
    
    // 3. Start a new Root Span with the link
    ctx, span := tracer.Start(newCtx, "AsyncJob", ion.WithLinks(link))
    defer span.End()
    
    // Now you have a safe, independent span correlated to the original request
    app.Info(ctx, "processing in background", ion.String("job", "email_send"))
}(ctx)

HTTP & gRPC Integration

Ion provides specialized middleware/interceptors to automate context propagation.

HTTP Middleware (middleware/ionhttp)
import "github.com/JupiterMetaLabs/ion/middleware/ionhttp"

mux := http.NewServeMux()
handler := ionhttp.Handler(mux, "payment-api") 
http.ListenAndServe(":8080", handler)
gRPC Interceptors (middleware/iongrpc)
import "github.com/JupiterMetaLabs/ion/middleware/iongrpc"

// Server
s := grpc.NewServer(
    grpc.StatsHandler(iongrpc.ServerHandler()),
)

// Client
conn, err := grpc.Dial(addr, 
    grpc.WithStatsHandler(iongrpc.ClientHandler()),
)

Production Failure Modes

Operators must understand how Ion behaves under stress:

  • OTEL Collector Down: The internal exporter will retry with exponential backoff. If buffers fill, new traces will be dropped. Application performance is preserved (failure is isolated).
  • Disk Full (File Logging): lumberjack rotation will attempt to write. If the write syscall fails, Zap internal error handling catches it. The application continues, but logs are lost (written to stderr fallback if possible).
  • High Load: Tracing uses a batch processor. Under extreme load, if the export rate lags generation, spans are dropped to prevent memory leaks (bounded buffer).

Globals & Dependency Injection

⚠️ WARNING: Global state is strictly for migration and legacy support.

Do not use ion.SetGlobal in new libraries or microservices. Do inject ion.Logger via struct constructors.

// CORRECT: Dependency Injection
type Server struct {
    log ion.Logger
}

// INCORRECT: Hidden dependency
func (s *Server) Handle() {
    ion.Info(...) // Relies on global state
}

If ion.GetGlobal() is called without initialization, it returns a safe no-op logger. It will not panic, but your logs will be silently discarded to protect the runtime.


Best Practices

  1. Pass Context Everywhere: context.Background() breaks the trace chain. Only use it in main or background worker roots.
  2. Shutdown is Mandatory: Failing to call Shutdown guarantees data loss (buffered traces/logs) on deployment.
  3. Structured Keys: Use consistent key names (e.g., user_id, not userID or uid) to make logs queryable.
  4. No Dynamic Keys: ion.String(userInput, "value") is a security risk and breaks indexing. Keys must be static constants.

Versioning

  • Public API: ion.go, logger.go, config.go. Stable v0.2.
  • Internal: internal/*. No stability guarantees.
  • Behavior: Log format changes or configuration defaults are considered breaking changes.

License

MIT Β© 2025 JupiterMeta Labs

Documentation ΒΆ

Overview ΒΆ

Package ion context helpers provide functions for propagating trace, request, and user IDs through context.Context. These values are automatically extracted and included in log entries.

For OTEL tracing, trace_id and span_id are automatically extracted from the span context. For non-OTEL scenarios, use WithTraceID to set manually.

Package ion provides production-grade logging and tracing for Go services.

Ion unifies structured logging (Zap) and distributed tracing (OpenTelemetry) behind a minimal, context-first API.

Guarantees ΒΆ

  • Process Safety: Ion never terminates the process (no os.Exit, no panic).
  • Concurrency: All Logger and Tracer APIs are safe for concurrent use.
  • Failure Isolation: Telemetry backend failures never crash application logic.
  • Lifecycle: Shutdown(ctx) flushes all buffers on a best-effort basis.

Architecture ΒΆ

  • Logs: Synchronous, structured, strongly typed.
  • Traces: Asynchronous, sampled, batched.
  • Correlation: Automatic injection of trace_id/span_id from context.Context.

Ion is designed for long-running services and distributed systems. It is not a metrics SDK or a web framework.

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
  • 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, warnings, _ := 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 ΒΆ

Examples ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

func Critical ΒΆ added in v0.2.0

func Critical(ctx context.Context, msg string, err error, fields ...Field)

Critical logs at critical level using global logger. Does NOT exit the process - caller decides what to do.

func Debug ΒΆ added in v0.1.1

func Debug(ctx context.Context, msg string, fields ...Field)

Debug logs at debug level using global logger.

func Error ΒΆ added in v0.1.1

func Error(ctx context.Context, msg string, err error, fields ...Field)

Error logs at error level using global logger.

func Info ΒΆ added in v0.1.1

func Info(ctx context.Context, msg string, fields ...Field)

Info logs at info level using global logger.

func New ΒΆ

func New(cfg Config) (*Ion, []Warning, error)

New creates a new Ion instance with the given configuration. This is the single entry point for creating ion observability.

Returns:

  • *Ion: Always returns a working Ion instance (may use fallbacks)
  • []Warning: Non-fatal issues (e.g., OTEL connection failed, tracing disabled)
  • error: Fatal configuration errors

func RequestIDFromContext ΒΆ

func RequestIDFromContext(ctx context.Context) string

RequestIDFromContext extracts the request ID from context.

func SetGlobal deprecated

func SetGlobal(logger Logger)

SetGlobal sets the global logger instance.

Deprecated: Use Dependency Injection instead. This method exists for migration purposes only and will be removed in a future version.

func Sync ΒΆ added in v0.1.1

func Sync() error

Sync flushes the global logger.

func UserIDFromContext ΒΆ

func UserIDFromContext(ctx context.Context) string

UserIDFromContext extracts the user ID from context.

func Warn ΒΆ added in v0.1.1

func Warn(ctx context.Context, msg string, fields ...Field)

Warn logs at warn level using global logger.

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.

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 = config.Config

Config holds the complete logger configuration. It is an alias to internal/config.Config to allow sharing with internal packages.

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.

type ConsoleConfig ΒΆ

type ConsoleConfig = config.ConsoleConfig

ConsoleConfig configures console 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).

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.

func Uint64 ΒΆ added in v0.2.0

func Uint64(key string, value uint64) Field

Uint64 creates a uint64 field without truncation. Use this for large unsigned values (e.g., block heights, slots).

type FieldType ΒΆ

type FieldType uint8

FieldType roughly mirrors zapcore.FieldType

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

type FileConfig ΒΆ

type FileConfig = config.FileConfig

FileConfig configures file output.

type Ion ΒΆ added in v0.2.0

type Ion struct {
	// contains filtered or unexported fields
}

Ion is the unified observability instance providing logging and tracing. It implements the Logger interface directly, so you can use it for logging. It also provides access to Tracer for distributed tracing.

Example:

app, err := ion.New(cfg)
if err != nil {
    log.Fatal(err)
}
defer app.Shutdown(context.Background())

// Logging (Ion implements Logger)
app.Info(ctx, "message", ion.F("key", "value"))

// Tracing
tracer := app.Tracer("myapp.component")
ctx, span := tracer.Start(ctx, "Operation")
defer span.End()

// Global usage (DEPRECATED: Prefer Dependency Injection)
ion.SetGlobal(app)
ion.Info(ctx, "works from anywhere (but try to avoid this)")

func (*Ion) Critical ΒΆ added in v0.2.0

func (i *Ion) Critical(ctx context.Context, msg string, err error, fields ...Field)

func (*Ion) Debug ΒΆ added in v0.2.0

func (i *Ion) Debug(ctx context.Context, msg string, fields ...Field)

func (*Ion) Error ΒΆ added in v0.2.0

func (i *Ion) Error(ctx context.Context, msg string, err error, fields ...Field)

func (*Ion) GetLevel ΒΆ added in v0.2.0

func (i *Ion) GetLevel() string

func (*Ion) Info ΒΆ added in v0.2.0

func (i *Ion) Info(ctx context.Context, msg string, fields ...Field)

func (*Ion) Named ΒΆ added in v0.2.0

func (i *Ion) Named(name string) Logger

func (*Ion) SetLevel ΒΆ added in v0.2.0

func (i *Ion) SetLevel(level string)

func (*Ion) Shutdown ΒΆ added in v0.2.0

func (i *Ion) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down logging and tracing.

func (*Ion) Sync ΒΆ added in v0.2.0

func (i *Ion) Sync() error

func (*Ion) Tracer ΒΆ added in v0.2.0

func (i *Ion) Tracer(name string) Tracer

Tracer returns a named tracer for creating spans. If tracing is not enabled, returns a no-op tracer (logs warning once).

func (*Ion) Warn ΒΆ added in v0.2.0

func (i *Ion) Warn(ctx context.Context, msg string, fields ...Field)

func (*Ion) With ΒΆ added in v0.2.0

func (i *Ion) With(fields ...Field) Logger
type Link = trace.Link

Link is an alias for trace.Link to avoid importing otel/trace.

func LinkFromContext ΒΆ added in v0.2.3

func LinkFromContext(ctx context.Context) Link

LinkFromContext extracts a link from the current context to connect spans.

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)

	// Critical logs a message at the highest severity level but does NOT exit.
	//
	// Critical logs are emitted at FATAL level to ensure visibility in backends,
	// but the process is GUARANTEED not to exit.
	//
	// Usage pattern:
	//   logger.Critical(ctx, "unrecoverable error", err)
	//   return err  // Let caller decide how to handle
	Critical(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 := newZapLogger(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 := newZapLogger(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 GetGlobal ΒΆ

func GetGlobal() Logger

GetGlobal returns the global logger. If SetGlobal has not been called, it returns a safe no-op logger.

func Named ΒΆ added in v0.1.1

func Named(name string) Logger

Named returns a child logger from global.

type OTELConfig ΒΆ

type OTELConfig = config.OTELConfig

OTELConfig configures OTEL log export.

type Span ΒΆ added in v0.2.0

type Span interface {
	// End marks the span as complete.
	End()
	// SetStatus sets the span status.
	SetStatus(code codes.Code, description string)
	// RecordError records an error as an event.
	RecordError(err error)
	// SetAttributes sets attributes on the span.
	SetAttributes(attrs ...attribute.KeyValue)
	// AddEvent adds an event to the span.
	AddEvent(name string, attrs ...attribute.KeyValue)
}

Span represents a unit of work in a trace.

type SpanOption ΒΆ added in v0.2.0

type SpanOption interface {
	// contains filtered or unexported methods
}

SpanOption configures span creation.

func WithAttributes ΒΆ added in v0.2.0

func WithAttributes(attrs ...attribute.KeyValue) SpanOption

WithAttributes adds attributes to the span.

func WithLinks(links ...trace.Link) SpanOption

WithLinks adds links to the span.

func WithOTELOptions ΒΆ added in v0.2.2

func WithOTELOptions(opts ...trace.SpanStartOption) SpanOption

WithOTELOptions allows passing raw OpenTelemetry options directly. This is an escape hatch for advanced features not yet wrapped by Ion.

func WithSpanKind ΒΆ added in v0.2.0

func WithSpanKind(kind trace.SpanKind) SpanOption

WithSpanKind sets the span kind (client, server, etc).

type Tracer ΒΆ added in v0.2.0

type Tracer interface {
	// Start creates a new span.
	Start(ctx context.Context, spanName string, opts ...SpanOption) (context.Context, Span)
}

Tracer creates spans for distributed tracing.

func GetTracer ΒΆ added in v0.2.0

func GetTracer(name string) Tracer

GetTracer returns a named tracer from global Ion. Note: This relies on SetGlobal being called with an *Ion instance (or implementation that supports Tracer()) If the global logger does not support Tracer, this might fail or return no-op. Since Logger interface doesn't have Tracer(), we can only check if global is *Ion. BUT, legacy GetTracer probably used `otel.Tracer`. We should probably just call `otel.Tracer` directly here or use `GetGlobal`? The previous implementation called `getGlobal().Tracer()`. Since `Logger` interface does NOT have `Tracer`, `ion.Tracer` works on `*Ion`. So `GetGlobal()` returning `Logger` logic is tricky for `Tracer`. Fix: Check type assertion.

type TracingConfig ΒΆ added in v0.2.0

type TracingConfig = config.TracingConfig

TracingConfig configures distributed tracing.

type Warning ΒΆ added in v0.2.0

type Warning struct {
	Component string // "otel", "tracing"
	Err       error
}

Warning represents a non-fatal initialization issue. Ion returns warnings instead of failing when optional components (like OTEL or tracing) cannot be initialized.

func (Warning) Error ΒΆ added in v0.2.0

func (w Warning) Error() string

Directories ΒΆ

Path Synopsis
examples
basic command
benchmark command
Package main provides a comprehensive benchmark suite for ion logging library.
Package main provides a comprehensive benchmark suite for ion logging library.
otel-test command
Package main tests ion trace correlation with OTEL and Jaeger.
Package main tests ion trace correlation with OTEL and Jaeger.
Package fields provides blockchain-specific logging field helpers.
Package fields provides blockchain-specific logging field helpers.
internal
core
Package core provides the internal implementation of Ion's logging and tracing.
Package core provides the internal implementation of Ion's logging and tracing.
middleware
iongrpc
Package grpc provides gRPC server and client instrumentation using OpenTelemetry.
Package grpc provides gRPC server and client instrumentation using OpenTelemetry.
ionhttp
Package http provides HTTP server and client instrumentation using OpenTelemetry.
Package http provides HTTP server and client instrumentation using OpenTelemetry.

Jump to

Keyboard shortcuts

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