Search by

migears / log

samxxu

Minimalist PSR-3 compliant logger

2.0.0 2026-10-01 13:26 UTC

This package is auto-updated.

Last update: 2026-10-01 14:06:21 UTC


README

Version

Minimalist PSR-3 compliant logger for PHP. One class, zero magic.

A single Logger class that implements the full PSR-3 interface. No handler chains, no formatters, no configuration files. Just a callable handler, level threshold, and channel support — small enough to read in minutes.

Background: miGears is the open-source successor of TinyGears, a self-developed PHP framework. It was renamed and open-sourced recently because the name TinyGears is already taken in the open-source community.

Features

  • Full PSR-3 compliance — all 8 log levels + log() method
  • Context interpolation — {placeholder} syntax per PSR-3 spec
  • Level threshold — discard messages below a minimum level
  • Channel support — tag log lines with a channel name
  • Three built-in handlers — file, stream, null
  • Custom handlers — pass any callable
  • Single class — read and understand the whole thing in minutes
  • Only dependency: psr/log — the standard interface

Boundaries

In scope

  • The full PSR-3 surface — all 8 levels plus log(), from Psr\Log\AbstractLogger, with {placeholder} context interpolation (scalars and Stringable; null → "null", booleans → true/false).
  • One level threshold and one optional channel name, configured at construction.
  • Three built-in handlers: Logger::toFile(), Logger::toStream() (any open stream, e.g. STDERR) and Logger::null().
  • Custom destinations via a single callable(string $line): void passed as the handler.

Not in scope (by design)

  • Handler chains, middleware, formatters and processors — the handler is one callable; a deeper pipeline is deliberately out of scope.
  • Log rotation and retention — use logrotate or a similar external tool.
  • Structured / JSON output, and async / buffered delivery — format or buffer inside your own handler.
  • Configuration files and DI container integration — a PSR-3 LoggerInterface is wired in by the container owner (e.g. migears-manager resolves one through its Registry); this library ships no config and no container.

Installation

composer require migears/log

Requires: PHP 8.1+.

Quick Start

use MiGears\Log\Logger;
use Psr\Log\LogLevel;

// File logger
$log = Logger::toFile('/var/log/app.log', LogLevel::DEBUG, 'app');

$log->info('User {user} logged in', ['user' => 'Alice']);
$log->warning('Disk space low: {percent}%', ['percent' => 85]);
$log->error('Database connection failed', ['host' => 'db1']);

Stream Logger (stderr)

$log = Logger::toStream(STDERR, LogLevel::WARNING, 'cli');
$log->error('Something went wrong');

Null Logger (testing / production disable)

$log = Logger::null();
$log->debug('This will be silently discarded');

Custom Handler

// Send logs to any destination via a callable
$log = new Logger(function (string $line): void {
    // e.g. send to syslog, Slack, database...
    syslog(LOG_INFO, $line);
}, LogLevel::INFO, 'app');

Log Levels

use Psr\Log\LogLevel;

// From lowest to highest priority:
LogLevel::DEBUG      // 0
LogLevel::INFO       // 1
LogLevel::NOTICE     // 2
LogLevel::WARNING    // 3
LogLevel::ERROR      // 4
LogLevel::CRITICAL   // 5
LogLevel::ALERT      // 6
LogLevel::EMERGENCY  // 7

Context Interpolation

Per PSR-3 spec, {placeholder} in the message is replaced with the corresponding context value:

$log->info('Hello, {name}! You have {count} messages.', [
    'name' => 'Bob',
    'count' => 5,
]);
// Output: [2024-01-01 12:00:00] [app] INFO: Hello, Bob! You have 5 messages.

Scalar values and Stringable objects are interpolated. null becomes "null" and booleans are spelled out as true/false. Non-scalar values (arrays, objects) are left as-is (placeholder stays).

Output Format

[YYYY-MM-DD HH:MM:SS] [channel] LEVEL: message

Example:

[2024-01-15 10:30:45] [app] INFO: User Alice logged in
[2024-01-15 10:31:02] [app] WARNING: Rate limit approaching for user 123
[2024-01-15 10:31:10] [db] ERROR: Connection timeout on host db2

