> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://cla-demo.postmansolutions.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://cla-demo.postmansolutions.com/_mcp/server.

# Implement logging in web api documents ai

## Business Value

* **Structured Logging**: Consistent log format across all services for easier parsing and analysis

* **Log Levels**: Proper severity levels (debug, info, warn, error) for better filtering and alerting

* **Centralized Logging**: Integration with Azure Monitor and Application Insights

* **Performance**: Better performance compared to console.log in production environments

* **Debugging**: Child loggers with request context for better traceability

* **Standards Compliance**: Alignment with organizational logging standards



---

## Current State

### Existing Console.log Usage

The DocumentAI service currently uses `console.log` statements throughout the codebase:

```typescript

// Example patterns currently in use  

console.log('Processing document:', documentId);  

console.error('Failed to process document:', error);  

console.warn('Missing optional field:', fieldName);  

console.debug('Debug information:', data);  

  

// In controllers  

console.log('Received request:', req.body);  

  

// In services  

console.log('Calling OpenAI API...');  

console.error('OpenAI API failed:', error.message);  

  

// In utilities  

console.log('Validating document format');  

```

### Issues with Current Approach

1. No structured logging format - difficult to parse logs programmatically

2. No log level filtering - all logs output at same level

3. No request correlation - cannot trace logs across a request lifecycle

4. Poor integration with Azure Monitor and Application Insights

5. Inconsistent log formatting across the application

6. No child logger support for request context

7. Console output not optimized for production environments

---

## Future State

### Framework Logger Implementation

All logging will use the framework logger pattern established in `fluxx-file-intake`:

```typescript

import { app } from './internal/app.js';  

  

// Instead of: console.log('Processing document:', documentId);  

app.log.info(`Processing document: ${documentId}`);  

  

// Instead of: console.error('Failed to process:', error);  

app.log.error('Failed to process document', error);  

  

// Instead of: console.warn('Missing field:', field);  

app.log.warn(`Missing optional field: ${field}`);  

  

// Instead of: console.debug('Debug data:', data);  

app.log.debug(`Debug information: ${JSON.stringify(data)}`);  

```

### Log Levels

* **debug**: Detailed diagnostic information for troubleshooting

* **info**: General informational messages about application flow

* **warn**: Warning messages for potentially problematic situations

* **error**: Error messages for failures and exceptions

---

## Design

### Architecture Pattern

Follow the exact pattern established in the `fluxx-file-intake` repository:

#### 1. **Logger Type Definition**

Define a Logger interface that picks specific methods from the framework logger.

#### 2. **App Container**

Central app container that holds the logger instance and makes it available throughout the application.

#### 3. **Missing Logger Placeholder**

Placeholder that throws errors if logger is accessed before initialization.

#### 4. **Logger Registration**

Function to register the framework logger into the app container during startup.

#### 5. **Usage Pattern**

Import and use `app.log` throughout the codebase for all logging operations.

### Component Architecture

```

┌─────────────────────────────────────────────────────────────┐  

│                    DocumentAI Service                        │  

├─────────────────────────────────────────────────────────────┤  

│                                                               │  

│  ┌──────────────────────────────────────────────────────┐   │  

│  │              app.log.info/warn/error/debug           │   │  

│  │              Unified Logging API                      │   │  

│  └────────────────────┬─────────────────────────────────┘   │  

│                       │                                      │  

│                       │                                      │  

│  ┌────────────────────▼─────────────────────────────────┐   │  

│  │         Framework Logger (Pino-based)                │   │  

│  │         - Structured JSON output                     │   │  

│  │         - Log level filtering                        │   │  

│  │         - Child logger support                       │   │  

│  └────────────────────┬─────────────────────────────────┘   │  

│                       │                                      │  

│  ┌────────────────────▼─────────────────────────────────┐   │  

│  │         Azure Application Insights                   │   │  

│  │         Azure Monitor Logs                           │   │  

│  └──────────────────────────────────────────────────────┘   │  

└─────────────────────────────────────────────────────────────┘  

```

