KLogger

Simple and lightweight PSR-3 compliant logging library. Lighter than Monolog, provides basic file logging functionality. Easy configuration, suitable for small-scale projects and lightweight logging requirements. Selected when minimizing dependencies is desired.

Logging LibraryPHPPSR-3LightweightSimpleFile Logging

Library

KLogger

Overview

KLogger is a simple and lightweight PSR-3 compliant PHP logging library designed with emphasis on "quickly incorporating into projects and starting immediate use," optimized for small-scale projects and lightweight logging requirements. Providing basic file logging functionality with less overhead than Monolog, it enables rapid log implementation without complex configuration processes. It's a valuable choice for scenarios requiring minimal dependencies or for educational purposes when introducing PSR-3 logging.

Details

KLogger continues to serve as a lightweight logging solution for small-scale PHP applications in 2025. Ensuring compatibility with standard logging interfaces through complete PSR-3 compliance, it supports 8 standard log levels (emergency, alert, critical, error, warning, notice, info, debug). Simple file-based log recording functionality eliminates configuration complexity and enables immediate use. It provides a basic yet practical feature set including JSON format log output, log level threshold settings, and custom format functionality.

Key Features

  • Complete PSR-3 Compliance: Compatibility guarantee through standard logging interface
  • Lightweight Design: Lighter than Monolog with minimized resource usage
  • Immediate Use: Rapid log implementation without complex configuration
  • File-Based Logging: Simple and reliable file output functionality
  • Log Level Control: Output level adjustment through threshold settings
  • Customizable: Flexibility in log format and output settings

Pros and Cons

Pros

  • PSR-3 compliance ensures compatibility and interchangeability with other libraries
  • Lighter than Monolog, suitable for use in resource-constrained environments
  • Easy configuration with low learning cost enabling rapid adoption
  • Simple and understandable API suitable for small-scale projects
  • Minimal dependencies reducing risk of conflicts with third-party libraries
  • JSON format output enables easy integration with log analysis tools

Cons

  • Limited advanced log management features (handlers, processors, formatters)
  • Lack of enterprise-level features like multiple output destinations or remote log transmission
  • Inferior ecosystem and extension richness compared to Monolog
  • Insufficient detailed log control features for large-scale applications
  • Absence of asynchronous log processing and buffering functionality
  • Difficult implementation of complex log filtering and conditional processing

Reference Pages

Usage Examples

Installation and Basic Setup

# Installation via Composer
composer require katzgrau/klogger

# Check dependencies
composer show katzgrau/klogger

# Project dependencies (composer.json)
{
    "require": {
        "katzgrau/klogger": "^1.2"
    }
}
<?php
require 'vendor/autoload.php';

use Katzgrau\KLogger\Logger;
use Psr\Log\LogLevel;

// Basic initialization
$logger = new Logger('/var/log/app');

// Initialization with log level setting
$logger = new Logger('/var/log/app', LogLevel::WARNING);

// Initialization with option settings
$logger = new Logger(
    '/var/log/app',           // Log directory
    LogLevel::INFO,           // Minimum log level
    [
        'extension' => 'log',         // File extension
        'prefix' => 'app_',          // File name prefix
        'logFormat' => '[{date}] [{level}] {message}',  // Log format
        'appendContext' => true       // Append context information
    ]
);

// PSR-3 interface confirmation
echo "PSR-3 Logger Interface: " . ($logger instanceof \Psr\Log\LoggerInterface ? 'Yes' : 'No') . "\n";
?>

Basic Log Level Usage (8 Level Support)

<?php
use Katzgrau\KLogger\Logger;
use Psr\Log\LogLevel;

// Logger initialization
$logger = new Logger('/var/log/myapp', LogLevel::DEBUG);

// 8 PSR-3 log level usage examples
$logger->emergency('System is completely unusable');
$logger->alert('Immediate action required: Database server down');
$logger->critical('Critical error: Payment processing system failure');
$logger->error('Error: User authentication failed');
$logger->warning('Warning: Approaching API call limit');
$logger->notice('Notice: New user registered');
$logger->info('Info: Batch processing completed successfully');
$logger->debug('Debug: Variable value check - $userId = 12345');

// Log with context information
$context = [
    'user_id' => 12345,
    'action' => 'login_attempt',
    'ip_address' => '192.168.1.100',
    'user_agent' => 'Mozilla/5.0...'
];

$logger->info('User login attempt', $context);
$logger->error('Login failed', array_merge($context, ['reason' => 'invalid_password']));

