Monitoring Logging - luckydeva03/barbershop_app GitHub Wiki

📊 Monitoring & Logging

Panduan komprehensif untuk monitoring sistem, error tracking, performance metrics, dan log management barbershop management system.

🎯 Monitoring Overview

Aspek monitoring yang dicakup:

  • Application Monitoring: Health checks, uptime monitoring
  • Performance Monitoring: Response times, memory usage, database queries
  • Error Tracking: Exception handling, error logging, alerts
  • Security Monitoring: Login attempts, suspicious activities
  • Business Metrics: User activities, booking stats, revenue tracking

📈 Application Monitoring

1. Health Check Service

Health Check Service (app/Services/HealthCheckService.php)

<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

class HealthCheckService
{
    /**
     * Perform comprehensive health check
     */
    public static function performHealthCheck()
    {
        $checks = [
            'database' => self::checkDatabase(),
            'cache' => self::checkCache(),
            'storage' => self::checkStorage(),
            'external_apis' => self::checkExternalApis(),
            'queue' => self::checkQueue(),
            'memory' => self::checkMemoryUsage(),
            'disk_space' => self::checkDiskSpace(),
        ];
        
        $overallHealth = collect($checks)->every(fn($check) => $check['status'] === 'healthy');
        
        return [
            'status' => $overallHealth ? 'healthy' : 'unhealthy',
            'timestamp' => now()->toISOString(),
            'checks' => $checks,
            'summary' => self::generateHealthSummary($checks),
        ];
    }
    