---

## Implementation Steps

### Phase 1: Setup Logger Infrastructure

#### Task 1.1: Create Logger Type Definition

**File**: `src/internal/Logger.d.ts`

```typescript

import { Logger as FrameworkLogger } from 'framework-nodejs-extensions';  

  

export type Logger = Pick<FrameworkLogger, 'debug' | 'info' | 'warn' | 'error'>;  

```

**Rationale**: Define a TypeScript interface that extracts only the logging methods we need from the framework logger. This provides type safety and clear contract.

#### Task 1.2: Create Missing Logger Placeholder

**File**: `src/internal/frameworkAdapter/missingLogger.ts`

```typescript

import { Logger } from '../Logger.d.js';  

  

const throwMissingLogger = () => {  

    throw Error('Attempted to use the application log before one was registered');  

};  

  

export const missingLogger: Logger = {  

    debug: throwMissingLogger,  

    error: throwMissingLogger,  

    info: throwMissingLogger,  

    warn: throwMissingLogger,  

};  

```

**Rationale**: Provides fail-fast behavior if code attempts to log before the logger is properly initialized. This prevents silent failures and makes initialization errors obvious.

#### Task 1.3: Create Register Logger Function

**File**: `src/internal/frameworkAdapter/registerLogger.ts`

```typescript

import { app } from '../app.js';  

import { Logger } from '../Logger.d.js';  

  

export const registerLogger = (frameworkApp: FrameworkAppWithLogger): void => {  

    if (!frameworkApp.server) {  

        throw Error('The logger registration attempted to register a logger from an app with no "server" assigned');  

    }  

    if (!frameworkApp.server.log) {  

        throw Error('The logger registration attempted to register a logger from an app & server with no "log" assigned');  

    }  

    app.log = frameworkApp.server.log;  

};  

  

interface FrameworkAppWithLogger {  

    server?: { log?: Logger }  

}  

```

**Rationale**: Safely extracts the framework logger and assigns it to our app container with proper validation to ensure the framework is fully initialized.

#### Task 1.4: Create App Container

**File**: `src/internal/app.ts`

```typescript

import { Logger } from './Logger.d.js';  

import { missingLogger } from './frameworkAdapter/missingLogger.js';  

import { registerLogger } from './frameworkAdapter/registerLogger.js';  

  

// A holder for all the important parts of the app that are attached to the  

// frameworkApp in a DI container or as a property such as the logger  

export const app: App = {  

    log: missingLogger,  

    registerLogger,  

};  

  

interface App {  

    log: Logger;  

    registerLogger: typeof registerLogger;  

}  

```

**Rationale**: Central container that provides application-wide access to the logger. Uses the missing logger as default until properly initialized.

---

### Phase 2: Update Application Entry Point

#### Task 2.1: Initialize Logger in index.ts

**File**: `src/index.ts`

Add after framework initialization:

```typescript

import { DevFramework } from 'framework-nodejs-core';  

import { app } from './internal/app.js';  

  

// Initialize the framework  

const frameworkApp = await DevFramework.create();  

  

// Register the logger (add this line)  

app.registerLogger(frameworkApp);  

  

// Log application startup  

app.log.info('DocumentAI service starting...');  

  

// Continue with rest of initialization...  

```

**Rationale**: Register the logger as early as possible in the application lifecycle so it's available for all subsequent initialization steps.

---

### Phase 3: Migrate Console.log Statements

#### Task 3.1: Create Migration Checklist

Identify all files with console.log usage:

```bash

# Run this command to find all console.log usage  

grep -r "console\." src/ --include="*.ts"  

```

Common patterns to migrate:

\| Current Pattern | New Pattern | Log Level |

\|----------------|-------------|-----------|

\| `console.log('Info message')` | `app.log.info('Info message')` | info |

\| `console.error('Error:', error)` | `app.log.error('Error message', error)` | error |

