Add comprehensive logging for OMG API debugging and application tracking

OMG API Debug Logging:
- Log full URL being called
- Log API key (first 20 chars for security)
- Log HTTP response code
- Log response body (first 500 chars)
- Log campaign number and business area extraction
- Log business unit mapping result

ApplicationLogger Class:
- Structured JSON logging to logs/application.log
- Track all actions: master_asset_submission, global_to_local_transform, box_upload
- Capture user email, timestamp, IP address, user agent
- Methods for reporting: getRecentLogs(), getLogsByAction(), getLogsByUser()
- Generate statistics: total actions, by user, by action, errors

Email Configuration:
- Configured SMTP via Mailgun (smtp.mailgun.org:587)
- Using twist@mail.dev.oliver.solutions
- Emails sent to logged-in user

This enables full audit trail and troubleshooting capability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DJP 2025-11-18 08:26:20 -05:00
parent 9b8dbbf20c
commit 0571ce403f
3 changed files with 217 additions and 0 deletions

200
ApplicationLogger.php Normal file
View file

@ -0,0 +1,200 @@
<?php
/**
* Application Logger
* Tracks all application actions for reporting and auditing
*/
class ApplicationLogger {
private $logFile;
private $config;
public function __construct() {
$appConfig = require __DIR__ . '/config.php';
$this->config = $appConfig;
// Create logs directory if it doesn't exist
$logsDir = __DIR__ . '/logs';
if (!file_exists($logsDir)) {
mkdir($logsDir, 0755, true);
}
$this->logFile = $logsDir . '/application.log';
}
/**
* Log action with structured data
*/
public function log($action, $user, $data = [], $status = 'success') {
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'action' => $action,
'user_email' => $user['email'] ?? 'unknown',
'user_name' => $user['name'] ?? 'unknown',
'status' => $status,
'data' => $data,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
];
$logLine = json_encode($logEntry) . "\n";
// Append to log file
file_put_contents($this->logFile, $logLine, FILE_APPEND | LOCK_EX);
// Also log to PHP error log for debugging
error_log('[APP LOG] ' . $action . ' - ' . $status . ' - ' . ($user['email'] ?? 'unknown'));
return $logEntry;
}
/**
* Log Master Asset Submission
*/
public function logAssetSubmission($user, $boxId, $masterCampaignNumber, $dates, $status = 'success', $error = null) {
return $this->log('master_asset_submission', $user, [
'box_id' => $boxId,
'master_campaign_number' => $masterCampaignNumber,
'supply_date' => $dates['supply'] ?? null,
'live_date' => $dates['live'] ?? null,
'end_date' => $dates['end'] ?? null,
'error' => $error
], $status);
}
/**
* Log Global to Local Transformation
*/
public function logGlobalToLocal($user, $campaignNumber, $businessUnit, $inputRows, $outputFiles, $status = 'success', $error = null) {
return $this->log('global_to_local_transform', $user, [
'campaign_number' => $campaignNumber,
'business_unit' => $businessUnit,
'input_rows' => $inputRows,
'output_file_count' => $outputFiles,
'total_output_rows' => $inputRows * $outputFiles,
'error' => $error
], $status);
}
/**
* Log Box Upload
*/
public function logBoxUpload($user, $fileCount, $folderId, $status = 'success', $error = null) {
return $this->log('box_upload', $user, [
'file_count' => $fileCount,
'folder_id' => $folderId,
'error' => $error
], $status);
}
/**
* Log OMG API Call
*/
public function logOMGAPICall($user, $campaignNumber, $businessArea, $businessUnit, $status = 'success', $error = null) {
return $this->log('omg_api_lookup', $user, [
'campaign_number' => $campaignNumber,
'business_area' => $businessArea,
'business_unit' => $businessUnit,
'error' => $error
], $status);
}
/**
* Get recent logs
*/
public function getRecentLogs($limit = 100) {
if (!file_exists($this->logFile)) {
return [];
}
$lines = file($this->logFile, FILE_IGNORE_NEW_LINES);
$lines = array_reverse($lines); // Most recent first
$lines = array_slice($lines, 0, $limit);
$logs = [];
foreach ($lines as $line) {
$log = json_decode($line, true);
if ($log) {
$logs[] = $log;
}
}
return $logs;
}
/**
* Get logs by action type
*/
public function getLogsByAction($action, $limit = 100) {
$allLogs = $this->getRecentLogs(1000);
$filtered = array_filter($allLogs, function($log) use ($action) {
return $log['action'] === $action;
});
return array_slice(array_values($filtered), 0, $limit);
}
/**
* Get logs by user
*/
public function getLogsByUser($userEmail, $limit = 100) {
$allLogs = $this->getRecentLogs(1000);
$filtered = array_filter($allLogs, function($log) use ($userEmail) {
return $log['user_email'] === $userEmail;
});
return array_slice(array_values($filtered), 0, $limit);
}
/**
* Generate report statistics
*/
public function getStatistics($startDate = null, $endDate = null) {
$allLogs = $this->getRecentLogs(10000);
// Filter by date if provided
if ($startDate || $endDate) {
$allLogs = array_filter($allLogs, function($log) use ($startDate, $endDate) {
$timestamp = strtotime($log['timestamp']);
if ($startDate && $timestamp < strtotime($startDate)) return false;
if ($endDate && $timestamp > strtotime($endDate)) return false;
return true;
});
}
$stats = [
'total_actions' => count($allLogs),
'by_action' => [],
'by_user' => [],
'by_status' => [],
'errors' => []
];
foreach ($allLogs as $log) {
// Count by action
$action = $log['action'];
$stats['by_action'][$action] = ($stats['by_action'][$action] ?? 0) + 1;
// Count by user
$user = $log['user_email'];
$stats['by_user'][$user] = ($stats['by_user'][$user] ?? 0) + 1;
// Count by status
$status = $log['status'];
$stats['by_status'][$status] = ($stats['by_status'][$status] ?? 0) + 1;
// Collect errors
if ($status === 'error') {
$stats['errors'][] = [
'timestamp' => $log['timestamp'],
'action' => $log['action'],
'user' => $log['user_email'],
'error' => $log['data']['error'] ?? 'Unknown error'
];
}
}
return $stats;
}
}