API Reference

Method Description
new Logger(callable $handler, string $minLevel = DEBUG, string $channel = '') Create with custom handler
Logger::toFile(string $path, $level = DEBUG, $channel = '') Create file logger
Logger::toStream($stream, $level = DEBUG, $channel = '') Create stream logger
Logger::null() Create null (no-op) logger
debug($message, $context = []) Debug level log
info($message, $context = []) Info level log
notice($message, $context = []) Notice level log
warning($message, $context = []) Warning level log
error($message, $context = []) Error level log
critical($message, $context = []) Critical level log
alert($message, $context = []) Alert level log
emergency($message, $context = []) Emergency level log
log($level, $message, $context = []) Log at arbitrary level

Design Philosophy

miGears Log follows the miGears philosophy: minimal, readable, and useful.

  • One class — no handler chains, no formatters, no processors
  • Callable handler — flexibility without interface bloat
  • PSR-3 compliant — drop-in replacement for any PSR-3 logger
  • Small enough to read — read and understand it in minutes

What we don't do:

  • No handler stacks / middleware chains
  • No log rotation (use logrotate or similar tools)
  • No structured / JSON logging out of the box (format it in your handler)
  • No built-in async / buffered logging (implement in your handler)
  • No configuration files or DI container integration

Integration with miGears Web

use MiGears\Web\MiRest;
use MiGears\Log\Logger;

$rest = new MiRest(__DIR__ . '/resources', 'App\\Resources');

// Register logger as a service
$rest->set('logger', function () {
    return Logger::toFile(__DIR__ . '/logs/app.log', 'debug', 'app');
});

// In a resource:
class Users extends AbstractResource
{
    public function POST(Request $request): Response
    {
        $this->resolve('logger')->info('User created', ['id' => $userId]);
        return Response::json(['status' => 'ok']);
    }
}

License

MIT

migears/log

Version

极简 PSR-3 兼容 PHP 日志器。一个类,零魔法。

单个 Logger 类实现完整的 PSR-3 接口。没有处理器链,没有格式化器,没有配置文件。只有一个可调用的 handler、级别阈值和 channel 支持——小到几分钟就能读完。

特性

  • 完全 PSR-3 兼容 — 全部 8 个日志级别 + log() 方法
  • 上下文插值 — 按 PSR-3 规范的 {placeholder} 语法
  • 级别阈值 — 丢弃低于最小级别的消息
  • Channel 支持 — 给日志行打上 channel 名称标签
  • 三种内置 handler — 文件、流、空操作
  • 自定义 handler — 传入任意可调用函数
  • 单类 — 几分钟就能读完理解
  • 唯一依赖:psr/log — 标准接口

边界

范围内

  • 完整的 PSR-3 日志接口 —— 全部 8 个级别加 log(),继承自 Psr\Log\AbstractLogger,支持 {placeholder} 上下文插值(标量与 Stringable;null → "null",布尔值 → true/false)。
  • 一个级别阈值和一个可选的 channel 名称,在构造时配置。
  • 三种内置 handler:Logger::toFile()、Logger::toStream()(任意已打开的流,例如 STDERR)与 Logger::null()。
  • 通过以 handler 形式传入的单个 callable(string $line): void 自定义输出目的地。

范围外(刻意不做)

  • Handler 链、中间件、格式化器与处理器 —— handler 就是一个可调用函数,更深的管道刻意不做。
  • 日志轮转与保留 —— 请使用 logrotate 或类似外部工具。
  • 结构化 / JSON 输出,以及异步 / 缓冲投递 —— 在你自己的 handler 里格式化或缓冲。
  • 配置文件与 DI 容器集成 —— PSR-3 的 LoggerInterface 由容器持有方装配(例如 migears-manager 通过其 Registry 解析 LoggerInterface);本库不提供配置,也不提供容器。

安装

composer require migears/log

要求:PHP 8.1+。

快速开始

use MiGears\Log\Logger;
use Psr\Log\LogLevel;

// 文件日志
$log = Logger::toFile('/var/log/app.log', LogLevel::DEBUG, 'app');