\| `console.warn('Warning')` | `app.log.warn('Warning message')` | warn |

\| `console.debug('Debug')` | `app.log.debug('Debug message')` | debug |

\| `console.log('Starting...')` | `app.log.info('Starting...')` | info |

#### Task 3.2: Migrate Controllers

**Example transformation in a controller file:**

**Before:**

```typescript

export class DocumentController {  

    async processDocument(req: Request, res: Response) {  

        console.log('Received document processing request:', req.body);  

         

        try {  

            console.log('Processing document ID:', documentId);  

            // ... processing logic  

            console.log('Document processed successfully');  

            return result;  

        } catch (error) {  

            console.error('Failed to process document:', error);  

            throw error;  

        }  

    }  

}  

```

**After:**

```typescript

import { app } from '../internal/app.js';  

  

export class DocumentController {  

    async processDocument(req: Request, res: Response) {  

        app.log.info('Received document processing request', { body: req.body });  

         

        try {  

            app.log.info(`Processing document ID: ${documentId}`);  

            // ... processing logic  

            app.log.info('Document processed successfully');  

            return result;  

        } catch (error) {  

            app.log.error('Failed to process document', error);  

            throw error;  

        }  

    }  

}  

```

#### Task 3.3: Migrate Services

**Example transformation in OpenAI service:**

**Before:**

```typescript

export class OpenAIService {  

    async callOpenAI(prompt: string) {  

        console.log('Calling OpenAI API with prompt:', prompt);  

         

        try {  

            const response = await this.client.chat.completions.create({  

                model: this.model,  

                messages: [{ role: 'user', content: prompt }],  

            });  

            console.log('OpenAI API response received');  

            return response;  

        } catch (error) {  

            console.error('OpenAI API call failed:', error);  

            throw error;  

        }  

    }  

}  

```

**After:**

```typescript

import { app } from '../internal/app.js';  

  

export class OpenAIService {  

    async callOpenAI(prompt: string) {  

        app.log.info('Calling OpenAI API', { promptLength: prompt.length });  

         

        try {  

            const response = await this.client.chat.completions.create({  

                model: this.model,  

                messages: [{ role: 'user', content: prompt }],  

            });  

            app.log.info('OpenAI API response received');  

            return response;  

        } catch (error) {  

            app.log.error('OpenAI API call failed', error);  

            throw error;  

        }  

    }  

}  

```

#### Task 3.4: Migrate Form Recognizer Service

**Before:**

```typescript

export class FormRecognizerService {  

    async analyzeDocument(documentUrl: string) {  

        console.log('Analyzing document:', documentUrl);  

        console.debug('Using model:', this.modelId);  

         

        try {  

            const poller = await this.client.beginAnalyzeDocument(this.modelId, documentUrl);  

            console.log('Analysis started, polling for results...');  

            const result = await poller.pollUntilDone();  

            console.log('Document analysis completed');  

            return result;  

        } catch (error) {  

            console.error('Document analysis failed:', error.message);  

            throw error;  

        }  

    }  

}  

```

**After:**

```typescript

import { app } from '../internal/app.js';  

  

export class FormRecognizerService {  

    async analyzeDocument(documentUrl: string) {  

        app.log.info(`Analyzing document: ${documentUrl}`);  

        app.log.debug(`Using model: ${this.modelId}`);  

         

        try {  

            const poller = await this.client.beginAnalyzeDocument(this.modelId, documentUrl);  

            app.log.info('Analysis started, polling for results...');  

            const result = await poller.pollUntilDone();  

            app.log.info('Document analysis completed');  

            return result;  

        } catch (error) {  

            app.log.error('Document analysis failed', error);  

            throw error;  

        }  

    }  

}  

```

#### Task 3.5: Migrate Utility Functions

**Before:**

