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
| Transport | When used | Config |
|---|---|---|
"console" (default) | Always — great for dev and log aggregators | — |
"file" | Append to a local file | filePath: "./audit.log" |
"agentlair" | Ship to AgentLair for governed audit trail | agentlairApiKey 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