---
title: "Chapter 06: Session Logging"
description: "Record every message in JSONL format for debugging, analysis, and future compaction. Add slash commands for session control."
type: lesson
order: 6
chapter: "02-core"
---

# Chapter 06: Session Logging

## What We're Building

Session logging records every message — system prompts, user input, tool calls, tool results, assistant responses — into a dated JSONL file. This is essential for:

- **Debugging**: What did the agent actually do?
- **Analysis**: Which tools are used most? Where does it fail?
- **Compaction**: Summarize old context to stay within the token limit (Chapter 11)
- **Resumption**: Pick up where you left off

## Step 1: Add Session Logging

Paste this prompt into your agent:

> Add session-logging functionality to the system. Create a jsonl file in .session_logs/ with the name of the current date, like YYYY-MM-DD-HH-MM.jsonl that contains an entry for each message. We'll use these files to examine and tune the system, and manage subsessions and session compactions and stuff like that.

## Step 2: The Implementation

The agent will create `src/lib/logger.ts`:

```typescript
import { randomUUID } from "node:crypto";
import * as fs from "node:fs";
import type { Message } from "./types";

export interface Logger {
  log(message: Message): void;
  child(agentName: string): Logger;
}

export class SessionLogger implements Logger {
  private logFile: string;
  private sessionId: string;
  private parentId: string | null;
  private agentName: string;

  constructor(
    logFile: string,
    sessionId: string = randomUUID(),
    parentId: string | null = null,
    agentName: string = "main",
  ) {
    this.logFile = logFile;
    this.sessionId = sessionId;
    this.parentId = parentId;
    this.agentName = agentName;
  }

  log(message: Message): void {
    const entry = {
      timestamp: new Date().toISOString(),
      sessionId: this.sessionId,
      parentId: this.parentId,
      agentName: this.agentName,
      message,
    };
    fs.appendFileSync(this.logFile, `${JSON.stringify(entry)}\n`);
  }

  child(agentName: string): Logger {
    const childSessionId = randomUUID();
    return new SessionLogger(
      this.logFile,
      childSessionId,
      this.sessionId,
      agentName,
    );
  }
}
```

Key design decisions:

- **sessionId**: UUID unique to this session, links all messages from one run
- **parentId**: For subagents, links child messages back to the parent session
- **agentName**: Which agent (main, code-map, tech-researcher, etc.) generated this message
- **timestamp**: ISO format for chronological ordering

### The Startup Sequence (in `src/index.ts`)

```typescript
const logDir = path.join(process.cwd(), ".session_logs");
if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir, { recursive: true });
}

const now = new Date();
const logFileName = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}.jsonl`;
const logFile = path.join(logDir, logFileName);
const logger = new SessionLogger(logFile);
```

## Step 3: Log Every Message

The logger is passed through the system and records every message type:

```typescript
// System prompt
logger.log(history[0]);

// User messages
logger.log(userMsg);

// Assistant messages (including tool calls)
logger.log(assistantMsg);

// Tool results
toolResults.forEach((msg) => logger.log(msg));
```

A typical log entry looks like:

```json
{
  "timestamp": "2025-04-15T14:23:01.234Z",
  "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "parentId": null,
  "agentName": "main",
  "message": {
    "role": "assistant",
    "tool_calls": [
      {
        "function": {
          "name": "read_file",
          "arguments": "{\"path\": \"src/agent.ts\"}"
        }
      }
    ]
  }
}
```

## Step 4: Subagent Logging

When a subagent is spawned via `run_agent`, it gets a child logger:

```typescript
const subLogger = logger ? logger.child(subAgentName) : undefined;
```

The child logger:

- Gets a new `sessionId` (UUID)
- Records the parent's `sessionId` as `parentId`
- Sets the `agentName` to the subagent's name

This creates a tree structure in your logs — you can reconstruct which parent spawned which subagent and what happened in each.

## Step 5: Verify

Start your agent, have a few turns of conversation, then check the logs:

```bash
ls -la .session_logs/
```

```bash
cat .session_logs/2025-04-15-14-23.jsonl | head -5
```

You should see every message from your session, timestamped and attributed.

## Step 6: Add Slash Commands

Paste this additional prompt:

> Add slash commands for /help, /models, /search, /compact, /restore, /fork, /session. These should work in the REPL input.

The agent will add command parsing to `src/index.ts` that intercepts lines starting with `/`:

| Command         | What It Does                                    |
| --------------- | ----------------------------------------------- |
| `/help`         | Show available commands and agent info          |
| `/models`       | Display model stats and pricing                 |
| `/search`       | Search session logs for a pattern               |
| `/compact`      | Summarize conversation history (see Chapter 11) |
| `/restore <id>` | Resume a previous session                       |
| `/fork <id>`    | Branch off a previous session                   |
| `/session`      | Show current session info (ID, messages, cost)  |

---

## Why JSONL?

JSONL (JSON Lines) is one JSON object per line. It's ideal for logging because:

- **Append-only**: You can add to the file without reading the whole thing
- **Streamable**: Process line by line without loading into memory
- **Crash-safe**: Even if the program crashes, all lines up to the crash are valid
- **grep-friendly**: You can use standard Unix tools to search

```bash
# Find all tool calls
grep '"tool_calls"' .session_logs/2025-04-15-14-23.jsonl | wc -l

# Find all errors
grep -i "error" .session_logs/2025-04-15-14-23.jsonl

# Extract just user messages
grep '"role":"user"' .session_logs/2025-04-15-14-23.jsonl
```

---

[← Chapter 05](/build-ai-coding-agent/02-core/05-cost-context/) · [Next: Chapter 07 →](/build-ai-coding-agent/02-core/07-research/)
