Help us shape the future of Go! Take the 2026 Go Developer Survey

xyliumotel

package module
v0.0.0-...-504e041 Latest Latest
Warning

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

Go to latest
Published: May 22, 2025 License: MIT Imports: 18 Imported by: 0

README ยถ

Xylium OpenTelemetry (OTel) Connector (xylium-otel)

xylium-otel is the official Xylium framework connector for seamless integration with OpenTelemetry (OTel), enabling robust distributed tracing for your Xylium web applications. This connector simplifies the setup and usage of OTel, allowing you to gain deep insights into request flows, identify performance bottlenecks, and understand interactions between your microservices.

Table of Contents

โœจ Key Features

  • Simplified OTel SDK Setup: Manages OpenTelemetry TracerProvider, Exporter (OTLP gRPC, Stdout), Sampler, and Propagator initialization.
  • Automatic HTTP Instrumentation: Provides Xylium middleware (Connector.OtelMiddleware()) to automatically create server spans for incoming requests.
  • Context Propagation: Seamlessly integrates with Xylium's c.GoContext() and c.WithGoContext() for propagating trace context through your application.
  • Xylium Logger Integration: Automatically injects trace_id and span_id into xylium.Context, making them available to c.Logger() for correlated logging.
  • Semantic Convention Adherence: Follows OpenTelemetry semantic conventions for HTTP attributes on spans.
  • Flexible Configuration: Offers comprehensive Config options for service identification, exporter choice, sampling, and more.
  • Graceful Shutdown: Implements io.Closer, allowing Xylium to automatically shut down the managed OTel TracerProvider.
  • External Provider Support: Allows usage of pre-configured external OpenTelemetry TracerProviders.

๐Ÿ› ๏ธ Prerequisites

  • Go (version compatible with Xylium Core, e.g., 1.18+).
  • Xylium Core framework installed in your project.
  • (Optional) An OpenTelemetry Collector (e.g., Jaeger, Grafana Agent, OpenTelemetry Collector Contrib) if using the OTLP exporter.

๐Ÿš€ Installation

  1. Add xylium-otel to your project:

    go get github.com/arwahdevops/xylium-otel
    
  2. Ensure you have Xylium Core:

    go get github.com/arwahdevops/xylium-core
    

    xylium-otel depends on several OpenTelemetry SDK packages, which will be fetched automatically as dependencies.

๐Ÿ Basic Usage

Here's a quick overview of integrating xylium-otel into your Xylium application.

1. Initialize the Connector

In your main() function or application setup:

package main

import (
	"os"
	"time"

	"github.com/arwahdevops/xylium-core/src/xylium"
	xyliumotel "github.com/arwahdevops/xylium-otel"
)

func main() {
	app := xylium.New()
	appLogger := app.Logger()

	// Determine exporter based on environment (example)
	otelExporterEndpoint := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
	exporterType := xyliumotel.ExporterStdout
	if otelExporterEndpoint != "" {
		exporterType = xyliumotel.ExporterOTLPGRPC
	}

	otelConfig := xyliumotel.Config{
		AppLogger:      appLogger,
		ServiceName:    "my-awesome-service",
		ServiceVersion: "1.0.0",
		Environment:    "production",
		Exporter:       exporterType,
		OTLP: xyliumotel.OTLPConfig{
			Endpoint: otelExporterEndpoint, // e.g., "localhost:4317"
			Insecure: true,                 // Set to false for production OTLP endpoints with TLS
			Timeout:  10 * time.Second,
		},
		// Optional: Default is ParentBased(AlwaysSample())
		// Sampler: sdktrace.TraceIDRatioBased(0.1), // Sample 10% of traces
	}

	otelConnector, err := xyliumotel.New(otelConfig)
	if err != nil {
		appLogger.Fatalf("Failed to initialize Xylium OTel connector: %v", err)
	}

	// ... rest of your application setup ...
}
2. Register with Xylium AppStore

Store the connector instance in Xylium's application store for easy access in handlers and to enable automatic graceful shutdown.

	// ... after otelConnector is successfully initialized
	app.AppSet("otel", otelConnector) // Key "otel" or any preferred key
3. Apply OTel Middleware