```typescript

export const validateDocumentFormat = (document: Document): boolean => {  

    console.log('Validating document format');  

     

    if (!document.type) {  

        console.warn('Document type is missing');  

        return false;  

    }  

     

    if (!SUPPORTED_TYPES.includes(document.type)) {  

        console.error('Unsupported document type:', document.type);  

        return false;  

    }  

     

    console.log('Document format valid');  

    return true;  

};  

```

**After:**

```typescript

import { app } from '../internal/app.js';  

  

export const validateDocumentFormat = (document: Document): boolean => {  

    app.log.debug('Validating document format');  

     

    if (!document.type) {  

        app.log.warn('Document type is missing');  

        return false;  

    }  

     

    if (!SUPPORTED_TYPES.includes(document.type)) {  

        app.log.error(`Unsupported document type: ${document.type}`);  

        return false;  

    }  

     

    app.log.debug('Document format valid');  

    return true;  

};  

```

#### Task 3.6: Update Middleware

**Before:**

```typescript

export const requestLogger = (req: Request, res: Response, next: NextFunction) => {  

    console.log(`${req.method} ${req.path}`);  

    console.log('Headers:', req.headers);  

    next();  

};  

```

**After:**

```typescript

import { app } from '../internal/app.js';  

  

export const requestLogger = (req: Request, res: Response, next: NextFunction) => {  

    app.log.info(`${req.method} ${req.path}`);  

    app.log.debug('Request headers', { headers: req.headers });  

    next();  

};  

```

---

### Phase 4: Remove All Console.log Statements

#### Task 4.1: Add ESLint Rule

**File**: `eslint.config.js` or `.eslintrc.json`

```json

{  

  "rules": {  

    "no-console": ["error", {  

      "allow": []  

    }]  

  }  

}  

```

**Rationale**: Prevent future console.log usage by enforcing the rule at the linting level.

#### Task 4.2: Verify No Console.log Remains

```bash

# This should return 0 results after migration  

grep -r "console\." src/ --include="*.ts"  

npm run lint  

```

---

## Testing Strategy

### Unit Tests

#### Test 1: Missing Logger Throws Error

**File**: `test/unit/internal/frameworkAdapter/missingLogger.test.ts`

```typescript

import { expect, it } from 'vitest';  

import { missingLogger as subject } from '../../../../src/internal/frameworkAdapter/missingLogger.js';  

  

it('must throw when trying to log at debug level', async () => {  

    expect(subject.debug).toThrowError(/log/i);  

    expect(subject.debug).toThrowError(/registered/i);  

});  

  

it('must throw when trying to log at info level', async () => {  

    expect(subject.info).toThrowError(/log/i);  

    expect(subject.info).toThrowError(/registered/i);  

});  

  

it('must throw when trying to log at warn level', async () => {  

    expect(subject.warn).toThrowError(/log/i);  

    expect(subject.warn).toThrowError(/registered/i);  

});  

  

it('must throw when trying to log at error level', async () => {  

    expect(subject.error).toThrowError(/log/i);  

    expect(subject.error).toThrowError(/registered/i);  

});  

```

**Purpose**: Verify that attempting to use the logger before registration throws descriptive errors.

#### Test 2: Logger Registration Success

**File**: `test/unit/internal/frameworkAdapter/registerLogger.test.ts`

```typescript

import { afterEach, expect, it, vi } from 'vitest';  

import { mock } from 'vitest-mock-extended';  

import { registerLogger as subject } from '../../../../src/internal/frameworkAdapter/registerLogger.js';  

import { app } from '../../../../src/internal/app.js';  

  

afterEach(() => {  

    vi.restoreAllMocks();  

});  

  

it('must register Logger from the framework app.server if it is available', async () => {  

    const mockFrameworkLogger = mock<typeof app.log>();  

  

    subject({ server: { log: mockFrameworkLogger } });  

  

    expect(app.log).toBe(mockFrameworkLogger);  

});  

  

it('must throw if the app.server.log is undefined', async () => {  

    expect(() => subject({ server: {} })).toThrowError();  

});  

  

it('must throw if the app.server is undefined', async () => {  

    expect(() => subject({})).toThrowError();  

});  

```

