Skip to content

AI Prompt: Forma3D.Connect — Phase 5g: Structured Logging Cleanup

Purpose: This prompt instructs an AI to replace console.log statements with structured logging
Estimated Effort: 1 day (~6-8 hours)
Prerequisites: Phase 5f completed (Shared API Types)
Output: All console statements replaced with Pino/NestJS logger, consistent log format
Status: 🟡 PENDING


🎯 Mission

You are continuing development of Forma3D.Connect, building on the Phase 5f foundation. Your task is to implement Phase 5g: Structured Logging Cleanup — specifically addressing TD-005 (Console Logging in Production Code) from the technical debt register.

Why This Matters:

The codebase uses console.log/warn/error in some production files, causing:

  1. Log Format Inconsistency: Console output bypasses Pino formatting
  2. Missing Context: No correlation IDs, timestamps, or structured metadata
  3. Hard to Filter: Unstructured logs difficult to query in log aggregation tools
  4. Production Visibility: Console logs may not appear in container logs properly

Phase 5g delivers:

  • All console statements replaced with structured logger
  • Consistent log format with correlation IDs
  • Proper log levels (debug, info, warn, error)
  • Metadata attached to all log entries

📋 Context: Technical Debt Item

TD-005: Console Logging in Production Code

Attribute Value
Type Code Debt
Priority High
Location Multiple files (observability, services)
Interest Rate Medium
Principal (Effort) 1 day

Current State

File Console Usage Context
apps/api/src/observability/instrument.ts 2 occurrences OpenTelemetry init
apps/web/src/observability/sentry.ts 2 occurrences Sentry init
Test files Many Expected (test output)

🛠️ Implementation Phases

Phase 1: Backend Console Replacement (2 hours)

Priority: Critical | Impact: High | Dependencies: None

1. Update instrument.ts

Update apps/api/src/observability/instrument.ts:

import { Logger } from '@nestjs/common';

const logger = new Logger('OpenTelemetry');

// Replace:
// console.log(`OpenTelemetry initialized for service: ${serviceName}`);

// With:
logger.log({
  message: 'OpenTelemetry initialized',
  serviceName,
  environment: process.env.NODE_ENV,
});

// Replace:
// console.warn('OpenTelemetry disabled - missing configuration');

// With:
logger.warn({
  message: 'OpenTelemetry disabled',
  reason: 'Missing configuration',
  requiredEnvVars: ['OTEL_EXPORTER_OTLP_ENDPOINT'],
});

2. Create Logging Guidelines Document

Create docs/04-development/logging-guidelines.md:

# Logging Guidelines

## Overview

This document establishes logging standards for the Forma3D.Connect codebase.

## Logger Usage

### Backend (NestJS)

Always use the NestJS Logger:

\`\`\`typescript
import { Logger } from '@nestjs/common';

@Injectable()
export class MyService {
  private readonly logger = new Logger(MyService.name);

  doSomething() {
    this.logger.log({
      message: 'Action performed',
      userId: 'user-123',
      durationMs: 150,
    });
  }
}
\`\`\`

### Log Levels

| Level | Use Case | Example |
|-------|----------|---------|
| `debug` | Detailed debugging info | Variable values, state changes |
| `log` | Normal operations | Request processed, job started |
| `warn` | Potential issues | Retry triggered, deprecated usage |
| `error` | Errors requiring attention | Failed operations, exceptions |

### Structured Log Format

Always use object format with a `message` field:

\`\`\`typescript
// Good
this.logger.log({
  message: 'Order processed',
  orderId: 'order-123',
  duration: 250,
});

// Avoid
this.logger.log('Order order-123 processed in 250ms');
\`\`\`

### Required Metadata

| Context | Required Fields |
|---------|-----------------|
| HTTP Request | `method`, `path`, `statusCode`, `duration` |
| Order Events | `orderId`, `shopifyOrderNumber`, `status` |
| Print Jobs | `printJobId`, `simplyPrintJobId`, `status` |
| Webhooks | `webhookId`, `topic`, `source` |
| Errors | `errorName`, `errorMessage`, `stack` |

### Forbidden Patterns`console.log()` - Bypasses structured logging
❌ `console.error()` - Use `logger.error()` instead
❌ String-only logs - Always use objects
❌ Logging sensitive data (API keys, passwords)

## Frontend Logging

The frontend uses Sentry for error tracking. For development logging:

\`\`\`typescript
// Development only - stripped in production
if (import.meta.env.DEV) {
  console.log('Debug info:', data);
}
\`\`\`

Phase 2: Frontend Console Cleanup (2 hours)

Priority: Medium | Impact: Medium | Dependencies: None

1. Update Sentry Integration

Update apps/web/src/observability/sentry.ts:

// For frontend, we can't use NestJS Logger
// Use conditional logging that gets stripped in production

const isDev = import.meta.env.DEV;

function logInfo(message: string, data?: Record<string, unknown>): void {
  if (isDev) {
    console.log(`[Sentry] ${message}`, data ?? '');
  }
  // In production, Sentry captures its own initialization
}

function logWarn(message: string, data?: Record<string, unknown>): void {
  if (isDev) {
    console.warn(`[Sentry] ${message}`, data ?? '');
  }
}

// Replace:
// console.log(`[Sentry] Initialized for environment: ${config.environment}`);

// With:
logInfo('Initialized', {
  environment: config.environment,
  dsn: config.dsn ? '[CONFIGURED]' : '[MISSING]',
});

// Replace:
// console.warn('[Sentry] Disabled - missing DSN');

// With:
logWarn('Disabled - missing DSN');

2. Create Development Logger Utility

Create apps/web/src/lib/dev-logger.ts:

/**
 * Development-only logger that gets stripped in production builds.
 * Use for debugging information that shouldn't appear in production.
 */

const isDev = import.meta.env.DEV;

export const devLog = {
  info: (message: string, data?: Record<string, unknown>) => {
    if (isDev) {
      console.log(`[DEV] ${message}`, data ?? '');
    }
  },

  warn: (message: string, data?: Record<string, unknown>) => {
    if (isDev) {
      console.warn(`[DEV] ${message}`, data ?? '');
    }
  },

  error: (message: string, error?: unknown) => {
    if (isDev) {
      console.error(`[DEV] ${message}`, error ?? '');
    }
    // In production, errors should go to Sentry
  },

  table: (data: unknown) => {
    if (isDev) {
      console.table(data);
    }
  },

  group: (label: string, fn: () => void) => {
    if (isDev) {
      console.group(label);
      fn();
      console.groupEnd();
    }
  },
};

Phase 3: Add ESLint Rule (1 hour)

Priority: High | Impact: High | Dependencies: None

1. Update ESLint Configuration

Update .eslintrc.json or relevant ESLint config:

{
  "rules": {
    "no-console": ["error", {
      "allow": ["warn", "error"]
    }]
  },
  "overrides": [
    {
      "files": ["*.spec.ts", "*.test.ts", "*.test.tsx", "*.e2e-spec.ts"],
      "rules": {
        "no-console": "off"
      }
    },
    {
      "files": ["apps/web/src/lib/dev-logger.ts", "apps/web/src/observability/*.ts"],
      "rules": {
        "no-console": "off"
      }
    }
  ]
}

Phase 4: Verify and Document (1 hour)

Priority: Medium | Impact: Medium | Dependencies: Phase 1-3

1. Search for Remaining Console Usage

# Find all console usage outside allowed files
rg "console\.(log|warn|error|info|debug)" \
  --type ts \
  --type tsx \
  -g '!*.spec.ts' \
  -g '!*.test.ts' \
  -g '!*.test.tsx' \
  -g '!**/test/**' \
  -g '!**/e2e/**' \
  -g '!dev-logger.ts'

2. Update Technical Debt Register

Update docs/04-development/techdebt/technical-debt-register.md:

### ~~TD-005: Console Logging in Production Code~~ ✅ RESOLVED

**Type:** Code Debt  
**Status:****Resolved in Phase 5g**  
**Resolution Date:** 2026-XX-XX

#### Resolution

Replaced all console.log statements with structured logging:

- **Backend**: Using NestJS Logger with structured objects
- **Frontend**: Dev-only logger utility for development debugging
- **ESLint**: `no-console` rule enforced with test file exceptions
- **Documentation**: Logging guidelines document created

**Files Created:**
- `docs/04-development/logging-guidelines.md`
- `apps/web/src/lib/dev-logger.ts`

**Files Modified:**
- `apps/api/src/observability/instrument.ts`
- `apps/web/src/observability/sentry.ts`
- `.eslintrc.json` - Added no-console rule

📁 Files to Create/Modify

New Files

docs/04-development/logging-guidelines.md
apps/web/src/lib/dev-logger.ts

Modified Files

apps/api/src/observability/instrument.ts
apps/web/src/observability/sentry.ts
.eslintrc.json                                    # Add no-console rule
docs/04-development/techdebt/technical-debt-register.md

✅ Validation Checklist

Phase 1: Backend

  • instrument.ts uses NestJS Logger
  • All console statements replaced
  • Structured log format used

Phase 2: Frontend

  • Sentry.ts uses conditional logging
  • dev-logger utility created
  • No production console output

Phase 3: ESLint

  • no-console rule enabled
  • Test files excluded
  • Dev logger file excluded
  • Lint passes

Final Verification

# Lint passes
pnpm nx lint api
pnpm nx lint web

# Search returns no results
rg "console\.log" apps/api/src --type ts -g '!*.spec.ts'

# Build passes
pnpm nx build api
pnpm nx build web

🚫 Constraints and Rules

MUST DO

  • Use NestJS Logger in backend
  • Use structured log format (objects with message field)
  • Add no-console ESLint rule
  • Document logging guidelines

MUST NOT

  • Use console.log in production code
  • Log sensitive information (API keys, passwords)
  • Skip error context in error logs
  • Remove console from test files

END OF PROMPT


This prompt resolves TD-005 from the technical debt register by replacing console.log statements with structured logging.