View file

@ -27,6 +27,10 @@ class OMGService {
$url = $this->baseUrl . '/getProject';
$url .= '?project_number=' . urlencode($projectNumber);
error_log('===== OMG API CALL START =====');
error_log('OMG API: Base URL: ' . $this->baseUrl);
error_log('OMG API: Full URL: ' . $url);
error_log('OMG API: API Key (first 20 chars): ' . substr($this->apiKey, 0, 20) . '...');
error_log('OMG API: Requesting project ' . $projectNumber);
$ch = curl_init($url);
@ -44,6 +48,10 @@ class OMGService {
$curlError = curl_error($ch);
curl_close($ch);
error_log('OMG API: Response code ' . $httpCode);
error_log('OMG API: Response body (first 500 chars): ' . substr($response, 0, 500));
error_log('===== OMG API CALL END =====');
if ($curlError) {
return [
'success' => false,

View file

@ -114,8 +114,13 @@ try {
// STAGE 4: Call OMG API
$progress[] = ['stage' => 'omg_api', 'status' => 'processing', 'message' => 'Looking up campaign in OMG...'];
error_log('=== OMG API LOOKUP ===');
error_log('Campaign Number: ' . $campaignNumber);
$projectResult = $omgService->getProject($campaignNumber);
error_log('OMG API Result: ' . json_encode($projectResult));
if (!$projectResult['success']) {
echo json_encode([
'success' => false,
@ -138,6 +143,8 @@ try {
$businessArea = $omgService->getBusinessArea($projectData);
error_log('Extracted Business Area: ' . ($businessArea ?? 'NULL'));
if (empty($businessArea)) {
echo json_encode([
'success' => false,
@ -153,6 +160,8 @@ try {
$mappingResult = $omgService->mapBusinessUnit($businessArea);
error_log('Business Unit Mapping Result: ' . json_encode($mappingResult));
if (!$mappingResult['success']) {
// Non-fatal warning - continue with "ERROR" value
$progress[] = [