$log->info('用户 {user} 已登录', ['user' => 'Alice']);
$log->warning('磁盘空间不足:{percent}%', ['percent' => 85]);
$log->error('数据库连接失败', ['host' => 'db1']);

流日志(stderr)

$log = Logger::toStream(STDERR, LogLevel::WARNING, 'cli');
$log->error('出错了');

空日志器(测试 / 生产环境禁用)

$log = Logger::null();
$log->debug('这条会被静默丢弃');

自定义 Handler

// 通过可调用函数将日志发送到任何目的地
$log = new Logger(function (string $line): void {
    // 例如发送到 syslog、Slack、数据库...
    syslog(LOG_INFO, $line);
}, LogLevel::INFO, 'app');

日志级别

use Psr\Log\LogLevel;

// 从低到高优先级:
LogLevel::DEBUG      // 0
LogLevel::INFO       // 1
LogLevel::NOTICE     // 2
LogLevel::WARNING    // 3
LogLevel::ERROR      // 4
LogLevel::CRITICAL   // 5
LogLevel::ALERT      // 6
LogLevel::EMERGENCY  // 7

上下文插值

按 PSR-3 规范,消息中的 {placeholder} 会被对应的上下文值替换:

$log->info('你好,{name}!你有 {count} 条消息。', [
    'name' => 'Bob',
    'count' => 5,
]);
// 输出:[2024-01-01 12:00:00] [app] INFO: 你好,Bob!你有 5 条消息。

标量值和 Stringable 对象会被插值。null 变为 "null",布尔值明确输出为 true/false。非标量值(数组、对象)保持原样(占位符保留)。

输出格式

[YYYY-MM-DD HH:MM:SS] [channel] LEVEL: message

示例:

[2024-01-15 10:30:45] [app] INFO: 用户 Alice 已登录
[2024-01-15 10:31:02] [app] WARNING: 用户 123 即将达到速率限制
[2024-01-15 10:31:10] [db] ERROR: 主机 db2 连接超时

API 参考

方法 说明
new Logger(callable $handler, string $minLevel = DEBUG, string $channel = '') 使用自定义 handler 创建
Logger::toFile(string $path, $level = DEBUG, $channel = '') 创建文件日志器
Logger::toStream($stream, $level = DEBUG, $channel = '') 创建流日志器
Logger::null() 创建空(无操作)日志器
debug($message, $context = []) Debug 级别日志
info($message, $context = []) Info 级别日志
notice($message, $context = []) Notice 级别日志
warning($message, $context = []) Warning 级别日志
error($message, $context = []) Error 级别日志
critical($message, $context = []) Critical 级别日志
alert($message, $context = []) Alert 级别日志
emergency($message, $context = []) Emergency 级别日志
log($level, $message, $context = []) 任意级别日志

设计哲学

miGears Log 遵循 miGears 设计哲学:极简、可读、实用。

  • 一个类 — 没有 handler 链、没有格式化器、没有处理器
  • 可调用 handler — 灵活但不臃肿
  • PSR-3 兼容 — 可直接替换任何 PSR-3 日志器
  • 小到可以读完 — 几分钟就能读完理解

我们不做的事:

  • 没有 handler 栈 / 中间件链
  • 没有日志轮转(使用 logrotate 或类似工具)
  • 没有内置结构化 / JSON 日志(在 handler 中自己格式化)
  • 没有内置异步 / 缓冲日志(在 handler 中实现)
  • 没有配置文件或 DI 容器集成

与 miGears Web 集成

use MiGears\Web\MiRest;
use MiGears\Log\Logger;

$rest = new MiRest(__DIR__ . '/resources', 'App\\Resources');

// 将日志器注册为服务
$rest->set('logger', function () {
    return Logger::toFile(__DIR__ . '/logs/app.log', 'debug', 'app');
});

// 在资源类中:
class Users extends AbstractResource
{
    public function POST(Request $request): Response
    {
        $this->resolve('logger')->info('用户已创建', ['id' => $userId]);
        return Response::json(['status' => 'ok']);
    }
}

许可证

MIT