**Purpose**: Verify that logger registration correctly assigns the framework logger and validates inputs.

#### Test 3: Logger Usage in Services

**File**: `test/unit/services/openAIService.test.ts`

```typescript

import { describe, it, expect, vi, beforeEach } from 'vitest';  

import { OpenAIService } from '../../../src/services/openAIService.js';  

import { app } from '../../../src/internal/app.js';  

  

describe('OpenAIService', () => {  

    beforeEach(() => {  

        vi.spyOn(app.log, 'info').mockImplementation(() => {});  

        vi.spyOn(app.log, 'error').mockImplementation(() => {});  

    });  

  

    it('should log info when calling OpenAI API', async () => {  

        const service = new OpenAIService();  

        await service.callOpenAI('test prompt');  

  

        expect(app.log.info).toHaveBeenCalledWith(  

            expect.stringContaining('Calling OpenAI API'),  

            expect.any(Object)  

        );  

    });  

  

    it('should log error when OpenAI API fails', async () => {  

        const service = new OpenAIService();  

        const error = new Error('API Error');  

         

        // Mock API to throw error  

        vi.spyOn(service.client.chat.completions, 'create').mockRejectedValue(error);  

  

        await expect(service.callOpenAI('test')).rejects.toThrow();  

        expect(app.log.error).toHaveBeenCalledWith('OpenAI API call failed', error);  

    });  

});  

```

**Purpose**: Verify that services correctly use the logger and log at appropriate levels.

#### Test 4: Logger Usage in Controllers

**File**: `test/unit/controllers/documentController.test.ts`

```typescript

import { describe, it, expect, vi, beforeEach } from 'vitest';  

import { DocumentController } from '../../../src/controllers/documentController.js';  

import { app } from '../../../src/internal/app.js';  

  

describe('DocumentController', () => {  

    let logInfoSpy: ReturnType<typeof vi.spyOn>;  

    let logErrorSpy: ReturnType<typeof vi.spyOn>;  

  

    beforeEach(() => {  

        logInfoSpy = vi.spyOn(app.log, 'info').mockImplementation(() => {});  

        logErrorSpy = vi.spyOn(app.log, 'error').mockImplementation(() => {});  

    });  

  

    it('should log request received at info level', async () => {  

        const controller = new DocumentController();  

        const mockRequest = { body: { documentId: '123' } };  

  

        await controller.processDocument(mockRequest, mockResponse);  

  

        expect(logInfoSpy).toHaveBeenCalledWith(  

            'Received document processing request',  

            expect.any(Object)  

        );  

    });  

  

    it('should log errors at error level', async () => {  

        const controller = new DocumentController();  

        const error = new Error('Processing failed');  

         

        // Mock service to throw error  

        vi.spyOn(controller.service, 'process').mockRejectedValue(error);  

  

        await expect(controller.processDocument(mockRequest, mockResponse)).rejects.toThrow();  

        expect(logErrorSpy).toHaveBeenCalledWith('Failed to process document', error);  

    });  

});  

```

**Purpose**: Verify that controllers log requests and errors appropriately.

#### Test 5: No Console.log Usage

**File**: `test/unit/noConsoleLogs.test.ts`

```typescript

import { describe, it, expect } from 'vitest';  

import { execSync } from 'child_process';  

  

describe('Console.log Usage', () => {  

    it('should not contain any console.log statements in source code', () => {  

        try {  

            // This will throw if grep finds matches  

            execSync('grep -r "console\\." src/ --include="*.ts"', { encoding: 'utf-8' });  

            expect.fail('Found console.log statements in source code');  

        } catch (error) {  

            // grep returns non-zero exit code when no matches found (expected)  

            if (error.status === 1) {  

                expect(true).toBe(true);  

            } else {  

                throw error;  

            }  

        }  

    });  

  

    it('should have no-console ESLint rule enabled', () => {  

        const eslintConfig = require('../../../eslint.config.ts');  

        const noConsoleRule = eslintConfig.rules['no-console'];  

         

        expect(noConsoleRule).toBeDefined();  

        expect(noConsoleRule[0]).toBe('error');  

    });  

});  

```

