Monitoring Logging - luckydeva03/barbershop_app GitHub Wiki
Panduan komprehensif untuk monitoring sistem, error tracking, performance metrics, dan log management barbershop management system.
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
<?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,
];
}
}
<?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);
}
}
<?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;
}
}
<?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);
}
}
<?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'),
],
],
];
<?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');
}
}
<?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();
}
}
<?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');
}
}
<?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.