obs

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

README ΒΆ

Nexss Observability πŸ”­

Go Reference License

Zero-Touch Telemetry and Pragmatic Observability for High-Performance Go Applications.

nexssp/observability separates application infrastructure concerns from your core business domain rules [1]. By utilizing Go's native context.Context propagation and the nexssp/kernel zero-allocation AnyHook execution hooks, we achieve full metric tracking, distributed tracing, and structured contextual logging without introducing external SDK dependencies into your business logic [1].

πŸ—οΈ Architecture Philosophy

Observability is an infrastructure concern, not a domain concern.

We strictly forbid polluting business logic (kernel/action handlers) with tracing setup, metric counters, or correlation ID extraction.

Instead, this package utilizes the Kernel's AnyHook interface to intercept executions at the boundary.

Telemetry Stack Integration:
  • Prometheus Metrics: Automatic action_latency_ms and action_errors_total on every action.
  • OpenTelemetry Traces: Spans automatically created and closed for every action invocation.
  • Contextual Logging: log/slog automatically enriched with trace_id, span_id, and action names.
  • Transparent Propagation: Wrapped DB drivers and HTTP clients that automatically extract/inject W3C trace headers.
  • Health Aggregation: A unified /healthz endpoint with strict timeouts to prevent cascading infrastructure failures.

🀯 The Magic of Nexss: "Zero-Touch" Telemetry

In traditional microservices, observability is a disease that infects your business logic. Developers are forced to become infrastructure experts, mixing OpenTelemetry APIs, Prometheus counters, and context extraction directly into their domain code.

❌ The "Old Way" (Spaghetti Code)

Look at a standard Go handler. Over 70% of this code is infrastructure boilerplate.

func CreateOrder(ctx context.Context, req OrderReq) (OrderRes, error) {
    // 1. Tracing Boilerplate
    ctx, span := tracer.Start(ctx, "CreateOrder")
    defer span.End()

    // 2. Metrics Boilerplate
    start := time.Now()
    defer func() {
        requestLatency.WithLabelValues("CreateOrder").Observe(time.Since(start).Seconds())
    }()

    // 3. Logging Boilerplate
    log.With("trace_id", span.SpanContext().TraceID().String()).Info("creating order")

    // --- FINALLY, THE ACTUAL BUSINESS LOGIC ---
    err := db.ExecContext(ctx, "INSERT INTO orders...", req.ID)
    if err != nil {
        errorCounter.WithLabelValues("CreateOrder").Inc() // More boilerplate
        span.RecordError(err)                             // More boilerplate
        return OrderRes{}, err
    }
    return OrderRes{Status: "created"}, nil
}
✨ The "Nexss Way" (Pristine Domain Logic)

In Nexss, your developers never write a single line of tracing or metrics code. They write pure business logic.

var CreateOrder = action.New("order.create", func(ctx context.Context, req OrderReq) (OrderRes, error) {

    // 1. Contextual Logger automatically knows the Trace ID!
    logger.InfoContext(ctx, "creating order")

    // 2. Passing 'ctx' automatically creates a Child Span for the DB query!
    err := db.ExecContext(ctx, "INSERT INTO orders...", req.ID)
    if err != nil {
        return OrderRes{}, err
    }

    return OrderRes{Status: "created"}, nil
}).Build()
πŸͺ„ How is this possible? (The AnyHook Architecture)

Where did the OpenTelemetry spans and Prometheus metrics go? They are completely decoupled.

Instead of hardcoding infrastructure into the action, you plug it in at the gateway boundary during server boot using the lock-free AnyHook interface:

// Wire infrastructure ONCE in main.go
telemetryPlugin := actionhook.New(obsProvider)

// Instantly instrument EVERY action in your app
CreateOrder.AddAnyHook(telemetryPlugin)