**Purpose**: Automated verification that no console.log statements exist after migration.

### Integration Tests

#### Test 6: Logger Integration with Framework

**File**: `test/integration/logger.test.ts`

```typescript

import { describe, it, expect, beforeAll } from 'vitest';  

import { DevFramework } from 'framework-nodejs-core';  

import { app } from '../../src/internal/app.js';  

  

describe('Logger Integration', () => {  

    beforeAll(async () => {  

        const frameworkApp = await DevFramework.create();  

        app.registerLogger(frameworkApp);  

    });  

  

    it('should have a registered logger after initialization', () => {  

        expect(app.log).toBeDefined();  

        expect(app.log.info).toBeDefined();  

        expect(app.log.error).toBeDefined();  

        expect(app.log.warn).toBeDefined();  

        expect(app.log.debug).toBeDefined();  

    });  

  

    it('should not throw when logging at any level', () => {  

        expect(() => app.log.info('Test info')).not.toThrow();  

        expect(() => app.log.warn('Test warn')).not.toThrow();  

        expect(() => app.log.error('Test error')).not.toThrow();  

        expect(() => app.log.debug('Test debug')).not.toThrow();  

    });  

  

    it('should support logging with objects', () => {  

        expect(() => app.log.info('Test with object', { key: 'value' })).not.toThrow();  

    });  

});  

```

**Purpose**: Verify that the logger integrates correctly with the framework and supports all required operations.

### Contract Tests

#### Test 7: Logger Output Format

**File**: `test/contract/logger-format.test.ts`

```typescript

import { describe, it, expect, beforeAll } from 'vitest';  

import { DevFramework } from 'framework-nodejs-core';  

import { app } from '../../src/internal/app.js';  

  

describe('Logger Output Format', () => {  

    beforeAll(async () => {  

        const frameworkApp = await DevFramework.create();  

        app.registerLogger(frameworkApp);  

    });  

  

    it('should produce structured JSON logs', () => {  

        // Capture log output  

        const logSpy = vi.spyOn(process.stdout, 'write');  

         

        app.log.info('Test message');  

         

        const logOutput = logSpy.mock.calls[0][0].toString();  

        const logJson = JSON.parse(logOutput);  

         

        expect(logJson).toHaveProperty('level');  

        expect(logJson).toHaveProperty('time');  

        expect(logJson).toHaveProperty('msg');  

        expect(logJson.msg).toBe('Test message');  

         

        logSpy.mockRestore();  

    });  

  

    it('should include error stack traces in error logs', () => {  

        const logSpy = vi.spyOn(process.stdout, 'write');  

        const testError = new Error('Test error');  

         

        app.log.error('Error occurred', testError);  

         

        const logOutput = logSpy.mock.calls[0][0].toString();  

        const logJson = JSON.parse(logOutput);  

         

        expect(logJson).toHaveProperty('err');  

        expect(logJson.err).toHaveProperty('stack');  

        expect(logJson.err.message).toBe('Test error');  

         

        logSpy.mockRestore();  

    });  

});  

```

**Purpose**: Verify that the logger produces correctly formatted structured logs.

---

## Acceptance Criteria

### Functional Requirements

* [ ] All `console.log` statements replaced with `app.log.info`

* [ ] All `console.error` statements replaced with `app.log.error`

* [ ] All `console.warn` statements replaced with `app.log.warn`

* [ ] All `console.debug` statements replaced with `app.log.debug`

* [ ] Logger initialized in application entry point (`index.ts`)

* [ ] Logger available throughout application via `app.log`

* [ ] Structured JSON logs produced in all environments

