Search by

wilycode / logger-file

szymonpietrek-design

Lightweight PSR-3 file logger with channels, level filtering, structured context and atomic appends.

Package info

github.com/wilycode/logger-file

pkg:composer/wilycode/logger-file

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-10-12 21:26 UTC

This package is auto-updated.

Last update: 2026-10-12 21:31:40 UTC


README

PHP 8.4+ License: MIT Tests

A lightweight PSR-3 file logger for PHP 8.4+. Write structured, append-only log entries with configurable severity, named channels, message interpolation and JSON context. No framework required.

WilyCode PHP Ecosystem

Part of WilyCode — independent, reusable PHP components with their own repositories, releases and Composer installation.

Explore the WilyCode ecosystem →

Features

  • Implements Psr\Log\LoggerInterface (psr/log:^3.0).
  • Supports the eight PSR-3 severity methods and log().
  • Filters records using a minimum Level enum; Level::None disables logging.
  • Writes timestamped, tab-separated entries with channel, interpolated message, JSON context and exception details.
  • Appends using FILE_APPEND | LOCK_EX; creates missing parent directories.
  • Replaces tabs and line breaks in visible fields to prevent extra log columns or lines.
  • Optionally mirrors entries to PHP output (disabled by default).

Requirements

  • PHP 8.4+.
  • psr/log 3.x (installed automatically by Composer).

Installation

composer require wilycode/logger-file

Quick start

<?php

require __DIR__ . '/vendor/autoload.php';

use WilyCode\Logger\FileLogger;
use WilyCode\Logger\Level;

$logger = new FileLogger(
    logFile: __DIR__ . '/var/log/app.log',
    channel: 'app',
    minLevel: Level::Info,
);

$logger->info('Article {id} saved', ['id' => 15]);
$logger->warning('Retrying request {attempt}', ['attempt' => 2]);
$logger->debug('Ignored at the Info threshold');

The parent directory is created if needed. Each record is appended to the specified log file. Keep log files outside your publicly accessible web directory.

Output format

Each record has six tab-separated fields (the timestamp is followed by five more fields):

2026-10-12 18:42:13.123456    [INFO]    [app]    Article 15 saved    {"id":15}    {}

The displayed spacing above represents tabs. The fields are:

  1. Timestamp (Y-m-d H:i:s.u, using the PHP process timezone).
  2. Uppercase level in brackets.
  3. Channel in brackets.
  4. Interpolated message.
  5. Context serialized as JSON, excluding a recognized exception entry.
  6. Exception serialized as JSON, or {} if none was supplied.

User-controlled tabs and line breaks are replaced with spaces in the channel and other fields to keep each event on one line. This is a tab-separated log format, not JSON Lines.

Severity levels

Use any of the eight PSR-3 helper methods (debug(), info(), notice(), warning(), error(), critical(), alert() and emergency()) or call log() with a string or the Level enum:

$logger->log('warning', 'Request took {duration} ms', ['duration' => 120]);
$logger->log(Level::Error, 'Request failed');

$logger->setLogLevel(Level::Error);
$logger->getLogLevel(); // Level::Error

$logger->setLogLevel(Level::None); // Disable all writes.

Level::fromName('warning') also recognizes the aliases warn and off (Level::None). An unknown level throws InvalidArgumentException.

Channels and optional output

$logger->setChannel('payments');
$logger->getChannel(); // 'payments'

$logger->setOutput(true); // Also emit each written line to PHP output.
$logger->isOutputEnabled(); // true

The optional output uses echo, so it may appear in terminal output or in an HTTP response. Leave it disabled for web applications unless explicitly required.

Context, placeholders and exceptions

PSR-3 placeholders in braces are replaced with matching context values. The original context is also included in the JSON field.

$logger->info('Order {order} processed', [
    'order' => 42,
    'paid' => true,
]);

try {
    throw new RuntimeException('Connection refused');
} catch (RuntimeException $exception) {
    $logger->error('Payment processing failed', [
        'order' => 42,
        'exception' => $exception,
    ]);
}

A Throwable passed under the exception key is stored separately from the remaining context, including its message, code, file, line and trace. Do not log passwords, access tokens, session IDs or other secrets; exceptions may expose file paths and application details.

JSON serialization is best-effort. Unencodable values produce a __json_error__ marker rather than a JSON encoding exception.

Filesystem errors

File writing uses an exclusive lock and raises RuntimeException if the directory cannot be created or the complete line cannot be appended. Ensure your PHP process has appropriate directory permissions. The logger does not silently swallow filesystem failures.

The logger does not include log rotation, retention, remote logging or automatic sensitive-data redaction. Use an external rotation mechanism when needed.

Development

composer install
composer validate --strict
composer test

The test suite runs in GitHub Actions on pushes and pull requests. Development files and tests stay in Git but are excluded from distribution archives.

Contributing

Contributions are welcome. See CONTRIBUTING.md and GitHub Issues.

License

MIT © 2026 Simon Piter. See LICENSE.