    /**
     * Check database connectivity and performance
     */
    protected static function checkDatabase()
    {
        try {
            $start = microtime(true);
            
            // Test basic connectivity
            DB::connection()->getPdo();
            
            // Test query performance
            $queryTime = self::measureQueryTime();
            
            $responseTime = (microtime(true) - $start) * 1000;
            
            $status = 'healthy';
            $message = 'Database is accessible';
            
            if ($responseTime > 1000) { // > 1 second
                $status = 'warning';
                $message = 'Database response time is slow';
            }
            
            if ($queryTime > 500) { // > 500ms for test query
                $status = 'warning';
                $message = 'Database queries are slow';
            }
            
            return [
                'status' => $status,
                'message' => $message,
                'response_time_ms' => round($responseTime, 2),
                'query_time_ms' => round($queryTime, 2),
                'connection' => config('database.default'),
            ];
            
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Database connection failed',
                'error' => $e->getMessage(),
            ];
        }
    }
    
    /**
     * Check cache system
     */
    protected static function checkCache()
    {
        try {
            $testKey = 'health_check_' . time();
            $testValue = 'test_value';
            
            // Test cache write
            Cache::put($testKey, $testValue, 60);
            
            // Test cache read
            $retrieved = Cache::get($testKey);
            
            // Clean up
            Cache::forget($testKey);
            
            if ($retrieved === $testValue) {
                return [
                    'status' => 'healthy',
                    'message' => 'Cache is working properly',
                    'driver' => config('cache.default'),
                ];
            }
            
            return [
                'status' => 'unhealthy',
                'message' => 'Cache read/write failed',
                'driver' => config('cache.default'),
            ];
            
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Cache system error',
                'error' => $e->getMessage(),
            ];
        }
    }
    
    /**
     * Check storage systems
     */
    protected static function checkStorage()
    {
        try {
            $checks = [];
            $disks = ['local', 'public'];
            
            foreach ($disks as $disk) {
                try {
                    $testFile = 'health_check_' . time() . '.txt';
                    $testContent = 'Health check test';
                    
                    // Test write
                    Storage::disk($disk)->put($testFile, $testContent);
                    
                    // Test read
                    $retrieved = Storage::disk($disk)->get($testFile);
                    
                    // Test delete
                    Storage::disk($disk)->delete($testFile);
                    
                    $checks[$disk] = [
                        'status' => $retrieved === $testContent ? 'healthy' : 'unhealthy',
                        'writable' => true,
                        'readable' => true,
                    ];
                    
                } catch (\Exception $e) {
                    $checks[$disk] = [
                        'status' => 'unhealthy',
                        'error' => $e->getMessage(),
                        'writable' => false,
                        'readable' => false,
                    ];
                }
            }
            
            $allHealthy = collect($checks)->every(fn($check) => $check['status'] === 'healthy');
            
            return [
                'status' => $allHealthy ? 'healthy' : 'unhealthy',
                'message' => $allHealthy ? 'All storage disks accessible' : 'Some storage disks have issues',
                'disks' => $checks,
            ];
            
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Storage check failed',
                'error' => $e->getMessage(),
            ];
        }
    }
    
    /**
     * Check external API connectivity
     */
    protected static function checkExternalApis()
    {
        $apis = [
            'google_oauth' => [
                'url' => 'https://www.googleapis.com/oauth2/v1/tokeninfo',
                'timeout' => 5,
            ],
            'google_maps' => [
                'url' => 'https://maps.googleapis.com/maps/api/geocode/json',
                'timeout' => 5,
            ],
        ];
        
        $results = [];
        
        foreach ($apis as $name => $config) {
            try {
                $start = microtime(true);
                $response = Http::timeout($config['timeout'])->get($config['url']);
                $responseTime = (microtime(true) - $start) * 1000;
                
                $results[$name] = [
                    'status' => $response->successful() ? 'healthy' : 'unhealthy',
                    'response_time_ms' => round($responseTime, 2),
                    'http_code' => $response->status(),
                ];
                
            } catch (\Exception $e) {
                $results[$name] = [
                    'status' => 'unhealthy',
                    'error' => $e->getMessage(),
                ];
            }
        }
        
        $allHealthy = collect($results)->every(fn($result) => $result['status'] === 'healthy');
        
        return [
            'status' => $allHealthy ? 'healthy' : 'warning',
            'message' => $allHealthy ? 'All external APIs accessible' : 'Some external APIs unavailable',
            'apis' => $results,
        ];
    }
    
    /**
     * Check queue system
     */
    protected static function checkQueue()
    {
        try {
            // Check failed jobs count
            $failedJobs = DB::table('failed_jobs')->count();
            
            // Check jobs table if it exists
            $pendingJobs = 0;
            if (Schema::hasTable('jobs')) {
                $pendingJobs = DB::table('jobs')->count();
            }
            
            $status = 'healthy';
            $message = 'Queue system operational';
            
            if ($failedJobs > 10) {
                $status = 'warning';
                $message = 'High number of failed jobs';
            }
            
            if ($pendingJobs > 100) {
                $status = 'warning';
                $message = 'High number of pending jobs';
            }
            
            return [
                'status' => $status,
                'message' => $message,
                'failed_jobs' => $failedJobs,
                'pending_jobs' => $pendingJobs,
                'driver' => config('queue.default'),
            ];
            
        } catch (\Exception $e) {
            return [
                'status' => 'unhealthy',
                'message' => 'Queue check failed',
                'error' => $e->getMessage(),
            ];
        }
    }
    
    /**
     * Check memory usage
     */
    protected static function checkMemoryUsage()
    {
        $memoryUsage = memory_get_usage(true);
        $memoryLimit = self::convertToBytes(ini_get('memory_limit'));
        $memoryPercentage = ($memoryUsage / $memoryLimit) * 100;
        
        $status = 'healthy';
        $message = 'Memory usage normal';
        
        if ($memoryPercentage > 80) {
            $status = 'warning';
            $message = 'High memory usage';
        }
        
        if ($memoryPercentage > 95) {
            $status = 'unhealthy';
            $message = 'Critical memory usage';
        }
        
        return [
            'status' => $status,
            'message' => $message,
            'memory_usage_mb' => round($memoryUsage / 1024 / 1024, 2),
            'memory_limit_mb' => round($memoryLimit / 1024 / 1024, 2),
            'memory_percentage' => round($memoryPercentage, 2),
        ];
    }
    
    /**
     * Check disk space
     */
    protected static function checkDiskSpace()
    {
        $path = storage_path();
        $totalSpace = disk_total_space($path);
        $freeSpace = disk_free_space($path);
        $usedPercentage = (($totalSpace - $freeSpace) / $totalSpace) * 100;
        
        $status = 'healthy';
        $message = 'Disk space sufficient';
        
        if ($usedPercentage > 80) {
            $status = 'warning';
            $message = 'Disk space running low';
        }
        
        if ($usedPercentage > 95) {
            $status = 'unhealthy';
            $message = 'Critical disk space';
        }
        
        return [
            'status' => $status,
            'message' => $message,
            'total_gb' => round($totalSpace / 1024 / 1024 / 1024, 2),
            'free_gb' => round($freeSpace / 1024 / 1024 / 1024, 2),
            'used_percentage' => round($usedPercentage, 2),
        ];
    }
    
    /**
     * Measure database query time
     */
    protected static function measureQueryTime()
    {
        $start = microtime(true);
        DB::table('users')->count();
        return (microtime(true) - $start) * 1000;
    }
    
    /**
     * Convert string memory limit to bytes
     */
    protected static function convertToBytes($value)
    {
        $unit = strtolower(substr($value, -1));
        $value = (int) $value;
        
        switch ($unit) {
            case 'g':
                $value *= 1024;
            case 'm':
                $value *= 1024;
            case 'k':
                $value *= 1024;
        }
        
        return $value;
    }
    
    /**
     * Generate health summary
     */
    protected static function generateHealthSummary($checks)
    {
        $healthy = collect($checks)->filter(fn($check) => $check['status'] === 'healthy')->count();
        $warning = collect($checks)->filter(fn($check) => $check['status'] === 'warning')->count();
        $unhealthy = collect($checks)->filter(fn($check) => $check['status'] === 'unhealthy')->count();
        
        return [
            'total_checks' => count($checks),
            'healthy' => $healthy,
            'warning' => $warning,
            'unhealthy' => $unhealthy,
        ];
    }
}