When CreateOrder runs, the Nexss Kernel does the following with zero heap allocations:

  1. Interceptor Fires: The hook generates the OTel Span and starts the Prometheus timer.
  2. Context Injection: The hook silently places the active trace into the standard context.Context.
  3. Execution: Your pristine business logic runs. The wrapped dbtrace.DB finds the trace in the context and attaches its queries to it.
  4. Cleanup: The hook catches any returned errors, increments failure metrics, logs the stack trace, and closes the span.

πŸ“¦ Getting Started

1. Installation
go get github.com/nexssp/observability
2. Basic Initialization (Auto-configuration)

Calling obs.Auto() configures the telemetry provider out-of-the-box using standard environment variables:

export SERVICE_NAME="order-processor"
export ENV="production"
export OTLP_ENDPOINT="http://openobserve:5080/v1/traces"
export OTLP_HEADERS="Authorization=Bearer abc123xyz"
package main

import (
	"context"
	"log"

	obs "github.com/nexssp/observability"
)

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

	// Auto-configure tracer and metrics providers from env
	provider, shutdown, err := obs.Auto()
	if err != nil {
		log.Fatalf("failed to initialize telemetry: %v", err)
	}
	defer shutdown(ctx)

	provider.Logger().InfoContext(ctx, "telemetry successfully initialized")
}
3. Advanced Configuration (Prometheus, VictoriaMetrics & Grafana)

For shared infrastructure environments or custom metric layouts, use AutoWithOptions to configure custom registries, naming structures, and custom histogram boundaries:

provider, shutdown, _ := obs.AutoWithOptions(
	// 1. Enforce Prometheus standard naming conventions (namespace_subsystem_metric)
	obs.WithMetricsNamespace("nexssp"),
	obs.WithMetricsSubsystem("kernel"),

	// Or apply a flat, direct prefix if you are using simpler systems (e.g., Datadog, OpenObserve)
	// obs.WithMetricsPrefix("custom_flat_prefix"),

	// 2. Customize latency histogram buckets for fine-grained millisecond tracking
	obs.WithHistogramBuckets([]float64{
		0.1, 0.5, 1.0, 2.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0,
	}),

	// 3. Inject a shared or global Prometheus Registry
	obs.WithPrometheusRegistry(prometheus.NewRegistry()),

	// 4. Wrap any custom slog.Handler (e.g., console output or a custom Loki log shipper)
	obs.WithLoggerHandler(slog.NewJSONHandler(os.Stdout, nil)),
)
defer shutdown(context.Background())
4. Zero-Dependency Trace ID Extraction

Avoid forcing your business logic to import the heavy OpenTelemetry trace SDK just to retrieve a trace or span ID as a string. Use our zero-dependency string helpers:

import obs "github.com/nexssp/observability"

func Process(ctx context.Context) {
	traceID := obs.TraceID(ctx) // Returns string (e.g., "4bf92f3577b34da6a3ce929d0e0e4736")
	spanID := obs.SpanID(ctx)   // Returns string (e.g., "00f067aa0ba902b7")

	println("Active Trace ID:", traceID)
}

⚑ SRE Edge Cases Covered

1. Panic Isolation inside Sub-Spans

When creating manual spans inside your code, Go's panic recovery may bypass the standard closure function, causing orphan spans. We use deferred named-error processing to capture panics:

import obs "github.com/nexssp/observability"

func ParseComplexJSON(ctx context.Context, data []byte) (res string, err error) {
    ctx, endSpan := obs.StartSpan(ctx, "json.parse")
    defer func() {
        if r := recover(); r != nil {
            err = xerr.PanicRecovery(r)
        }
        endSpan(err) // Safely closes the span and records the recovered panic
    }()

    // ... code that might panic ...
}
2. Dynamic Span Enrichment

Because the context contains standard OpenTelemetry data, you can dynamically add tags or attributes to the current running span anywhere in your business logic without vendor lock-in.

import "go.opentelemetry.io/otel/trace"
import "go.opentelemetry.io/otel/attribute"

