# error_log() to ordio_log() Migration Patterns


**Last Updated:** 2025-11-20

**Date:** 2025-11-17  
**Status:** Complete - Pattern Catalog

## Overview

This document catalogs all patterns encountered during the migration of 850+ `error_log()` calls to structured `ordio_log()` calls across the Ordio codebase.

## Log Level Decision Tree

```
Is it a fatal/system error?
├─ YES → CRITICAL
└─ NO → Is it an error that breaks functionality?
    ├─ YES → ERROR
    └─ NO → Is it a warning/validation failure?
        ├─ YES → WARN
        └─ NO → Is it normal operation info?
            ├─ YES → INFO
            └─ NO → Is it debugging/detailed flow?
                ├─ YES → DEBUG
                └─ NO → Default to INFO
```

## Pattern Catalog

### Pattern 1: Simple String Messages

**Before:**

```php
error_log("Processing request");
```

**After:**

```php
ordio_log('INFO', 'Processing request', [
    'request_id' => $this->requestId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Always include `request_id`, `endpoint`, and `function` for traceability.

---

### Pattern 2: Messages with String Interpolation

**Before:**

```php
error_log("Processing request for user: $userId");
```

**After:**

```php
ordio_log('INFO', 'Processing request for user', [
    'request_id' => $this->requestId,
    'user_id' => $userId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Extract variables from string interpolation into context array.

---

### Pattern 3: Error Messages with Exception Details

**Before:**

```php
error_log("Error occurred: " . $e->getMessage());
error_log("Stack trace: " . $e->getTraceAsString());
```

**After:**

```php
ordio_log('ERROR', 'Error occurred', [
    'request_id' => $this->requestId,
    'error' => $e->getMessage(),
    'exception_type' => get_class($e),
    'stack_trace' => $e->getTraceAsString(),
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Include exception type, message, and stack trace in context.

---

### Pattern 4: Debug Messages with Emoji Indicators

**Before:**

```php
error_log("📊 calculateShiftOpsScore() called");
error_log("✅ Analysis completed successfully");
error_log("⚠️ Warning: Something unexpected");
error_log("❌ Error in processing");
```

**After:**

```php
ordio_log('DEBUG', 'calculateShiftOpsScore() called', [
    'request_id' => $this->requestId,
    'endpoint' => 'shiftops',
    'function' => 'calculateShiftOpsScore'
]);

ordio_log('INFO', 'Analysis completed successfully', [
    'request_id' => $this->requestId,
    'endpoint' => 'shiftops',
    'function' => 'analyzeBusiness'
]);

ordio_log('WARN', 'Something unexpected', [
    'request_id' => $this->requestId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);

ordio_log('ERROR', 'Error in processing', [
    'request_id' => $this->requestId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Remove emoji indicators; use appropriate log levels instead.

---

### Pattern 5: Multi-line Log Messages

**Before:**

```php
error_log("   - Variable 1: $var1");
error_log("   - Variable 2: $var2");
error_log("   - Variable 3: $var3");
```

**After:**

```php
ordio_log('DEBUG', 'Multiple variables', [
    'request_id' => $this->requestId,
    'variable_1' => $var1,
    'variable_2' => $var2,
    'variable_3' => $var3,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Consolidate related log messages into a single structured log entry.

---

### Pattern 6: Conditional Logging

**Before:**

```php
if ($condition) {
    error_log("Condition met: $value");
}
```

**After:**

```php
if ($condition) {
    ordio_log('DEBUG', 'Condition met', [
        'request_id' => $this->requestId,
        'value' => $value,
        'endpoint' => 'endpoint-name',
        'function' => 'functionName'
    ]);
}
```

**Context:** Preserve conditional logic; add structured context.

---

### Pattern 7: API Call Logging

**Before:**

```php
error_log("Calling API: $endpoint");
error_log("Response: " . json_encode($response));
error_log("HTTP Code: $httpCode");
```

**After:**

```php
ordio_log('DEBUG', 'Calling API', [
    'request_id' => $this->requestId,
    'endpoint' => $endpoint,
    'endpoint_name' => 'endpoint-name',
    'function' => 'functionName'
]);

ordio_log('INFO', 'API response received', [
    'request_id' => $this->requestId,
    'http_code' => $httpCode,
    'response_keys' => is_array($response) ? array_keys($response) : 'N/A',
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Log API calls at DEBUG, responses at INFO. Include HTTP codes and response structure.

---

### Pattern 8: Error Handling in Try-Catch Blocks

**Before:**

```php
try {
    // Code here
} catch (Exception $e) {
    error_log("Error: " . $e->getMessage());
    error_log("Stack trace: " . $e->getTraceAsString());
}
```

**After:**

```php
try {
    // Code here
} catch (Exception $e) {
    ordio_log('ERROR', 'Exception caught', [
        'request_id' => $this->requestId,
        'error' => $e->getMessage(),
        'exception_type' => get_class($e),
        'stack_trace' => $e->getTraceAsString(),
        'endpoint' => 'endpoint-name',
        'function' => 'functionName'
    ]);
}
```

**Context:** Use ERROR level for exceptions. Include exception type and full stack trace.

---

### Pattern 9: Fatal Error Handling

**Before:**

```php
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        error_log("Fatal error: " . $error['message']);
    }
});
```

**After:**

```php
register_shutdown_function(function() use ($correlationId) {
    $error = error_get_last();
    if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        ordio_log('CRITICAL', 'Fatal error', [
            'correlation_id' => $correlationId,
            'error' => $error,
            'endpoint' => 'endpoint-name'
        ]);
    }
});
```

**Context:** Use CRITICAL level for fatal errors. Include correlation ID for request tracking.

---

### Pattern 10: Validation Failure Logging

**Before:**

```php
if (!$valid) {
    error_log("Validation failed: $reason");
}
```

**After:**

```php
if (!$valid) {
    ordio_log('WARN', 'Validation failed', [
        'request_id' => $this->requestId,
        'reason' => $reason,
        'endpoint' => 'endpoint-name',
        'function' => 'functionName'
    ]);
}
```

**Context:** Use WARN level for validation failures. Include reason in context.

---

### Pattern 11: Success/Completion Messages

**Before:**

```php
error_log("✅ Operation completed successfully");
error_log("Processed $count items");
```

**After:**

```php
ordio_log('INFO', 'Operation completed successfully', [
    'request_id' => $this->requestId,
    'items_processed' => $count,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Use INFO level for successful operations. Include relevant metrics.

---

### Pattern 12: Rate Limit and Retry Logging

**Before:**

```php
error_log("Rate limit hit, retrying in $seconds seconds");
error_log("Retry attempt $attempt of $maxAttempts");
```

**After:**

```php
ordio_log('WARN', 'Rate limit hit, retrying', [
    'request_id' => $this->requestId,
    'retry_after_seconds' => $seconds,
    'attempt' => $attempt,
    'max_attempts' => $maxAttempts,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Use WARN level for rate limits. Include retry details.

---

### Pattern 13: Data Structure Logging

**Before:**

```php
error_log("Data structure: " . json_encode($data));
error_log("Array keys: " . implode(', ', array_keys($data)));
```

**After:**

```php
ordio_log('DEBUG', 'Data structure', [
    'request_id' => $this->requestId,
    'keys' => is_array($data) ? array_keys($data) : 'N/A',
    'count' => is_array($data) ? count($data) : 'N/A',
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Log structure metadata, not full data (to avoid log bloat). Use DEBUG level.

---

### Pattern 14: Performance/Timing Logging

**Before:**

```php
error_log("Execution time: $time ms");
error_log("Memory usage: $memory MB");
```

**After:**

```php
ordio_log('DEBUG', 'Performance metrics', [
    'request_id' => $this->requestId,
    'execution_time_ms' => $time,
    'memory_usage_mb' => $memory,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Use DEBUG level for performance metrics. Include timing and memory data.

---

### Pattern 15: Configuration/Initialization Logging

**Before:**

```php
error_log("Initializing component: $componentName");
error_log("Configuration loaded: " . json_encode($config));
```

**After:**

```php
ordio_log('INFO', 'Initializing component', [
    'request_id' => $this->requestId,
    'component_name' => $componentName,
    'config_keys' => is_array($config) ? array_keys($config) : 'N/A',
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

**Context:** Use INFO level for initialization. Log config structure, not full values.

---

## Context Field Standards

### Required Fields

Every `ordio_log()` call should include:

1. **`request_id`** or **`correlation_id`**: Unique identifier for request tracking
2. **`endpoint`**: API endpoint name (e.g., 'shiftops', 'lead-capture')
3. **`function`**: Function/method name where logging occurs

### Optional Fields

Common optional fields based on context:

- **`error`**: Error message
- **`exception_type`**: Exception class name
- **`stack_trace`**: Full stack trace for errors
- **`http_code`**: HTTP status code
- **`place_id`**: Business/place identifier (ShiftOps specific)
- **`user_id`**: User identifier
- **`email`**: Email address (sanitized if sensitive)
- **`count`**, **`total`**, **`items_processed`**: Numeric metrics
- **`keys`**, **`array_keys`**: Array structure metadata
- **`latency_ms`**, **`execution_time_ms`**: Performance metrics
- **`memory_usage_mb`**: Memory consumption

## Edge Cases

### Case 1: Logging in Static Contexts

**Challenge:** No `$this->requestId` available in static functions.

**Solution:**

```php
ordio_log('INFO', 'Static function log', [
    'correlation_id' => getCorrelationId(), // Use global correlation ID
    'endpoint' => 'endpoint-name',
    'function' => 'staticFunctionName'
]);
```

### Case 2: Logging Before Request ID Generation

**Challenge:** Need to log before `$requestId` is created.

**Solution:**

```php
// Generate correlation ID early
$correlationId = 'LC-' . date('YmdHis') . '-' . bin2hex(random_bytes(4));

ordio_log('INFO', 'Early log', [
    'correlation_id' => $correlationId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

### Case 3: Sensitive Data

**Challenge:** Logging contains sensitive information (passwords, tokens, PII).

**Solution:**

```php
ordio_log('INFO', 'Processing request', [
    'request_id' => $this->requestId,
    'user_id' => $userId, // OK - not sensitive
    // 'password' => $password, // NEVER log passwords
    // 'api_token' => substr($token, 0, 4) . '...', // Mask tokens
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

### Case 4: Very Large Data Structures

**Challenge:** Logging entire arrays/objects causes log bloat.

**Solution:**

```php
ordio_log('DEBUG', 'Large data structure', [
    'request_id' => $this->requestId,
    'structure_type' => gettype($data),
    'keys' => is_array($data) ? array_keys($data) : 'N/A',
    'count' => is_array($data) ? count($data) : 'N/A',
    'sample' => is_array($data) ? array_slice($data, 0, 3) : null, // First 3 items only
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

### Case 5: Conditional Logging Based on Log Level

**Challenge:** Need to log only in development/debug mode.

**Solution:**

```php
// Logger.php handles log level filtering automatically
// Just use DEBUG level - it won't log in production if DEBUG is disabled
ordio_log('DEBUG', 'Detailed debug info', [
    'request_id' => $this->requestId,
    'endpoint' => 'endpoint-name',
    'function' => 'functionName'
]);
```

## Migration Checklist

For each file:

- [ ] Include `require_once __DIR__ . '/../helpers/logger.php';`
- [ ] Generate/obtain correlation ID or request ID
- [ ] Replace all `error_log()` calls with `ordio_log()`
- [ ] Assign appropriate log levels (DEBUG/INFO/WARN/ERROR/CRITICAL)
- [ ] Extract variables from string interpolation into context array
- [ ] Add required context fields (`request_id`, `endpoint`, `function`)
- [ ] Remove emoji indicators (use log levels instead)
- [ ] Consolidate multi-line related logs into single entries
- [ ] Sanitize sensitive data (passwords, tokens, PII)
- [ ] Test syntax: `php -l filename.php`
- [ ] Verify no `error_log()` calls remain: `grep -r "error_log(" filename.php`
- [ ] Test log output format
- [ ] Verify log levels are appropriate

## Common Mistakes to Avoid

1. **Forgetting context fields**: Always include `request_id`, `endpoint`, `function`
2. **Wrong log levels**: Using ERROR for warnings, INFO for debug info
3. **Logging sensitive data**: Passwords, API tokens, full credit card numbers
4. **Log bloat**: Logging entire large arrays instead of metadata
5. **Inconsistent naming**: Using `request_id` in some places, `correlation_id` in others
6. **Missing error details**: Not including exception type or stack trace
7. **String interpolation**: Keeping variables in strings instead of context array
8. **Emoji in messages**: Removing emoji but not adjusting log level appropriately

## Examples from Completed Migrations

### Example 1: Customer Matching (shiftops-customer-matcher.php)

**Before:**

```php
error_log("EXACT DOMAIN MATCH FOUND");
error_log("   - Customer: {$matchedName}");
error_log("   - Confidence: {$confidence}%");
```

**After:**

```php
ordio_log('INFO', 'EXACT DOMAIN MATCH FOUND', [
    'request_id' => $this->requestId,
    'matched_customer' => $matchedName,
    'confidence' => $confidence,
    'endpoint' => 'shiftops-customer-matcher',
    'function' => 'matchBusinessToCustomer'
]);
```

### Example 2: API Error Handling (shiftops.php)

**Before:**

```php
error_log("❌ Error in competitive analyzer: " . $e->getMessage());
error_log("   - Exception type: " . get_class($e));
error_log("   - Stack trace: " . $e->getTraceAsString());
```

**After:**

```php
ordio_log('ERROR', 'Error in competitive analyzer', [
    'request_id' => $this->requestId,
    'error' => $e->getMessage(),
    'exception_type' => get_class($e),
    'stack_trace' => $e->getTraceAsString(),
    'endpoint' => 'shiftops',
    'function' => 'analyzeCompetitivePosition'
]);
```

### Example 3: Performance Logging (shiftops.php)

**Before:**

```php
error_log("📊 calculateShiftOpsScore() called with isOrdioCustomer=" . ($isOrdioCustomer ? 'true' : 'false'));
error_log("   - Raw pillar scores: " . json_encode($rawScores));
```

**After:**

```php
ordio_log('DEBUG', 'calculateShiftOpsScore() called', [
    'request_id' => $this->requestId,
    'is_ordio_customer' => $isOrdioCustomer,
    'raw_scores' => $rawScores,
    'endpoint' => 'shiftops',
    'function' => 'calculateShiftOpsScore'
]);
```

## Statistics

- **Total files migrated**: 16
- **Total calls migrated**: 880+
- **Patterns cataloged**: 15
- **Edge cases documented**: 5
- **Common mistakes identified**: 8

### Files Completed

1. `v2/api/collect-lead.php` - 12 calls
2. `v2/api/shiftops-nps.php` - 8 calls
3. `v2/api/shiftops-cost-calculator.php` - 1 call
4. `v2/api/shiftops-competitive-analyzer.php` - 5 calls
5. `v2/api/shiftops-hubspot-customers.php` - 3 calls
6. `v2/api/export-workdays.php` - 4 calls
7. `v2/api/addon-request.php` - 5 calls
8. `v2/api/submit-template.php` - 6 calls
9. `v2/api/shiftops-hubspot.php` - 34 calls
10. `v2/api/shiftops-recommendations-engine.php` - 43 calls
11. `v2/api/generate_excel.php` - 71 calls
12. `v2/api/shiftops-customer-matcher.php` - 126 calls
13. `v2/api/lead-capture.php` - 173 calls
14. `v2/api/shiftops.php` - 233 calls
15. `v2/api/webinar-registration.php` - 12 calls
16. `v2/api/payroll-webinar-registration.php` - 16 calls

## Next Steps

1. ✅ Migrate remaining files (webinar-registration.php, payroll-webinar-registration.php) - **COMPLETED**
2. Create test script for validation
3. Performance analysis
4. Final documentation