2. Health Check Endpoint

Health Check Controller (app/Http/Controllers/HealthController.php)

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\HealthCheckService;

class HealthController extends Controller
{
    /**
     * Get application health status
     */
    public function check()
    {
        $health = HealthCheckService::performHealthCheck();
        
        $httpStatus = match($health['status']) {
            'healthy' => 200,
            'warning' => 200, // Still operational
            'unhealthy' => 503, // Service unavailable
            default => 200,
        };
        
        return response()->json($health, $httpStatus);
    }
    
    /**
     * Simple ping endpoint
     */
    public function ping()
    {
        return response()->json([
            'status' => 'ok',
            'timestamp' => now()->toISOString(),
            'service' => 'barbershop-api',
        ]);
    }
    
    /**
     * Get detailed health metrics
     */
    public function metrics()
    {
        $health = HealthCheckService::performHealthCheck();
        
        // Add additional metrics
        $metrics = [
            'health' => $health,
            'system' => [
                'php_version' => PHP_VERSION,
                'laravel_version' => app()->version(),
                'server_time' => now()->toISOString(),
                'timezone' => config('app.timezone'),
                'environment' => app()->environment(),
            ],
            'performance' => [
                'memory_usage' => memory_get_usage(true),
                'memory_peak' => memory_get_peak_usage(true),
                'execution_time' => microtime(true) - LARAVEL_START,
            ],
        ];
        
        return response()->json($metrics);
    }
}

📊 Performance Monitoring

1. Performance Metrics Collector

Metrics Collector (app/Services/MetricsCollectorService.php)

<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;

class MetricsCollectorService
{
    /**
     * Collect and store performance metrics
     */
    public static function collectMetrics()
    {
        $metrics = [
            'timestamp' => now(),
            'response_times' => self::getResponseTimeMetrics(),
            'database_metrics' => self::getDatabaseMetrics(),
            'cache_metrics' => self::getCacheMetrics(),
            'user_metrics' => self::getUserMetrics(),
            'business_metrics' => self::getBusinessMetrics(),
            'system_metrics' => self::getSystemMetrics(),
        ];
        
        // Store metrics
        self::storeMetrics($metrics);
        
        return $metrics;
    }
    
