Skip to content
skyl

otel.Hook

Returns a skyl.Option registering OpenTelemetry instrumentation.

One line wires skyl into OpenTelemetry. Hook returns a skyl.Option, so it composes with every other option.

Reference#

func Hook(opts ...Option) skyl.Option

Parameters

  • optsWithTracerProvider, WithMeterProvider, WithoutSpans.

Returns

A skyl.Option you pass to skyl.New.

Caveats

  • Prompt content is never recorded. Only model, sampling parameters and token counts.
  • Like every skyl hook, it runs synchronously on the calling goroutine. Span creation is cheap; a slow exporter is not — configure batching in your SDK setup, not here.
  • WithoutSpans() gives you metrics only. On a high-volume gateway, spans are the expensive half.
  • With no provider options it uses the global tracer and meter providers, so it does nothing until you have configured an SDK.
  • It observes streams too, via the stream_end event — including abandoned ones, whose tokens were still billed.

Usage#

The whole integration

goCompiles
client := skyl.New(openai.New(key), otel.Hook())
client := skyl.New(openai.New(key), otel.Hook())

With trace propagation to the provider

goCompiles
client := skyl.New(
	// HTTPClient puts the trace context on the outbound request, so the
	// provider call appears as a child span.
	openai.New(key, openai.WithHTTPClient(otel.HTTPClient(nil))),
	otel.Hook(),
)
client := skyl.New(
	// HTTPClient puts the trace context on the outbound request, so the
	// provider call appears as a child span.
	openai.New(key, openai.WithHTTPClient(otel.HTTPClient(nil))),
	otel.Hook(),
)

Metrics only, for a high-volume service

goCompiles
client := skyl.New(p, otel.Hook(otel.WithoutSpans()))
client := skyl.New(p, otel.Hook(otel.WithoutSpans()))

Explicit providers, rather than the globals

goCompiles
client := skyl.New(p, otel.Hook(
	otel.WithTracerProvider(tp),
	otel.WithMeterProvider(mp),
))
client := skyl.New(p, otel.Hook(
	otel.WithTracerProvider(tp),
	otel.WithMeterProvider(mp),
))

Alongside your own hook

goCompiles
// Hooks accumulate, so both run — in the order registered.
client := skyl.New(p,
	otel.Hook(),
	skyl.WithHook(func(_ context.Context, ev skyl.HookEvent) {
		if ev.Err != nil {
			log.Printf("%s failed: %v", ev.Provider, ev.Err)
		}
	}),
)
// Hooks accumulate, so both run — in the order registered.
client := skyl.New(p,
	otel.Hook(),
	skyl.WithHook(func(_ context.Context, ev skyl.HookEvent) {
		if ev.Err != nil {
			log.Printf("%s failed: %v", ev.Provider, ev.Err)
		}
	}),
)

Troubleshooting#

No telemetry appears

With no options it uses the global providers. If you have not called otel.SetTracerProvider / SetMeterProvider, the global ones are no-ops. Either configure them or pass providers explicitly.

Latency rose after enabling it

Span creation is cheap; exporting is not. Use a batching span processor, and consider WithoutSpans() if you only need the metrics.

I want prompts in my traces

This package will not put them there. Write your own hook if you have decided that is appropriate for your data — and note that HookEvent.Request carries the whole conversation.

Edit this page on GitHub