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.
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']);
?>