Audit logging
Every HTTP request gets a row in PostHog. Not optional.
Pipeline
What's logged
Each entry includes:
| Field | Source |
|---|---|
event | api_request |
requestId | UUID generated by LoggingInterceptor |
userId | req.user?.id |
userRole | req.user?.role |
orgId | req.user?.activeOrgId if present |
method, path, statusCode | Standard HTTP |
result | success or failure (4xx/5xx) |
errorCode | error.code if failure |
duration_ms | Measured by LoggingInterceptor |
userAgent, ip | From request headers |
PII surface is intentional: no request bodies, no response bodies, no query params. Only metadata.
Flush triggers
flush when:
- queue.length >= LOG_BATCH_SIZE // default 100
- LOG_BATCH_INTERVAL_MS elapses // default 30000
- onApplicationShutdown // best-effort drain
Env
| Var | Default |
|---|---|
POSTHOG_API_KEY | (none — batching silently disabled) |
POSTHOG_HOST | https://app.posthog.com |
LOG_BATCH_SIZE | 100 |
LOG_BATCH_INTERVAL_MS | 30000 |
If POSTHOG_API_KEY is absent, the queue still receives entries (so dev environments can hold the line) but nothing leaves the process. No errors thrown.
Why PostHog, not Stripe-style append-only
PostHog gives free querying, dashboards, alerts, and retention. The trade-off is that you don't have a local immutable log. If that matters, mirror the queue to a local audit_logs table at the same time (the schema is there but currently unused by middleware).
Adding a business event
Don't add ad-hoc PostHog calls from controllers. Use the queue:
constructor(@Inject(AUDIT_LOGGER) private logger: AuditLoggerService) {}
async someMethod() {
// ... business logic ...
await this.logger.log({
event: "agent_published",
properties: { agentId, by: req.user.id },
});
}
This keeps batching, retry, and shutdown drain behavior uniform.
Direct querying
For ad-hoc investigation, the PostHog UI's Insights tab is the fastest. Common queries:
- "Show all
api_requestwithresult = 'failure'in the last hour" - "Show top 20 paths by 5xx rate"
- "Group
api_requestbyuserIdfor usage by user"
Sampling
Not currently sampled. At our scale this is fine. If request volume grows, sample 4xx/5xx fully and sample 2xx at 1–10%.
See also
- Security: Audit logs for retention and privacy.