BMAD-METHOD/src/modules/bmm/testarch/knowledge/log.md

7.8 KiB

Log Utility

Principle

Use structured logging that integrates with Playwright's test reports. Support object logging, test step decoration, and multiple log levels (info, step, success, warning, error, debug).

Rationale

Console.log in Playwright tests has limitations:

  • Not visible in HTML reports
  • No test step integration
  • No structured output
  • Lost in terminal noise during CI

The log utility provides:

  • Report integration: Logs appear in Playwright HTML reports
  • Test step decoration: log.step() creates collapsible steps in UI
  • Object logging: Automatically formats objects/arrays
  • Multiple levels: info, step, success, warning, error, debug
  • Optional console: Can disable console output but keep report logs

Pattern Examples

Example 1: Basic Logging Levels

Context: Log different types of messages throughout test execution.

Implementation:

import { log } from '@seontechnologies/playwright-utils';

test('logging demo', async ({ page }) => {
  await log.step('Navigate to login page');
  await page.goto('/login');

  await log.info('Entering credentials');
  await page.fill('#username', 'testuser');

  await log.success('Login successful');

  await log.warning('Rate limit approaching');

  await log.debug({ userId: '123', sessionId: 'abc' });

  // Errors still throw but get logged first
  try {
    await page.click('#nonexistent');
  } catch (error) {
    await log.error('Click failed', false); // false = no console output
    throw error;
  }
});

Key Points:

  • step() creates collapsible steps in Playwright UI
  • info(), success(), warning() for different message types
  • debug() for detailed data (objects/arrays)
  • error() with optional console suppression
  • All logs appear in test reports

Example 2: Object and Array Logging

Context: Log structured data for debugging without cluttering console.

Implementation:

test('object logging', async ({ apiRequest }) => {
  const { body } = await apiRequest({
    method: 'GET',
    path: '/api/users',
  });

  // Log array of objects
  await log.debug(body); // Formatted as JSON in report

  // Log specific object
  await log.info({
    totalUsers: body.length,
    firstUser: body[0]?.name,
    timestamp: new Date().toISOString(),
  });

  // Complex nested structures
  await log.debug({
    request: {
      method: 'GET',
      path: '/api/users',
      timestamp: Date.now(),
    },
    response: {
      status: 200,
      body: body.slice(0, 3), // First 3 items
    },
  });
});

Key Points:

  • Objects auto-formatted as pretty JSON
  • Arrays handled gracefully
  • Nested structures supported
  • All visible in Playwright report attachments

Example 3: Test Step Organization

Context: Organize test execution into collapsible steps for better readability in reports.

Implementation:

test('organized with steps', async ({ page, apiRequest }) => {
  await log.step('ARRANGE: Setup test data');
  const { body: user } = await apiRequest({
    method: 'POST',
    path: '/api/users',
    body: { name: 'Test User' },
  });

  await log.step('ACT: Perform user action');
  await page.goto(`/users/${user.id}`);
  await page.click('#edit');
  await page.fill('#name', 'Updated Name');
  await page.click('#save');

  await log.step('ASSERT: Verify changes');
  await expect(page.getByText('Updated Name')).toBeVisible();

  // In Playwright UI, each step is collapsible
});

Key Points:

  • log.step() creates collapsible sections
  • Organize by Arrange-Act-Assert
  • Steps visible in Playwright trace viewer
  • Better debugging when tests fail

Example 4: Conditional Logging

Context: Log different messages based on environment or test conditions.

Implementation:

test('conditional logging', async ({ page }) => {
  const isCI = process.env.CI === 'true';

  if (isCI) {
    await log.info('Running in CI environment');
  } else {
    await log.debug('Running locally');
  }

  const isKafkaWorking = await checkKafkaHealth();

  if (!isKafkaWorking) {
    await log.warning('Kafka unavailable - skipping event checks');
  } else {
    await log.step('Verifying Kafka events');
    // ... event verification
  }
});

Key Points:

  • Log based on environment
  • Skip logging with conditionals
  • Use appropriate log levels
  • Debug info for local, minimal for CI

Example 5: Integration with Auth and API

Context: Log authenticated API requests with tokens (safely).

Implementation:

import { test } from '@seontechnologies/playwright-utils/fixtures';

// Helper to create safe token preview
function createTokenPreview(token: string): string {
  if (!token || token.length < 10) return '[invalid]';
  return `${token.slice(0, 6)}...${token.slice(-4)}`;
}

test('should log auth flow', async ({ authToken, apiRequest }) => {
  await log.info(`Using token: ${createTokenPreview(authToken)}`);

  await log.step('Fetch protected resource');
  const { status, body } = await apiRequest({
    method: 'GET',
    path: '/api/protected',
    headers: { Authorization: `Bearer ${authToken}` },
  });

  await log.debug({
    status,
    bodyPreview: {
      id: body.id,
      recordCount: body.data?.length,
    },
  });

  await log.success('Protected resource accessed successfully');
});

Key Points:

  • Never log full tokens (security risk)
  • Use preview functions for sensitive data
  • Combine with auth and API utilities
  • Log at appropriate detail level

Log Levels Guide

Level When to Use Shows in Report Shows in Console
step Test organization, major actions Collapsible steps Yes
info General information, state changes Yes Yes
success Successful operations Yes Yes
warning Non-critical issues, skipped checks Yes Yes
error Failures, exceptions Yes Configurable
debug Detailed data, objects Yes (attached) Configurable

Comparison with console.log

console.log log Utility
Not in reports Appears in reports
No test steps Creates collapsible steps
Manual JSON.stringify() Auto-formats objects
No log levels 6 log levels
Lost in CI output Preserved in artifacts
  • overview.md - Basic usage and imports
  • api-request.md - Log API requests
  • auth-session.md - Log auth flow (safely)
  • recurse.md - Log polling progress

Anti-Patterns

Logging objects in steps:

await log.step({ user: 'test', action: 'create' }); // Shows empty in UI

Use strings for steps, objects for debug:

await log.step('Creating user: test'); // Readable in UI
await log.debug({ user: 'test', action: 'create' }); // Detailed data

Logging sensitive data:

await log.info(`Password: ${password}`); // Security risk!
await log.info(`Token: ${authToken}`); // Full token exposed!

Use previews or omit sensitive data:

await log.info('User authenticated successfully'); // No sensitive data
await log.debug({ tokenPreview: token.slice(0, 6) + '...' });

Excessive logging in loops:

for (const item of items) {
  await log.info(`Processing ${item.id}`); // 100 log entries!
}

Log summary or use debug level:

await log.step(`Processing ${items.length} items`);
await log.debug({ itemIds: items.map((i) => i.id) }); // One log entry