Dependencies
moon add cybershang/agent-telemetry// Option 1: one-line initialization from environment variables.
// Reads OTEL_SERVICE_NAME, OTEL_STDOUT, and OTEL_EXPORTER_OTLP_ENDPOINT.
// Use ProcessUniqueRandom to avoid duplicate trace/span IDs across restarts.
let providers = @telemetry.init_from_env(
id_generator=@telemetry.ProcessUniqueRandom,
)
// Option 2: manually select an exporter.
let config = @telemetry.TelemetryConfig::new(service_name="my-agent")
let providers = @telemetry.init_telemetry(
config,
@telemetry.Otlp("http://localhost:4318"),
id_generator=@telemetry.ProcessUniqueRandom,
)
// Metrics and logs need background tasks.
@async.with_task_group((group) => {
providers.spawn_background_tasks(group)
// Create a GenAI chat span.
let tracer = @telemetry.tracer("my-agent/llm")
let meter = @telemetry.meter("my-agent/llm")
let span = @telemetry.start_chat_span(
tracer,
"stepfun",
"step-3.7-flash",
1024,
temperature=Some(0.7),
stream=Some(true),
)
// ... send the request and obtain response_json ...
@telemetry.set_usage(span, 100L, 25L, cache_read_input_tokens=50L)
@telemetry.set_response(span, response_json, output_messages~)
@telemetry.end_span(span)
providers.force_flush()
providers.shutdown()
})| Scenario | Functions |
|---|---|
| General | TelemetryConfig::new, init_telemetry, init_from_env, TelemetryProviders, IdGeneratorOption, tracer, meter, logger, conversation_logger, start_span, end_span, set_attributes, set_string, set_int, set_double, set_bool, set_json |
| LLM chat | start_chat_span, set_usage, set_response, set_http_error |
| Tool | start_tool_span, set_tool_result, set_tool_error |
| Agent turn | start_agent_turn_span, set_turn, set_turn_exhausted |
| Agent invocation | start_invoke_agent_span |
| Plan | start_plan_span |
| Metrics | record_llm_latency, record_usage, record_tool_call, record_turn, record_agent_duration, record_tool_duration |
| Logs | emit_log, log_info, log_warn, log_error, log_conversation_message |
let span = @telemetry.start_span(tracer, "my.step")
@telemetry.set_string(span, "app.user.id", "user-42")
@telemetry.set_int(span, "app.retry.count", 3L)
@telemetry.set_double(span, "app.score", 0.95)
@telemetry.set_bool(span, "app.cached", true)
@telemetry.set_json(span, "app.metadata", { "source": "api", "depth": 2 })
@telemetry.end_span(span)| Variable | Description | Default |
|---|---|---|
| OTEL_SERVICE_NAME | Service name | agent-telemetry |
| OTEL_STDOUT | Use the stdout exporter when set to true | false |
| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP/HTTP endpoint | http://localhost:4318 |
| OTEL_EXPORTER_OTLP_HEADERS | Global OTLP headers, comma-separated key=value pairs | (empty) |
| OTEL_EXPORTER_OTLP_TRACES_HEADERS | Trace-specific OTLP headers | (empty, falls back to global) |
| OTEL_EXPORTER_OTLP_METRICS_HEADERS | Metric-specific OTLP headers | (empty, falls back to global) |
| OTEL_EXPORTER_OTLP_LOGS_HEADERS | Log-specific OTLP headers | (empty, falls back to global) |
| GREPTIME_TRACE_PIPELINE | GreptimeDB trace pipeline name | greptime_trace_v1 |
| GREPTIME_LOG_TABLE | Target table for conversation logs | genai_conversations |
OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer my-token, X-Scope-OrgID=tenant-1"providers.force_flush()
providers.shutdown()| Option | Description |
|---|---|
| @telemetry.SdkDefault | Use the SDK default RandomIdGenerator |
| @telemetry.ProcessUniqueRandom | Generate a process-unique seed from the current timestamp and a counter, avoiding duplicate trace/span IDs across restarts |
| @telemetry.Custom(generator) | Provide your own IdGenerator |
moon test -p cybershang/agent-telemetry --target nativepub struct TelemetryProviders {
tracer_provider : SdkTracerProvider
meter_provider : SdkMeterProvider
logger_provider : SdkLoggerProvider
conversation_logger_provider : SdkLoggerProvider
resource : Resource
}fn TelemetryProviders::spawn_background_tasks(self : TelemetryProviders, group : TaskGroup[Unit], allow_failure? : Bool) -> Unitasync fn emit_log(scope_name : String, severity : Severity, body : String, attributes? : Array[KeyValue], trace_context? : SpanContext?) -> Unitasync fn init_from_env(service_name? : String, id_generator? : IdGeneratorOption) -> TelemetryProvidersasync fn init_telemetry(config : TelemetryConfig, exporter_type : ExporterType, id_generator? : IdGeneratorOption) -> TelemetryProvidersasync fn log_conversation_message(scope_name : String, role : String, content : String, index? : Int, trace_context? : SpanContext?) -> Unitasync fn log_error(scope_name : String, body : String, attributes? : Array[KeyValue], trace_context? : SpanContext?) -> Unitasync fn log_info(scope_name : String, body : String, attributes? : Array[KeyValue], trace_context? : SpanContext?) -> Unitasync fn log_warn(scope_name : String, body : String, attributes? : Array[KeyValue], trace_context? : SpanContext?) -> Unitasync fn now_seconds() -> Doublefn record_llm_latency(meter : Meter, provider_name : String, model : String, seconds~ : Double) -> Unitfn record_usage(meter : Meter, provider_name : String, model : String, token_type : String, value : Int64) -> Unitfn set_usage(span : Span, prompt_tokens : Int64, completion_tokens : Int64, cache_read_input_tokens? : Int64, reasoning_output_tokens? : Int64) -> Unitfn start_chat_span(tracer : Tracer, provider_name : String, model : String, max_tokens : Int, temperature? : Double?, top_p? : Double?, stream? : Bool?, reasoning_level? : String?, stop_sequences? : Array[String]?, frequency_penalty? : Double?, presence_penalty? : Double?, seed? : Int64?, input_messages? : Json?, server_address? : String?, server_port? : Int?, parent_context? : Context) -> SpanInstall
Download zipDependencies