Monitoring and Maintenance¶
Comprehensive monitoring and maintenance procedures ensure the Stratpoint Timesheet Application operates reliably, performs optimally, and maintains high availability. This section covers monitoring strategies, alerting systems, and maintenance procedures.
Monitoring Architecture¶
Monitoring Stack Overview¶
graph TB
A[Application Servers] --> B[Metrics Collection]
C[Database Servers] --> B
D[Cache Servers] --> B
E[Queue Workers] --> B
B --> F[Prometheus]
F --> G[Grafana Dashboards]
F --> H[AlertManager]
I[Log Aggregation] --> J[ELK Stack]
J --> K[Kibana Dashboards]
L[APM Monitoring] --> M[New Relic/DataDog]
H --> N[Notification Channels]
N --> O[Email Alerts]
N --> P[Slack Notifications]
N --> Q[SMS Alerts]
N --> R[PagerDuty]
Monitoring Components¶
Infrastructure Monitoring: - Server resource utilization (CPU, Memory, Disk, Network) - Database performance and connections - Cache hit rates and memory usage - Load balancer health and distribution
Application Monitoring: - Response times and throughput - Error rates and exception tracking - Queue job processing and failures - User session and authentication metrics
Business Monitoring: - Timesheet submission rates - Approval workflow performance - Integration sync status - User activity patterns
Application Performance Monitoring (APM)¶
Laravel Application Metrics¶
Performance Metrics Collection:
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
class PerformanceMonitoring
{
public function handle(Request $request, Closure $next)
{
$startTime = microtime(true);
$startMemory = memory_get_usage(true);
$response = $next($request);
$endTime = microtime(true);
$endMemory = memory_get_usage(true);
$metrics = [
'endpoint' => $request->path(),
'method' => $request->method(),
'response_time' => round(($endTime - $startTime) * 1000, 2), // milliseconds
'memory_usage' => $endMemory - $startMemory,
'status_code' => $response->getStatusCode(),
'user_id' => auth()->id(),
'timestamp' => now()->toISOString()
];
// Send metrics to monitoring system
$this->sendMetrics($metrics);
// Log slow requests
if ($metrics['response_time'] > 1000) {
Log::warning('Slow request detected', $metrics);
}
return $response;
}
private function sendMetrics($metrics)
{
// Send to Prometheus
Redis::lpush('metrics:api_requests', json_encode($metrics));
// Send to APM service
if (app()->bound('apm')) {
app('apm')->recordMetric('api.response_time', $metrics['response_time'], [
'endpoint' => $metrics['endpoint'],
'method' => $metrics['method']
]);
}
}
}
Database Performance Monitoring¶
Query Performance Tracking:
<?php
namespace App\Providers;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
class DatabaseMonitoringServiceProvider extends ServiceProvider
{
public function boot()
{
DB::listen(function (QueryExecuted $query) {
$this->logSlowQueries($query);
$this->trackQueryMetrics($query);
});
}
private function logSlowQueries(QueryExecuted $query)
{
if ($query->time > 1000) { // Log queries taking more than 1 second
Log::warning('Slow database query detected', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time' => $query->time,
'connection' => $query->connectionName
]);
}
}
private function trackQueryMetrics(QueryExecuted $query)
{
$metrics = [
'query_time' => $query->time,
'connection' => $query->connectionName,
'query_type' => $this->getQueryType($query->sql),
'timestamp' => now()->toISOString()
];
Redis::lpush('metrics:database_queries', json_encode($metrics));
}
private function getQueryType($sql)
{
$sql = strtoupper(trim($sql));
if (str_starts_with($sql, 'SELECT')) return 'SELECT';
if (str_starts_with($sql, 'INSERT')) return 'INSERT';
if (str_starts_with($sql, 'UPDATE')) return 'UPDATE';
if (str_starts_with($sql, 'DELETE')) return 'DELETE';
return 'OTHER';
}
}
Health Checks and Endpoints¶
Application Health Check¶
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Cache;
class HealthCheckController extends Controller
{
public function index()
{
$checks = [
'database' => $this->checkDatabase(),
'redis' => $this->checkRedis(),
'cache' => $this->checkCache(),
'queue' => $this->checkQueue(),
'storage' => $this->checkStorage(),
'external_apis' => $this->checkExternalAPIs()
];
$overall = collect($checks)->every(fn($check) => $check['status'] === 'healthy');
return response()->json([
'status' => $overall ? 'healthy' : 'unhealthy',
'timestamp' => now()->toISOString(),
'checks' => $checks,
'version' => config('app.version'),
'environment' => config('app.env')
], $overall ? 200 : 503);
}
private function checkDatabase()
{
try {
$start = microtime(true);
DB::select('SELECT 1');
$responseTime = round((microtime(true) - $start) * 1000, 2);
return [
'status' => 'healthy',
'response_time' => $responseTime,
'message' => 'Database connection successful'
];
} catch (\Exception $e) {
return [
'status' => 'unhealthy',
'message' => 'Database connection failed: ' . $e->getMessage()
];
}
}
private function checkRedis()
{
try {
$start = microtime(true);
Redis::ping();
$responseTime = round((microtime(true) - $start) * 1000, 2);
return [
'status' => 'healthy',
'response_time' => $responseTime,
'message' => 'Redis connection successful'
];
} catch (\Exception $e) {
return [
'status' => 'unhealthy',
'message' => 'Redis connection failed: ' . $e->getMessage()
];
}
}
private function checkQueue()
{
try {
$failedJobs = DB::table('failed_jobs')->count();
$queueSize = Redis::llen('queues:default');
$status = 'healthy';
$message = 'Queue system operational';
if ($failedJobs > 100) {
$status = 'warning';
$message = "High number of failed jobs: {$failedJobs}";
}
if ($queueSize > 1000) {
$status = 'warning';
$message = "Large queue backlog: {$queueSize} jobs";
}
return [
'status' => $status,
'failed_jobs' => $failedJobs,
'queue_size' => $queueSize,
'message' => $message
];
} catch (\Exception $e) {
return [
'status' => 'unhealthy',
'message' => 'Queue check failed: ' . $e->getMessage()
];
}
}
private function checkExternalAPIs()
{
$apis = [
'netsuite' => $this->checkNetSuiteAPI(),
'sso' => $this->checkSSOAPI()
];
$overallStatus = collect($apis)->every(fn($api) => $api['status'] === 'healthy')
? 'healthy' : 'degraded';
return [
'status' => $overallStatus,
'apis' => $apis
];
}
}
Detailed Health Endpoints¶
// Specific component health checks
Route::get('/health/database', [HealthCheckController::class, 'database']);
Route::get('/health/redis', [HealthCheckController::class, 'redis']);
Route::get('/health/queue', [HealthCheckController::class, 'queue']);
Route::get('/health/storage', [HealthCheckController::class, 'storage']);
Alerting System¶
Alert Configuration¶
Prometheus Alert Rules:
# alerts.yml
groups:
- name: timesheet_application
rules:
- alert: HighResponseTime
expr: avg(api_response_time) > 2000
for: 5m
labels:
severity: warning
annotations:
summary: "High API response time detected"
description: "Average response time is {{ $value }}ms"
- alert: DatabaseConnectionFailure
expr: database_connection_failures > 0
for: 1m
labels:
severity: critical
annotations:
summary: "Database connection failures detected"
description: "{{ $value }} database connection failures in the last minute"
- alert: QueueBacklog
expr: queue_size > 1000
for: 10m
labels:
severity: warning
annotations:
summary: "Large queue backlog detected"
description: "Queue size is {{ $value }} jobs"
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }} requests per second"
Custom Alert Handlers¶
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class AlertService
{
public function sendAlert($level, $title, $message, $context = [])
{
$alert = [
'level' => $level,
'title' => $title,
'message' => $message,
'context' => $context,
'timestamp' => now()->toISOString(),
'application' => 'timesheet',
'environment' => config('app.env')
];
// Log the alert
Log::channel('alerts')->log($level, $title, $alert);
// Send to notification channels based on severity
switch ($level) {
case 'critical':
$this->sendToAllChannels($alert);
break;
case 'warning':
$this->sendToSlack($alert);
$this->sendToEmail($alert);
break;
case 'info':
$this->sendToSlack($alert);
break;
}
}
private function sendToSlack($alert)
{
$webhook = config('monitoring.slack_webhook');
if (!$webhook) return;
$payload = [
'text' => $alert['title'],
'attachments' => [
[
'color' => $this->getSlackColor($alert['level']),
'fields' => [
[
'title' => 'Level',
'value' => strtoupper($alert['level']),
'short' => true
],
[
'title' => 'Environment',
'value' => $alert['environment'],
'short' => true
],
[
'title' => 'Message',
'value' => $alert['message'],
'short' => false
]
],
'ts' => now()->timestamp
]
]
];
Http::post($webhook, $payload);
}
private function sendToEmail($alert)
{
$recipients = config('monitoring.alert_emails');
foreach ($recipients as $email) {
Mail::to($email)->send(new AlertNotificationMail($alert));
}
}
private function sendToPagerDuty($alert)
{
$integrationKey = config('monitoring.pagerduty_integration_key');
if (!$integrationKey) return;
$payload = [
'routing_key' => $integrationKey,
'event_action' => 'trigger',
'payload' => [
'summary' => $alert['title'],
'severity' => $this->mapToPagerDutySeverity($alert['level']),
'source' => 'timesheet-application',
'custom_details' => $alert['context']
]
];
Http::post('https://events.pagerduty.com/v2/enqueue', $payload);
}
}
Log Management¶
Centralized Logging¶
Log Configuration:
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'elk'],
'ignore_exceptions' => false,
],
'elk' => [
'driver' => 'custom',
'via' => App\Logging\ELKLogger::class,
'host' => env('ELK_HOST', 'localhost'),
'port' => env('ELK_PORT', 5044),
'index' => 'timesheet-logs',
],
'alerts' => [
'driver' => 'daily',
'path' => storage_path('logs/alerts.log'),
'level' => 'info',
'days' => 30,
],
'performance' => [
'driver' => 'daily',
'path' => storage_path('logs/performance.log'),
'level' => 'info',
'days' => 7,
],
]
Structured Logging¶
<?php
namespace App\Services;
use Illuminate\Support\Facades\Log;
class StructuredLogger
{
public function logUserAction($action, $userId, $context = [])
{
Log::info('User action performed', [
'action' => $action,
'user_id' => $userId,
'context' => $context,
'timestamp' => now()->toISOString(),
'session_id' => session()->getId(),
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent()
]);
}
public function logAPIRequest($endpoint, $method, $responseTime, $statusCode)
{
Log::info('API request processed', [
'endpoint' => $endpoint,
'method' => $method,
'response_time' => $responseTime,
'status_code' => $statusCode,
'timestamp' => now()->toISOString(),
'user_id' => auth()->id()
]);
}
public function logIntegrationEvent($system, $operation, $status, $data = [])
{
Log::info('Integration event', [
'system' => $system,
'operation' => $operation,
'status' => $status,
'data' => $data,
'timestamp' => now()->toISOString()
]);
}
}
Maintenance Procedures¶
Automated Maintenance Tasks¶
Daily Maintenance:
// app/Console/Commands/DailyMaintenance.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class DailyMaintenance extends Command
{
protected $signature = 'maintenance:daily';
protected $description = 'Run daily maintenance tasks';
public function handle()
{
$this->info('Starting daily maintenance...');
// Clean up expired sessions
$this->call('session:gc');
// Clean up expired tokens
$this->call('auth:clear-resets');
// Optimize database
$this->call('db:optimize');
// Clear expired cache entries
$this->call('cache:prune-stale-tags');
// Generate daily reports
$this->call('reports:generate-daily');
// Check system health
$this->call('health:check');
$this->info('Daily maintenance completed.');
}
}
Weekly Maintenance:
// app/Console/Commands/WeeklyMaintenance.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class WeeklyMaintenance extends Command
{
protected $signature = 'maintenance:weekly';
protected $description = 'Run weekly maintenance tasks';
public function handle()
{
$this->info('Starting weekly maintenance...');
// Database maintenance
$this->call('db:analyze-tables');
$this->call('db:optimize-tables');
// Log rotation
$this->call('logs:rotate');
// Security scan
$this->call('security:scan');
// Performance analysis
$this->call('performance:analyze');
// Backup verification
$this->call('backup:verify');
$this->info('Weekly maintenance completed.');
}
}
Performance Optimization¶
Database Optimization:
-- Weekly database optimization queries
ANALYZE TABLE users, projects, timelogs, project_users;
OPTIMIZE TABLE users, projects, timelogs, project_users;
-- Index analysis
SELECT
TABLE_NAME,
INDEX_NAME,
CARDINALITY,
PAGES,
FILTER_CONDITION
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'timesheet'
ORDER BY CARDINALITY DESC;
-- Query performance analysis
SELECT
DIGEST_TEXT,
COUNT_STAR,
AVG_TIMER_WAIT/1000000000 as avg_time_ms,
MAX_TIMER_WAIT/1000000000 as max_time_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;
Cache Optimization:
// Cache warming strategy
public function warmCache()
{
// Warm frequently accessed data
Cache::remember('active_projects', 3600, function () {
return Project::where('status', 'active')->get();
});
Cache::remember('user_permissions', 3600, function () {
return Permission::with('roles')->get();
});
Cache::remember('system_config', 3600, function () {
return Config::all();
});
}
This comprehensive monitoring and maintenance strategy ensures the Stratpoint Timesheet Application maintains optimal performance, reliability, and availability while providing early detection and resolution of potential issues.