Dev.to · 6 min read

Before You Add a Tracing SDK, Turn Your JSON Logs Into an Agent Execution View

Before You Add a Tracing SDK, Turn Your JSON Logs Into an Agent Execution View

Your agent failed in production. You have the logs. You also have three browser tabs, four requestId searches, and a growing suspicion that the answer is somewhere between two retries that completed out of order. The usual recommendation is to add tracing. That may be the right destination, but it is a poor first experiment if adopting a new SDK means changing application code, deployment configuration, and data policy before you know whether the resulting view will help. Start with the evidence you already have. Flat JSON is more useful than it looks Suppose agent.log contains ordinary line-delimited JSON from Pino, Winston, NestJS, or a custom logger: {"timestamp":"2026-09-02T10:00:00.000Z","requestId":"req_42","event":"agent.started"} {"timestamp":"2026-09-02T10:00:00.050Z","requestId":"req_42","event":"tool.search.started","tool":"searchDocs"} {"timestamp":"2026-09-02T10:00:00.080Z","requestId":"req_99","event":"agent.started"} {"timestamp":"2026-09-02T10:00:00.280Z","requestId":"req_42","event":"tool.search.completed","durationMs":230} {"timestamp":"2026-09-02T10:00:00.300Z","requestId":"req_42","event":"llm.answer.started","model":"example-model"} {"timestamp":"2026-09-02T10:00:00.900Z","requestId":"req_42","event":"agent.completed","status":"ok"} The file does not use an AgentInspect schema. It already contains four valuable ingredients: a grouping key; an event name; a timestamp; and optional duration, status, tool, or model metadata. Map those fields at the CLI: npx agent-inspect logs ./agent.log \ --format json \ --run-id-key requestId \ --event-key event \ --timestamp-key timestamp The local view is grouped by requestId instead of file position: Run req_42 ├─ agent.started ├─ tool.search.started ├─ tool.search.completed (230ms) ├─ llm.answer.started model=example-model └─ agent.completed (ok) Run req_99 └─ agent.started That is not full tracing. It is still a substantial improvement over mentally filtering interleaved lines from concurrent requests. The real design problem is epistemic Parsing JSON is easy. Deciding what the log actually proves is harder. Two adjacent events may be related. They may also belong to parallel work. A completion event probably closes an earlier start event with the same name, but “probably” is not the same as “the application emitted a parent identifier.” AgentInspect assigns log-derived relationships one of four confidence labels: Confidence Meaning explicit The source log supplied the relationship correlated Shared identifiers connected the events heuristic A configured or recognized pattern suggested it unknown The available fields were insufficient These labels describe how structure was reconstructed. They do not rate the quality of the agent's answer. With only requestId, the honest representation is usually a grouped flat timeline. The parser should not invent a parent-child tree because two lines were close together. If your logs include a parent identifier, map it: npx agent-inspect logs ./agent.log \ --format json \ --run-id-key requestId \ --event-key event \ --timestamp-key timestamp \ --parent-id-key parentSpanId \ --warnings all Explicit structure wins. Inference remains visible. raw JSONL | v field mapping -----> warnings | v normalized events | +---- explicit relationships +---- correlated relationships +---- heuristic relationships | v grouped execution view Move repeated mappings into configuration Flags are ideal for the first experiment. A checked-in config is better when production event names need consistent normalization: { "runIdKeys": ["requestId", "jobId"], "eventKey": "event", "timestampKey": "timestamp", "mappings": { "agent.started": { "kind": "RUN", "name": "agent:started", "startsRun": true }, "tool.*": { "kind": "TOOL" }, "llm.*": { "kind": "LLM" }, "*.failed": { "kind": "ERROR", "status": "error" } }, "redact": [ "authorization", "cookie", "token", "apiKey", "password", "secret", "email" ] } Then run: npx agent-inspect logs ./agent.log \ --format json \ --config ./agent-inspect.logs.json \ --warnings all Use --json when another script needs normalized events, trees, warnings, and summary data. For a local process that is still writing, tail --file ./agent.log produces an updating terminal view. What I would add to the source logs next The first reconstructed view tells you where ambiguity is expensive. Improve only those boundaries: Add a stable run or request ID. Give start and completion events consistent names. Add explicit step IDs and parent IDs where nesting matters. Record duration and status on completion. Keep tool and model identifiers bounded. Do not dump prompts, tool payloads, headers, or complete responses merely to make the view feel rich. For many incidents, structure answers the first question with less collection risk. A low-friction adoption ladder I use this order because every step earns the next one: copy representative JSONL | map 3–5 existing fields | inspect warnings and ambiguity | improve the highest-value source fields | instrument only boundaries logs cannot recover If the grouped view answers nothing, you learned that before changing the application. If it reveals useful structure but leaves one handoff ambiguous, you now know exactly where explicit instrumentation pays for itself. Run the experiment on a copy, not the firehose For a first pass, choose one sanitized incident window and copy only the relevant JSONL locally. Then ask three questions: Can I separate concurrent runs reliably? Can I distinguish start, completion, failure, retry, and fallback events? Which important causal edge is still missing? The goal is not to produce a beautiful universal parser on day one. It is to learn whether your current event vocabulary contains enough structure to answer a real debugging question. Warnings are part of that result. A warning that says an event has unknown confidence is not parser noise to suppress automatically. It may be the strongest signal from the experiment: the application never recorded the relationship you expected operations to reconstruct later. Once the mapping is useful, add it to source control with a tiny fixture log and expected output. That fixture becomes a compatibility test when teams rename fields or logging libraries change serialization behavior. Do not confuse reconstruction with capture Log ingestion can recover grouping and some relationships after the fact. It cannot create an event the application never emitted. If a policy decision, approval boundary, or tool payload digest matters to the incident and no safe representation exists in the log, add intentional instrumentation at that boundary. This is why the adoption ladder ends with selective instrumentation rather than a promise that logs can replace traces forever. The boundary matters JSONL is the first-class path. Log4js-style text with embedded JSON is best-effort. JavaScript object-literal strings are not evaluated, and eval is not used. Log parsing is documented as experimental. A reconstructed execution view can have lower fidelity than manual instrumentation or a framework adapter. tail is a local developer tool, not a production monitor, and current file-rotation behavior is deliberately limited. Those limitations are features of an honest migration story: the view says what it knows, what it inferred, and what it could not recover. The pinned log-to-tree quickstart and logging playbook use production-shaped JSON examples and were checked against agent-inspect@6.17.6. Before adding another tracing SDK, try one copied log file. Which missing field creates the most ambiguity in your agent runs?

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News