Pramnos Framework - Logging System Guide¶
The Pramnos Framework includes a comprehensive logging system with structured logging support, log analytics, file management, and advanced viewing capabilities. This guide covers all aspects of the logging system from basic usage to advanced features.
Table of Contents¶
- Architecture Overview
- Basic Logging Usage
- Structured Logging
- Log Levels and PSR-3 Compliance
- Log File Management
- Log Viewer and Analytics
- Log Migration and Format Conversion
- Console Commands
- Web Interface
- Performance and Best Practices
- Configuration
- Advanced Features
Architecture Overview¶
The Pramnos logging system consists of several key components:
src/Pramnos/Logs/
├── Logger.php # Core logging functionality
├── LogManager.php # Log file management and analytics
├── LogViewer.php # Log reading and viewing
├── LogMigrator.php # Format migration tools
└── Application/Controllers/LogController.php # Web interface
Key Features¶
- PSR-3 Compatible: Standard log levels and interfaces
- Structured Logging: JSON-based log entries with context
- Real-time Viewing: Live log monitoring with auto-refresh
- Analytics Dashboard: Trends, error tracking, and statistics
- File Management: Rotation, archiving, and cleanup
- Export Capabilities: CSV, JSON, and ZIP export formats
- Search and Filtering: Advanced log search with multiple criteria
- Migration Tools: Convert legacy log formats to structured format
Basic Logging Usage¶
Simple Logging¶
use Pramnos\Logs\Logger;
// Basic log entry
Logger::log('User login successful', 'application');
// Log with custom file and extension
Logger::log('Database connection established', 'database', 'log');
// Log to beginning of file (for critical messages)
Logger::log('System startup initiated', 'system', 'log', true);
Using Log Level Methods¶
// Emergency: System is unusable
Logger::emergency('Database server is down');
// Alert: Action must be taken immediately
Logger::alert('Disk space critically low');
// Critical: Critical conditions
Logger::critical('Application component failed');
// Error: Runtime errors that do not require immediate action
Logger::error('Failed to send email notification');
// Warning: Exceptional occurrences that are not errors
Logger::warning('Deprecated function called');
// Notice: Normal but significant events
Logger::notice('User password changed');
// Info: Interesting events
Logger::info('User logged in successfully');
// Debug: Detailed debug information
Logger::debug('Processing user data', ['user_id' => 123]);
Structured Logging¶
Adding Context to Log Entries¶
// Log with additional context
Logger::info('User action performed', [
'user_id' => 123,
'action' => 'profile_update',
'ip_address' => '192.168.1.1',
'user_agent' => 'Mozilla/5.0...',
'timestamp' => time(),
'session_id' => session_id()
]);
// Error logging with stack trace
try {
// Some operation
} catch (Exception $e) {
Logger::error('Operation failed', [
'exception' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
'context' => [
'user_id' => $user->id ?? null,
'operation' => 'data_processing'
]
]);
}
Performance Logging¶
// Performance monitoring
$startTime = microtime(true);
// ... perform operation ...
$endTime = microtime(true);
$duration = $endTime - $startTime;
Logger::info('Operation completed', [
'operation' => 'data_export',
'duration_ms' => round($duration * 1000, 2),
'memory_peak' => memory_get_peak_usage(true),
'memory_current' => memory_get_usage(true),
'records_processed' => $recordCount
]);
Database Operation Logging¶
// Database query logging
Logger::debug('Database query executed', [
'query' => $sql,
'params' => $parameters,
'execution_time' => $queryTime,
'affected_rows' => $affectedRows,
'connection' => $dbConnection->getName()
]);
Log Levels and PSR-3 Compliance¶
The logging system follows PSR-3 standards with eight severity levels:
| Level | Constant | Usage |
|---|---|---|
| emergency | Logger::LEVEL_EMERGENCY |
System is unusable |
| alert | Logger::LEVEL_ALERT |
Action must be taken immediately |
| critical | Logger::LEVEL_CRITICAL |
Critical conditions |
| error | Logger::LEVEL_ERROR |
Runtime errors |
| warning | Logger::LEVEL_WARNING |
Exceptional occurrences that are not errors |
| notice | Logger::LEVEL_NOTICE |
Normal but significant events |
| info | Logger::LEVEL_INFO |
Interesting events |
| debug | Logger::LEVEL_DEBUG |
Detailed debug information |
Level-Based Filtering¶
// Only log errors and above in production
if (ENVIRONMENT === 'production') {
Logger::setMinimumLevel(Logger::LEVEL_ERROR);
}
// Log everything in development
if (ENVIRONMENT === 'development') {
Logger::setMinimumLevel(Logger::LEVEL_DEBUG);
}
Log File Management¶
File Rotation¶
use Pramnos\Logs\Logger;
// Rotate log file when it exceeds 10MB, keep 5 backups
Logger::truncateLogFile('application', 'log', 10 * 1024 * 1024, true, 5);
// Simple truncation without backup
Logger::truncateLogFile('debug', 'log', 5 * 1024 * 1024, false);
Automatic Log Management¶
use Pramnos\Logs\LogManager;
// Get statistics for all log files
$stats = LogManager::getLogFiles(true, true);
foreach ($stats as $file => $info) {
echo "File: {$file}, Size: {$info['size']}, Lines: {$info['lines']}\n";
}
// Archive old log files (older than 30 days)
$result = LogManager::archiveOldLogs(30);
echo "Archived {$result['archived']} files\n";
// Clear specific log files
LogManager::clearLog('debug'); // Clear debug.log
LogManager::clearLog('application', 'log'); // Clear application.log
Log File Statistics¶
// Get detailed statistics for a log file
$stats = LogManager::getLogFileStats('application', 'log');
echo "File size: {$stats['size_formatted']}\n";
echo "Line count: {$stats['lines']}\n";
echo "JSON percentage: {$stats['json_percentage']}%\n";
echo "Modified: {$stats['modified_formatted']}\n";
// Level distribution
foreach ($stats['level_distribution'] as $level => $count) {
echo "Level {$level}: {$count} entries\n";
}
Log Viewer and Analytics¶
Real-time Log Viewing¶
use Pramnos\Logs\LogViewer;
// Create log viewer instance
$viewer = new LogViewer();
// Configure viewing options
$viewer->setFile('application.log')
->setParameters(
true, // reverse order (newest first)
1, // page number
50, // max lines per page
'error' // search term
)
->setLogLevel('error'); // filter by level
// Get log content
$result = $viewer->getLogContent();
// Display results
foreach ($result['lines'] as $line) {
echo $line . "\n";
}
echo "Total lines: {$result['total']}\n";
echo "Matched lines: {$result['matched_total']}\n";
Log Analytics¶
use Pramnos\Logs\LogManager;
// Get analytics for the last 24 hours
$analytics = LogManager::getLogAnalytics(
'application', // filename
'log', // extension
time() - 86400, // start time (24 hours ago)
time(), // end time (now)
'hour' // group by hour
);
// Display trends
echo "Log entry trends:\n";
foreach ($analytics['trends'] as $timestamp => $count) {
$time = date('H:i', $timestamp);
echo "{$time}: {$count} entries\n";
}
// Display level distribution
echo "\nLevel distribution:\n";
foreach ($analytics['levels'] as $level => $count) {
echo "{$level}: {$count} entries\n";
}
// Display top errors
echo "\nTop errors:\n";
foreach ($analytics['topErrors'] as $error) {
echo "Count: {$error['count']}, Message: {$error['message']}\n";
}
Advanced Filtering¶
// Filter logs by multiple criteria
$entries = LogManager::getFilteredLogEntries(
'application', // filename
'log', // extension
['error', 'critical'], // levels to include
strtotime('-1 week'), // start timestamp
time(), // end timestamp
'database', // search query
100 // limit
);
foreach ($entries as $entry) {
echo "[{$entry['timestamp']}] {$entry['level']}: {$entry['message']}\n";
if (!empty($entry['context'])) {
echo "Context: " . json_encode($entry['context']) . "\n";
}
}
Log Migration and Format Conversion¶
Migrating Legacy Log Formats¶
use Pramnos\Logs\LogMigrator;
// Create migrator with progress callback
$migrator = new LogMigrator(function($processed, $total) {
$percent = round(($processed / $total) * 100);
echo "Progress: {$percent}% ({$processed}/{$total})\n";
});
// Migrate a single file
$stats = $migrator->migrateFile('/path/to/old-format.log', true);
echo "Migration completed:\n";
echo "Total lines: {$stats['total_lines']}\n";
echo "Converted lines: {$stats['converted_lines']}\n";
echo "Errors: {$stats['errors']}\n";
// Migrate all files in directory
$files = glob('/path/to/logs/*.log');
foreach ($files as $file) {
try {
$stats = $migrator->migrateFile($file, true);
echo "Migrated {$file}: {$stats['converted_lines']} lines\n";
} catch (Exception $e) {
echo "Failed to migrate {$file}: " . $e->getMessage() . "\n";
}
}
Bulk Processing¶
// Process multiple files with error handling
$migrator = new LogMigrator();
$results = $migrator->migrateDirectory('/path/to/logs', [
'backup' => true,
'skip_errors' => false,
'chunk_size' => 8 * 1024 * 1024 // 8MB chunks
]);
foreach ($results as $file => $result) {
if ($result['success']) {
echo "✓ {$file}: {$result['converted_lines']} lines converted\n";
} else {
echo "✗ {$file}: {$result['error']}\n";
}
}
Console Commands¶
Migrate Logs Command¶
# Migrate a single log file
php bin/pramnos migrate:logs /path/to/file.log --backup
# Migrate all logs in directory
php bin/pramnos migrate:logs /path/to/logs --all --backup
# Migrate without backup
php bin/pramnos migrate:logs /path/to/file.log --no-backup
# Display help
php bin/pramnos migrate:logs --help
Log Management Commands¶
# View log file statistics
php bin/pramnos logs:stats
# Rotate large log files
php bin/pramnos logs:rotate --max-size=10 --backups=5
# Archive old logs
php bin/pramnos logs:archive --days=30
# Clear debug logs
php bin/pramnos logs:clear debug
# Export logs to CSV
php bin/pramnos logs:export application.log --format=csv
# Monitor logs in real-time
php bin/pramnos logs:tail application.log --follow
Web Interface¶
Accessing the Log Viewer¶
The web interface is accessible through the LogController:
/logs # Main log listing
/logs/view/filename # View specific log file
/logs/stats # Log file statistics
/logs/dashboard # Analytics dashboard
/logs/export # Export logs
/logs/rotate # Rotate log files
/logs/archive # Archive old logs
/logs/search # Advanced search
Log Viewer Features¶
- Real-time Updates: Auto-refresh capabilities
- Search and Filter: Full-text search with regex support
- Level Filtering: Filter by log levels
- Pagination: Navigate through large log files
- Export Options: Download as CSV, JSON, or raw format
- Date Range Export: Export logs within specific time ranges
- Context Display: Expandable JSON context for structured logs
Dashboard Analytics¶
The dashboard provides:
- Trend Charts: Visual representation of log entry patterns
- Level Distribution: Pie charts showing log level breakdown
- Error Tracking: Top errors with occurrence counts
- System Status: Health overview across all log files
- Performance Metrics: Response times and resource usage
Performance and Best Practices¶
Optimal Logging Practices¶
// 1. Use appropriate log levels
Logger::debug('Detailed debugging info'); // Development only
Logger::info('Normal application flow'); // General information
Logger::warning('Unusual but handled event'); // Potential issues
Logger::error('Error that needs attention'); // Actual problems
// 2. Include relevant context
Logger::error('Database query failed', [
'query' => $sql,
'error' => $e->getMessage(),
'user_id' => $currentUser->id,
'execution_time' => $queryTime
]);
// 3. Avoid logging sensitive data
Logger::info('User login attempt', [
'username' => $username,
'ip' => $ipAddress,
// DON'T LOG: 'password' => $password
'success' => $loginSuccess
]);
// 4. Use structured logging for complex data
Logger::info('API request processed', [
'endpoint' => $endpoint,
'method' => $method,
'status_code' => $response->getStatusCode(),
'response_time' => $responseTime,
'request_id' => $requestId
]);
Performance Considerations¶
// 1. Conditional debug logging
if (Logger::isDebugEnabled()) {
Logger::debug('Expensive debug info', [
'data' => $expensiveToSerializeData
]);
}
// 2. Lazy evaluation for expensive operations
Logger::info('Operation result', [
'result' => function() use ($expensiveOperation) {
return $expensiveOperation->getResult();
}
]);
// 3. Log rotation to prevent large files
Logger::truncateLogFile('application', 'log', 50 * 1024 * 1024); // 50MB max
// 4. Archive old logs regularly
LogManager::archiveOldLogs(30); // Archive logs older than 30 days
Memory Management¶
// Use log viewer with chunked reading for large files
$viewer = new LogViewer();
$viewer->setFile('large-log.log')
->setChunkSize(8 * 1024 * 1024) // 8MB chunks
->setMaxLines(100); // Limit results
// Process logs in batches
LogManager::processLogFile('application', 'log', function($line) {
// Process each line
echo $line . "\n";
}, 1000); // Process 1000 lines at a time
Configuration¶
Log Directory Structure¶
app/logs/
├── application.log # Main application logs
├── error.log # Error-level logs only
├── debug.log # Debug information
├── performance.log # Performance metrics
├── security.log # Security events
├── api.log # API request logs
└── archives/ # Archived log files
├── logs_2024-01-01.zip
└── logs_2024-01-02.zip
Environment-Specific Configuration¶
// config/logging.php
return [
'default_level' => ENVIRONMENT === 'production' ? 'error' : 'debug',
'max_file_size' => 50 * 1024 * 1024, // 50MB
'max_backups' => 5,
'archive_after_days' => 30,
'structured_logging' => true,
'include_context' => ENVIRONMENT !== 'production',
'files' => [
'application' => [
'level' => 'info',
'max_size' => 100 * 1024 * 1024
],
'error' => [
'level' => 'error',
'max_size' => 50 * 1024 * 1024
],
'debug' => [
'level' => 'debug',
'max_size' => 200 * 1024 * 1024,
'enabled' => ENVIRONMENT !== 'production'
]
]
];
Advanced Features¶
Custom Log Handlers¶
// Create custom log handler
class EmailAlertHandler extends Logger
{
public static function critical($message, $context = [])
{
// Log normally
parent::critical($message, $context);
// Send email alert for critical errors
$emailData = [
'subject' => 'Critical Error Alert',
'message' => $message,
'context' => $context,
'timestamp' => date('Y-m-d H:i:s'),
'server' => $_SERVER['SERVER_NAME'] ?? 'Unknown'
];
// Send email using framework's email system
Email::send('admin@example.com', 'Critical Error Alert', $emailData);
}
}
// Use custom handler
EmailAlertHandler::critical('Database connection lost');
Log Aggregation¶
// Aggregate logs from multiple sources
class LogAggregator
{
public static function aggregateLogs($startDate, $endDate)
{
$aggregated = [];
$logFiles = ['application', 'api', 'error', 'performance'];
foreach ($logFiles as $logFile) {
$entries = LogManager::getFilteredLogEntries(
$logFile, 'log', [],
strtotime($startDate),
strtotime($endDate)
);
foreach ($entries as $entry) {
$entry['source'] = $logFile;
$aggregated[] = $entry;
}
}
// Sort by timestamp
usort($aggregated, function($a, $b) {
return strtotime($a['timestamp']) - strtotime($b['timestamp']);
});
return $aggregated;
}
}
// Use aggregator
$logs = LogAggregator::aggregateLogs('2024-01-01', '2024-01-31');
Real-time Log Monitoring¶
// WebSocket-based real-time log monitoring
class LogMonitor
{
private $watchers = [];
public function watchFile($filename, $callback)
{
$this->watchers[$filename] = [
'callback' => $callback,
'last_position' => filesize(Logger::getLogPath($filename, 'log'))
];
}
public function monitor()
{
while (true) {
foreach ($this->watchers as $filename => $watcher) {
$filepath = Logger::getLogPath($filename, 'log');
$currentSize = filesize($filepath);
if ($currentSize > $watcher['last_position']) {
$handle = fopen($filepath, 'r');
fseek($handle, $watcher['last_position']);
while (($line = fgets($handle)) !== false) {
call_user_func($watcher['callback'], trim($line));
}
$this->watchers[$filename]['last_position'] = $currentSize;
fclose($handle);
}
}
usleep(100000); // 100ms delay
}
}
}
// Use monitor
$monitor = new LogMonitor();
$monitor->watchFile('application', function($line) {
echo "New log entry: {$line}\n";
});
$monitor->monitor();
Log Encryption¶
// Encrypt sensitive log data
class EncryptedLogger extends Logger
{
private static function encryptContext($context)
{
if (empty($context)) return $context;
$sensitiveFields = ['password', 'token', 'api_key', 'credit_card'];
foreach ($sensitiveFields as $field) {
if (isset($context[$field])) {
$context[$field] = self::encrypt($context[$field]);
}
}
return $context;
}
private static function encrypt($data)
{
$key = ENCRYPTION_KEY;
$iv = openssl_random_pseudo_bytes(16);
$encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, 0, $iv);
return base64_encode($iv . $encrypted);
}
public static function log($message, $file = 'application', $ext = 'log', $startoffile = false, $context = [])
{
$context = self::encryptContext($context);
parent::log($message, $file, $ext, $startoffile, $context);
}
}
Troubleshooting¶
Common Issues¶
- Large Log Files: Use log rotation and archiving
- Memory Issues: Process logs in chunks
- Permission Problems: Ensure proper directory permissions
- Missing Context: Verify structured logging is enabled
- Search Performance: Use indexed search for large files
Debugging Log Issues¶
// Check log system health
$health = [
'log_directory_exists' => is_dir(LOG_PATH . '/logs'),
'log_directory_writable' => is_writable(LOG_PATH . '/logs'),
'disk_space' => disk_free_space(LOG_PATH),
'active_log_files' => count(LogManager::getLogFiles()),
'total_log_size' => LogManager::getTotalLogSize()
];
foreach ($health as $check => $status) {
echo "{$check}: " . ($status ? 'OK' : 'FAILED') . "\n";
}
Related Documentation¶
- Framework Guide - Core framework concepts
- Database API Guide - Database operations
- Authentication Guide - User authentication
- Console Commands Guide - CLI tools and commands
- Cache System Guide - Caching strategies
The Pramnos Framework logging system provides enterprise-grade logging capabilities with comprehensive tools for development, monitoring, and production environments. For additional support or advanced configurations, refer to the framework documentation or community resources.