Add the OTel middleware to your Xylium application globally or to specific route groups.

	// Apply Xylium's RequestID middleware first (recommended for correlation)
	app.Use(xylium.RequestID())

	// Apply the OTel middleware from the connector
	app.Use(otelConnector.OtelMiddleware(xyliumotel.MiddlewareConfig{
		// Optional: Customize middleware
		Filter: func(c *xylium.Context) bool {
			return c.Path() == "/healthz" // Don't trace health checks
		},
	}))
4. Create Custom Spans in Handlers

Access the tracer within your handlers to create child spans for specific operations.

import (
	"net/http"
	"github.com/arwahdevops/xylium-core/src/xylium"
	xyliumotel "github.com/arwahdevops/xylium-otel"
	"go.opentelemetry.io/otel/attribute"
)

func MyHandler(c *xylium.Context) error {
	// Retrieve the connector from AppStore
	otelConnVal, _ := c.AppGet("otel") // Use the key you registered with
	otelConnector := otelConnVal.(*xyliumotel.Connector)

	// Get a tracer
	tracer := otelConnector.GetTracer("my.handler.instrumentation")

	// c.GoContext() already contains the parent server span from the middleware
	handlerCtx, childSpan := tracer.Start(c.GoContext(), "myCustomOperation")
	defer childSpan.End()

	// Add attributes to your custom span
	childSpan.SetAttributes(attribute.String("custom.key", "custom.value"))

	// ... your handler logic ...
	c.Logger().Info("Custom operation performed within handler.") // Will include trace_id, span_id

	return c.String(http.StatusOK, "Handler executed with custom span.")
}

โš™๏ธ Configuration

xyliumotel.Config

The xyliumotel.Config struct is used when calling xyliumotel.New() to initialize the connector.

Field Type Description Default (if not set)
AppLogger xylium.Logger Required. Xylium application logger instance. -
ServiceName string Required (if no external provider). Logical name of your service (e.g., "user-service"). -
ServiceVersion string Optional. Version of your service (e.g., "v1.2.3"). ""
Environment string Optional. Deployment environment (e.g., "production", "staging"). ""
Exporter ExporterType Type of exporter to use (ExporterOTLPGRPC, ExporterStdout, ExporterNone). ExporterStdout (Debug/Test mode), ExporterNone (Release mode)
OTLP OTLPConfig Configuration for OTLP gRPC exporter. See OTLPConfig defaults below.
ExternalTracerProvider trace.TracerProvider Optional. Use a pre-configured OTel trace.TracerProvider. Connector won't manage its lifecycle. nil
ExternalSDKTracerProvider *sdktrace.TracerProvider Optional. Use a pre-configured OTel *sdktrace.TracerProvider. Takes precedence over ExternalTracerProvider. nil
ManageGlobalProviders *bool If true (default), connector sets global OTel provider/propagator. If false, app manages globals. true
Propagator propagation.TextMapPropagator Optional. OTel propagator. propagation.TraceContext{} & propagation.Baggage{}
Sampler sdktrace.Sampler Optional. OTel sampling strategy. sdktrace.ParentBased(sdktrace.AlwaysSample())
ShutdownTimeout time.Duration Timeout for graceful shutdown of the managed TracerProvider. 5 * time.Second
Disabled bool If true, disables OTel integration; connector becomes NoOp. false

OTLPConfig Defaults:

  • Insecure: false
  • Timeout: 10 * time.Second
xyliumotel.MiddlewareConfig

The xyliumotel.MiddlewareConfig struct is optional and can be passed to connector.OtelMiddleware().

Field Type Description Default (if not set)
TracerName string Name for the tracer used by the middleware itself. "xylium.otel.middleware"
SpanNameFormatter func(c *xylium.Context) string Function to customize server span names. Crucial for good cardinality. c.Method() + " " + c.Path()
AdditionalAttributes []attribute.KeyValue Static attributes to add to all server spans created by this middleware. nil
Filter func(c *xylium.Context) bool Function to conditionally skip tracing for requests. Return true to skip. nil (trace all requests)

Recommendation for SpanNameFormatter: For optimal tracing, use the HTTP method and the matched route pattern (e.g., GET /api/users/:id) for span names, not the raw path. If Xylium Core provides a way to get the matched route pattern (e.g., c.MatchedRoutePattern()), use that.