    /**
     * Get response time metrics
     */
    protected static function getResponseTimeMetrics()
    {
        $timeframe = now()->subHour();
        
        return DB::table('performance_metrics')
                 ->where('metric', 'page_load_time')
                 ->where('created_at', '>=', $timeframe)
                 ->selectRaw('
                     AVG(value) as avg_response_time,
                     MIN(value) as min_response_time,
                     MAX(value) as max_response_time,
                     COUNT(*) as total_requests,
                     COUNT(CASE WHEN value > 1000 THEN 1 END) as slow_requests
                 ')
                 ->first();
    }
    
    /**
     * Get database performance metrics
     */
    protected static function getDatabaseMetrics()
    {
        $timeframe = now()->subHour();
        
        $queryMetrics = DB::table('performance_metrics')
                         ->where('metric', 'query_time')
                         ->where('created_at', '>=', $timeframe)
                         ->selectRaw('
                             AVG(value) as avg_query_time,
                             MAX(value) as max_query_time,
                             COUNT(*) as total_queries,
                             COUNT(CASE WHEN value > 1000 THEN 1 END) as slow_queries
                         ')
                         ->first();
        
        // Get connection pool info
        $connections = [
            'active' => DB::getConnections(),
            'default' => config('database.default'),
        ];
        
        return [
            'queries' => $queryMetrics,
            'connections' => $connections,
        ];
    }
    
    /**
     * Get cache performance metrics
     */
    protected static function getCacheMetrics()
    {
        // Simulate cache metrics (implement based on your cache driver)
        return [
            'driver' => config('cache.default'),
            'hit_rate' => Cache::get('cache_hit_rate', 0),
            'miss_rate' => Cache::get('cache_miss_rate', 0),
            'memory_usage' => Cache::get('cache_memory_usage', 0),
        ];
    }
    
    /**
     * Get user activity metrics
     */
    protected static function getUserMetrics()
    {
        $timeframe = now()->subDay();
        
        return [
            'active_users_24h' => DB::table('users')
                                   ->where('last_login_at', '>=', $timeframe)
                                   ->count(),
            
            'new_users_24h' => DB::table('users')
                             ->where('created_at', '>=', $timeframe)
                             ->count(),
            
            'total_users' => DB::table('users')->count(),
            
            'admin_logins_24h' => DB::table('security_audit_log')
                                ->where('event', 'admin_login')
                                ->where('created_at', '>=', $timeframe)
                                ->count(),
        ];
    }
    
    /**
     * Get business metrics
     */
    protected static function getBusinessMetrics()
    {
        $timeframe = now()->subDay();
        
        return [
            'new_reviews_24h' => DB::table('reviews')
                               ->where('created_at', '>=', $timeframe)
                               ->count(),
            
            'points_earned_24h' => DB::table('history_points')
                                 ->where('type', 'earned')
                                 ->where('created_at', '>=', $timeframe)
                                 ->sum('points'),
            
            'points_redeemed_24h' => DB::table('history_points')
                                   ->where('type', 'redeemed')
                                   ->where('created_at', '>=', $timeframe)
                                   ->sum('points'),
            
            'active_stores' => DB::table('stores')
                             ->where('is_active', true)
                             ->count(),
            
            'avg_rating' => DB::table('reviews')
                          ->where('created_at', '>=', $timeframe)
                          ->avg('rating'),
        ];
    }
    
    /**
     * Get system metrics
     */
    protected static function getSystemMetrics()
    {
        return [
            'memory_usage' => memory_get_usage(true),
            'memory_peak' => memory_get_peak_usage(true),
            'cpu_usage' => self::getCpuUsage(),
            'disk_usage' => self::getDiskUsage(),
            'load_average' => self::getLoadAverage(),
        ];
    }
    
    /**
     * Store metrics in database
     */
    protected static function storeMetrics($metrics)
    {
        DB::table('system_metrics')->insert([
            'timestamp' => $metrics['timestamp'],
            'metrics' => json_encode($metrics),
            'created_at' => now(),
        ]);
        
        // Clean old metrics (keep 30 days)
        DB::table('system_metrics')
          ->where('created_at', '<', now()->subDays(30))
          ->delete();
    }
    
    /**
     * Get CPU usage (Linux/Unix)
     */
    protected static function getCpuUsage()
    {
        if (PHP_OS_FAMILY === 'Linux') {
            $load = sys_getloadavg();
            return $load ? $load[0] : 0;
        }
        
        return 0; // Not available on this system
    }
    
    /**
     * Get disk usage
     */
    protected static function getDiskUsage()
    {
        $path = storage_path();
        $total = disk_total_space($path);
        $free = disk_free_space($path);
        
        return [
            'total' => $total,
            'free' => $free,
            'used' => $total - $free,
            'percentage' => (($total - $free) / $total) * 100,
        ];
    }
    
    /**
     * Get system load average
     */
    protected static function getLoadAverage()
    {
        if (function_exists('sys_getloadavg')) {
            return sys_getloadavg();
        }
        
        return [0, 0, 0];
    }
    
    /**
     * Get metrics for dashboard
     */
    public static function getDashboardMetrics($hours = 24)
    {
        $timeframe = now()->subHours($hours);
        
        $metrics = DB::table('system_metrics')
                    ->where('created_at', '>=', $timeframe)
                    ->orderBy('created_at', 'desc')
                    ->get()
                    ->map(function ($record) {
                        $data = json_decode($record->metrics, true);
                        return [
                            'timestamp' => $record->timestamp,
                            'response_time' => $data['response_times']->avg_response_time ?? 0,
                            'memory_usage' => $data['system_metrics']['memory_usage'] ?? 0,
                            'cpu_usage' => $data['system_metrics']['cpu_usage'] ?? 0,
                            'active_users' => $data['user_metrics']['active_users_24h'] ?? 0,
                        ];
                    });
        
        return $metrics;
    }
}

🚨 Error Tracking & Logging

1. Advanced Error Handler

Custom Exception Handler (app/Exceptions/Handler.php)

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Throwable;

class Handler extends ExceptionHandler
{
    protected $dontReport = [
        //
    ];

    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    public function register()
    {
        $this->reportable(function (Throwable $e) {
            $this->logErrorToDatabase($e);
        });
    }

    /**
     * Log error to database for tracking
     */
    protected function logErrorToDatabase(Throwable $exception)
    {
        try {
            $request = request();
            
            DB::table('error_logs')->insert([
                'exception_class' => get_class($exception),
                'message' => $exception->getMessage(),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
                'url' => $request->fullUrl(),
                'method' => $request->method(),
                'ip_address' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'user_id' => auth()->id(),
                'session_id' => session()->getId(),
                'context' => json_encode([
                    'request_data' => $request->except(['password', 'password_confirmation']),
                    'headers' => $request->headers->all(),
                    'server' => $_SERVER,
                ]),
                'severity' => $this->getSeverity($exception),
                'created_at' => now(),
            ]);
            
            // Send critical errors to monitoring service
            if ($this->isCritical($exception)) {
                $this->notifyCriticalError($exception);
            }
            
        } catch (\Exception $e) {
            // Don't let error logging break the application
            Log::error('Failed to log error to database: ' . $e->getMessage());
        }
    }

    /**
     * Determine error severity
     */
    protected function getSeverity(Throwable $exception)
    {
        if ($exception instanceof \Error) {
            return 'critical';
        }
        
        if ($exception instanceof \Illuminate\Database\QueryException) {
            return 'high';
        }
        
        if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
            return 'medium';
        }
        
        if ($exception instanceof \Illuminate\Validation\ValidationException) {
            return 'low';
        }
        
        return 'medium';
    }

    /**
     * Check if error is critical
     */
    protected function isCritical(Throwable $exception)
    {
        return in_array(get_class($exception), [
            \Error::class,
            \ParseError::class,
            \TypeError::class,
            \Illuminate\Database\QueryException::class,
        ]);
    }

    /**
     * Notify about critical errors
     */
    protected function notifyCriticalError(Throwable $exception)
    {
        // Send to external monitoring service (e.g., Sentry, Bugsnag)
        // Send email/Slack notification
        // Log to external log service
        
        Log::critical('Critical error occurred', [
            'exception' => get_class($exception),
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'url' => request()->fullUrl(),
            'user_id' => auth()->id(),
        ]);
    }

    /**
     * Custom error response for API
     */
    public function render($request, Throwable $exception)
    {
        if ($request->expectsJson()) {
            return $this->handleApiException($request, $exception);
        }

        return parent::render($request, $exception);
    }

    /**
     * Handle API exceptions
     */
    protected function handleApiException($request, Throwable $exception)
    {
        $status = 500;
        $message = 'An error occurred';

        if ($exception instanceof \Illuminate\Validation\ValidationException) {
            $status = 422;
            $message = 'Validation failed';
        } elseif ($exception instanceof \Illuminate\Auth\AuthenticationException) {
            $status = 401;
            $message = 'Unauthenticated';
        } elseif ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
            $status = 403;
            $message = 'Unauthorized';
        } elseif ($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
            $status = 404;
            $message = 'Not found';
        }

        $response = [
            'error' => true,
            'message' => $message,
            'status_code' => $status,
        ];

        if (config('app.debug')) {
            $response['debug'] = [
                'exception' => get_class($exception),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
            ];
        }

        return response()->json($response, $status);
    }
}

