Logging
This guide covers Glueful's comprehensive logging system, including framework vs application logging boundaries, the LogManager, channels, performance monitoring, and production best practices.
Table of Contents
- Overview
- Framework vs Application Logging
- LogManager Core Features
- Channel-Based Logging
- Database Logging
- Query Performance Logging
- HTTP Request Logging
- Performance Monitoring
- Configuration
- Log Maintenance
- Production Best Practices
- Troubleshooting
Overview
Glueful provides a comprehensive logging system built on Monolog with advanced features designed for production environments:
Key Features
- PSR-3 Compliant: Standard logging interface with all log levels
- Clear Separation of Concerns: Framework infrastructure vs application business logic
- Advanced Performance Monitoring: Built-in timing, memory tracking, and performance analysis
- N+1 Query Detection: Automatic detection of N+1 query problems with recommendations
- Multiple Output Channels: App, API, framework, error, and debug channels
- Database Logging: Structured storage with automatic cleanup
- Intelligent Sampling: Configurable sampling rates for high-volume environments
- Security-First: Automatic sanitization of sensitive data
- Production Ready: File rotation, cleanup, and memory management
Architecture
The logging system separates concerns between:
- Framework Logging: Performance metrics, protocol errors, system health
- Application Logging: Business logic, user actions, custom events
- Query Logging: Database performance, slow queries, N+1 detection
- Request Logging: HTTP requests, responses, middleware processing
Framework vs Application Logging
Glueful follows industry best practices for framework logging boundaries, ensuring clear separation of concerns between framework infrastructure and application business logic.
✅ Framework Automatically Logs (Infrastructure Concerns)
The framework handles these logging concerns automatically:
- Unhandled Exceptions & Fatal Errors - Framework-level failures and PHP errors
- HTTP Protocol Errors - Malformed requests, routing failures, invalid JSON
- Framework Lifecycle Events - Startup, shutdown, configuration loading
- HTTP Auth Failures - Missing headers, malformed JWT tokens (protocol level)
- Slow Query Detection - Configurable performance monitoring
- HTTP Client Infrastructure Failures - Connection timeouts, DNS issues, server errors
- API Deprecation Warnings - Framework-managed endpoint versioning
🔧 Application Should Log (Business Concerns)
Your application code should handle these logging scenarios via events and custom logging:
- Business Authentication Logic - User login attempts, permission checks
- Business External Service Failures - Payment processing, email delivery status
- User Behavior Tracking - Custom analytics and business metrics
- Custom Validation - Business rule violations and domain-specific errors
- CRUD Operations - User data changes, audit trails
- Business State Changes - Order status updates, user role changes
Using Framework Events for Application Logging
The framework emits events that your application can listen to for business logging:
Security Events
use Glueful\Events\Auth\RateLimitExceededEvent;
use Glueful\Events\Http\HttpAuthFailureEvent;
use Psr\Log\LoggerInterface;
class SecurityLoggingListener
{
public function __construct(private LoggerInterface $logger) {}
public function onRateLimitExceeded(RateLimitExceededEvent $event): void
{
$this->logger->warning('Rate limit violation detected', [
'ip_address' => $event->getClientIp(),
'rule' => $event->getRule(),
'current_count' => $event->getCurrentCount(),
'limit' => $event->getLimit(),
'window_seconds' => $event->getWindowSeconds(),
'timestamp' => date('c')
]);
if ($event->isSevereViolation()) {
// blacklistIp(...); notifySecurity(...);
}
}
public function onHttpAuthFailure(HttpAuthFailureEvent $event): void
{
$this->logger->info('Authentication attempt failed', [
'reason' => $event->reason,
'ip_address' => $event->request->getClientIp(),
'endpoint' => $event->request->getPathInfo(),
'user_agent' => $event->request->headers->get('User-Agent'),
'timestamp' => date('c')
]);
}
}
Query Events
// Note: current class is Glueful\Events\Database\QueryExecutedEvent
use Glueful\Events\Database\QueryExecutedEvent;
class QueryLoggingListener
{
public function onQueryExecuted(QueryExecutedEvent $event): void
{
// Log business-specific query context
$tables = method_exists($event, 'getTables') ? ($event->getTables() ?? []) : [];
if (count(array_intersect($tables, ['orders','payments','users'])) > 0) {
$this->logger->info('Business critical query executed', [
'tables' => $tables,
'execution_time_ms' => isset($event->executionTime)
? round($event->executionTime * 1000, 2)
: null,
]);
}
}
}
Request Context Logging
Use the framework's contextual logging for consistent request correlation:
// In your controllers
public function createUser(Request $request, LoggerInterface $logger): Response
{
$logger->info('Creating new user', [
'type' => 'application',
'email' => $request->request->get('email'),
'registration_source' => 'api',
'request_id' => function_exists('request_id') ? request_id() : null,
'correlation_id' => $request->attributes->get('correlation_id')
]);
// ... business logic ...
$logger->info('User created successfully', [
'type' => 'application',
'user_uuid' => $user->uuid,
'email' => $user->email,
'request_id' => function_exists('request_id') ? request_id() : null,
'correlation_id' => $request->attributes->get('correlation_id')
]);
return Response::created($user, 'User created successfully');
}
LogManager Core Features
Basic Usage
use Glueful\Logging\LogManager;
// Get logger instance (singleton)
$logger = LogManager::getInstance();
// Basic logging with all PSR-3 levels
$logger->emergency('System is unusable');
$logger->alert('Action must be taken immediately');
$logger->critical('Critical conditions');
$logger->error('Error conditions', ['error' => $exception->getMessage()]);
$logger->warning('Warning conditions');
$logger->notice('Normal but significant condition');
$logger->info('Informational messages');
$logger->debug('Debug-level messages');
// Contextual logging
$logger->info('User login', [
'user_id' => 123,
'ip_address' => $request->getClientIp(),
'user_agent' => $request->headers->get('User-Agent')
]);
Advanced Features
Performance Timing
// Time operations
$timerId = $logger->startTimer('database_operation');
// Perform your operation
$results = $this->performDatabaseOperation();
// End timing (automatically logs duration)
$duration = $logger->endTimer($timerId);
Memory Monitoring
// Get current memory usage (formatted string)
$memoryUsage = $logger->getMemoryUsage();
// Include performance metrics in context (optional)
$logger->configure(['include_performance_metrics' => true]);
$logger->info('Processing complete');
Batch Logging
// Configure batch mode for high-volume logging
$logger->configure([
'batch_mode' => true,
'batch_size' => 100,
]);
// Or explicitly
$logger->setBatchMode(true, 100);
// Manual flush when needed
$logger->flush();
Channel-Based Logging
Glueful uses multiple channels to organize different types of logs:
Available Channels
- app - General application logging
- api - API request/response logging
- framework - Framework internals and performance
- error - Error and exception logging
- debug - Development and debugging information
Using Channels
// Channel-specific logging
$logger->channel('api')->info('API request processed', $context);
$logger->channel('error')->error('Database connection failed', $context);
$logger->channel('debug')->debug('Cache miss', ['key' => $cacheKey]);
// Switch channels
$apiLogger = $logger->channel('api');
$apiLogger->info('Request started');
$apiLogger->info('Request completed');
Note: channel names label records; writing to separate files per channel beyond the defaults requires configuring additional handlers.
Database Logging
Store logs in the database for structured querying and analysis.
Setup
Database logging uses an app_logs
table which is auto-created by the handler with fields like id
(auto-increment), uuid
, level
, message
, context
(JSON), exec_time
, batch_uuid
, channel
, and created_at
, including useful indexes.
Usage
use Glueful\Logging\LogManager;
// Enable database logging with options
$logger = LogManager::getInstance();
$logger->configureDatabaseLogging(true, [
'table' => 'app_logs',
'min_level' => 'info',
]);
// Logs are automatically stored in the database
$logger->info('User action', [
'user_id' => 123,
'action' => 'profile_update',
'ip_address' => $request->getClientIp()
]);
Pruning Database Logs
use Glueful\Logging\DatabaseLogPruner;
// Keep logs for up to 60 days and 500k records
$pruner = new DatabaseLogPruner(maxAgeInDays: 60, maxRecords: 500_000);
$result = $pruner->prune();
// $result = ['deleted_by_age' => ..., 'deleted_by_quantity' => ...]
Query Performance Logging
Glueful includes sophisticated database query logging with performance analysis.
Features
- Slow Query Detection: Configurable thresholds with automatic alerts
- N+1 Query Detection: Pattern recognition with recommendations
- Query Complexity Analysis: Scoring based on joins, subqueries, aggregations
- Performance Statistics: Comprehensive tracking by query type and performance
Basic Usage
use Glueful\Database\QueryLogger;
$queryLogger = new QueryLogger($logger);
// Enable query logging
$queryLogger->configure(true, true); // enable logging, enable analysis
// Log a query (usually automatic via database layer)
$startTime = $queryLogger->startTiming();
$result = $connection->select($sql, $params);
$queryLogger->logQuery($sql, $params, $startTime, null, 'user_lookup');
N+1 Query Detection
// Configure N+1 detection
$queryLogger->configureN1Detection(
threshold: 5, // Detect when 5+ similar queries
timeWindow: 5 // Within 5 seconds
);
// Automatic detection and recommendations
// Example log output:
// "N+1 Query Pattern Detected: 15 similar queries in 2.3 seconds
// Query: SELECT * FROM orders WHERE user_id = ?
// Recommendation: Use eager loading or joins to reduce query count"
Slow Query Analysis
Configure thresholds in config/logging.php
under framework.slow_queries
(enabled, threshold_ms, log_level). Slow queries are logged automatically by the QueryLogger
using these settings.
HTTP Request Logging
Comprehensive HTTP request and response logging via middleware.
Features
- Complete Request Lifecycle: From request start to response completion
- Performance Monitoring: Request timing and slow request detection
- Error Correlation: Request ID tracking across logs
- Context Injection: Automatic request context in all logs
Setup
Use the built-in route middleware alias request_logging
(provided by the DI container):
// Basic request/response logging
$router->get('/api/users', [UserController::class, 'index'])
->middleware(['request_logging']);
// Log requests only with headers
$router->post('/api/users', [UserController::class, 'create'])
->middleware(['request_logging:request,true,false']);
// Log everything with bodies for debugging
$router->get('/api/debug', [DebugController::class, 'info'])
->middleware(['request_logging:both,true,true,debug']);
// Performance monitoring for critical endpoints (slow threshold in ms)
$router->post('/api/payments', [PaymentController::class, 'process'])
->middleware(['request_logging:both,true,false,info,1000']);
The underlying class is Glueful\\Routing\\Middleware\\RequestResponseLoggingMiddleware
.
Features
// Automatic logging includes:
// - Request method, URL, headers
// - Request body (sanitized)
// - Response status, headers
// - Execution time
// - Memory usage
// - User context (if authenticated)
// Example log output:
// [INFO] HTTP Request: POST /api/users
// Context: {
// "request_id": "req_abc123",
// "method": "POST",
// "url": "/api/users",
// "user_id": 123,
// "ip_address": "192.168.1.100",
// "duration_ms": 45.2,
// "memory_usage_mb": 2.3,
// "status_code": 201
// }
Performance Monitoring
Built-in Performance Tracking
// Execution time tracking
$timer = $logger->startTimer('complex_calculation');
$result = $this->performComplexCalculation();
$elapsedMs = $logger->endTimer($timer);
// Request correlation in logs via processors/helpers
$logger->info('Processing started', [
'request_id' => function_exists('request_id') ? request_id() : null,
]);
Performance Alerts
Slow request detection is part of the HTTP request logging middleware and can be configured per-route (e.g., request_logging:both,true,false,info,1000
). Slow query thresholds are configured in config/logging.php
under framework.slow_queries
.
Configuration
Framework Logging Settings
Framework logging is configured in config/logging.php
:
'framework' => [
'enabled' => env('FRAMEWORK_LOGGING_ENABLED', true),
'level' => env('FRAMEWORK_LOG_LEVEL', 'info'),
'channel' => env('FRAMEWORK_LOG_CHANNEL', 'framework'),
// Feature-specific toggles
'log_exceptions' => env('LOG_FRAMEWORK_EXCEPTIONS', true),
'log_deprecations' => env('LOG_FRAMEWORK_DEPRECATIONS', true),
'log_lifecycle' => env('LOG_FRAMEWORK_LIFECYCLE', true),
'log_protocol_errors' => env('LOG_FRAMEWORK_PROTOCOL_ERRORS', true),
// Performance monitoring (optional)
'slow_requests' => [
'enabled' => env('LOG_SLOW_REQUESTS', true),
'threshold_ms' => env('SLOW_REQUEST_THRESHOLD', 1000),
],
'slow_queries' => [
'enabled' => env('LOG_SLOW_QUERIES', true),
'threshold_ms' => env('SLOW_QUERY_THRESHOLD', 200),
],
'http_client' => [
'log_failures' => env('LOG_HTTP_CLIENT_FAILURES', true),
'slow_threshold_ms' => env('HTTP_CLIENT_SLOW_THRESHOLD', 5000)
]
],
'channels' => [
'app' => [
'driver' => 'daily',
'path' => 'storage/logs/app.log',
'level' => 'info',
'days' => 14
],
'api' => [
'driver' => 'daily',
'path' => 'storage/logs/api.log',
'level' => 'info',
'days' => 30
],
'error' => [
'driver' => 'daily',
'path' => 'storage/logs/error.log',
'level' => 'error',
'days' => 90
]
],
'database' => [
'enabled' => false,
'table' => 'app_logs',
'retention_days' => 30
]
Environment Variables
# Framework Logging (Infrastructure/Protocol concerns)
FRAMEWORK_LOGGING_ENABLED=true
FRAMEWORK_LOG_LEVEL=info
LOG_FRAMEWORK_EXCEPTIONS=true
LOG_FRAMEWORK_DEPRECATIONS=true
LOG_FRAMEWORK_LIFECYCLE=true
LOG_FRAMEWORK_PROTOCOL_ERRORS=true
# Framework Performance Monitoring
LOG_SLOW_REQUESTS=true
SLOW_REQUEST_THRESHOLD=1000
LOG_SLOW_QUERIES=true
SLOW_QUERY_THRESHOLD=200
LOG_HTTP_CLIENT_FAILURES=true
HTTP_CLIENT_SLOW_THRESHOLD=5000
# Application logging
APP_LOG_LEVEL=info
DATABASE_LOGGING_ENABLED=false
LOG_RETENTION_DAYS=30
LOG_SAMPLING_RATE=1.0
Deprecation Management
Configure deprecated API endpoints in config/api.php
:
'deprecated_routes' => [
'/api/v1/users' => [
'since' => '2.0.0',
'removal_version' => '3.0.0',
'replacement' => '/api/v2/users',
'reason' => 'Improved user data structure'
],
'GET /api/legacy/*' => [
'since' => '1.5.0',
'removal_version' => '2.0.0',
'replacement' => '/api/v2/*'
]
]
The framework will automatically:
- Log deprecation warnings to the framework channel
- Add deprecation headers to responses
- Provide client guidance for migration
Log Maintenance
Automatic Cleanup
For database logs, use the DatabaseLogPruner
to prune old records by age and quantity. Files are rotated daily by the rotating handlers.
use Glueful\Logging\DatabaseLogPruner;
$pruner = new DatabaseLogPruner(maxAgeInDays: 30, maxRecords: 1_000_000);
$result = $pruner->prune();
There is also an internal LogCleanupTask
you can schedule to trim file and database logs based on retention settings.
Manual Maintenance
Rotate and prune logs via your scheduler/cron by invoking the above utilities in your preferred cadence.
Production Best Practices
Framework Logging (Production)
FRAMEWORK_LOGGING_ENABLED=true
FRAMEWORK_LOG_LEVEL=error # Only log errors in production
LOG_FRAMEWORK_EXCEPTIONS=true
LOG_FRAMEWORK_DEPRECATIONS=true
LOG_FRAMEWORK_LIFECYCLE=false # Reduce noise
LOG_FRAMEWORK_PROTOCOL_ERRORS=true
# Performance monitoring (adjust thresholds for production)
SLOW_REQUEST_THRESHOLD=2000 # 2 seconds
SLOW_QUERY_THRESHOLD=500 # 500ms
Application Logging (Production)
LOG_LEVEL=error # Application should log errors/warnings
LOG_TO_FILE=true
LOG_TO_DB=false # Consider impact on performance
LOG_ROTATION_DAYS=30
High-Volume Environments
// Configure for production
$logger->configure([
'sampling_rate' => 0.1, // Log only 10% of entries
'batch_mode' => true, // Batch writes for performance
'batch_size' => 100, // Write 100 entries at once
'minimum_level' => 'warning', // Only log warnings and above
]);
Security Configuration
Sensitive keys in log context (e.g., password, token, key, secret, auth, credential) are automatically sanitized.
Monitoring and Alerting
Framework Logs to Monitor
- High frequency of HTTP protocol errors (potential attacks)
- Unhandled exceptions (framework stability)
- Slow query/request patterns (performance issues)
- Deprecation usage (migration planning)
Application Logs to Monitor
- Authentication failure patterns (security)
- Business critical operation failures (reliability)
- User behavior anomalies (fraud detection)
- External service failures (integration health)
Troubleshooting
Common Issues
No framework logs appearing
- Check
FRAMEWORK_LOGGING_ENABLED=true
- Verify log file permissions
- Check framework log level configuration
Too many framework logs
- Increase
FRAMEWORK_LOG_LEVEL
(debug → info → warning → error) - Disable specific features (lifecycle, deprecations)
- Adjust performance thresholds
Missing application context
- Ensure event listeners are registered
- Use contextual logger in controllers
- Verify user context is available after authentication
High Memory Usage
// Check memory configuration
$logger->configure([
'batch_size' => 50 // Smaller batches
]);
Slow Logging Performance
// Optimize for speed
$logger->configure([
'sampling_rate' => 0.05, // Log only 5%
'minimum_level' => 'error', // Only errors
'batch_mode' => true
]);
Log File Locations
- Framework:
storage/logs/framework-YYYY-MM-DD.log
- Application:
storage/logs/app.log-YYYY-MM-DD
- Debug:
storage/logs/debug.log-YYYY-MM-DD
- Errors:
storage/logs/error.log-YYYY-MM-DD
Best Practices
✅ Do
- Use events for business logging
- Leverage contextual logging for request correlation
- Log user actions and business state changes
- Configure appropriate log levels for each environment
- Use structured logging with consistent field names
❌ Don't
- Log business logic in framework middleware
- Duplicate framework logging in application code
- Log sensitive information (passwords, tokens, PII)
- Create custom logging for framework concerns
- Mix business and infrastructure logging
Summary
Glueful's logging architecture provides:
✅ Automatic framework logging for infrastructure concerns
🔧 Event-driven application logging for business concerns
📊 Request context correlation for debugging
⚡ Configurable performance monitoring
🔒 Security-focused event emission
📈 Production-ready log management
🚀 Advanced Performance Monitoring: Automatic detection of slow queries, N+1 problems, and performance issues
🏭 Production-Ready Features: Sampling, batching, rotation, and cleanup
💾 Multiple Storage Options: Files, database, and custom handlers
🔒 Security-First Design: Automatic data sanitization and secure logging practices
🛠️ Developer-Friendly: Rich debugging information and performance insights
This approach ensures you get comprehensive infrastructure monitoring automatically while maintaining full control over your application-specific logging requirements.