Instrument Your Own Code
Add spans to your own agents, pipelines, and tool functions.
Auto-instrumentation covers library calls — OpenAI, LangChain, ChromaDB, and the rest. Your own orchestration code is invisible to the tracer unless you add spans to it.
The primary way to do this is @neatlogs.span:
import neatlogs
@neatlogs.span(kind="WORKFLOW")
def handle_request(user_input: str) -> str:
return support_agent(user_input)
@neatlogs.span(kind="AGENT", name="support_agent")
def support_agent(message: str) -> str:
...
@neatlogs.span(kind="TOOL", tool_name="get_order_status")
def get_order_status(order_id: str) -> dict:
return orders_db.get(order_id)This produces a trace like:
WORKFLOW handle_request 0.8s
AGENT support_agent 0.8s
LLM gpt-4o 0.6s
TOOL get_order_status 0.0sEvery trace needs a WORKFLOW span at the root. Without one, spans from instrumented libraries float to the top with no parent. Wrap your entry point function and everything else nests under it automatically.
Span kinds
Pick the kind that matches what the function does:
| Kind | Use for |
|---|---|
WORKFLOW | Entry point — one per trace root |
AGENT | A reasoning step that calls an LLM and decides what to do next |
CHAIN | A fixed sequence of steps with no branching LLM decisions |
TOOL | A function the agent calls to interact with the world |
RETRIEVER | A vector search or document lookup |
GUARDRAIL | A safety or validation check |
When the kind matches the data the function returns (e.g. a RETRIEVER returning documents), the dashboard renders a specialized view for it automatically.
Inline spans with trace()
For a block inside an existing function, use the context manager instead of a decorator:
@neatlogs.span(kind="CHAIN")
def rag_pipeline(query: str) -> str:
with neatlogs.trace("retrieve", kind="RETRIEVER") as span:
span.set_attribute("neatlogs.retrieval.query", query)
docs = my_retriever.search(query, k=5)
span.set_attribute("neatlogs.retrieval.documents", json.dumps(docs))
return generate(query, docs)For the full decorator reference — all parameters, async support, disabling content capture — see @span Decorator.
For setting attributes manually on custom span kinds, see Custom Attributes.