2. Logging Configuration

Advanced Logging Config (config/logging.php)

<?php

use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;

return [
    'default' => env('LOG_CHANNEL', 'stack'),

    'deprecations' => [
        'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
        'trace' => false,
    ],

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single', 'slack'],
            'ignore_exceptions' => false,
        ],

        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
            'replace_placeholders' => true,
        ],

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
            'days' => 14,
            'replace_placeholders' => true,
        ],

        // Separate channel for errors
        'errors' => [
            'driver' => 'daily',
            'path' => storage_path('logs/errors.log'),
            'level' => 'error',
            'days' => 30,
        ],

        // Separate channel for security events
        'security' => [
            'driver' => 'daily',
            'path' => storage_path('logs/security.log'),
            'level' => 'info',
            'days' => 90,
        ],

        // Performance monitoring
        'performance' => [
            'driver' => 'daily',
            'path' => storage_path('logs/performance.log'),
            'level' => 'info',
            'days' => 7,
        ],

        // Business logic events
        'business' => [
            'driver' => 'daily',
            'path' => storage_path('logs/business.log'),
            'level' => 'info',
            'days' => 30,
        ],

        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Barbershop Bot',
            'emoji' => ':boom:',
            'level' => env('LOG_SLACK_LEVEL', 'critical'),
        ],

        'papertrail' => [
            'driver' => 'monolog',
            'level' => env('LOG_LEVEL', 'debug'),
            'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
            'handler_with' => [
                'host' => env('PAPERTRAIL_URL'),
                'port' => env('PAPERTRAIL_PORT'),
                'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
            ],
            'processors' => [PsrLogMessageProcessor::class],
        ],

        'stderr' => [
            'driver' => 'monolog',
            'level' => env('LOG_LEVEL', 'debug'),
            'handler' => StreamHandler::class,
            'formatter' => env('LOG_STDERR_FORMATTER'),
            'with' => [
                'stream' => 'php://stderr',
            ],
            'processors' => [PsrLogMessageProcessor::class],
        ],

        'syslog' => [
            'driver' => 'syslog',
            'level' => env('LOG_LEVEL', 'debug'),
            'facility' => LOG_USER,
            'replace_placeholders' => true,
        ],

        'errorlog' => [
            'driver' => 'errorlog',
            'level' => env('LOG_LEVEL', 'debug'),
            'replace_placeholders' => true,
        ],

        'null' => [
            'driver' => 'monolog',
            'handler' => NullHandler::class,
        ],

        'emergency' => [
            'path' => storage_path('logs/laravel.log'),
        ],
    ],
];

3. Business Event Logging

Business Logger Service (app/Services/BusinessLoggerService.php)

<?php

namespace App\Services;

use Illuminate\Support\Facades\Log;