// Enrich the active span (if present)
if span := trace.SpanFromContext(ctx); span.IsRecording() {
    span.SetAttributes(
        attribute.String("user.tier", "VIP"),
        attribute.Float64("payment.amount", 250.00),
    )
}
3. Non-Blocking Async Trace Detachment

Launching background goroutines with standard request contexts is dangerousβ€”when the client request ends, the context cancels, killing any downstream background traces. We use trace detachment to let background operations execute under independent lifecycles while preserving tracing context:

import "github.com/nexssp/kernel/xctx"

func HandleRequest(ctx context.Context) {
    // Clone trace context without cancellation to prevent background tracing failure
    asyncCtx := xctx.CloneForAsync(ctx)

    go func() {
        ctx, endSpan := obs.StartSpan(asyncCtx, "async.background_cleanup")
        defer endSpan()

        // Executes safely even if the main client request has exited
        processCleanup(ctx)
    }()
}
4. Visual Timeline & Waterfall Extraction (e.g., for UI/CLI Dashboards)

If you are building interactive developer consoles (like TUI dashboards or agent control panels) and want to render a visual waterfall timeline of all sub-spans executed within a request, you can use the in-memory trace collector:

import obs "github.com/nexssp/observability"

func RunComplexTask(ctx context.Context) {
	// 1. Initialize an in-memory trace collector in the context
	ctx, collector := obs.WithCollector(ctx)

	// 2. Execute standard database queries, API calls, and sub-spans
	_ = executeFidelitySteps(ctx)

	// 3. Export a complete, machine-readable TraceRecord
	if record, ok := obs.TraceRecordFromContext(ctx, "task.run", "http", nil); ok {
		// Send to your frontend (e.g., via Server-Sent Events / SSE) to render Gantt charts
		renderTimelineWaterfall(record)
	}
}

License

Apache License 2.0. See LICENSE for details.

Documentation ΒΆ

Overview ΒΆ

Package obs provides OpenTelemetry-based tracing, metrics, contextual logging, and readiness health probes.

Package obs provides tracing hooks for framework action execution.

Package obs provides interfaces for observability components.

Package obs provides OpenTelemetry contextual logging.

Package obs initializes OpenTelemetry meters, tracers, Prometheus registries, and health endpoints.

Index ΒΆ

Constants ΒΆ

View Source
const DefaultCollectorCapacity = 64

Variables ΒΆ

This section is empty.

Functions ΒΆ

func FormatDuration ΒΆ

func FormatDuration(d time.Duration) string

func SpanID ΒΆ

func SpanID(ctx context.Context) string

func StartSpan ΒΆ

func StartSpan(ctx context.Context, name string, detail ...string) (spanCtx context.Context, endSpan func(err ...error))

func TraceID ΒΆ

func TraceID(ctx context.Context) string

TraceID extracts active trace ID from context as string (or returns empty).

Types ΒΆ

type CompletedSpan ΒΆ

type CompletedSpan struct {
	Name          string `json:"name"`
	Status        string `json:"status"` // "OK", "ERROR", or "SUSPENDED"
	StartOffsetUs int64  `json:"start_offset_us"`
	StartOffsetNs int64  `json:"start_offset_ns"`
	DurationUs    int64  `json:"duration_us"`
	DurationNs    int64  `json:"duration_ns"`
	Duration      string `json:"duration"`
	Detail        string `json:"detail,omitempty"`
}

type Config ΒΆ

type Config struct {
	ServiceName        string
	NodeID             string
	Env                string
	OTLPEndpoint       string
	OTLPHeaders        map[string]string
	OTLPInsecure       bool
	SampleRatio        float64
	HealthCheckTimeout time.Duration

	PrometheusRegistry *prometheus.Registry
	MetricsPrefix      string
	MetricsNamespace   string
	MetricsSubsystem   string
	HistogramBuckets   []float64
	LoggerHandler      slog.Handler
}

Config holds observability initialization parameters.