// Ideal SpanNameFormatter (if MatchedRoutePattern becomes available in Xylium Core)
// SpanNameFormatter: func(c *xylium.Context) string {
//     pattern := c.MatchedRoutePattern() // Hypothetical
//     if pattern != "" {
//         return c.Method() + " " + pattern
//     }
//     return c.Method() + " " + c.Path() // Fallback
// },
Exporter Configuration
  • OTLP gRPC (ExporterOTLPGRPC):
    • Requires Config.OTLP.Endpoint to be set (e.g., "localhost:4317" for a local collector).
    • Set Config.OTLP.Insecure = true for local collectors not using TLS. For production, ensure TLS is used and set Insecure = false.
    • Optional: Config.OTLP.Headers and Config.OTLP.Timeout.
  • Stdout (ExporterStdout):
    • Traces are printed to standard output in a human-readable format. Useful for local development.
    • No additional configuration needed beyond selecting this exporter type.
  • None (ExporterNone):
    • No exporter is configured by xylium-otel. If no ExternalTracerProvider is set, tracing will effectively be disabled by this connector instance unless a global provider is configured elsewhere and ManageGlobalProviders is false.
Managing Global OTel Providers

By default (Config.ManageGlobalProviders is true or not set), xylium-otel will call otel.SetTracerProvider() and otel.SetTextMapPropagator() when xyliumotel.New() is invoked with a configuration that results in an internally managed TracerProvider or a custom Propagator.

If your application manages the global OTel providers itself, set Config.ManageGlobalProviders = &manageGlobalFalse (where manageGlobalFalse := false). In this scenario:

  • xylium-otel will not modify the global OTel state.
  • The middleware and connector.GetTracer() will use the TracerProvider and Propagator instances that were either provided externally in Config or initialized internally by the connector (but not set globally).
  • You are responsible for ensuring that the global OTel providers (if needed by other parts of your app) are configured correctly.

๐Ÿ“„ Logging Integration