* [ ] Error objects logged with full stack traces

### Technical Requirements

* [ ] `Logger.d.ts` type definition created

* [ ] `missingLogger.ts` placeholder created

* [ ] `registerLogger.ts` function created

* [ ] `app.ts` container updated with logger

* [ ] No direct `console.*` usage in source code

* [ ] TypeScript type safety maintained for logger

* [ ] Logger registered before any logging operations

* [ ] Proper error handling for missing logger

### Code Quality Requirements

* [ ] ESLint `no-console` rule enabled and enforced

* [ ] All files import `app.log` where logging is needed

* [ ] Log messages are clear and descriptive

* [ ] Appropriate log levels used (debug for verbose, info for general, warn for issues, error for failures)

* [ ] Sensitive data not logged (passwords, tokens, PII)

* [ ] Log messages follow consistent format and style

### Testing Requirements

* [ ] Unit tests for `missingLogger` implementation

* [ ] Unit tests for `registerLogger` function

* [ ] Unit tests verify services use logger correctly

* [ ] Unit tests verify controllers use logger correctly

* [ ] Integration tests verify logger framework integration

* [ ] Contract tests verify structured log format

* [ ] Automated test to verify no console.log usage

* [ ] All tests pass in CI/CD pipeline

* [ ] Test coverage > 80% for logger modules

### Documentation Requirements

* [ ] Code comments explain logger initialization

* [ ] README updated with logging guidelines

* [ ] Examples of proper log usage documented

* [ ] Migration completed and documented

* [ ] Team trained on new logging pattern

### Deployment Requirements

* [ ] Application starts successfully with new logger

* [ ] Logs appear in Azure Application Insights

* [ ] Log levels filter correctly in production

* [ ] No performance degradation from logging

* [ ] Rollback plan documented

---

## Non-Functional Requirements

### Performance

* Logger operations should add \< 1ms overhead per log statement

* Structured logging should not significantly impact throughput

* Application startup time should not increase > 100ms

### Reliability

* Application should fail fast with clear error if logger not initialized

* Logger should handle errors gracefully without crashing application

* Logging failures should not prevent application operation

### Observability

* All logs should include timestamp, level, and message

* Error logs should include stack traces

* Logs should be parseable by Azure Monitor

* Log correlation IDs should be preserved across async operations

### Maintainability

* Log statements should be self-documenting

* Log levels should be consistent across codebase

* Adding logging should be straightforward for new developers

* Logger interface should remain stable across updates

---

## Migration Patterns

### Pattern 1: Simple Info Logs

```typescript

// Before  

console.log('Operation completed');  

  

// After  

app.log.info('Operation completed');  

```

### Pattern 2: Logs with Variables

```typescript

// Before  

console.log('Processing user:', userId);  

  

// After  

app.log.info(`Processing user: ${userId}`);  

```

### Pattern 3: Error Logging

```typescript

// Before  

console.error('Failed to save:', error);  

  

// After  

app.log.error('Failed to save document', error);  

```

### Pattern 4: Structured Logging with Context

```typescript

// Before  

console.log('User action:', action, 'by user:', userId);  

  

// After  

app.log.info('User action completed', { action, userId });  

```

### Pattern 5: Debug Logging

```typescript

// Before  

console.debug('Cache state:', cacheState);  

  

// After  

app.log.debug(`Cache state: ${JSON.stringify(cacheState)}`);  

```

### Pattern 6: Warning Logs

```typescript

// Before  

console.warn('Deprecated API used');  

  

// After  

app.log.warn('Deprecated API used - please migrate to v2');  

```

---

## Risks and Mitigation

### Risk 1: Missing Console.log Statements

**Impact**: Medium - Some logs might be missed during migration

**Mitigation**:

* Use grep/ripgrep to find all console.\* usage

* Enable ESLint no-console rule to prevent regressions

* Code review checklist includes verification

* Automated test to detect console.log usage