// Log usage in business logic
class UserService {
    private $logger;
    
    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
    
    public function authenticateUser($username, $password) {
        $this->logger->info('User authentication started', ['username' => $username]);
        
        try {
            if (empty($username) || empty($password)) {
                $this->logger->warning('Authentication failed: Empty credentials', ['username' => $username]);
                return false;
            }
            
            $user = $this->findUserByUsername($username);
            if (!$user) {
                $this->logger->notice('Authentication failed: User not found', ['username' => $username]);
                return false;
            }
            
            if ($this->verifyPassword($password, $user['password_hash'])) {
                $this->logger->info('Authentication successful', [
                    'user_id' => $user['id'],
                    'username' => $username,
                    'last_login' => date('c')
                ]);
                return true;
            } else {
                $this->logger->warning('Authentication failed: Password mismatch', ['username' => $username]);
                return false;
            }
            
        } catch (Exception $e) {
            $this->logger->error('Error occurred during authentication', [
                'username' => $username,
                'error' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
            return false;
        }
    }
}

// Usage example
$logger = new Logger('/var/log/userservice', LogLevel::INFO);
$userService = new UserService($logger);
$result = $userService->authenticateUser('[email protected]', 'password123');
?>

Log Format and Customization

<?php
use Katzgrau\KLogger\Logger;
use Psr\Log\LogLevel;

// 1. Default format
$defaultLogger = new Logger('/var/log/default');
$defaultLogger->info('Default format log message');
// Output example: [2025-06-24 10:30:45.123456] [info] Default format log message

// 2. Custom format
$customLogger = new Logger('/var/log/custom', LogLevel::DEBUG, [
    'logFormat' => '[{date}] [{level}] {message} | Context: {context}',
    'dateFormat' => 'Y-m-d H:i:s T'
]);
$customLogger->info('Custom format test', ['key' => 'value']);

// 3. JSON format log
$jsonLogger = new Logger('/var/log/json', LogLevel::DEBUG, [
    'logFormat' => false,  // Disable default format
    'extension' => 'json'
]);

// Manual JSON log implementation
class JsonKLogger extends Logger {
    public function log($level, $message, array $context = []) {
        $logData = [
            'timestamp' => date('c'),
            'level' => $level,
            'message' => $message,
            'context' => $context,
            'pid' => getmypid(),
            'memory_usage' => memory_get_usage(),
            'request_id' => $_SERVER['HTTP_X_REQUEST_ID'] ?? uniqid()
        ];
        
        $this->writeToFile(json_encode($logData) . PHP_EOL);
    }
    
    private function writeToFile($content) {
        $logFile = $this->getLogDirectory() . '/' . date('Y-m-d') . '.json';
        file_put_contents($logFile, $content, FILE_APPEND | LOCK_EX);
    }
    
    private function getLogDirectory() {
        // KLogger internal directory retrieval method
        return '/var/log/json-logs';
    }
}

// 4. Multiple file output
class MultiFileLogger {
    private $loggers = [];
    
    public function __construct($baseDir) {
        $this->loggers = [
            'error' => new Logger($baseDir . '/errors', LogLevel::ERROR),
            'info' => new Logger($baseDir . '/info', LogLevel::INFO),
            'debug' => new Logger($baseDir . '/debug', LogLevel::DEBUG)
        ];
    }
    
    public function log($level, $message, $context = []) {
        // Error level logs to dedicated file
        if (in_array($level, [LogLevel::ERROR, LogLevel::CRITICAL, LogLevel::ALERT, LogLevel::EMERGENCY])) {
            $this->loggers['error']->log($level, $message, $context);
        }
        
        // Info level logs
        if (in_array($level, [LogLevel::INFO, LogLevel::NOTICE])) {
            $this->loggers['info']->log($level, $message, $context);
        }
        
        // Debug logs
        if ($level === LogLevel::DEBUG) {
            $this->loggers['debug']->log($level, $message, $context);
        }
    }
    
    public function error($message, $context = []) {
        $this->log(LogLevel::ERROR, $message, $context);
    }
    
    public function info($message, $context = []) {
        $this->log(LogLevel::INFO, $message, $context);
    }
    
    public function debug($message, $context = []) {
        $this->log(LogLevel::DEBUG, $message, $context);
    }
}

// Usage example
$multiLogger = new MultiFileLogger('/var/log/myapp');
$multiLogger->error('Database connection error', ['host' => 'localhost', 'port' => 3306]);
$multiLogger->info('User login', ['user_id' => 123]);
$multiLogger->debug('Variable dump', ['data' => $someData]);

// 5. Logger with log rotation
class RotatingKLogger extends Logger {
    private $maxFiles;
    private $maxSize;
    
    public function __construct($dir, $level = LogLevel::DEBUG, $options = [], $maxFiles = 7, $maxSize = 10485760) {
        parent::__construct($dir, $level, $options);
        $this->maxFiles = $maxFiles;
        $this->maxSize = $maxSize;
    }
    
    public function log($level, $message, array $context = []) {
        $this->rotateLogsIfNeeded();
        parent::log($level, $message, $context);
    }
    
    private function rotateLogsIfNeeded() {
        $logFile = $this->getLogDirectory() . '/' . date('Y-m-d') . '.log';
        
        if (file_exists($logFile) && filesize($logFile) > $this->maxSize) {
            $this->rotateLogFiles($logFile);
        }
    }
    
    private function rotateLogFiles($currentLog) {
        $baseLog = $currentLog;
        
        // Move existing rotation files
        for ($i = $this->maxFiles - 1; $i >= 1; $i--) {
            $oldFile = $baseLog . '.' . $i;
            $newFile = $baseLog . '.' . ($i + 1);
            
            if (file_exists($oldFile)) {
                if ($i == $this->maxFiles - 1) {
                    unlink($oldFile);  // Delete oldest file
                } else {
                    rename($oldFile, $newFile);
                }
            }
        }
        
        // Rotate current log file
        if (file_exists($currentLog)) {
            rename($currentLog, $currentLog . '.1');
        }
    }
}

// Usage example
$rotatingLogger = new RotatingKLogger('/var/log/rotating', LogLevel::INFO, [], 5, 1048576); // Rotate every 1MB
$rotatingLogger->info('Log test with rotation functionality');
?>

Performance Monitoring and Debug Utilization

<?php
use Katzgrau\KLogger\Logger;
use Psr\Log\LogLevel;

// Performance monitoring Logger
class PerformanceLogger {
    private $logger;
    private $timers = [];
    
    public function __construct($logDir) {
        $this->logger = new Logger($logDir . '/performance', LogLevel::DEBUG, [
            'extension' => 'perf'
        ]);
    }
    
    public function startTimer($operation) {
        $this->timers[$operation] = [
            'start_time' => microtime(true),
            'start_memory' => memory_get_usage()
        ];
        
        $this->logger->debug("Performance timer started", [
            'operation' => $operation,
            'memory_start' => $this->formatMemory($this->timers[$operation]['start_memory'])
        ]);
    }
    
    public function endTimer($operation, $additionalContext = []) {
        if (!isset($this->timers[$operation])) {
            $this->logger->warning("Timer not found", ['operation' => $operation]);
            return;
        }
        
        $timer = $this->timers[$operation];
        $endTime = microtime(true);
        $endMemory = memory_get_usage();
        
        $duration = ($endTime - $timer['start_time']) * 1000; // ms
        $memoryUsed = $endMemory - $timer['start_memory'];
        
        $context = array_merge([
            'operation' => $operation,
            'duration_ms' => round($duration, 2),
            'memory_used' => $this->formatMemory($memoryUsed),
            'memory_peak' => $this->formatMemory(memory_get_peak_usage()),
            'memory_current' => $this->formatMemory($endMemory)
        ], $additionalContext);
        
        // Performance warnings
        if ($duration > 1000) { // Over 1 second
            $this->logger->warning("Slow operation detected", $context);
        } elseif ($duration > 500) { // Over 0.5 seconds
            $this->logger->info("Operation completed (slow)", $context);
        } else {
            $this->logger->debug("Operation completed", $context);
        }
        
        unset($this->timers[$operation]);
    }
    
    public function measureMemoryUsage($operation, $callable) {
        $this->startTimer($operation);
        
        try {
            $result = $callable();
            $this->endTimer($operation, ['status' => 'success']);
            return $result;
        } catch (Exception $e) {
            $this->endTimer($operation, [
                'status' => 'error',
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }
    
    private function formatMemory($bytes) {
        $units = ['B', 'KB', 'MB', 'GB'];
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        
        $bytes /= pow(1024, $pow);
        return round($bytes, 2) . ' ' . $units[$pow];
    }
}

// Usage example
$perfLogger = new PerformanceLogger('/var/log/app');

// Manual measurement
$perfLogger->startTimer('database_query');
// Database processing
sleep(1); // Simulation
$perfLogger->endTimer('database_query', ['query' => 'SELECT * FROM users']);

// Automatic measurement
$result = $perfLogger->measureMemoryUsage('heavy_calculation', function() {
    // Heavy processing simulation
    $data = [];
    for ($i = 0; $i < 100000; $i++) {
        $data[] = md5($i);
    }
    return count($data);
});

// Debug Logger
class DebugLogger {
    private $logger;
    private $debugMode;
    
    public function __construct($logDir, $debugMode = true) {
        $this->logger = new Logger($logDir . '/debug', LogLevel::DEBUG);
        $this->debugMode = $debugMode;
    }
    
    public function dumpVariable($variable, $label = 'Variable Dump') {
        if (!$this->debugMode) return;
        
        $this->logger->debug($label, [
            'type' => gettype($variable),
            'value' => $this->serializeVariable($variable),
            'memory_usage' => memory_get_usage(),
            'backtrace' => $this->getBacktrace()
        ]);
    }
    
    public function logFunctionCall($functionName, $args = [], $result = null) {
        if (!$this->debugMode) return;
        
        $this->logger->debug("Function call", [
            'function' => $functionName,
            'arguments' => $this->serializeVariable($args),
            'result' => $this->serializeVariable($result),
            'execution_time' => $this->getCurrentTime()
        ]);
    }
    
    public function logDatabaseQuery($query, $params = [], $executionTime = null) {
        $this->logger->info("Database query", [
            'query' => $query,
            'parameters' => $params,
            'execution_time_ms' => $executionTime ? round($executionTime * 1000, 2) : null,
            'memory_usage' => memory_get_usage()
        ]);
    }
    
    private function serializeVariable($variable) {
        if (is_object($variable)) {
            return '[Object ' . get_class($variable) . ']';
        } elseif (is_array($variable)) {
            return array_map([$this, 'serializeVariable'], $variable);
        } elseif (is_resource($variable)) {
            return '[Resource ' . get_resource_type($variable) . ']';
        } else {
            return $variable;
        }
    }
    
    private function getBacktrace() {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
        return array_map(function($frame) {
            return [
                'file' => $frame['file'] ?? 'unknown',
                'line' => $frame['line'] ?? 'unknown',
                'function' => $frame['function'] ?? 'unknown'
            ];
        }, array_slice($trace, 1));
    }
    
    private function getCurrentTime() {
        return date('Y-m-d H:i:s.u');
    }
}

// Debug Logger usage example
$debugLogger = new DebugLogger('/var/log/app', true);

$userData = [
    'id' => 123,
    'name' => 'John Doe',
    'email' => '[email protected]'
];

$debugLogger->dumpVariable($userData, 'User Data');
$debugLogger->logFunctionCall('calculateTotal', [$userData], 1500.00);
$debugLogger->logDatabaseQuery(
    'SELECT * FROM users WHERE id = ?',
    [123],
    0.025 // 25ms
);
?>

Error Handling and Practical Integration

<?php
use Katzgrau\KLogger\Logger;
use Psr\Log\LogLevel;

// Application integration Logger
class ApplicationLogger {
    private $logger;
    private $errorLogger;
    private $auditLogger;
    
    public function __construct($baseLogDir, $environment = 'production') {
        $logLevel = $environment === 'production' ? LogLevel::INFO : LogLevel::DEBUG;
        
        $this->logger = new Logger($baseLogDir . '/app', $logLevel);
        $this->errorLogger = new Logger($baseLogDir . '/errors', LogLevel::WARNING);
        $this->auditLogger = new Logger($baseLogDir . '/audit', LogLevel::INFO);
    }
    
    // Comprehensive error handling
    public function handleException(Exception $e, $context = []) {
        $errorContext = array_merge([
            'exception_type' => get_class($e),
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
            'request_uri' => $_SERVER['REQUEST_URI'] ?? '',
            'http_method' => $_SERVER['REQUEST_METHOD'] ?? '',
            'user_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'timestamp' => date('c')
        ], $context);
        
        $this->errorLogger->error('Unhandled exception', $errorContext);
        
        // Critical errors recorded separately
        if ($e instanceof CriticalException) {
            $this->errorLogger->critical('Critical system error', $errorContext);
        }
    }
    
    // Security audit log
    public function logSecurityEvent($event, $severity = 'info', $context = []) {
        $auditContext = array_merge([
            'event_type' => 'security',
            'event' => $event,
            'user_id' => $_SESSION['user_id'] ?? 'anonymous',
            'session_id' => session_id(),
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'timestamp' => date('c')
        ], $context);
        
        switch ($severity) {
            case 'critical':
                $this->auditLogger->critical($event, $auditContext);
                break;
            case 'warning':
                $this->auditLogger->warning($event, $auditContext);
                break;
            default:
                $this->auditLogger->info($event, $auditContext);
        }
    }
    
    // Business logic log
    public function logBusinessEvent($event, $data = []) {
        $this->logger->info($event, array_merge($data, [
            'event_category' => 'business',
            'timestamp' => date('c')
        ]));
    }
    
    // API call log
    public function logApiCall($endpoint, $method, $responseCode, $duration = null, $context = []) {
        $logContext = array_merge([
            'category' => 'api',
            'endpoint' => $endpoint,
            'method' => $method,
            'response_code' => $responseCode,
            'duration_ms' => $duration ? round($duration * 1000, 2) : null,
            'timestamp' => date('c')
        ], $context);
        
        if ($responseCode >= 500) {
            $this->errorLogger->error('API server error', $logContext);
        } elseif ($responseCode >= 400) {
            $this->logger->warning('API client error', $logContext);
        } else {
            $this->logger->info('API call', $logContext);
        }
    }
}

// Global error handler setup
$appLogger = new ApplicationLogger('/var/log/myapp', 'production');

// Exception handler
set_exception_handler(function($exception) use ($appLogger) {
    $appLogger->handleException($exception);
});

// Error handler
set_error_handler(function($severity, $message, $filename, $lineno) use ($appLogger) {
    $errorLevels = [
        E_ERROR => 'error',
        E_WARNING => 'warning',
        E_NOTICE => 'notice',
        E_USER_ERROR => 'error',
        E_USER_WARNING => 'warning',
        E_USER_NOTICE => 'notice'
    ];
    
    $level = $errorLevels[$severity] ?? 'info';
    $context = [
        'severity' => $severity,
        'file' => $filename,
        'line' => $lineno
    ];
    
    if ($level === 'error') {
        $appLogger->handleException(new ErrorException($message, 0, $severity, $filename, $lineno), $context);
    } else {
        $appLogger->logger->$level($message, $context);
    }
});

// Practical example
class UserController {
    private $logger;
    
    public function __construct(ApplicationLogger $logger) {
        $this->logger = $logger;
    }
    
    public function login($username, $password) {
        $startTime = microtime(true);
        
        try {
            $this->logger->logSecurityEvent('login_attempt', 'info', ['username' => $username]);
            
            if ($this->authenticateUser($username, $password)) {
                $duration = microtime(true) - $startTime;
                
                $this->logger->logBusinessEvent('user_login_success', [
                    'username' => $username,
                    'duration_ms' => round($duration * 1000, 2)
                ]);
                
                $this->logger->logSecurityEvent('login_success', 'info', ['username' => $username]);
                
                return true;
            } else {
                $this->logger->logSecurityEvent('login_failure', 'warning', [
                    'username' => $username,
                    'reason' => 'invalid_credentials'
                ]);
                
                return false;
            }
            
        } catch (Exception $e) {
            $this->logger->handleException($e, [
                'context' => 'user_login',
                'username' => $username
            ]);
            return false;
        }
    }
    
    public function callExternalAPI($endpoint, $data) {
        $startTime = microtime(true);
        
        try {
            // API call simulation
            $response = $this->makeHttpRequest($endpoint, $data);
            $duration = microtime(true) - $startTime;
            
            $this->logger->logApiCall(
                $endpoint,
                'POST',
                $response['status_code'],
                $duration,
                ['request_size' => strlen(json_encode($data))]
            );
            
            return $response;
            
        } catch (Exception $e) {
            $duration = microtime(true) - $startTime;
            
            $this->logger->logApiCall(
                $endpoint,
                'POST',
                0,
                $duration,
                ['error' => $e->getMessage()]
            );
            
            throw $e;
        }
    }
    
    private function authenticateUser($username, $password) {
        // Authentication logic
        return $username === 'admin' && $password === 'password';
    }
    
    private function makeHttpRequest($endpoint, $data) {
        // HTTP request simulation
        return [
            'status_code' => 200,
            'data' => ['result' => 'success']
        ];
    }
}

// Usage example
$controller = new UserController($appLogger);
$result = $controller->login('admin', 'password');
$apiResponse = $controller->callExternalAPI('/api/users', ['name' => 'test']);
?>