Telemetry events for LangChain.
This module defines telemetry events that other applications can attach to. It provides a standardized way to emit events for various operations in the LangChain library without implementing tracing functionality.
Event Naming
Events follow the convention: [:langchain, component, operation, stage]
Core Events
[:langchain, :llm, :call, :start]- Emitted when an LLM call starts[:langchain, :llm, :call, :stop]- Emitted when an LLM call completes[:langchain, :llm, :call, :exception]- Emitted when an LLM call raises an exception[:langchain, :llm, :prompt]- Emitted when a prompt is sent to an LLM[:langchain, :llm, :response]- Emitted when a response is received from an LLM[:langchain, :llm, :response, :non_streaming]- Emitted when a non-streaming response is received from an LLM[:langchain, :llm, :response, :streaming]- Emitted when a streaming response is received from an LLM[:langchain, :chain, :execute, :start]- Emitted when a chain execution starts[:langchain, :chain, :execute, :stop]- Emitted when a chain execution completes[:langchain, :chain, :execute, :exception]- Emitted when a chain execution raises an exception[:langchain, :tool, :call, :start]- Emitted when a tool call starts[:langchain, :tool, :call, :stop]- Emitted when a tool call completes[:langchain, :tool, :call, :exception]- Emitted when a tool call raises an exception[:langchain, :message, :process, :start]- Emitted when a message processor (e.g.LangChain.MessageProcessors.JsonProcessor) starts processing a received message[:langchain, :message, :process, :stop]- Emitted when message processing completes[:langchain, :message, :process, :exception]- Emitted when message processing raises an exception[:langchain, :llm, :stream, :first_token]- Emitted once per streaming LLM call when the first delta is received. Carries adurationmeasurement (time from the call's start to the first streamed chunk, in native units) — the basis for a time-to-first-token metric.
Reserved events (not currently emitted)
The following event names — and the *_start helper functions that would emit
them (memory_read_start/1, memory_write_start/1,
retriever_get_relevant_documents_start/1) — are reserved for future use and
are not emitted by LangChain today. They are kept so the naming convention is
stable if/when those subsystems are instrumented. Do not attach handlers
expecting them to fire yet:
[:langchain, :memory, :read, :start | :stop | :exception][:langchain, :memory, :write, :start | :stop | :exception][:langchain, :retriever, :get_relevant_documents, :start | :stop | :exception]
Metadata Fields
The following metadata fields are automatically injected or available in events:
:call_id- A UUID (viaEcto.UUID.generate/0) that correlates start, stop, and exception events within a singlespan/3orstart_event/2call. Automatically injected viaMap.put_new/3, so callers can supply their own ID to override.:provider- The LLM provider name (e.g."openai","anthropic","google"). Included in LLM call metadata by each chat model implementation via theChatModel.provider/0callback.:custom_context- User-supplied context data fromLLMChain.custom_context. Included in chain execution and tool call metadata. Not included in LLM-level telemetry (correlate viacall_idinstead).:token_usage- A%TokenUsage{}struct with input/output token counts. Included in LLM call:stopevents and chain execution:stopevents when available (via the:enrich_stopcallback).nilwhen the model does not report usage.:request_options- A map of the standard request parameters the chat model set (:temperature,:max_tokens,:top_p,:seed, ...), extracted from the model struct byLangChain.ChatModels.ChatModel.request_options/1and injected on LLM call events. Absent parameters are omitted; an empty map means none were captured. The OpenTelemetry layer maps these togen_ai.request.*span attributes.:output_type-"text"or"json", fromChatModel.output_type/1, injected on LLM call events. Maps togen_ai.output.type.:endpoint- The request URL, injected on LLM call events when the chat model exposes an:endpoint. The OpenTelemetry layer derivesserver.address/server.portfrom it.:last_message- The final assembled%Message{}from the LLM response. Included in chain execution:stopevents. For streaming responses this is the fully assembled message (not individual deltas).
Privacy Note
Message content is intentionally excluded from the lifecycle events (:start / :stop /
:exception) to avoid unconditional exposure of user/PII data. Message content is only
available through the purpose-specific [:langchain, :llm, :prompt] and
[:langchain, :llm, :response] events — subscribing to these is an explicit opt-in.
Expected Metadata Shape by Event
LLM call
:start:%{model: String.t(), provider: String.t(), message_count: integer(), tools_count: integer(), request_options: map(), output_type: String.t(), endpoint: String.t() | nil, call_id: String.t()}LLM call
:stop(includes enriched fields):%{model: String.t(), provider: String.t(), message_count: integer(), tools_count: integer(), request_options: map(), output_type: String.t(), endpoint: String.t() | nil, call_id: String.t(), token_usage: TokenUsage.t() | nil, result: term()}Chain execution
:start:%{chain_type: String.t(), mode: term(), message_count: integer(), tools_count: integer(), custom_context: term(), call_id: String.t()}Chain execution
:stop(includes enriched fields):%{chain_type: String.t(), mode: term(), message_count: integer(), tools_count: integer(), custom_context: term(), call_id: String.t(), last_message: Message.t() | nil, token_usage: TokenUsage.t() | nil, result: term()}Tool call (
:start/:stop/:exception):%{tool_name: String.t(), tool_call_id: String.t(), tool_description: String.t() | nil, async: boolean(), custom_context: term(), call_id: String.t()}
Usage
To attach to these events in your application:
:telemetry.attach(
"my-handler-id",
[:langchain, :llm, :call, :stop],
&MyApp.handle_llm_call/4,
nil
)
def handle_llm_call(_event_name, measurements, metadata, _config) do
# Process the event
IO.inspect(measurements)
IO.inspect(metadata)
end
Summary
Functions
Emits a chain execution start event.
Emits a telemetry event with the given name, measurements, and metadata.
Emits an LLM call start event.
Emits an LLM prompt event.
Emits an LLM response event.
Emits a memory read start event.
Emits a memory write start event.
Emits a message processing start event.
Emits a retriever get relevant documents start event.
Wraps a function call with start and stop telemetry events.
Emits a start event and returns a function to emit the corresponding stop event.
Emits a tool call event.
Emits a tool call start event.
Functions
Emits a chain execution start event.
Emits a telemetry event with the given name, measurements, and metadata.
Parameters
event_name- The name of the event as a list of atomsmeasurements- A map of measurements for the eventmetadata- A map of metadata for the event
Examples
iex> LangChain.Telemetry.emit_event([:langchain, :llm, :call, :start], %{system_time: System.system_time()}, %{model: "gpt-4"})
Emits an LLM call start event.
Emits an LLM prompt event.
Emits an LLM response event.
Emits a memory read start event.
Reserved
LangChain does not call this today. See "Reserved events" in the module doc.
Emits a memory write start event.
Reserved
LangChain does not call this today. See "Reserved events" in the module doc.
Emits a message processing start event.
Unused convenience helper
This helper is not called internally. The [:langchain, :message, :process, …]
events themselves are emitted — LangChain.MessageProcessors.JsonProcessor
emits the full span directly via span/4 when it runs in a chain's
message_processors. The helper is kept for callers who want to emit the same
event from their own message processors.
Emits a retriever get relevant documents start event.
Reserved
LangChain does not call this today. See "Reserved events" in the module doc.
Wraps a function call with start and stop telemetry events.
Parameters
event_prefix- The prefix for the event name as a list of atomsmetadata- A map of metadata for the eventfun- The function to executeopts- Optional keyword list::enrich_stop- A 1-arity function that receives the result and returns a map of additional metadata to merge into the stop event. Useful for extracting data (e.g. token usage) from the result into top-level metadata.
Returns
The result of the function call.
Examples
iex> LangChain.Telemetry.span([:langchain, :llm, :call], %{model: "gpt-4"}, fn ->
...> # Call the LLM
...> {:ok, "response"}
...> end)
# With enrich_stop to surface token usage:
iex> LangChain.Telemetry.span([:langchain, :llm, :call], %{model: "gpt-4"}, fn ->
...> {:ok, response}
...> end, enrich_stop: fn {:ok, msg} -> %{token_usage: msg.metadata[:usage]} end)
Emits a start event and returns a function to emit the corresponding stop event.
This is useful for span-like events where you want to measure the duration of an operation.
Parameters
event_prefix- The prefix for the event name as a list of atomsmetadata- A map of metadata for the event
Returns
A function that accepts additional metadata to be merged with the original metadata and emits the stop event with the duration measurement.
Examples
iex> stop_fun = LangChain.Telemetry.start_event([:langchain, :llm, :call], %{model: "gpt-4"})
iex> # Do some work
iex> stop_fun.(%{result: "success"})
Emits a tool call event.
Emits a tool call start event.