### Risk 2: Incorrect Log Levels

**Impact**: Low - Logs at wrong severity level

**Mitigation**:

* Document log level guidelines clearly

* Code review focuses on appropriate log levels

* Team training on when to use each level

### Risk 3: Performance Impact

**Impact**: Low - Increased logging overhead

**Mitigation**:

* Benchmark logging performance

* Use appropriate log levels (avoid excessive debug logs)

* Configure log levels per environment

### Risk 4: Breaking Changes During Migration

**Impact**: Medium - Application errors during deployment

**Mitigation**:

* Test thoroughly in development environment

* Gradual migration file by file

* Maintain backward compatibility during transition

* Feature flag for new logger if needed

---

## Rollout Plan

### Phase 1: Infrastructure (Week 1)

1. Create logger infrastructure files

2. Update application entry point

3. Deploy to development environment

4. Verify logger initialization

5. Run integration tests

### Phase 2: Core Services Migration (Week 2)

1. Migrate OpenAI service

2. Migrate Form Recognizer service

3. Migrate storage services

4. Test services individually

5. Deploy to QA environment

### Phase 3: Controllers and Utilities (Week 3)

1. Migrate controllers

2. Migrate utility functions

3. Migrate middleware

4. Enable ESLint no-console rule

5. Deploy to UAT environment

### Phase 4: Cleanup and Production (Week 4)

1. Verify no console.log statements remain

2. Complete all tests

3. Update documentation

4. Deploy to production

5. Monitor logs in Azure Application Insights

---

## Success Metrics

### Technical Metrics

* Zero console.log statements in source code

* 100% of log statements use framework logger

* All log levels used appropriately (info, warn, error, debug)

* Log output appears in Azure Application Insights

* No logging-related runtime errors

### Quality Metrics

* Test coverage > 80% for logger modules

* All unit tests passing

* All integration tests passing

* ESLint no-console rule enforced

* Code review approval on all changes

### Operational Metrics

* Application starts successfully with new logger

* Logs searchable in Azure Monitor

* Log queries work with structured format

* No performance degradation (\< 5% overhead)

* Mean time to find logs reduced by 50%

### Team Metrics

* All developers trained on new logging pattern

* Documentation complete and reviewed

* Zero questions about how to log after training

* Consistent log usage across all new code

---

## Related Documentation

* [Pino Logger Documentation](https://getpino.io/)

* [Framework-nodejs-logging Guide](https://github.com/your-org/framework-nodejs-logging)

* [Azure Application Insights Integration](https://learn.microsoft.com/en-us/azure/azure-monitor/app/nodejs)

* [Structured Logging Best Practices](https://www.loggly.com/ultimate-guide/node-logging-basics/)

---

## Checklist for Developers

During migration, verify each file:

* [ ] Replace all `console.log` with `app.log.info`

* [ ] Replace all `console.error` with `app.log.error`

* [ ] Replace all `console.warn` with `app.log.warn`

* [ ] Replace all `console.debug` with `app.log.debug`

* [ ] Import `app` from `./internal/app.js`

* [ ] Use appropriate log level for message severity

* [ ] Include context objects for structured logging

* [ ] Do not log sensitive data (passwords, tokens, PII)

* [ ] Test that logging works correctly

* [ ] Verify ESLint passes with no console warnings

---

## Story Points: 8

**Complexity**: Medium

**Effort**: 2 weeks

**Dependencies**: Framework-nodejs-logging, Framework-nodejs-extensions

---

## Definition of Done

* [ ] All acceptance criteria met

* [ ] All console.log statements migrated

* [ ] Code reviewed and approved

* [ ] All tests passing (unit, integration, contract)

* [ ] ESLint no-console rule enabled

* [ ] Deployed to all environments

* [ ] Documentation updated

* [ ] Team training completed

* [ ] Logs visible in Azure Application Insights

* [ ] Post-deployment validation successful

* [ ] Stakeholder sign-off received