Audit Logger

A lightweight, framework-agnostic agent action logger. Log locally by default. Connect to AgentLair for persistent, queryable audit trails.

Available as @piiiico/agent-logger on npm — zero runtime dependencies, works in Node ≥ 18, Bun, Deno, and modern browsers.


Install

npm install @piiiico/agent-logger
# or
bun add @piiiico/agent-logger

Quick start

import { auditLog } from "@piiiico/agent-logger";

// Logs to console — no config needed
await auditLog({
  agent: "customer-support-bot",
  action: "respond",
  tool: "knowledge_base_search",
  input: { query: "How do I cancel my subscription?" },
  output: { answer: "Go to Settings → Billing → Cancel." },
});
// → {"agent":"customer-support-bot","action":"respond",...,"timestamp":"2026-04-01T12:00:00.000Z"}

No API key, no sign-up, no configuration required for local logging.


Pre-bound logger

createAuditLogger binds the agent name so you don’t repeat it on every call:

import { createAuditLogger } from "@piiiico/agent-logger";

const log = createAuditLogger("inventory-agent");

await log({ action: "check_stock", tool: "db_query", input: { sku: "ABC-123" } });
await log({ action: "reorder", output: { orderId: "PO-9999" } });

Connect to AgentLair

Set AGENTLAIR_API_KEY to ship audit logs to AgentLair for persistent storage and querying.

export AGENTLAIR_API_KEY=aal_...

That’s it. All auditLog() calls will now also POST to AgentLair asynchronously — non-blocking, fire-and-forget. Your agent’s performance is unaffected.

Or pass the key explicitly:

import { createAuditLogger } from "@piiiico/agent-logger";

const log = createAuditLogger("my-agent", {
  transport: "agentlair",
  agentlairApiKey: process.env.AGENTLAIR_API_KEY,
});

await log({ action: "tool_call", tool: "web_search", input: "latest AI news" });

Get a free API key at agentlair.dev.


API

auditLog(entry, opts?) — module-level convenience

The simplest way to log. Uses a shared module-level logger instance.

await auditLog({
  agent: string;        // Name/ID of the agent
  action: string;       // Action category (e.g. "tool_call", "llm_response", "decision")
  tool?: string;        // Tool name, if this is a tool call
  input?: unknown;      // Input to the tool or LLM
  output?: unknown;     // Output from the tool or LLM
  timestamp?: string;   // ISO 8601 — defaults to now
  metadata?: Record<string, unknown>;  // Any additional context
});

Transports

TransportWhen usedConfig
"console" (default)Always — great for dev and log aggregators
"file"Append to a local filefilePath: "./audit.log"
"agentlair"Ship to AgentLair for governed audit trailagentlairApiKey or AGENTLAIR_API_KEY
"silent"Testing

Auto-detection: if AGENTLAIR_API_KEY is set, the default transport becomes "agentlair". Otherwise, "console".


Framework adapters

LangChain.js

Drop AgentAuditCallback into any chain, agent, or model’s callbacks array — no manual auditLog() calls needed.

import { AgentAuditCallback } from "@piiiico/agent-logger/langchain";
import { LLMChain } from "langchain/chains";
import { ChatOpenAI } from "langchain/chat_models/openai";

const llm = new ChatOpenAI();
const chain = new LLMChain({
  llm,
  prompt,
  callbacks: [new AgentAuditCallback("my-langchain-agent")],
});

await chain.call({ question: "What is 2+2?" });
// Automatically logs: llm_start, llm_end, tool_start, tool_end, chain_start, chain_end

With AgentLair backend:

new AgentAuditCallback("my-agent", { transport: "agentlair" })

Anthropic / Claude SDK

Wrap the Anthropic client to log every request and response automatically.

import Anthropic from "@anthropic-ai/sdk";
import { wrapAnthropicClient } from "@piiiico/agent-logger/anthropic";

const client = wrapAnthropicClient(new Anthropic(), "researcher");

// Use exactly like the normal client — all calls are logged
const msg = await client.messages.create({
  model: "claude-3-5-sonnet-20241022",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Explain AgentLair in one sentence." }],
});

Logged: request params summary + response usage (input/output tokens, stop reason).

CrewAI

import { wrapCrewTask } from "@piiiico/agent-logger/crewai";

const auditedSearch = wrapCrewTask(webSearch, {
  agent: "research-crew",
  taskName: "web_search",
});

const results = await auditedSearch("latest AI agent frameworks");
// Logs: crew_task_start → crew_task_end (with duration)

File transport example

await auditLog(
  { agent: "batch-processor", action: "process", input: { jobId: "job-42" } },
  { transport: "file", filePath: "/var/log/agents/audit.log" }
);

Appends JSON lines. Compatible with any log aggregator (Datadog, Loki, CloudWatch).


Querying stored audit logs

When AGENTLAIR_API_KEY is set, audit entries are stored as Observations under the audit-log topic in AgentLair. Query them with the AgentLair SDK:

import { AgentLair } from "@agentlair/sdk";

const lair = new AgentLair(process.env.AGENTLAIR_API_KEY!);
const { observations } = await lair.observations.read({
  topic: "audit-log",
  limit: 50,
});

for (const obs of observations) {
  console.log(obs.data); // ResolvedAuditEntry
}

Each stored entry includes the full agent, action, tool, input, output, timestamp, and metadata fields from the original log call.


GitHub

Source code and issues: github.com/piiiico/agent-logger


What’s next

  • Vault — Zero-knowledge secret storage for your agents
  • API Keys — Get your free aal_... API key