Logging

Write structured logs from framework apps and Hono routes

For Agentuity apps, keep your framework's normal stdout path or structured logger for process logs. Keep Pino, LogTape, a platform log drain, or a vendor agent when your app already standardizes on it. Use @agentuity/telemetry when server code needs Agentuity's logger and telemetry export setup. Hono apps can use @agentuity/hono when route handlers need the same logger from c.var.logger.

Bring Your Own Pino Logger

Keep this path when the app already owns process logs and your platform or log drain reads stdout. This is app-owned logging; Agentuity does not need a Pino integration for these lines to keep working.

npm install pino
import pino from 'pino';
 
const appLogger = pino({
  level: process.env.LOG_LEVEL ?? 'info',
});
 
const checkoutLogger = appLogger.child({
  requestId: 'req_123',
  orderId: 'ord_123',
});
 
checkoutLogger.info({ totalCents: 4900 }, 'Checkout accepted');

Child loggers keep request and order fields on every line in that scope. See Structured Logging with Pino for a complete server-function example that keeps Pino app-owned.

Bring Your Own LogTape Logger

Keep this path when the app already uses LogTape categories, structured message placeholders, or a LogTape OpenTelemetry sink. Agentuity does not need a LogTape integration for those logs to keep flowing through your process logger or collector.

npm install @logtape/logtape @logtape/otel

See Using LogTape with an OpenTelemetry Sink for a complete startup and server-function example.

Use Agentuity Telemetry Logger

Use the Agentuity logger when you want child loggers and the same telemetry configuration as your traces:

npm install @agentuity/telemetry
import { logger } from '@agentuity/telemetry';
 
interface CheckoutInput {
  readonly orderId: string;
  readonly userId: string;
  readonly totalCents: number;
}
 
export async function recordCheckout(
  input: CheckoutInput
): Promise<{ readonly accepted: boolean }> {
  const checkoutLogger = logger.child({
    orderId: input.orderId,
    userId: input.userId,
  });
 
  checkoutLogger.info('Checkout started');
 
  if (input.totalCents <= 0) {
    checkoutLogger.warn('Checkout rejected');
    return { accepted: false };
  }
 
  checkoutLogger.child({ totalCents: input.totalCents }).info('Checkout accepted');
  return { accepted: true };
}

logger.child(fields) attaches fields as log attributes. Additional arguments passed to info(), warn(), or error() are formatted into the log message body. When Agentuity telemetry env is present, these logs export through the same telemetry pipeline as traces.

These paths compose: keep the app logger for process logs, then add OpenTelemetry spans where timing and cross-service context matter.

Configure at Startup

The named exports initialize telemetry from Agentuity environment variables on first use. Call register() once at app startup when you want explicit service metadata, a different log level, or predictable initialization before other modules log.

import { register } from '@agentuity/telemetry';
 
const telemetry = register({
  name: 'checkout-api',
  version: '1.4.0',
  environment: process.env.NODE_ENV ?? 'development',
  logLevel: 'info',
});
 
telemetry.logger.info('Telemetry initialized');

The telemetry package supports trace, debug, info, warn, and error as minimum levels. If you do not pass logLevel, the package default is warn.

Log Levels

MethodUse For
trace()noisy diagnostics you only enable briefly
debug()local request context and intermediate values
info()normal milestones, state changes, and accepted work
warn()recoverable problems or missing optional configuration
error()failed operations that need investigation
fatal()unrecoverable errors that exit the process

fatal() logs through the error path and exits the process. Use it sparingly in server apps, because it terminates the current process.

Child Loggers

Create child loggers when every line in a small scope needs the same IDs or category fields.

import { logger } from '@agentuity/telemetry';
 
const requestLogger = logger.child({
  requestId: 'req_123',
  component: 'billing',
});
 
requestLogger.info('Invoice requested');
requestLogger.child({ durationMs: 42, rows: 8 }).debug('Invoice rows loaded');

Nested children inherit parent fields and add their own fields.

Hono Routes

@agentuity/hono initializes telemetry once and exposes the logger on c.var.logger.

npm install @agentuity/hono hono
import { Hono } from 'hono';
import { agentuity } from '@agentuity/hono';
import type { Logger } from '@agentuity/hono';
 
interface Env {
  readonly Variables: {
    readonly logger: Logger;
  };
}
 
function readEventType(value: unknown): string | undefined {
  if (typeof value !== 'object' || value === null || !('type' in value)) {
    return undefined;
  }
 
  const { type } = value;
  return typeof type === 'string' && type.trim() ? type.trim() : undefined;
}
 
const app = new Hono<Env>();
 
app.use('*', agentuity({ telemetry: { logLevel: 'info' } }));
 
app.post('/webhooks/payments', async (c) => {
  const body: unknown = await c.req.json();
  const eventType = readEventType(body);
  const routeLogger = c.var.logger.child({
    provider: 'stripe',
    eventType: eventType ?? 'unknown',
  });
 
  routeLogger.info('Webhook received');
 
  if (!eventType) {
    routeLogger.warn('Webhook missing event type');
    return c.json({ error: 'type is required' }, 400);
  }
 
  routeLogger.info('Webhook accepted');
  return c.json({ received: true });
});
 
export default app;

View Logs

During local development, agentuity dev runs your framework's own dev script with injected service environment values, so stdout and console output appear in the terminal. In deployed apps, inspect logs by session or deployment.

agentuity dev
 
agentuity cloud session list
agentuity cloud session logs sess_abc123xyz
 
agentuity cloud deployment logs deploy_abc123xyz --limit=50

Best Practices

  • Use stdout, console JSON, or your existing logger when that is already your app standard.
  • Use logger or c.var.logger when you want Agentuity telemetry configuration and child logger fields.
  • Add IDs, counts, and durations through child logger fields.
  • Keep log messages short. Put queryable context in attributes.
  • Use info for accepted work, warn for recoverable problems, and error for failed operations.
  • Avoid logging secrets, tokens, raw emails, or full request bodies.

Next Steps