Skip to main content
Mastra provides structured logging with automatic correlation to traces and spans, making it easy to debug issues and understand system behavior.

Overview

Mastra’s logging system:
  • Automatically correlates logs with traces and spans
  • Supports multiple log levels (debug, info, warn, error, fatal)
  • Captures structured data with each log message
  • Works with any logger that implements the LoggerContext interface

Basic Usage

Default Logger

Mastra includes a console logger by default:
import { Mastra } from '@mastra/core';

const mastra = new Mastra({
  agents: { /* ... */ },
});

// Get the logger
const logger = mastra.getLogger();

// Log messages
logger.info('Processing request', { userId: '123' });
logger.error('Failed to connect', { service: 'database', error: 'timeout' });

Log Levels

Available log levels in order of severity:
logger.debug('Debug info', { data: {...} });  // Lowest
logger.info('Info message', { status: 'ok' });
logger.warn('Warning', { threshold: 0.8 });
logger.error('Error occurred', { error: err });
logger.fatal('Critical failure', { service: 'auth' }); // Highest

Structured Logging

Adding Context

Include structured data with log messages:
logger.info('User login', {
  userId: 'user-123',
  email: '[email protected]',
  ipAddress: '192.168.1.1',
  timestamp: new Date(),
});

logger.error('Database query failed', {
  query: 'SELECT * FROM users',
  database: 'production',
  duration: 5000,
  error: {
    message: 'Connection timeout',
    code: 'ETIMEDOUT',
  },
});

Automatic Trace Correlation

Logs automatically include trace and span IDs when logged within a traced operation:
const result = await agent.generate({
  messages: [{ role: 'user', content: 'Hello' }],
  
  onStepStart: () => {
    // This log will include traceId and spanId automatically
    logger.info('Agent step started');
  },
  
  onStepFinish: ({ result }) => {
    logger.info('Agent step completed', { 
      finishReason: result.finishReason,
    });
    // Log entry:
    // {
    //   level: 'info',
    //   message: 'Agent step completed',
    //   data: { finishReason: 'stop' },
    //   traceId: 'a1b2c3d4...',
    //   spanId: 'e5f67890...',
    //   timestamp: Date(...)
    // }
  },
});

Custom Logger

Implementing LoggerContext

Create a custom logger by implementing the LoggerContext interface:
import { LoggerContext } from '@mastra/core/observability';

class CustomLogger implements LoggerContext {
  debug(message: string, data?: Record<string, unknown>): void {
    console.log('[DEBUG]', message, data);
  }
  
  info(message: string, data?: Record<string, unknown>): void {
    console.log('[INFO]', message, data);
  }
  
  warn(message: string, data?: Record<string, unknown>): void {
    console.warn('[WARN]', message, data);
  }
  
  error(message: string, data?: Record<string, unknown>): void {
    console.error('[ERROR]', message, data);
  }
  
  fatal(message: string, data?: Record<string, unknown>): void {
    console.error('[FATAL]', message, data);
  }
}

// Use custom logger
const mastra = new Mastra({
  logger: new CustomLogger(),
  agents: { /* ... */ },
});

Winston Integration

Integrate with Winston logger:
import winston from 'winston';
import { LoggerContext, LogLevel } from '@mastra/core/observability';

class WinstonLogger implements LoggerContext {
  private logger: winston.Logger;
  
  constructor() {
    this.logger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
      ),
      transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'mastra.log' }),
      ],
    });
  }
  
  private log(level: LogLevel, message: string, data?: Record<string, unknown>) {
    this.logger.log(level, message, data);
  }
  
  debug(message: string, data?: Record<string, unknown>): void {
    this.log('debug', message, data);
  }
  
  info(message: string, data?: Record<string, unknown>): void {
    this.log('info', message, data);
  }
  
  warn(message: string, data?: Record<string, unknown>): void {
    this.log('warn', message, data);
  }
  
  error(message: string, data?: Record<string, unknown>): void {
    this.log('error', message, data);
  }
  
  fatal(message: string, data?: Record<string, unknown>): void {
    this.log('error', message, { ...data, fatal: true });
  }
}

