otelwebsocket

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

README

otelwebsocket

otelwebsocket wraps gorilla/websocket and adds OpenTelemetry distributed-tracing support by propagating the W3C Trace Context inside the WebSocket message body.

How it works

Side What happens
Sender (WriteMessage) The current span's trace-context headers (e.g. traceparent, tracestate) are injected into a lightweight JSON envelope that wraps the original payload. The envelope is sent as the WebSocket message body.
Receiver (ReadMessage) The JSON envelope is unwrapped, trace-context headers are extracted and used to reconstruct the remote span context, and a new Go context.Context that carries the propagated span is returned to the caller.
┌─────────────────────────────────────┐
│  WebSocket message body (JSON)      │
│  {                                  │
│    "headers": {                     │
│      "traceparent": "00-abc…-01"    │
│    },                               │
│    "payload": <original bytes>      │
│  }                                  │
└─────────────────────────────────────┘

Installation

go get github.com/Marz32onE/otelwebsocket

Quick start

// ── Client (sender) ──────────────────────────────────────────────────────────
raw, _, err := websocket.DefaultDialer.DialContext(ctx, serverURL, nil)
if err != nil { /* handle */ }

conn := otelwebsocket.NewConn(raw)

ctx, span := tracer.Start(ctx, "send-message")
defer span.End()

// Trace context is automatically injected into the message body.
err = conn.WriteMessage(ctx, websocket.TextMessage, []byte("hello"))

// ── Server (receiver) ────────────────────────────────────────────────────────
raw, err := upgrader.Upgrade(w, r, nil)
if err != nil { /* handle */ }

conn := otelwebsocket.NewConn(raw)

// recvCtx carries the propagated span from the client.
recvCtx, msgType, data, err := conn.ReadMessage(context.Background())

// Create a child span that is linked to the client's trace.
_, childSpan := tracer.Start(recvCtx, "handle-message")
defer childSpan.End()

Configuration

conn := otelwebsocket.NewConn(raw,
    // Use a custom propagator (default: otel.GetTextMapPropagator()).
    otelwebsocket.WithPropagator(myPropagator),
    // Use a custom TracerProvider (default: otel.GetTracerProvider()).
    otelwebsocket.WithTracerProvider(myTracerProvider),
)

Convenience dial helper

conn, resp, err := otelwebsocket.Dial(ctx, serverURL, nil)

Backward compatibility

If a message was not produced by this library (i.e. it is not a valid JSON envelope), ReadMessage returns the raw bytes unchanged and no span context is injected into the returned context. This makes it safe to introduce otelwebsocket incrementally alongside plain WebSocket messages.

Documentation

Overview

Package otelwebsocket wraps github.com/gorilla/websocket and adds OpenTelemetry distributed-tracing support by propagating the W3C Trace Context inside the WebSocket message body.

Tracer initialization: Call InitTracer(endpoint, attrs...) once at startup to set the global TracerProvider and propagator. If NewConn or Dial is used without a prior InitTracer (and without WithTracerProvider), the package calls InitTracer("", nil) once with default endpoint and service attributes.

How it works

On the sender side, WriteMessage serialises the application payload into a small JSON envelope that also contains the current span's trace-context headers (e.g. "traceparent" and "tracestate"). On the receiver side, ReadMessage deserialises the envelope, re-creates the remote span context from those headers, and returns a new Go context that carries the propagated span so that the handler can create child spans that are correctly linked to the originating trace.

Usage

// Dialling side
raw, _, err := websocket.DefaultDialer.Dial(url, nil)
conn := otelwebsocket.NewConn(raw)
err = conn.WriteMessage(ctx, websocket.TextMessage, []byte("hello"))

// Upgrading side
raw, err := upgrader.Upgrade(w, r, nil)
conn := otelwebsocket.NewConn(raw)
ctx, msgType, data, err := conn.ReadMessage(context.Background())

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InitTracer

func InitTracer(endpoint string, args ...interface{}) error

InitTracer sets the global TracerProvider and TextMapPropagator for the process. Call once at startup (e.g. in main) with OTLP endpoint and resource attributes. Propagator is fixed to TraceContext + Baggage. Empty endpoint uses OTEL_EXPORTER_OTLP_ENDPOINT env or "localhost:4317".

For tests, pass WithTracerProviderOption(tp) to use a custom provider.

func SemVersion

func SemVersion() string

SemVersion is the semantic version for tracer creation.

func ShutdownTracer

func ShutdownTracer()

ShutdownTracer shuts down the TracerProvider set by InitTracer and resets global state. For guaranteed flush before exit, call "defer otelwebsocket.ShutdownTracer()" in main.

func Version

func Version() string

Version is the current release version of the WebSocket instrumentation.

Types

type Conn

type Conn struct {
	*websocket.Conn
	// contains filtered or unexported fields
}

Conn is a WebSocket connection with built-in OpenTelemetry trace-context propagation. It embeds *websocket.Conn so that callers can still use all other gorilla/websocket methods directly.

func Dial

func Dial(ctx context.Context, urlStr string, requestHeader http.Header, opts ...Option) (*Conn, *http.Response, error)

Dial connects to the WebSocket server at the given URL and returns a *Conn with trace-context propagation enabled. It is a thin wrapper around websocket.DefaultDialer.DialContext.

func NewConn

func NewConn(conn *websocket.Conn, opts ...Option) *Conn

NewConn wraps an existing gorilla *websocket.Conn. Any number of Option values may be provided to customise the propagator or tracer provider.

func (*Conn) ReadMessage

func (c *Conn) ReadMessage(ctx context.Context) (context.Context, int, []byte, error)

ReadMessage reads the next envelope from the connection, extracts the trace-context headers embedded in it, and returns a new context that carries the remote span. The returned context is derived from the provided parent ctx.

The returned messageType, data, and error values have the same semantics as those of the underlying gorilla *websocket.Conn.ReadMessage.

func (*Conn) WriteMessage

func (c *Conn) WriteMessage(ctx context.Context, messageType int, data []byte) error

WriteMessage encodes data together with the trace-context headers extracted from ctx and sends the resulting JSON envelope over the WebSocket connection.

The messageType must be websocket.TextMessage or websocket.BinaryMessage; the encoded payload is always JSON text regardless of the original type, but the WebSocket frame type is preserved so that receivers can use the same type-switch logic they would use without this library.

type Option

type Option func(*Conn)

Option configures a Conn.

func WithPropagator

func WithPropagator(p propagation.TextMapPropagator) Option

WithPropagator sets the TextMapPropagator used to inject and extract trace context. Defaults to otel.GetTextMapPropagator().

func WithTracerProvider

func WithTracerProvider(tp trace.TracerProvider) Option

WithTracerProvider sets the TracerProvider used to create spans. Defaults to otel.GetTracerProvider().

type TracerProviderInitOption

type TracerProviderInitOption struct {
	TracerProvider trace.TracerProvider
}

TracerProviderInitOption holds a TracerProvider to use when passed to InitTracer.

func WithTracerProviderOption

func WithTracerProviderOption(tp trace.TracerProvider) TracerProviderInitOption

WithTracerProviderOption returns an InitTracer argument that uses the given TracerProvider (for tests). Do not confuse with Conn option WithTracerProvider.

Jump to

Keyboard shortcuts

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