When the xylium-otel middleware is active:

  • The trace_id and span_id of the current server span are automatically injected into the xylium.Context store.
  • c.Logger() (Xylium's contextual logger) will automatically pick up these IDs and include them in your structured logs, enabling easy correlation between logs and traces.

Example log output (JSON format) with c.Logger():

{
    "timestamp": "2023-10-28T10:00:00.123Z",
    "level": "INFO",
    "message": "Handler logic executed.",
    "fields": {
        "xylium_request_id": "uuid-abc-123",
        "otel_trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
        "otel_span_id": "0123456789abcdef"
        // ... other custom log fields ...
    }
}

Graceful Shutdown

The xyliumotel.Connector implements the io.Closer interface.

  • If you register the Connector instance with Xylium's application store using app.AppSet("key", otelConnector), Xylium's graceful shutdown mechanism will automatically call otelConnector.Close().
  • The Close() method will shut down the internally managed TracerProvider (if one was created by this connector), flushing any pending traces. This respects the Config.ShutdownTimeout.
  • If an ExternalTracerProvider was supplied in the Config, otelConnector.Close() will be a no-op for the provider's lifecycle (as the application is responsible for managing it).

๐Ÿ“š Full Example

For a runnable example demonstrating initialization, middleware usage, and custom span creation, please see the example/main.go file in this repository.

๐Ÿค Contributing

Contributions are welcome! Please open an issue to discuss bugs or feature proposals, or submit a pull request.

๐Ÿ“œ License

xylium-otel is licensed under the MIT License.

Documentation ยถ

Overview ยถ

Package xyliumotel provides the OpenTelemetry connector for the Xylium framework. This file contains the OpenTelemetry middleware for instrumenting HTTP requests.

Package xyliumotel provides the OpenTelemetry (OTel) connector for the Xylium framework. It simplifies the integration of distributed tracing into Xylium applications by managing the OTel SDK setup (TracerProvider, Exporter, Sampler, Propagator) and providing middleware for automatic HTTP request instrumentation.

Index ยถ

Constants ยถ

This section is empty.

Variables ยถ

This section is empty.

Functions ยถ

This section is empty.

Types ยถ

type Config ยถ

type Config struct {
	// AppLogger is the Xylium application logger instance used by the connector for its own logging.
	// This field is required.
	AppLogger xylium.Logger

	// ServiceName is the logical name of the service, e.g., "user-service".
	// Required if not providing ExternalTracerProvider or ExternalSDKTracerProvider.
	// Used to create the OTel resource.
	ServiceName string
	// ServiceVersion is the version of the service, e.g., "v1.2.3". Optional.
	ServiceVersion string
	// Environment is the deployment environment, e.g., "production", "staging". Optional.
	Environment string

	// Exporter defines the type of trace exporter to initialize if an internal
	// TracerProvider is being created.
	// Defaults to ExporterStdout if Xylium mode is Debug/Test, or ExporterNone if Release,
	// unless an external provider is specified.
	Exporter ExporterType
	// OTLP holds configuration for the OTLP gRPC exporter if Exporter is ExporterOTLPGRPC.
	OTLP OTLPConfig

	// ExternalTracerProvider allows providing a pre-configured trace.TracerProvider.
	// If set, the connector will use this provider and will not manage its lifecycle
	// (e.g., it won't call Shutdown on it). Internal exporter configuration is ignored.
	// If ManageGlobalProviders is true, this provider will be set as the global OTel provider.
	ExternalTracerProvider trace.TracerProvider
	// ExternalSDKTracerProvider allows providing a pre-configured *sdktrace.TracerProvider.
	// Similar to ExternalTracerProvider but for the SDK-specific type. Takes precedence over ExternalTracerProvider.
	// If ManageGlobalProviders is true, this provider will be set as the global OTel provider.
	ExternalSDKTracerProvider *sdktrace.TracerProvider

	// ManageGlobalProviders determines if this connector should manage (set) the global
	// OTel TracerProvider and TextMapPropagator using otel.SetTracerProvider and otel.SetTextMapPropagator.
	// If false, the application is responsible for setting global providers if needed.
	// The connector will then use its internally configured/provided tracer and propagator
	// instances for its operations (e.g., middleware).
	// Defaults to true.
	ManageGlobalProviders *bool // Pointer to distinguish between not set (use default true) and explicitly false.

	// Propagator is the OpenTelemetry TextMapPropagator to use.
	// If nil, a default composite propagator (TraceContext and Baggage) is used.
	// If ManageGlobalProviders is true, this propagator will be set as the global OTel propagator.
	Propagator propagation.TextMapPropagator
	// Sampler defines the sampling strategy for traces.
	// If nil, ParentBased(AlwaysSample()) is used as a default.
	Sampler sdktrace.Sampler

	// ShutdownTimeout is the duration to wait for the managed TracerProvider to shut down gracefully.
	// Defaults to 5 seconds. Only applicable if the connector manages the TracerProvider lifecycle.
	ShutdownTimeout time.Duration
	// Disabled, if true, completely disables OpenTelemetry integration by this connector.
	// The connector will operate in a no-op mode.
	Disabled bool
}

Config holds all configuration options for initializing the OpenTelemetry Connector.

type Connector ยถ

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

Connector is the Xylium-aware wrapper for OpenTelemetry functionality. It manages the TracerProvider, Propagator, and provides middleware for instrumentation.

func New ยถ

func New(cfg Config) (*Connector, error)

New creates and initializes a new OpenTelemetry Connector instance based on the provided configuration. It sets up the TracerProvider (either by using an external one or initializing one internally based on exporter settings), configures the Propagator and Sampler, and potentially registers these with the global OpenTelemetry instance if `ManageGlobalProviders` is true. Returns the configured Connector or an error if initialization fails.

func (*Connector) Close ยถ

func (c *Connector) Close() error

Close shuts down the internally managed TracerProvider, if one was created by this connector. It respects the Config.ShutdownTimeout. If an external TracerProvider was used, this method is a no-op for the provider's lifecycle. Implements io.Closer, allowing Xylium to manage its lifecycle during graceful shutdown when the connector instance is stored using `app.AppSet()`.

func (*Connector) GetTracer ยถ

func (c *Connector) GetTracer(instrumentationName string, opts ...trace.TracerOption) trace.Tracer

GetTracer returns a trace.Tracer instance. If ManageGlobalProviders is false and an internal TracerProvider was initialized, it returns a tracer from that internal provider. Otherwise, it returns a tracer from the (potentially globally set) OTel TracerProvider. `instrumentationName` is the name of the library or component creating spans. `opts` are optional `trace.TracerOption`s.

func (*Connector) IsNoOp ยถ

func (c *Connector) IsNoOp() bool

IsNoOp returns true if the connector is configured to be a no-operation instance (e.g., due to Config.Disabled being true or inability to initialize a TracerProvider). Middleware and other operations will effectively be pass-throughs if IsNoOp is true.

func (*Connector) OtelMiddleware ยถ

func (connector *Connector) OtelMiddleware(mwCustomCfg ...MiddlewareConfig) xylium.Middleware

OtelMiddleware returns a Xylium middleware function for OpenTelemetry HTTP server instrumentation. This method is called on an initialized xyliumotel.Connector instance. It can optionally take a MiddlewareConfig to customize its behavior. If no config is provided, sensible defaults are used for span naming and filtering.

The middleware performs the following:

  1. Extracts trace context from incoming request headers using the Connector's Propagator.
  2. Starts a new server span for the request, linking it to an existing trace if context was propagated.
  3. Sets standard OpenTelemetry semantic attributes for HTTP servers on the span.
  4. Injects the `trace_id` and `span_id` of the active span into the `xylium.Context` store.
  5. Propagates the Go `context.Context` (enriched with the active span) to subsequent handlers.
  6. Records errors from the handler chain on the span and sets the span status accordingly.
  7. Sets the HTTP response status code as a span attribute.

func (*Connector) Propagator ยถ

func (c *Connector) Propagator() propagation.TextMapPropagator

Propagator returns the configured TextMapPropagator. If ManageGlobalProviders is false, it returns the propagator instance held by the connector. Otherwise, it returns the global OTel propagator.

type ExporterType ยถ

type ExporterType string

ExporterType defines the type of OpenTelemetry trace exporter to configure.

const (
	// ExporterOTLPGRPC configures the OTLP (OpenTelemetry Protocol) gRPC exporter.
	// Requires OTLPConfig.Endpoint to be set.
	ExporterOTLPGRPC ExporterType = "otlp_grpc"
	// ExporterStdout configures an exporter that writes traces to standard output.
	// Useful for local development and debugging.
	ExporterStdout ExporterType = "stdout"
	// ExporterNone indicates that no exporter should be configured by this connector.
	// Tracing will be effectively disabled unless an external TracerProvider is used,
	// or if ManageGlobalProviders is false and a global provider is set elsewhere.
	ExporterNone ExporterType = "none"
)

type MiddlewareConfig ยถ

type MiddlewareConfig struct {
	// TracerName is the name of the tracer that will be specifically used by this middleware
	// to create server spans. If empty, a default name like "xylium.otel.middleware" is used.
	// This can be different from the connector's main tracer if desired.
	TracerName string

	// SpanNameFormatter is a function to customize the name of the server span created for each request.
	// The xylium.Context of the current request is passed to this function.
	// For best observability and lower cardinality in tracing backends, it is highly recommended
	// to name spans using the HTTP method and the matched route pattern (e.g., "GET /users/:id").
	// If nil, a default formatter is used which typically combines c.Method() and c.Path().
	// Example: `func(c *xylium.Context) string { return c.Method() + " " + c.MatchedRoutePattern() }` (if available)
	SpanNameFormatter func(c *xylium.Context) string

	// AdditionalAttributes allows adding a list of custom key-value attributes
	// to every server span created by this specific middleware instance.
	// These are added in addition to attributes from the global Connector config.
	AdditionalAttributes []attribute.KeyValue

	// Filter is an optional function to conditionally skip tracing for some requests.
	// If Filter returns true for a given xylium.Context, tracing is bypassed for that request.
	// Useful for excluding health checks, metrics endpoints, etc.
	Filter func(c *xylium.Context) bool
}

MiddlewareConfig holds configuration options specific to the OpenTelemetry middleware's operation. These settings allow customization of how server spans are named, what additional attributes are added, and which requests might be filtered out from tracing.

type OTLPConfig ยถ

type OTLPConfig struct {
	// Endpoint is the target URL for the OTLP gRPC exporter (e.g., "localhost:4317").
	Endpoint string
	// Insecure determines whether to use an insecure gRPC connection (e.g., for local testing).
	// Defaults to false (secure connection) if not specified and Endpoint is set.
	Insecure bool
	// Headers is a map of additional headers to send with OTLP gRPC requests.
	Headers map[string]string
	// Timeout for OTLP gRPC export operations.
	// Defaults to 10 seconds if not set.
	Timeout time.Duration
}

OTLPConfig holds configuration specific to the OTLP exporter.

Directories ยถ

Path Synopsis
Package main provides a runnable example demonstrating the use of the xylium-otel connector with the Xylium core framework for distributed tracing.
Package main provides a runnable example demonstrating the use of the xylium-otel connector with the Xylium core framework for distributed tracing.

Jump to

Keyboard shortcuts

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