const mastra = new Mastra({
  logger: new WinstonLogger(),
  agents: { /* ... */ },
});

Pino Integration

Integrate with Pino logger:
import pino from 'pino';
import { LoggerContext } from '@mastra/core/observability';

class PinoLogger implements LoggerContext {
  private logger: pino.Logger;
  
  constructor() {
    this.logger = pino({
      level: 'debug',
      transport: {
        target: 'pino-pretty',
        options: { colorize: true },
      },
    });
  }
  
  debug(message: string, data?: Record<string, unknown>): void {
    this.logger.debug(data, message);
  }
  
  info(message: string, data?: Record<string, unknown>): void {
    this.logger.info(data, message);
  }
  
  warn(message: string, data?: Record<string, unknown>): void {
    this.logger.warn(data, message);
  }
  
  error(message: string, data?: Record<string, unknown>): void {
    this.logger.error(data, message);
  }
  
  fatal(message: string, data?: Record<string, unknown>): void {
    this.logger.fatal(data, message);
  }
}

const mastra = new Mastra({
  logger: new PinoLogger(),
  agents: { /* ... */ },
});

Log Format

ExportedLog Structure

Logs are exported with this structure:
interface ExportedLog {
  timestamp: Date;               // When log was emitted
  level: LogLevel;               // Log severity level
  message: string;               // Human-readable message
  data?: Record<string, unknown>; // Structured data
  traceId?: string;              // Correlated trace ID
  spanId?: string;               // Correlated span ID
  tags?: string[];               // Optional tags
  metadata?: Record<string, unknown>; // Additional metadata
}

Example Log Entry

{
  "timestamp": "2024-01-15T10:30:00.000Z",
  "level": "info",
  "message": "Agent generation completed",
  "data": {
    "agentId": "myAgent",
    "duration": 1250,
    "tokenUsage": {
      "input": 150,
      "output": 75
    }
  },
  "traceId": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "spanId": "e5f67890a1b2c3d4",
  "tags": ["production"],
  "metadata": {
    "userId": "user-123",
    "sessionId": "session-abc"
  }
}

Best Practices

Use Appropriate Log Levels

// DEBUG - Detailed information for troubleshooting
logger.debug('Processing message', { messageId: '123', content: 'Hello' });

// INFO - General informational messages
logger.info('Request processed successfully', { userId: '123', duration: 250 });

// WARN - Warning messages that don't prevent operation
logger.warn('Rate limit approaching', { current: 95, limit: 100 });

// ERROR - Error conditions that should be investigated
logger.error('Failed to call external API', { api: 'weather', error: 'timeout' });

// FATAL - Critical errors that require immediate attention
logger.fatal('Database connection lost', { database: 'production' });

Include Relevant Context

// Good: Includes context for debugging
logger.error('Tool execution failed', {
  toolName: 'calculator',
  input: { operation: 'divide', a: 10, b: 0 },
  error: 'Division by zero',
  userId: 'user-123',
});

// Bad: Missing context
logger.error('Tool failed');

Structure Complex Data

logger.info('Model response received', {
  model: 'gpt-4',
  usage: {
    prompt: 150,
    completion: 75,
    total: 225,
  },
  timing: {
    ttft: 250,    // Time to first token
    total: 1200,
  },
  finishReason: 'stop',
});

Avoid Logging Sensitive Data

// Good: Redact sensitive information
logger.info('User authenticated', {
  userId: user.id,
  email: user.email.replace(/(.{2}).*(@.*)/, '$1***$2'), // Redact email
});

// Bad: Logging sensitive data
logger.info('User authenticated', {
  userId: user.id,
  password: user.password, // Never log passwords!
  apiKey: user.apiKey,     // Never log API keys!
});

Querying Logs

If using an observability platform, query logs via the HTTP API:
# Query logs with filters
curl "http://localhost:3000/api/logs?level=error&limit=100"

# Query logs for specific trace
curl "http://localhost:3000/api/logs?traceId=a1b2c3d4e5f67890a1b2c3d4e5f67890"

Next Steps

Tracing

Learn about OpenTelemetry tracing

Server API

Explore server routes and handlers

Build docs developers (and LLMs) love