PHPackages                             senza1dio/enterprise-psr3-logger - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. senza1dio/enterprise-psr3-logger

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

senza1dio/enterprise-psr3-logger
================================

Enterprise PSR-3 Logger - Channel-based logging with pattern detection, rate limiting, Redis backup, and honest limitations

00PHPCI failing

Since Jan 25Pushed 3mo agoCompare

[ Source](https://github.com/senza1dio/enterprise-psr3-logger)[ Packagist](https://packagist.org/packages/senza1dio/enterprise-psr3-logger)[ RSS](/packages/senza1dio-enterprise-psr3-logger/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Enterprise PSR-3 Logger
=======================

[](#enterprise-psr-3-logger)

PSR-3 compliant logging library built on Monolog with **enterprise-grade configuration-based filtering**.

Part of the **Enterprise Lightning Framework** - integrates with admin-panel and bootstrap for UI-driven configuration.

🎯 What This Package Does
------------------------

[](#-what-this-package-does)

- **PSR-3 compliant**: Works with any PSR-3 compatible code
- **Channel-based logging**: Multiple named loggers (security, api, database, etc.)
- **Configuration-driven filtering**: Uses `should_log()` function for dynamic log level control
- **Multiple output formats**: JSON, human-readable, pretty-printed
- **File rotation**: Daily/hourly rotation with compression
- **Context enrichment**: Automatic request ID, memory, timing
- **Telegram notifications**: Separate notification level from channel level
- **Admin panel integration**: UI for channel configuration when admin-panel is installed

🏗️ Enterprise Framework Integration
-----------------------------------

[](#️-enterprise-framework-integration)

This package integrates with the Enterprise Lightning Framework for UI-driven configuration:

```
┌─────────────────────────────────────────────────────────────────────────┐
│                        YOUR APPLICATION                                  │
├─────────────────────────────────────────────────────────────────────────┤
│   ┌─────────────────────┐    ┌─────────────────────┐                   │
│   │ enterprise-admin-panel│◄───│ enterprise-psr3-logger (THIS PACKAGE) │
│   │                     │    │                     │                   │
│   │ • LogConfigService  │    │ • PSR-3 Logging     │                   │
│   │ • Channel Config UI │    │ • Calls should_log()│                   │
│   │ • Telegram Config   │    │ • Telegram Handler  │                   │
│   └─────────┬───────────┘    └──────────┬──────────┘                   │
│             │         INTEGRATION       │                               │
│             ▼                           ▼                               │
│   ┌─────────────────────────────────────────────────┐                   │
│   │            enterprise-bootstrap                  │                   │
│   │  • should_log() function (intelligent filter)   │                   │
│   │  • Multi-layer caching (~0.001μs per decision)  │                   │
│   └─────────────────────────────────────────────────┘                   │
└─────────────────────────────────────────────────────────────────────────┘

```

### Installation Order (RECOMMENDED)

[](#installation-order-recommended)

```
# 1. FIRST: Admin Panel (creates log_channels table, provides UI)
composer require senza1dio/enterprise-admin-panel
php vendor/senza1dio/enterprise-admin-panel/setup/install.php \
    --driver=pgsql --host=localhost --database=myapp \
    --username=admin --password=secret

# 2. SECOND: Bootstrap (provides should_log() with multi-layer caching)
composer require senza1dio/enterprise-bootstrap

# 3. THIRD: PSR-3 Logger (THIS PACKAGE)
composer require senza1dio/enterprise-psr3-logger
php vendor/senza1dio/enterprise-psr3-logger/setup/install.php \
    --driver=pgsql --host=localhost --database=myapp \
    --username=admin --password=secret
```

### Integration Benefits

[](#integration-benefits)

FeatureStandaloneWith BootstrapWith Admin PanelLogging✅ Works✅ Works✅ Works`should_log()`Stub (always true)✅ Intelligent filtering✅ Database-drivenPerformanceBasic~0.001μs/decision~0.001μs/decisionChannel configENV vars onlyENV vars✅ UI + DatabaseTelegramManual configManual config✅ UI configuration⚠️ ENTERPRISE REQUIREMENT: `should_log()` Function
--------------------------------------------------

[](#️-enterprise-requirement-should_log-function)

**CRITICAL**: This package requires a global `should_log()` function for production use.

### Why `should_log()` is Required

[](#why-should_log-is-required)

Enterprise applications need **dynamic log level configuration** without code changes:

- ✅ Change log levels from admin panel (no deploy needed)
- ✅ Enable debug logs temporarily for troubleshooting
- ✅ Reduce log volume in production (disable info/debug by default)
- ✅ Multi-level caching for ultra-fast filtering (~0.01μs overhead)

**Random sampling is NOT recommended** for enterprise applications because:

- ❌ You lose critical logs randomly (non-deterministic)
- ❌ No control over WHAT you log (just probability)
- ❌ Cannot enable/disable specific channels/levels dynamically

📦 Installation
--------------

[](#-installation)

```
composer require senza1dio/enterprise-psr3-logger

# Run installer to create logs table
php vendor/senza1dio/enterprise-psr3-logger/setup/install.php \
    --driver=pgsql --host=localhost --database=myapp \
    --username=admin --password=secret
```

🚀 Bootstrap Setup (REQUIRED for Production)
-------------------------------------------

[](#-bootstrap-setup-required-for-production)

### Step 1: Define `should_log()` Function in GLOBAL Namespace

[](#step-1-define-should_log-function-in-global-namespace)

**CRITICAL**: The `should_log()` function MUST be defined in the **global namespace** (no `namespace` declaration).

Create this function in your bootstrap file that checks if a channel/level should be logged.

**Example: Simple Implementation**

```
// bootstrap.php

// ⚠️ IMPORTANT: NO namespace declaration here!
// This function MUST be in the global namespace

/**
 * Check if logging is enabled for channel and level
 *
 * @param string $channel Channel name (e.g., 'default', 'security', 'api')
 * @param string $level PSR-3 log level (debug, info, notice, warning, error, critical, alert, emergency)
 * @return bool True if should log, false to skip
 */
function should_log(string $channel, string $level): bool
{
    // Simple example: Log only warnings and errors in production
    if (getenv('APP_ENV') === 'production') {
        return in_array($level, ['warning', 'error', 'critical', 'alert', 'emergency']);
    }

    // Development: Log everything
    return true;
}
```

**Example: Enterprise Implementation (Database-driven)**

```
// bootstrap.php

function should_log(string $channel, string $level): bool
{
    static $cache = [];
    static $service = null;

    // Ultra-fast static cache (same process)
    $cacheKey = "{$channel}:{$level}";
    if (isset($cache[$cacheKey])) {
        return $cache[$cacheKey]; // ~0.01μs - FASTEST
    }

    // Fetch from database configuration (with Redis cache)
    if ($service === null) {
        $service = new LoggingConfigService($pdo, $redis);
    }

    $result = $service->shouldLog($channel, $level);
    $cache[$cacheKey] = $result;

    return $result;
}
```

**Example: Full Enterprise Implementation**

See `examples/should_log.php` for a complete production-ready implementation with:

- Multi-layer cache (static → APCu → Redis → Database)
- Database configuration service
- Performance: ~0.01μs for cache hits (99% of calls)

### Step 2: Load `should_log()` BEFORE Composer Autoload

[](#step-2-load-should_log-before-composer-autoload)

**CRITICAL ORDER**: Define `should_log()` BEFORE loading Composer autoload.

#### Option A: Define in Bootstrap (RECOMMENDED)

[](#option-a-define-in-bootstrap-recommended)

```
// index.php

// 1. Define should_log() FIRST (before Composer autoload)
function should_log(string $channel, string $level): bool {
    // Your custom logic here
    if (getenv('APP_ENV') === 'production') {
        return in_array($level, ['warning', 'error', 'critical', 'alert', 'emergency']);
    }
    return true;
}

// 2. Load Composer autoload (includes PSR-3 logger)
require __DIR__ . '/vendor/autoload.php';

// 3. NOW create loggers (they will use your should_log() implementation)
use Senza1dio\EnterprisePSR3Logger\LoggerFactory;

$logger = LoggerFactory::production('app', '/var/log/app');
```

#### Option B: Separate Bootstrap File

[](#option-b-separate-bootstrap-file)

```
// bootstrap.php

// Define should_log() in global namespace
function should_log(string $channel, string $level): bool {
    // Your custom logic
    return true;
}
```

```
// index.php

// 1. Load bootstrap FIRST
require __DIR__ . '/bootstrap.php';

// 2. Load Composer autoload
require __DIR__ . '/vendor/autoload.php';

// 3. Use loggers
use Senza1dio\EnterprisePSR3Logger\LoggerFactory;

$logger = LoggerFactory::production('app', '/var/log/app');
```

#### Option C: Use Composer Autoload Files (ADVANCED)

[](#option-c-use-composer-autoload-files-advanced)

If you want Composer to load your `should_log()` automatically:

```
// composer.json (your project, NOT the package)

{
    "autoload": {
        "files": [
            "app/Helpers/should_log.php"
        ]
    }
}
```

```
// app/Helpers/should_log.php

function should_log(string $channel, string $level): bool {
    // Your custom logic
    return true;
}
```

Then run `composer dump-autoload` and your function will be loaded automatically.

### ⚠️ What if I Don't Define `should_log()`?

[](#️-what-if-i-dont-define-should_log)

The package includes a **stub implementation** that always returns `true` (logs everything).

This is OK for development/testing, but **NOT recommended for production** because:

- ❌ No configuration-based filtering
- ❌ No dynamic log level control
- ❌ All logs written (high disk usage)

**You'll see a warning** in your error log:

```
[ENTERPRISE PSR-3 LOGGER] WARNING: should_log() function not found.
All logs will be written without configuration-based filtering.
Define should_log() in the GLOBAL namespace in your bootstrap for production use.

```

### Step 3: Use Channel-Based Logging

[](#step-3-use-channel-based-logging)

```
use Senza1dio\EnterprisePSR3Logger\LoggerRegistry;

// Register channels
LoggerRegistry::register(LoggerFactory::production('default', '/var/log/app'), 'default');
LoggerRegistry::register(LoggerFactory::production('security', '/var/log/security'), 'security');
LoggerRegistry::register(LoggerFactory::production('api', '/var/log/api'), 'api');
LoggerRegistry::register(LoggerFactory::production('database', '/var/log/db'), 'database');

// Use it
$logger = LoggerRegistry::get('security');
$logger->warning('Failed login attempt', ['ip' => '1.2.3.4', 'username' => 'admin']);
// Calls: should_log('security', 'warning') internally
```

### ⚠️ What Happens Without `should_log()`

[](#️-what-happens-without-should_log)

If you don't define `should_log()`, the logger will:

1. ✅ **Still work** (logs everything - no filtering)
2. ⚠️ **Emit a warning** on first use (visible in error\_log)
3. ❌ **NOT filter logs** based on configuration (all logs written)

**This is OK for development/testing, but NOT recommended for production.**

---

Quick Start
-----------

[](#quick-start)

### Development (Colored Terminal Output)

[](#development-colored-terminal-output)

```
use Senza1dio\EnterprisePSR3Logger\LoggerFactory;

$logger = LoggerFactory::development('my-app');

$logger->info('Application started');
$logger->error('Database connection failed', [
    'host' => 'db.example.com',
    'error' => 'Connection refused',
]);
```

Output:

```
┌──────────────────────────────────────────────────────────────────────────────
│ 2024-01-15 10:30:00.123456 │ ERROR │ my-app
├──────────────────────────────────────────────────────────────────────────────
│ MESSAGE: Database connection failed
├──────────────────────────────────────────────────────────────────────────────
│ CONTEXT:
│   host ........... db.example.com
│   error .......... Connection refused
└──────────────────────────────────────────────────────────────────────────────

```

### Production (JSON to Rotating Files)

[](#production-json-to-rotating-files)

```
$logger = LoggerFactory::production(
    channel: 'my-app',
    logDir: '/var/log/app',
    maxFiles: 14,
    compress: true
);

$logger->info('User logged in', ['user_id' => 123]);
```

Output (`/var/log/app/my-app-2024-01-15.log`):

```
{"timestamp":"2024-01-15T10:30:00.123456+00:00","level":"info","channel":"my-app","message":"User logged in","context":{"user_id":123},"extra":{"request_id":"abc-123-def","hostname":"web-01"}}
```

### Container (JSON to stdout)

[](#container-json-to-stdout)

```
$logger = LoggerFactory::container('my-app', environment: 'production');
```

Manual Configuration
--------------------

[](#manual-configuration)

```
use Senza1dio\EnterprisePSR3Logger\Logger;
use Senza1dio\EnterprisePSR3Logger\Handlers\StreamHandler;
use Senza1dio\EnterprisePSR3Logger\Handlers\RotatingFileHandler;
use Senza1dio\EnterprisePSR3Logger\Formatters\DetailedLineFormatter;
use Senza1dio\EnterprisePSR3Logger\Processors\RequestProcessor;
use Monolog\Level;

// Create handlers
$fileHandler = new RotatingFileHandler(
    filename: '/var/log/app.log',
    level: Level::Info,
    rotationType: RotatingFileHandler::ROTATION_DAILY,
    maxFiles: 14,
    compress: true
);
$fileHandler->setFormatter(new DetailedLineFormatter());

$stdoutHandler = new StreamHandler('php://stdout', Level::Debug);

// Create logger
$logger = new Logger('app', [$fileHandler, $stdoutHandler]);

// Add processors
$logger->addProcessor(new RequestProcessor());

// Use it
$logger->info('Hello world');
```

Components
----------

[](#components)

### Formatters

[](#formatters)

FormatterDescriptionUse Case`JsonFormatter`JSON output, one object per lineLog aggregators (ELK, Loki)`LineFormatter`Single line with key=valueSimple log files`DetailedLineFormatter`Multi-line with metadataHuman-readable files`PrettyFormatter`Box-drawing with colorsTerminal/development### Handlers

[](#handlers)

HandlerDescription`StreamHandler`Write to any PHP stream (file, stdout, etc.)`RotatingFileHandler`File rotation by date or size`SyslogHandler`System syslog`ErrorLogHandler`PHP error\_log()`FilterHandler`Route logs by level range`GroupHandler`Send to multiple handlers`BufferHandler`Buffer logs and flush in batches`AsyncHandler`Async logging (shutdown, fork, fastcgi strategies)`DatabaseHandler`Write logs to database (MySQL, PostgreSQL, SQLite)`RedisHandler`Write logs to Redis (list, pubsub, stream strategies)`WebhookHandler`Send logs to webhooks (Slack, Discord, Teams, custom)### Processors

[](#processors)

ProcessorAdded Fields`RequestProcessor`request\_id, http\_method, url, ip, user\_agent, referrer`MemoryProcessor`memory\_usage, memory\_peak, memory\_percent`ExecutionTimeProcessor`execution\_time\_us (microseconds)`HostnameProcessor`hostname, environment, php\_version`ContextProcessor`Custom static fields**RequestProcessor Security:**

```
// Default: Only REMOTE_ADDR (safe for direct connections)
$processor = new RequestProcessor();

// Behind trusted proxy: enable X-Forwarded-For
$processor = new RequestProcessor(
    trustProxyHeaders: true,
    trustedProxyHeaders: ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR']
);

// Anonymize IP addresses (GDPR compliance)
$processor = new RequestProcessor(anonymizeIp: true);
```

🔄 Channel-Based Static API (LoggerFacade)
-----------------------------------------

[](#-channel-based-static-api-loggerfacade)

**NEW**: Use the `LoggerFacade` for **clean channel-based logging** with static methods.

### Why Use LoggerFacade?

[](#why-use-loggerfacade)

- ✅ **Clean syntax**: `Logger::channel($level, $message, $context)`
- ✅ **Static methods**: No need to pass logger instances around
- ✅ **Channel-based**: `Logger::security()`, `Logger::api()`, `Logger::database()`
- ✅ **Simple**: One line to log to any channel

### Setup

[](#setup)

```
// bootstrap.php

// Step 1: Define should_log() (see above)
function should_log(string $channel, string $level): bool {
    // Your custom logic
    return true;
}

// Step 2: Load Composer autoload
require 'vendor/autoload.php';

// Step 3: Setup channels
use Senza1dio\EnterprisePSR3Logger\LoggerFactory;
use Senza1dio\EnterprisePSR3Logger\LoggerRegistry;

LoggerRegistry::register(LoggerFactory::production('default', '/var/log/app'), 'default');
LoggerRegistry::register(LoggerFactory::production('security', '/var/log/security'), 'security');
LoggerRegistry::register(LoggerFactory::production('api', '/var/log/api'), 'api');
LoggerRegistry::register(LoggerFactory::production('database', '/var/log/db'), 'database');
LoggerRegistry::register(LoggerFactory::production('email', '/var/log/email'), 'email');

// Step 4: Alias LoggerFacade as Logger
use Senza1dio\EnterprisePSR3Logger\LoggerFacade as Logger;
```

### Usage (Channel-Based Syntax)

[](#usage-channel-based-syntax)

```
// Channel-based logging (clean syntax)
Logger::security('warning', 'Failed login attempt', [
    'ip' => '1.2.3.4',
    'username' => 'admin',
]);

Logger::api('info', 'HTTP Request', [
    'method' => 'GET',
    'uri' => '/api/users',
]);

Logger::database('error', 'Query failed', [
    'query' => 'SELECT * FROM users',
    'error' => 'Connection timeout',
]);

Logger::email('debug', 'Email sent', [
    'to' => 'user@example.com',
]);

// Convenience methods (channel = 'default')
Logger::error('Database connection failed');
Logger::warning('Cache miss detected');
Logger::info('User logged in');
Logger::debug('Session synced');
```

### Available Channels

[](#available-channels)

MethodChannelDescription`Logger::default($level, $msg, $ctx)``default`General application logs`Logger::security($level, $msg, $ctx)``security`Security events, auth, etc.`Logger::api($level, $msg, $ctx)``api`HTTP requests, API calls`Logger::database($level, $msg, $ctx)``database`Database queries, slow queries`Logger::email($level, $msg, $ctx)``email`Email sending, SMTP errors`Logger::debug_general($level, $msg, $ctx)``debug_general`Debug logs, workers`Logger::performance($level, $msg, $ctx)``performance`Performance metrics`Logger::js_errors($level, $msg, $ctx)``js_errors`Frontend JavaScript errors`Logger::channel($ch, $lvl, $msg, $ctx)`customCustom channel### Complete Example

[](#complete-example)

See `examples/channel-syntax.php` for a complete working example with all features.

---

Multi-Channel Logging (Standard PSR-3 API)
------------------------------------------

[](#multi-channel-logging-standard-psr-3-api)

Alternatively, use the standard PSR-3 API for instance-based logging:

```
use Senza1dio\EnterprisePSR3Logger\LoggerManager;

$manager = new LoggerManager();

// Set default handler
$manager->setDefaultHandler(new StreamHandler('/var/log/app.log'));

// Set channel-specific handlers
$manager->setChannelHandlers('security', [
    new StreamHandler('/var/log/security.log'),
]);

// Get loggers
$appLog = $manager->channel('app');
$securityLog = $manager->channel('security');

// Channel inheritance: 'app.http' inherits from 'app'
$httpLog = $manager->channel('app.http');
```

Sampling (DEPRECATED - Use `should_log()` Instead)
--------------------------------------------------

[](#sampling-deprecated---use-should_log-instead)

**⚠️ DEPRECATED**: Random sampling is NOT recommended for enterprise applications.

**Why sampling is a bad idea:**

- ❌ Non-deterministic (you lose critical logs randomly)
- ❌ No control over WHAT you log (just probability)
- ❌ Cannot enable/disable specific channels/levels dynamically
- ❌ Makes debugging impossible ("this log appeared 10% of the time...")

**Use `should_log()` instead:**

```
// ❌ OLD WAY (random sampling - CAZZATA)
$logger->setLevelSamplingRate('debug', 0.1); // Log 10% of debug randomly

// ✅ NEW WAY (configuration-based filtering)
function should_log(string $channel, string $level): bool {
    // Fetch from database configuration (admin panel can change it)
    return $config->isEnabled($channel, $level);
}

// Now you can:
// - Enable debug logs for 'security' channel only
// - Disable info logs in production
// - Enable all logs temporarily for troubleshooting
// - Change configuration without code deploy
```

**Migration:**

```
// If you were using sampling, replace it with should_log()
// OLD:
$logger = new Logger('app');
$logger->setLevelSamplingRate('debug', 0.1);

// NEW:
// Define should_log() in bootstrap (see above)
$logger = new Logger('app'); // Automatically uses should_log()
```

❓ Frequently Asked Questions
----------------------------

[](#-frequently-asked-questions)

### Q: Do all frameworks have a bootstrap?

[](#q-do-all-frameworks-have-a-bootstrap)

**A: No**, not all frameworks have an explicit bootstrap file. Here's how to handle different scenarios:

**Laravel:**

```
// bootstrap/app.php or config/app.php

function should_log(string $channel, string $level): bool {
    return config('logging.channels.' . $channel . '.level', 'debug') shouldLog($channel, $level);
}
```

Your service can fetch configuration from database and cache it in Redis/APCu.

### Q: Is `should_log()` called for every log?

[](#q-is-should_log-called-for-every-log)

**A: Yes**, but with **ultra-fast caching**:

- **WITHOUT caching**: ~1-2μs per call (database query)
- **WITH static cache**: ~0.01μs per call (99% of calls)
- **WITH Redis cache**: ~0.1μs per call (cache miss)

See `examples/should_log.php` for full enterprise implementation with 3-layer cache.

### Q: How do I test my `should_log()` implementation?

[](#q-how-do-i-test-my-should_log-implementation)

**A: Run the included bootstrap test:**

```
# From package directory
php examples/bootstrap-test.php

# Expected output:
# ✅ should_log() defined in global namespace
# ✅ Composer autoload loaded
# ✅ should_log() exists in global namespace
# ✅ Logger created with channel: test-channel
# ✅ ALL TESTS PASSED
```

This verifies:

- Function is in global namespace
- Function is callable from any namespace
- Logger calls your function correctly
- Works with multiple channels

### Q: What if `should_log()` throws an exception?

[](#q-what-if-should_log-throws-an-exception)

**A: The logger catches it** and logs a warning, then allows the log through (fail-safe).

**Example:**

```
function should_log(string $channel, string $level): bool {
    // Oops, database connection failed
    throw new \PDOException('Connection refused');
}

// Logger behavior:
// 1. Catches exception
// 2. Logs warning: "should_log() failed: Connection refused"
// 3. Returns TRUE (allows log to go through)
// 4. Continues normally (doesn't crash your app)
```

---

Exception Logging
-----------------

[](#exception-logging)

```
try {
    // ...
} catch (\Exception $e) {
    $logger->error('Operation failed', [
        'exception' => $e,
        'operation' => 'user_creation',
    ]);
}
```

The `PrettyFormatter` and `DetailedLineFormatter` will display:

- Exception class name
- Message
- File and line
- Stack trace

Log Separation by Level
-----------------------

[](#log-separation-by-level)

```
use Senza1dio\EnterprisePSR3Logger\Handlers\FilterHandler;

// Error log (ERROR and above)
$errorHandler = new FilterHandler(
    new StreamHandler('/var/log/error.log'),
    minLevel: Level::Error
);

// Info log (INFO to WARNING only)
$infoHandler = new FilterHandler(
    new StreamHandler('/var/log/info.log'),
    minLevel: Level::Info,
    maxLevel: Level::Warning
);

// Debug log (DEBUG only)
$debugHandler = new FilterHandler(
    new StreamHandler('/var/log/debug.log'),
    minLevel: Level::Debug,
    maxLevel: Level::Debug
);

$logger = new Logger('app', [$errorHandler, $infoHandler, $debugHandler]);
```

Database Logging
----------------

[](#database-logging)

```
use Senza1dio\EnterprisePSR3Logger\Handlers\DatabaseHandler;

$pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass');

// Create table (run once)
DatabaseHandler::createTable($pdo, 'logs', 'mysql');

// Use handler
$handler = new DatabaseHandler($pdo, 'logs', batchSize: 50);
$logger->addHandler($handler);

// Query logs later
$logs = DatabaseHandler::query($pdo, [
    'channel' => 'app',
    'min_level' => 400, // Error and above
    'from' => '2024-01-01',
    'limit' => 100,
]);
```

Redis Logging
-------------

[](#redis-logging)

```
use Senza1dio\EnterprisePSR3Logger\Handlers\RedisHandler;

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);

// List-based (for processing queues)
$handler = new RedisHandler($redis, 'logs:app');

// Stream-based (for log aggregation with consumer groups)
$handler = new RedisHandler($redis, 'logs:app', strategy: 'stream', maxLength: 10000);

// Pub/Sub (for real-time monitoring)
$handler = new RedisHandler($redis, 'logs:app', strategy: 'pubsub');
```

Webhook Alerting
----------------

[](#webhook-alerting)

```
use Senza1dio\EnterprisePSR3Logger\Handlers\WebhookHandler;
use Monolog\Level;

// Slack
$slackHandler = WebhookHandler::slack(
    webhookUrl: 'https://hooks.slack.com/services/xxx/yyy/zzz',
    channel: '#alerts',
    username: 'Logger Bot',
    level: Level::Error
);

// Discord
$discordHandler = WebhookHandler::discord(
    webhookUrl: 'https://discord.com/api/webhooks/xxx/yyy',
    username: 'Logger Bot'
);

// Microsoft Teams
$teamsHandler = WebhookHandler::teams(
    webhookUrl: 'https://outlook.office.com/webhook/xxx',
    title: 'Production Alerts'
);

// Custom webhook
$customHandler = new WebhookHandler(
    url: 'https://api.example.com/logs',
    headers: ['Authorization' => 'Bearer token']
);
```

Async Logging
-------------

[](#async-logging)

```
use Senza1dio\EnterprisePSR3Logger\Handlers\AsyncHandler;

// Wrap any slow handler with AsyncHandler
$dbHandler = new DatabaseHandler($pdo);
$asyncHandler = new AsyncHandler($dbHandler, strategy: 'shutdown');

// Strategies:
// - 'shutdown': Write after response sent (default, safest)
// - 'fastcgi': Use fastcgi_finish_request() then write (PHP-FPM)
// - 'fork': Fork child process (requires pcntl, resource inheritance warning)

$logger->addHandler($asyncHandler);
```

Configuration from Array
------------------------

[](#configuration-from-array)

```
$logger = LoggerFactory::fromConfig([
    'channel' => 'my-app',
    'handlers' => [
        [
            'type' => 'rotating',
            'path' => '/var/log/app.log',
            'level' => 'info',
            'rotation' => 'daily',
            'max_files' => 14,
            'formatter' => 'json',
        ],
        [
            'type' => 'stream',
            'path' => 'php://stderr',
            'level' => 'error',
        ],
    ],
    'processors' => ['request', 'memory', 'execution_time'],
    'context' => [
        'app_version' => '1.2.3',
    ],
]);
```

Helper Functions
----------------

[](#helper-functions)

```
use Senza1dio\EnterprisePSR3Logger\LoggerRegistry;
use function Senza1dio\EnterprisePSR3Logger\log_info;
use function Senza1dio\EnterprisePSR3Logger\log_error;
use function Senza1dio\EnterprisePSR3Logger\log_exception;

// Register logger globally
LoggerRegistry::register($logger, 'app', setAsDefault: true);

// Use helper functions
log_info('Application started');
log_error('Something went wrong', ['code' => 500]);
log_exception($exception, 'Failed to process request');
```

Limitations
-----------

[](#limitations)

This package has the following limitations:

1. **File Rotation**

    - Rotation happens on write, not on schedule
    - Size check uses actual file size (multi-process safe) but has I/O overhead
    - Compression runs synchronously (blocks during gzip)
    - Multi-process rotation uses lock file (brief blocking possible)
2. **Performance**

    - Default handlers use synchronous I/O (blocking writes)
    - File locking may impact high-concurrency scenarios
    - AsyncHandler available for non-blocking writes (fork/shutdown/fastcgi strategies)
3. **Memory**

    - Context data depth is limited by `setMaxContextDepth()` (default: 10 levels)
    - Large contexts can cause memory issues
    - BufferHandler limits consecutive failures to prevent memory exhaustion
4. **AsyncHandler Caveats**

    - fork strategy requires pcntl extension
    - fork strategy inherits parent resources (DB connections, sockets)
    - shutdown strategy blocks after response (but after output sent)
    - Logs may be lost if PHP crashes before shutdown
5. **WebhookHandler**

    - Synchronous HTTP requests (blocking)
    - Recommend wrapping with AsyncHandler for production
    - Uses file\_get\_contents (no curl dependency)
6. **Sampling**

    - Random-based, not deterministic
    - Same request may have some logs sampled out
    - No request-level sampling (all or nothing per request)
7. **Security Considerations**

    - RequestProcessor defaults to REMOTE\_ADDR only (safe)
    - Proxy headers (X-Forwarded-For) must be explicitly enabled
    - File handlers validate paths against directory traversal

Security Features
-----------------

[](#security-features)

- **Path traversal protection**: All file handlers validate paths to prevent `../` attacks
- **Null byte injection protection**: Paths are validated for null bytes
- **Log injection protection**: Newlines, ANSI sequences, and control characters are sanitized
- **Exception serialization**: Exceptions are serialized to arrays, not stored as objects
- **IP spoofing protection**: RequestProcessor defaults to REMOTE\_ADDR only; proxy headers require explicit opt-in
- **Circular reference detection**: Context sanitization detects and handles object cycles
- **SQL injection protection**: DatabaseHandler validates table names and uses prepared statements
- **Chained exception support**: Previous exceptions are normalized recursively (with depth limit)

Framework Compatibility
-----------------------

[](#framework-compatibility)

Tested compatible with:

- **WordPress**: Works as PSR-3 drop-in replacement for WP logging
- **Laravel**: Multi-channel support, context merging, Monolog backend
- **Symfony**: Direct Monolog compatibility via `getMonolog()`
- **Slim/Lumen**: Middleware-friendly with request context processors
- **Any PSR-3 compatible framework**

Is This Enterprise-Grade?
-------------------------

[](#is-this-enterprise-grade)

### What Makes It Enterprise-Ready

[](#what-makes-it-enterprise-ready)

1. **PSR-3 compliance** - Standard interface, works everywhere
2. **Security hardened** - Path traversal, log injection, IP spoofing protection
3. **Multi-channel architecture** - Separation of concerns with inheritance
4. **Context enrichment** - Request ID, memory, timing, hostname automatically added
5. **Log rotation** - Multi-process safe with file locking
6. **Error resilience** - Handlers continue if one fails, consecutive failure protection
7. **Multiple backends** - File, database (MySQL/PostgreSQL/SQLite), Redis, webhooks
8. **Async support** - AsyncHandler with fork/shutdown/fastcgi strategies
9. **Alerting** - WebhookHandler with Slack/Discord/Teams integration
10. **169 passing tests** - Including security, integration, and real file I/O tests

### What It Lacks for Full Enterprise

[](#what-it-lacks-for-full-enterprise)

1. **No distributed tracing** - No OpenTelemetry/Jaeger integration
2. **No log aggregation** - No built-in ELK/Loki/Datadog clients (but JSON format is compatible)
3. **No encryption at rest** - Logs are plaintext
4. **No email handler** - Use external service or SMTP library
5. **Limited field testing** - Not battle-tested in high-traffic production yet

### Verdict

[](#verdict)

This package is **suitable for production use** in most PHP applications. It provides a solid foundation for logging with good security practices. For truly large-scale enterprise deployments, you would need to add:

- External log aggregation (ship logs to ELK/Loki)
- Async handlers for high-throughput scenarios
- Distributed tracing integration

Requirements
------------

[](#requirements)

**Required:**

- PHP 8.1+
- ext-json
- ext-pdo (for DatabaseHandler)
- monolog/monolog ^3.0
- psr/log ^3.0

**Optional:**

- ext-redis (for RedisHandler with phpredis, recommended for production)
- ext-pcntl (for AsyncHandler fork strategy)
- predis/predis (alternative Redis client)

**Recommended Packages:**

- `senza1dio/enterprise-bootstrap` - Provides intelligent `should_log()` with multi-layer caching
- `senza1dio/enterprise-admin-panel` - Provides UI for channel configuration

🌟 Related Packages
------------------

[](#-related-packages)

PackageDescriptionIntegration**[senza1dio/enterprise-admin-panel](https://github.com/senza1dio/enterprise-admin-panel)**Admin interface with channel config UIProvides LogConfigService + UI**[senza1dio/enterprise-bootstrap](https://github.com/senza1dio/enterprise-bootstrap)**Application foundationProvides `should_log()` with caching**[senza1dio/enterprise-security-shield](https://github.com/senza1dio/enterprise-security-shield)**WAF, Honeypot, securityLogs security events**[senza1dio/database-pool](https://github.com/senza1dio/database-pool)**Connection poolingLogs connection events---

**Part of the Enterprise Lightning Framework**

License
-------

[](#license)

MIT

###  Health Score

18

—

LowBetter than 8% of packages

Maintenance54

Moderate activity, may be stable

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/5aad1876ec1856ddc1a427bd3d05d683214d86630d1c08e35a7586f40762ef3d?d=identicon)[senza1dio](/maintainers/senza1dio)

---

Top Contributors

[![senza1dio](https://avatars.githubusercontent.com/u/231508360?v=4)](https://github.com/senza1dio "senza1dio (4 commits)")

### Embed Badge

![Health badge](/badges/senza1dio-enterprise-psr3-logger/health.svg)

```
[![Health](https://phpackages.com/badges/senza1dio-enterprise-psr3-logger/health.svg)](https://phpackages.com/packages/senza1dio-enterprise-psr3-logger)
```

###  Alternatives

[psr/log

Common interface for logging libraries

10.4k1.2B9.2k](/packages/psr-log)[itsgoingd/clockwork

php dev tools in your browser

5.9k27.6M94](/packages/itsgoingd-clockwork)[graylog2/gelf-php

A php implementation to send log-messages to a GELF compatible backend like Graylog2.

41838.2M138](/packages/graylog2-gelf-php)[bugsnag/bugsnag-psr-logger

Official Bugsnag PHP PSR Logger.

32132.5M2](/packages/bugsnag-bugsnag-psr-logger)[consolidation/log

Improved Psr-3 / Psr\\Log logger based on Symfony Console components.

15462.2M7](/packages/consolidation-log)[datadog/php-datadogstatsd

An extremely simple PHP datadogstatsd client

19124.6M15](/packages/datadog-php-datadogstatsd)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