func LoadConfigFromEnv ΒΆ

func LoadConfigFromEnv() Config

LoadConfigFromEnv builds Config from standard environment variables.

type ContextHandler ΒΆ

type ContextHandler struct {
	Handler slog.Handler
}

ContextHandler wraps a slog.Handler to inject active trace and span IDs into log records.

func NewContextHandler ΒΆ

func NewContextHandler(base slog.Handler) *ContextHandler

NewContextHandler constructs a ContextHandler around a base slog.Handler.

func (*ContextHandler) Enabled ΒΆ

func (h *ContextHandler) Enabled(ctx context.Context, level slog.Level) bool

Enabled reports whether the handler handles records at the given level.

func (*ContextHandler) Handle ΒΆ

func (h *ContextHandler) Handle(ctx context.Context, r slog.Record) error

Handle injects OpenTelemetry trace context and action metadata into the slog.Record.

func (*ContextHandler) WithAttrs ΒΆ

func (h *ContextHandler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs returns a new ContextHandler whose attributes include given attrs.

func (*ContextHandler) WithGroup ΒΆ

func (h *ContextHandler) WithGroup(name string) slog.Handler

WithGroup returns a new ContextHandler with the given group name appended.

type HealthChecker ΒΆ

type HealthChecker interface {
	Check(ctx context.Context) error
}

HealthChecker defines a component that can be polled for readiness.

type HealthRegistry ΒΆ

type HealthRegistry interface {
	RegisterCheck(name string, check func(ctx context.Context) error)
	HealthHandler() http.Handler
}

HealthRegistry allows dynamic registration of system readiness probes.

type Hook ΒΆ

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

Hook instruments action executions with OpenTelemetry spans and metrics.

func (*Hook) After ΒΆ

func (h *Hook) After(ctx context.Context, err error)

After ends the OpenTelemetry span and records latency/error/suspension metrics.

func (*Hook) Before ΒΆ

func (h *Hook) Before(ctx context.Context, actionName string, meta map[string]string) context.Context

Before is preserved for backward compatibility with map-based callers.

func (*Hook) BeforeWithAttributes ΒΆ added in v0.2.0

func (h *Hook) BeforeWithAttributes(ctx context.Context, actionName string, extraAttrs ...attribute.KeyValue) context.Context

BeforeWithAttributes starts an OTel span with stack-allocated attributes (0 heap allocations on hot path).

type Option ΒΆ

type Option func(*Config)

func WithHistogramBuckets ΒΆ added in v0.2.0

func WithHistogramBuckets(buckets []float64) Option

func WithLoggerHandler ΒΆ

func WithLoggerHandler(handler slog.Handler) Option

func WithMetricsNamespace ΒΆ added in v0.2.0

func WithMetricsNamespace(ns string) Option

func WithMetricsPrefix ΒΆ

func WithMetricsPrefix(prefix string) Option

func WithMetricsSubsystem ΒΆ added in v0.2.0

func WithMetricsSubsystem(sub string) Option

func WithPrometheusRegistry ΒΆ

func WithPrometheusRegistry(reg *prometheus.Registry) Option

type Provider ΒΆ

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

Provider manages OpenTelemetry meters, tracers, and health checks.

func Auto ΒΆ

func Auto() (*Provider, func(context.Context) error, error)

Auto initializes standard observability using environment configuration.

func AutoWithOptions ΒΆ

func AutoWithOptions(opts ...Option) (*Provider, func(context.Context) error, error)

AutoWithOptions initializes observability with optional functional modifications.

func New ΒΆ

func New(ctx context.Context, cfg Config, opts ...Option) (*Provider, error)

New constructs Provider with OpenTelemetry tracer, meter, and Prometheus registry.

func NewWithShutdown ΒΆ

func NewWithShutdown(cfg Config) (*Provider, func(context.Context) error, error)

NewWithShutdown instantiates Provider alongside an explicit cleanup function.

func (*Provider) Config ΒΆ

func (p *Provider) Config() Config

Config exposes active Provider configuration.

func (*Provider) Handler ΒΆ

func (p *Provider) Handler() slog.Handler

Handler returns the ContextHandler.

func (*Provider) HealthHandler ΒΆ

func (p *Provider) HealthHandler() http.Handler

HealthHandler serves JSON HTTP readiness probe responses.

func (*Provider) Hook ΒΆ

func (p *Provider) Hook() *Hook

Hook initializes an execution Hook from the Provider.

func (*Provider) LLMMetrics ΒΆ added in v0.2.0

func (p *Provider) LLMMetrics() *llm.Metrics

func (*Provider) Logger ΒΆ

func (p *Provider) Logger() *slog.Logger

Logger returns the configured contextual slog logger.

func (*Provider) MetricsHandler ΒΆ

func (p *Provider) MetricsHandler() http.Handler

MetricsHandler provides standard HTTP handler for Prometheus scraping.

func (*Provider) RegisterCheck ΒΆ

func (p *Provider) RegisterCheck(name string, checkFunc func(context.Context) error)

RegisterCheck attaches a readiness health probe.

func (*Provider) Sink ΒΆ added in v0.2.0

func (p *Provider) Sink() *Sink

Sink returns an implementation of kernel/observe.Sink that routes Kernel lifecycle events to OpenTelemetry and Prometheus.

type Sink ΒΆ added in v0.2.0

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

Sink bridges kernel/observe.Event lifecycle events to OpenTelemetry and Prometheus.

func NewSink ΒΆ added in v0.2.0

func NewSink(provider *Provider) *Sink

NewSink creates an observe.Sink backed by Provider.

func (*Sink) Emit ΒΆ added in v0.2.0

func (s *Sink) Emit(ctx context.Context, event observe.Event)

Emit processes lifecycle events from kernel/observe.

type TraceCollector ΒΆ added in v0.3.0

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

TraceCollector is a true circular ring buffer with index arithmetic (zero slice reallocations).

func WithCollector ΒΆ

func WithCollector(ctx context.Context) (context.Context, *TraceCollector)

WithCollector attaches a TraceCollector to the context for capturing spans.

func (*TraceCollector) Spans ΒΆ added in v0.3.0

func (c *TraceCollector) Spans() []CompletedSpan

type TraceRecord ΒΆ

type TraceRecord struct {
	TraceID         string          `json:"trace_id"`
	Action          string          `json:"action"`
	Transport       string          `json:"transport"`
	Timestamp       time.Time       `json:"timestamp"`
	TotalDurationUs int64           `json:"total_duration_us"`
	Duration        string          `json:"duration"`
	Status          string          `json:"status"`
	Spans           []CompletedSpan `json:"spans"`
}

func TraceRecordFromContext ΒΆ

func TraceRecordFromContext(ctx context.Context, actionName, transportName string, err error) (TraceRecord, bool)

type Tracer ΒΆ

type Tracer interface {
	Span(ctx context.Context, name string) (context.Context, func(error))
}

Tracer defines custom span generation wrappers.

Directories ΒΆ

Path Synopsis
Package actionhook adapts *obs.Hook to the framework action system.
Package actionhook adapts *obs.Hook to the framework action system.
Package dbtrace provides OpenTelemetry tracing wrappers for database/sql operations.
Package dbtrace provides OpenTelemetry tracing wrappers for database/sql operations.
Package events provides business telemetry event emission.
Package events provides business telemetry event emission.
examples
Package httptrace provides OpenTelemetry instrumentation wrappers for http Clients and Transports.
Package httptrace provides OpenTelemetry instrumentation wrappers for http Clients and Transports.
Package llm provides OpenTelemetry and Prometheus instrumentation for LLM calls.
Package llm provides OpenTelemetry and Prometheus instrumentation for LLM calls.

Jump to

Keyboard shortcuts

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