class BusinessLoggerService
{
    /**
     * Log user registration
     */
    public static function logUserRegistration($user, $method = 'email')
    {
        Log::channel('business')->info('User registered', [
            'event' => 'user_registration',
            'user_id' => $user->id,
            'email' => $user->email,
            'method' => $method,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log user login
     */
    public static function logUserLogin($user, $guard = 'web')
    {
        Log::channel('business')->info('User logged in', [
            'event' => 'user_login',
            'user_id' => $user->id,
            'email' => $user->email,
            'guard' => $guard,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log point transactions
     */
    public static function logPointTransaction($userId, $points, $type, $description = null)
    {
        Log::channel('business')->info('Points transaction', [
            'event' => 'points_transaction',
            'user_id' => $userId,
            'points' => $points,
            'type' => $type,
            'description' => $description,
            'balance_after' => self::getUserPointBalance($userId),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log review submission
     */
    public static function logReviewSubmission($review)
    {
        Log::channel('business')->info('Review submitted', [
            'event' => 'review_submission',
            'review_id' => $review->id,
            'user_id' => $review->user_id,
            'store_id' => $review->store_id,
            'rating' => $review->rating,
            'has_comment' => !empty($review->comment),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log code redemption
     */
    public static function logCodeRedemption($code, $userId)
    {
        Log::channel('business')->info('Code redeemed', [
            'event' => 'code_redemption',
            'code_id' => $code->id,
            'code' => $code->code,
            'user_id' => $userId,
            'points_awarded' => $code->points,
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log suspicious activity
     */
    public static function logSuspiciousActivity($description, $data = [])
    {
        Log::channel('security')->warning('Suspicious activity detected', [
            'event' => 'suspicious_activity',
            'description' => $description,
            'data' => $data,
            'ip_address' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'user_id' => auth()->id(),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Log admin actions
     */
    public static function logAdminAction($action, $target = null, $data = [])
    {
        Log::channel('security')->info('Admin action', [
            'event' => 'admin_action',
            'action' => $action,
            'admin_id' => auth('admin')->id(),
            'admin_email' => auth('admin')->user()?->email,
            'target' => $target,
            'data' => $data,
            'ip_address' => request()->ip(),
            'timestamp' => now()->toISOString(),
        ]);
    }

    /**
     * Get user point balance
     */
    protected static function getUserPointBalance($userId)
    {
        return \DB::table('history_points')
                  ->where('user_id', $userId)
                  ->sum('points');
    }
}

🔍 Log Analysis & Reporting

1. Log Analysis Service

Log Analyzer (app/Services/LogAnalysisService.php)

<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class LogAnalysisService
{
    /**
     * Get error statistics
     */
    public static function getErrorStatistics($days = 7)
    {
        $startDate = now()->subDays($days);
        
        return [
            'total_errors' => DB::table('error_logs')
                               ->where('created_at', '>=', $startDate)
                               ->count(),
            
            'by_severity' => DB::table('error_logs')
                              ->where('created_at', '>=', $startDate)
                              ->select('severity', DB::raw('COUNT(*) as count'))
                              ->groupBy('severity')
                              ->get(),
            
            'by_exception' => DB::table('error_logs')
                               ->where('created_at', '>=', $startDate)
                               ->select('exception_class', DB::raw('COUNT(*) as count'))
                               ->groupBy('exception_class')
                               ->orderByDesc('count')
                               ->limit(10)
                               ->get(),
            
            'by_endpoint' => DB::table('error_logs')
                              ->where('created_at', '>=', $startDate)
                              ->select('url', DB::raw('COUNT(*) as count'))
                              ->groupBy('url')
                              ->orderByDesc('count')
                              ->limit(10)
                              ->get(),
            
            'trend' => self::getErrorTrend($days),
        ];
    }

    /**
     * Get performance insights
     */
    public static function getPerformanceInsights($hours = 24)
    {
        $startTime = now()->subHours($hours);
        
        $slowQueries = DB::table('performance_metrics')
                        ->where('metric', 'query_time')
                        ->where('value', '>', 1000) // > 1 second
                        ->where('created_at', '>=', $startTime)
                        ->get();
        
        $slowPages = DB::table('performance_metrics')
                      ->where('metric', 'page_load_time')
                      ->where('value', '>', 2000) // > 2 seconds
                      ->where('created_at', '>=', $startTime)
                      ->get();
        
        return [
            'slow_queries' => $slowQueries->count(),
            'slow_pages' => $slowPages->count(),
            'avg_response_time' => DB::table('performance_metrics')
                                    ->where('metric', 'page_load_time')
                                    ->where('created_at', '>=', $startTime)
                                    ->avg('value'),
            'performance_trend' => self::getPerformanceTrend($hours),
        ];
    }

    /**
     * Get security events
     */
    public static function getSecurityEvents($days = 7)
    {
        $startDate = now()->subDays($days);
        
        return [
            'failed_logins' => DB::table('security_audit_log')
                              ->where('event', 'login_failed')
                              ->where('created_at', '>=', $startDate)
                              ->count(),
            
            'suspicious_activities' => DB::table('security_audit_log')
                                     ->where('severity', 'warning')
                                     ->where('created_at', '>=', $startDate)
                                     ->count(),
            
            'admin_actions' => DB::table('security_audit_log')
                             ->where('event', 'admin_action')
                             ->where('created_at', '>=', $startDate)
                             ->count(),
            
            'by_ip' => DB::table('security_audit_log')
                        ->where('created_at', '>=', $startDate)
                        ->select('ip_address', DB::raw('COUNT(*) as count'))
                        ->groupBy('ip_address')
                        ->orderByDesc('count')
                        ->limit(10)
                        ->get(),
        ];
    }

    /**
     * Get business metrics
     */
    public static function getBusinessMetrics($days = 30)
    {
        $startDate = now()->subDays($days);
        
        // Parse business logs (this would require log parsing)
        // For now, return database metrics
        
        return [
            'new_users' => DB::table('users')
                            ->where('created_at', '>=', $startDate)
                            ->count(),
            
            'active_users' => DB::table('users')
                             ->where('last_login_at', '>=', $startDate)
                             ->count(),
            
            'reviews_submitted' => DB::table('reviews')
                                  ->where('created_at', '>=', $startDate)
                                  ->count(),
            
            'points_earned' => DB::table('history_points')
                             ->where('type', 'earned')
                             ->where('created_at', '>=', $startDate)
                             ->sum('points'),
            
            'points_redeemed' => DB::table('history_points')
                               ->where('type', 'redeemed')
                               ->where('created_at', '>=', $startDate)
                               ->sum('points'),
        ];
    }

    /**
     * Generate daily report
     */
    public static function generateDailyReport()
    {
        $report = [
            'date' => now()->toDateString(),
            'errors' => self::getErrorStatistics(1),
            'performance' => self::getPerformanceInsights(24),
            'security' => self::getSecurityEvents(1),
            'business' => self::getBusinessMetrics(1),
            'system_health' => \App\Services\HealthCheckService::performHealthCheck(),
        ];
        
        // Store report
        DB::table('daily_reports')->insert([
            'report_date' => now()->toDateString(),
            'report_data' => json_encode($report),
            'created_at' => now(),
        ]);
        
        return $report;
    }

    /**
     * Get error trend data
     */
    protected static function getErrorTrend($days)
    {
        return DB::table('error_logs')
                 ->where('created_at', '>=', now()->subDays($days))
                 ->select(
                     DB::raw('DATE(created_at) as date'),
                     DB::raw('COUNT(*) as count')
                 )
                 ->groupBy('date')
                 ->orderBy('date')
                 ->get();
    }

    /**
     * Get performance trend data
     */
    protected static function getPerformanceTrend($hours)
    {
        return DB::table('performance_metrics')
                 ->where('metric', 'page_load_time')
                 ->where('created_at', '>=', now()->subHours($hours))
                 ->select(
                     DB::raw('DATE_FORMAT(created_at, "%Y-%m-%d %H:00:00") as hour'),
                     DB::raw('AVG(value) as avg_time')
                 )
                 ->groupBy('hour')
                 ->orderBy('hour')
                 ->get();
    }
}

2. Monitoring Dashboard

Monitoring Command (app/Console/Commands/MonitoringReport.php)

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\LogAnalysisService;
use App\Services\MetricsCollectorService;

class MonitoringReport extends Command
{
    protected $signature = 'monitoring:report 
                           {--type=daily : Report type (daily, weekly, health)}
                           {--send-email : Send report via email}';
    
    protected $description = 'Generate monitoring reports';

    public function handle()
    {
        $type = $this->option('type');
        
        $this->info("📊 Generating {$type} monitoring report...");
        
        switch ($type) {
            case 'daily':
                $report = $this->generateDailyReport();
                break;
            case 'weekly':
                $report = $this->generateWeeklyReport();
                break;
            case 'health':
                $report = $this->generateHealthReport();
                break;
            default:
                $this->error('Invalid report type');
                return 1;
        }
        
        $this->displayReport($report);
        
        if ($this->option('send-email')) {
            $this->sendReportEmail($report, $type);
        }
        
        return 0;
    }

    protected function generateDailyReport()
    {
        return LogAnalysisService::generateDailyReport();
    }

    protected function generateWeeklyReport()
    {
        return [
            'period' => 'Last 7 days',
            'errors' => LogAnalysisService::getErrorStatistics(7),
            'performance' => LogAnalysisService::getPerformanceInsights(168), // 7 days in hours
            'security' => LogAnalysisService::getSecurityEvents(7),
            'business' => LogAnalysisService::getBusinessMetrics(7),
        ];
    }

    protected function generateHealthReport()
    {
        return \App\Services\HealthCheckService::performHealthCheck();
    }

    protected function displayReport($report)
    {
        if (isset($report['errors'])) {
            $this->newLine();
            $this->info('🚨 Error Statistics:');
            $this->line("Total Errors: {$report['errors']['total_errors']}");
            
            if ($report['errors']['by_severity']->isNotEmpty()) {
                $this->table(
                    ['Severity', 'Count'],
                    $report['errors']['by_severity']->map(fn($item) => [$item->severity, $item->count])
                );
            }
        }

        if (isset($report['performance'])) {
            $this->newLine();
            $this->info('⚡ Performance Metrics:');
            $this->line("Slow Queries: {$report['performance']['slow_queries']}");
            $this->line("Slow Pages: {$report['performance']['slow_pages']}");
            $this->line("Avg Response Time: " . round($report['performance']['avg_response_time'], 2) . "ms");
        }

        if (isset($report['security'])) {
            $this->newLine();
            $this->info('🔒 Security Events:');
            $this->line("Failed Logins: {$report['security']['failed_logins']}");
            $this->line("Suspicious Activities: {$report['security']['suspicious_activities']}");
            $this->line("Admin Actions: {$report['security']['admin_actions']}");
        }

        if (isset($report['business'])) {
            $this->newLine();
            $this->info('📈 Business Metrics:');
            $this->line("New Users: {$report['business']['new_users']}");
            $this->line("Active Users: {$report['business']['active_users']}");
            $this->line("Reviews: {$report['business']['reviews_submitted']}");
        }

        if (isset($report['system_health'])) {
            $this->newLine();
            $this->info('🏥 System Health:');
            $status = $report['system_health']['status'];
            $this->line("Overall Status: " . strtoupper($status));
            
            if (isset($report['system_health']['summary'])) {
                $summary = $report['system_health']['summary'];
                $this->line("Healthy Checks: {$summary['healthy']}/{$summary['total_checks']}");
                $this->line("Warning Checks: {$summary['warning']}");
                $this->line("Unhealthy Checks: {$summary['unhealthy']}");
            }
        }
    }

    protected function sendReportEmail($report, $type)
    {
        // Implementation would depend on your mail setup
        $this->info('📧 Report sent via email');
    }
}

📋 Database Tables

Migration for Monitoring Tables

Monitoring Tables Migration (database/migrations/create_monitoring_tables.php)

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        // Error logs table
        Schema::create('error_logs', function (Blueprint $table) {
            $table->id();
            $table->string('exception_class');
            $table->text('message');
            $table->string('file');
            $table->integer('line');
            $table->longText('trace');
            $table->string('url')->nullable();
            $table->string('method')->nullable();
            $table->ipAddress('ip_address')->nullable();
            $table->text('user_agent')->nullable();
            $table->unsignedBigInteger('user_id')->nullable();
            $table->string('session_id')->nullable();
            $table->json('context')->nullable();
            $table->enum('severity', ['low', 'medium', 'high', 'critical'])->default('medium');
            $table->timestamp('created_at');
            
            $table->index(['severity', 'created_at']);
            $table->index(['exception_class', 'created_at']);
            $table->index(['user_id', 'created_at']);
        });

        // System metrics table
        Schema::create('system_metrics', function (Blueprint $table) {
            $table->id();
            $table->timestamp('timestamp');
            $table->json('metrics');
            $table->timestamp('created_at');
            
            $table->index(['timestamp']);
            $table->index(['created_at']);
        });

        // Performance metrics table (if not exists)
        if (!Schema::hasTable('performance_metrics')) {
            Schema::create('performance_metrics', function (Blueprint $table) {
                $table->id();
                $table->string('metric');
                $table->decimal('value', 10, 2);
                $table->json('tags')->nullable();
                $table->timestamp('created_at');
                
                $table->index(['metric', 'created_at']);
                $table->index(['created_at']);
            });
        }

        // Daily reports table
        Schema::create('daily_reports', function (Blueprint $table) {
            $table->id();
            $table->date('report_date')->unique();
            $table->json('report_data');
            $table->timestamp('created_at');
            
            $table->index(['report_date']);
        });

        // Security audit log table (if not exists)
        if (!Schema::hasTable('security_audit_log')) {
            Schema::create('security_audit_log', function (Blueprint $table) {
                $table->id();
                $table->string('event');
                $table->unsignedBigInteger('user_id')->nullable();
                $table->ipAddress('ip_address');
                $table->text('user_agent')->nullable();
                $table->json('data')->nullable();
                $table->enum('severity', ['info', 'warning', 'error', 'critical'])->default('info');
                $table->timestamp('created_at');
                
                $table->index(['event', 'created_at']);
                $table->index(['user_id', 'created_at']);
                $table->index(['ip_address', 'created_at']);
                $table->index(['severity', 'created_at']);
            });
        }
    }

    public function down()
    {
        Schema::dropIfExists('daily_reports');
        Schema::dropIfExists('system_metrics');
        Schema::dropIfExists('error_logs');
        Schema::dropIfExists('security_audit_log');
        Schema::dropIfExists('performance_metrics');
    }
};

Next: Troubleshooting untuk panduan debugging dan penyelesaian masalah umum.

⚠️ **GitHub.com Fallback** ⚠️