# Logging Best Practices


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

**Date:** 2025-11-17  
**Status:** Active Guidelines

## Overview

Best practices for using structured logging (`ordio_log()`) across the Ordio codebase.

## Log Level Guidelines

### DEBUG

Use for detailed diagnostic information, typically only of interest during development or troubleshooting.

**Examples:**

- Function entry/exit
- Variable values
- Flow control decisions
- Detailed API request/response data

```php
ordio_log('DEBUG', 'Processing request', [
    'correlation_id' => $correlationId,
    'user_id' => $userId,
    'request_data' => $requestData,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### INFO

Use for general informational messages about normal application flow.

**Examples:**

- Successful operations
- Request processing start/end
- Configuration loaded
- User actions (non-sensitive)

```php
ordio_log('INFO', 'Request processed successfully', [
    'correlation_id' => $correlationId,
    'duration_ms' => $duration,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### WARN

Use for warning messages about unexpected but handled conditions.

**Examples:**

- Validation failures
- Rate limit warnings
- Retry attempts
- Deprecated feature usage
- Missing optional data

```php
ordio_log('WARN', 'Rate limit approaching', [
    'correlation_id' => $correlationId,
    'remaining_requests' => $remaining,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### ERROR

Use for error conditions that affect functionality but don't stop the application.

**Examples:**

- API call failures
- Processing errors
- Exception handling
- Business logic failures

```php
ordio_log('ERROR', 'API call failed', [
    'correlation_id' => $correlationId,
    'error' => $e->getMessage(),
    'exception_type' => get_class($e),
    'stack_trace' => $e->getTraceAsString(),
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### CRITICAL

Use for critical error conditions that require immediate attention.

**Examples:**

- System failures
- Configuration errors
- Fatal errors
- Security issues
- Data corruption

```php
ordio_log('CRITICAL', 'Fatal error occurred', [
    'correlation_id' => $correlationId,
    'error' => $error,
    'endpoint' => 'api-name'
]);
```

## Context Field Standards

### Required Fields

Every `ordio_log()` call **must** include:

1. **`endpoint`**: API endpoint name (e.g., 'shiftops', 'lead-capture')
2. **`correlation_id` or `request_id`**: Unique identifier for request tracking

### Recommended Fields

Include when available:

1. **`function`**: Function/method name where logging occurs
2. **`user_id`**: User identifier (if applicable)
3. **`email`**: Email address (sanitized if sensitive)
4. **`place_id`**: Business/place identifier (ShiftOps specific)

### Optional Fields

Include based on context:

- **`error`**: Error message
- **`exception_type`**: Exception class name
- **`stack_trace`**: Full stack trace for errors
- **`http_code`**: HTTP status code
- **`duration_ms`**: Execution time in milliseconds
- **`memory_usage_mb`**: Memory consumption
- **`count`**, **`total`**, **`items_processed`**: Numeric metrics
- **`keys`**, **`array_keys`**: Array structure metadata

## Security Guidelines

### Never Log

1. **Passwords** - Never log passwords, even hashed
2. **API tokens** - Mask tokens (show only first 4 characters)
3. **Credit card numbers** - Never log full card numbers
4. **Social security numbers** - Never log SSNs
5. **Full request bodies** - Log structure, not full content

### Sanitize Before Logging

```php
// ❌ BAD
ordio_log('INFO', 'User login', [
    'password' => $password, // NEVER!
    'api_token' => $token,   // NEVER!
]);

// ✅ GOOD
ordio_log('INFO', 'User login', [
    'user_id' => $userId,
    'api_token_prefix' => substr($token, 0, 4) . '...', // Masked
]);
```

## Performance Guidelines

### Avoid Log Bloat

1. **Don't log entire large arrays/objects**

   ```php
   // ❌ BAD
   ordio_log('DEBUG', 'Data', ['full_data' => $hugeArray]);

   // ✅ GOOD
   ordio_log('DEBUG', 'Data', [
       'keys' => array_keys($hugeArray),
       'count' => count($hugeArray),
       'sample' => array_slice($hugeArray, 0, 3)
   ]);
   ```

2. **Limit string lengths**

   ```php
   // ✅ GOOD
   ordio_log('DEBUG', 'Response', [
       'response_preview' => substr($response, 0, 200)
   ]);
   ```

3. **Use appropriate log levels**
   - DEBUG: Only in development
   - INFO: Normal operations
   - WARN/ERROR/CRITICAL: Always log

### Conditional Logging

```php
// Only log in development
if (defined('DEBUG_MODE') && DEBUG_MODE) {
    ordio_log('DEBUG', 'Detailed info', [...]);
}
```

## Code Examples

### Basic Logging

```php
ordio_log('INFO', 'Operation completed', [
    'correlation_id' => $correlationId,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### Error Logging

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

### Performance Logging

```php
$startTime = microtime(true);
// ... code ...
$duration = (microtime(true) - $startTime) * 1000;

ordio_log('DEBUG', 'Performance metric', [
    'correlation_id' => $correlationId,
    'duration_ms' => round($duration, 2),
    'memory_usage_mb' => round(memory_get_usage() / 1024 / 1024, 2),
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

### API Call Logging

```php
ordio_log('DEBUG', 'API call initiated', [
    'correlation_id' => $correlationId,
    'api_endpoint' => $url,
    'method' => $method,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);

// ... make API call ...

ordio_log('INFO', 'API call completed', [
    'correlation_id' => $correlationId,
    'http_code' => $httpCode,
    'duration_ms' => $duration,
    'endpoint' => 'api-name',
    'function' => 'functionName'
]);
```

## Common Mistakes to Avoid

1. **Missing required fields**

   ```php
   // ❌ BAD
   ordio_log('INFO', 'Message');

   // ✅ GOOD
   ordio_log('INFO', 'Message', [
       'correlation_id' => $correlationId,
       'endpoint' => 'api-name'
   ]);
   ```

2. **Wrong log levels**

   ```php
   // ❌ BAD - Using ERROR for warnings
   ordio_log('ERROR', 'Validation failed');

   // ✅ GOOD
   ordio_log('WARN', 'Validation failed');
   ```

3. **Logging sensitive data**

   ```php
   // ❌ BAD
   ordio_log('INFO', 'Login', ['password' => $password]);

   // ✅ GOOD
   ordio_log('INFO', 'Login', ['user_id' => $userId]);
   ```

4. **String interpolation instead of context**

   ```php
   // ❌ BAD
   ordio_log('INFO', "User $userId logged in");

   // ✅ GOOD
   ordio_log('INFO', 'User logged in', [
       'user_id' => $userId,
       'correlation_id' => $correlationId,
       'endpoint' => 'api-name'
   ]);
   ```

5. **Logging entire large objects**

   ```php
   // ❌ BAD
   ordio_log('DEBUG', 'Data', ['full_response' => $hugeResponse]);

   // ✅ GOOD
   ordio_log('DEBUG', 'Data', [
       'response_keys' => array_keys($hugeResponse),
       'response_size' => strlen(json_encode($hugeResponse))
   ]);
   ```

## Migration Checklist

When migrating from `error_log()` to `ordio_log()`:

- [ ] 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 (`correlation_id`, `endpoint`)
- [ ] Add recommended context fields (`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

## Tools

### Validation Script

Use the validation script to check your logging:

```bash
php scripts/test-logging-migration.php [--verbose] [--file=filename.php]
```

### Syntax Check

Always check PHP syntax after changes:

```bash
php -l v2/api/your-file.php
```

### Search for Remaining error_log()

```bash
grep -r "error_log(" v2/api/
```

## References

- [ERROR_LOG_MIGRATION_PATTERNS.md](./ERROR_LOG_MIGRATION_PATTERNS.md) - Pattern catalog
- [ERROR_LOG_MIGRATION_PLAN.md](./ERROR_LOG_MIGRATION_PLAN.md) - Migration plan
- `v2/helpers/logger.php` - Logger implementation (see codebase)
