Documentation
¶
Overview ¶
Package neatlogs is the Go SDK for Neatlogs — OpenTelemetry-based tracing for Go LLM agents.
It exports spans over OTLP/HTTP to the Neatlogs ingestion endpoint ({endpoint}/v1/traces) and normalizes their attributes into the neatlogs.* namespace shared with the Python and TypeScript SDKs.
Spans reach Neatlogs through explicit SDK wrappers and tracing helpers:
- Active wrapping. Call WrapGenAI on a google.golang.org/genai client to trace each GenerateContent / GenerateContentStream / EmbedContent / CountTokens call with full request/response detail (including message text) on the span.
Init never changes the process-global OpenTelemetry provider or propagator. This keeps Neatlogs isolated from Datadog and other co-tenant instrumentation. OTel-native frameworks that only use the global provider must be integrated through an explicit Neatlogs wrapper.
Typical usage:
ctx := context.Background()
shutdown, err := neatlogs.Init(ctx, neatlogs.Config{
APIKey: os.Getenv("NEATLOGS_API_KEY"),
WorkflowName: "my-agent",
})
if err != nil { log.Fatal(err) }
defer shutdown(ctx)
Index ¶
- func ExtractTraceContext(ctx context.Context, carrier propagation.TextMapCarrier) context.Context
- func Flush(ctx context.Context) error
- func Identify(ctx context.Context, opts IdentifyOptions) context.Context
- func InjectTraceContext(ctx context.Context, carrier propagation.TextMapCarrier)
- func StartProviderSpan(ctx context.Context, name, kind string) (context.Context, trace.Span, func())
- func StartSpan(ctx context.Context, name string, kind string, ...) (context.Context, trace.Span, func())
- func Trace(ctx context.Context, name string) (context.Context, trace.Span, func())
- type Config
- type IdentifyOptions
- type LLMCallOptions
- type LLMMessage
- type LLMSpan
- func (s *LLMSpan) End()
- func (s *LLMSpan) SetError(err error)
- func (s *LLMSpan) SetFinishReason(reason string)
- func (s *LLMSpan) SetModel(model string)
- func (s *LLMSpan) SetOutputMessage(role, content string)
- func (s *LLMSpan) SetProvider(provider string)
- func (s *LLMSpan) SetResponseID(id string)
- func (s *LLMSpan) SetUsage(promptTokens, completionTokens, totalTokens int)
- type Option
- type RetrieverSpan
- type ShutdownFunc
- type ToolSpan
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractTraceContext ¶ added in v0.1.1
func ExtractTraceContext( ctx context.Context, carrier propagation.TextMapCarrier, ) context.Context
ExtractTraceContext returns a context containing the remote Neatlogs parent found in carrier. It does not activate or alter process-global OTel context.
func Flush ¶
Flush forces a synchronous export of all buffered spans. Safe to call even when the SDK is not initialized (it is a no-op then).
func Identify ¶
func Identify(ctx context.Context, opts IdentifyOptions) context.Context
Identify returns a copy of ctx carrying the given session + end-user identity. It is the single entry point for session/end-user on the Go SDK. Pass the returned context into Trace and/or your wrapped LLM calls; the (auto-)root span picks the identity up. Only set fields are bound, so calling Identify again overrides individual fields without clearing the others.
func InjectTraceContext ¶ added in v0.1.1
func InjectTraceContext(ctx context.Context, carrier propagation.TextMapCarrier)
InjectTraceContext writes the Neatlogs span carried by ctx into carrier.
func StartProviderSpan ¶ added in v0.1.1
func StartProviderSpan(ctx context.Context, name, kind string) (context.Context, trace.Span, func())
StartProviderSpan starts a span of the given kind, opening an auto-root first when needed. The returned end func ends the provider span and the auto-root (if one was opened); callers must invoke it exactly once. Every provider-span kind passes through here, so all wrapper paths (sync, stream finalize, error) get auto-root coverage by calling end().
func StartSpan ¶ added in v0.1.1
func StartSpan( ctx context.Context, name string, kind string, attributes ...attribute.KeyValue, ) (context.Context, trace.Span, func())
StartSpan starts an explicitly typed Neatlogs span on the private provider. Use it at framework and service boundaries where Trace's WORKFLOW kind is not appropriate (for example a TOOL child extracted from an incoming trace).
func Trace ¶
Trace opens a WORKFLOW root span named `name` and returns the child context, the span, and an end func the caller must invoke exactly once (usually via defer). It is the Go equivalent of Python's `with neatlogs.trace(...)` and TypeScript's `neatlogs.trace({...}, fn)`.
Session and end-user identity are NOT passed as arguments here — they ride on the context. Set them once at the request/turn boundary with Identify, then pass that context in:
ctx = neatlogs.Identify(ctx, neatlogs.IdentifyOptions{
SessionID: "chat_123", EndUserID: "user_456",
EndUserMetadata: map[string]any{"plan": "pro"},
})
ctx, span, end := neatlogs.Trace(ctx, "chat_turn") // reads identity from ctx
defer end()
Identity (session + end-user bound via Identify) is stamped on the root span by the identityProcessor, which reads it from the span's start context — so it applies to this WORKFLOW root, the WrapGenAI auto-root, and spans created by integrations using the private provider. Trace itself only opens the span.
Types ¶
type Config ¶
type Config struct {
// APIKey authenticates with Neatlogs. Falls back to NEATLOGS_API_KEY.
// If empty after that fallback, export is disabled and spans are dropped.
APIKey string
// Endpoint is the Neatlogs ingestion base URL (without the /v1/traces
// path). Falls back to NEATLOGS_ENDPOINT, then the staging default.
Endpoint string
// WorkflowName labels this service/run. Defaults to the executable name.
WorkflowName string
// Tags are attached to every span as a resource attribute.
Tags []string
// Debug enables verbose diagnostics on stderr.
Debug bool
// DisableExport drops all spans instead of sending them. Useful in tests.
DisableExport bool
}
Config controls SDK initialization. All fields are optional except APIKey (which may also be supplied via the NEATLOGS_API_KEY environment variable).
type IdentifyOptions ¶
IdentifyOptions carries the request-scoped identity for Identify. All fields are optional; only set ones are bound onto the context.
type LLMCallOptions ¶ added in v0.1.1
type LLMCallOptions struct {
// Provider is the neatlogs provider id, e.g. "openai" or "anthropic".
Provider string
// System is the provider system label; defaults to Provider when empty.
System string
// Model is the model name, e.g. "gpt-5.5" or "claude-sonnet-4-6".
Model string
Streaming bool
MaxTokens int
Temperature *float64
TopP *float64
// Messages are the input messages (including any system message).
Messages []LLMMessage
// Name overrides the span name; defaults to "{provider}.chat".
Name string
}
LLMCallOptions describes a provider LLM call for StartLLMSpan. Only Provider and Model are typically required; the rest are recorded when set.
type LLMMessage ¶ added in v0.1.1
LLMMessage is one request or response message on an LLM span.
type LLMSpan ¶ added in v0.1.1
type LLMSpan struct {
// contains filtered or unexported fields
}
LLMSpan is a handle to an LLM span opened by StartLLMSpan. Record the result with SetOutputMessage/SetUsage/SetFinishReason (or SetError), then call End exactly once (via defer).
It is the Go analogue of the Python/TypeScript SDKs' LLM span: it captures provider/model, input and output messages, invocation parameters, token usage and finish reason under the neatlogs.* namespace. Use it to instrument direct provider SDK calls (OpenAI, Anthropic, ...) that WrapGenAI does not cover. The caller imports only this package — no OpenTelemetry types.
func StartLLMSpan ¶ added in v0.1.1
StartLLMSpan opens an LLM span for a provider call, auto-rooting under a WORKFLOW span when the context has no active parent (so a standalone call still yields a renderable trace) and nesting under an existing parent otherwise (e.g. a TOOL span continued from an upstream service). It uses the private Neatlogs provider only; the process-global OTel / Datadog context is never read or mutated. When Neatlogs is not initialized the span is a no-op.
Call End on the returned LLMSpan exactly once, usually via defer.
func (*LLMSpan) End ¶ added in v0.1.1
func (s *LLMSpan) End()
End closes the span (and its auto-root, if one was opened). Call exactly once.
func (*LLMSpan) SetFinishReason ¶ added in v0.1.1
SetFinishReason records the provider finish/stop reason.
func (*LLMSpan) SetModel ¶ added in v0.1.1
SetModel overrides the model name recorded at start. Use it when the authoritative model is only known from the response (e.g. a provider that resolves an alias, or a fallback path that switches models). No-op for "".
func (*LLMSpan) SetOutputMessage ¶ added in v0.1.1
SetOutputMessage records the assistant's response message (index 0).
func (*LLMSpan) SetProvider ¶ added in v0.1.1
SetProvider overrides the provider/system recorded at start. Use it when the serving provider is only known after the call (e.g. a primary→fallback switch). No-op for "".
func (*LLMSpan) SetResponseID ¶ added in v0.1.1
SetResponseID records the provider response id.
type Option ¶
type Option func(*initOptions)
Option customizes Init. Options are for advanced/testing use; the common path needs only Config.
func WithExporter ¶
func WithExporter(exp sdktrace.SpanExporter) Option
WithExporter overrides the OTLP/HTTP exporter with a custom SpanExporter. The SDK still wraps it so attributes are normalized to neatlogs.* before export. Useful for tests (in-memory exporter) or alternate sinks (stdout). When set, Config.Endpoint/APIKey are ignored for transport, but DisableExport still suppresses all export.
type RetrieverSpan ¶ added in v0.1.1
type RetrieverSpan struct {
// contains filtered or unexported fields
}
RetrieverSpan is a handle to a retrieval / semantic-memory span opened by StartRetrieverSpan. Record results with SetDocuments/SetDocumentCount (or SetError), then call End exactly once (via defer).
Retrieval-shaped operations — vector search, RAG document lookup, and agent memory recall — all use span kind "retriever"; there is no separate "memory" kind in the canonical mapping.
func StartRetrieverSpan ¶ added in v0.1.1
func StartRetrieverSpan(ctx context.Context, name, query string, topK int) (context.Context, *RetrieverSpan)
StartRetrieverSpan opens a RETRIEVER span for a retrieval / memory operation named name, recording the query and (optional) top-k. It auto-roots when the context has no active parent and nests under an existing parent otherwise (e.g. a TOOL span continued from an upstream service). Uses the private Neatlogs provider only; when Neatlogs is not initialized the span is a no-op.
Call End on the returned RetrieverSpan exactly once, usually via defer.
func (*RetrieverSpan) End ¶ added in v0.1.1
func (r *RetrieverSpan) End()
End closes the span (and its auto-root, if one was opened). Call exactly once.
func (*RetrieverSpan) SetDocumentCount ¶ added in v0.1.1
func (r *RetrieverSpan) SetDocumentCount(count int)
SetDocumentCount records how many documents the retrieval returned.
func (*RetrieverSpan) SetDocuments ¶ added in v0.1.1
func (r *RetrieverSpan) SetDocuments(documents any, count int)
SetDocuments records the retrieved documents (JSON-encoded) and their count. It also writes the same JSON to the generic output field so every completed retrieval, including an empty result set, has an explicit I/O output.
func (*RetrieverSpan) SetError ¶ added in v0.1.1
func (r *RetrieverSpan) SetError(err error)
SetError marks the span failed and records err.
type ShutdownFunc ¶
ShutdownFunc flushes pending spans and releases SDK resources. Call it (often via defer) before the process exits so buffered spans are not lost.
type ToolSpan ¶ added in v0.1.1
type ToolSpan struct {
// contains filtered or unexported fields
}
ToolSpan is a handle to a TOOL span opened by StartToolSpanFromHeaders. Record the result with SetOutput or SetError, then call End exactly once (via defer).
func StartToolSpanFromHeaders ¶ added in v0.1.1
func StartToolSpanFromHeaders( ctx context.Context, headers http.Header, toolName string, input string, opts IdentifyOptions, ) (context.Context, *ToolSpan)
StartToolSpanFromHeaders continues the Neatlogs trace carried on an incoming request's headers (injected by an upstream Neatlogs SDK) and opens a TOOL child span named toolName, binding the given identity and recording input.
It exists so a service boundary can be instrumented with Neatlogs alone: header extraction, the private W3C propagator, span-kind and tool.* attribute keys, and error status are all handled here, so the caller imports only this package — no OpenTelemetry types. It uses the private Neatlogs propagator and provider only; the process-global OTel / Datadog context is never read or mutated. When Neatlogs is not initialized the span is a no-op and this just returns a wrapped context.
Call End on the returned ToolSpan exactly once, usually via defer.
func (*ToolSpan) End ¶ added in v0.1.1
func (t *ToolSpan) End()
End closes the span. Call exactly once, usually via defer.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
contrib
|
|
|
adk
module
|
|
|
internal
|
|
|
attributes
Package attributes defines the canonical neatlogs.* span attribute keys the SDK's own wrappers emit, plus a JSON-driven Mapper (see mapper.go) that normalizes attributes from any source — OpenTelemetry GenAI semconv (as emitted by Google ADK and others), OpenInference, Traceloop — into the same neatlogs.* namespace.
|
Package attributes defines the canonical neatlogs.* span attribute keys the SDK's own wrappers emit, plus a JSON-driven Mapper (see mapper.go) that normalizes attributes from any source — OpenTelemetry GenAI semconv (as emitted by Google ADK and others), OpenInference, Traceloop — into the same neatlogs.* namespace. |