ubccr/log

PEAR 日志框架

1.13.2 2018-02-05 19:40 UTC

This package is not auto-updated.

Last update: 2024-09-21 16:56:15 UTC


README

用户文档

内容

1   使用日志处理器

Log 包实现为一个框架,该框架支持特定后端日志处理器的概念。基本日志对象(由 Log 类 定义)主要是当前配置的处理器的抽象接口。

Log 包附带了许多处理器,如果您发现它们都不适合您的应用程序需求,编写自己的处理器也很容易。

1.1   创建日志对象

创建 Log 对象有三种方式

  • 使用 Log::factory() 方法
  • 使用 Log::singleton() 方法
  • 直接实例化

1.1.1   工厂方法

Log::factory() 方法实现了 工厂模式。它允许在运行时参数化构造具体的 Log 实例。传递给 Log::factory() 方法的第一个参数表示要创建的具体处理器的名称。其余参数将传递给处理器的构造函数(参见下文的 配置处理器)。

新的 Log 实例通过引用返回。

require_once 'Log.php';

$console = Log::factory('console', '', 'TEST');
$console->log('Logging to the console.');

$file = Log::factory('file', 'out.log', 'TEST');
$file->log('Logging to out.log.');

1.1.2   单例方法

Log::singleton() 方法实现了 单例模式。单例模式确保只创建特定日志类型和配置的单个实例。这有两个好处:首先,它防止了重复构建 Log 实例;其次,它使所有代码都可以访问相同的 Log 实例。后一点在记录到文件时尤为重要,因为只需管理一个文件处理器。

Log::singleton() 方法的参数与 Log::factory() 方法相同。新的 Log 实例通过引用返回。

require_once 'Log.php';

/* Same construction parameters */
$a = Log::singleton('console', '', 'TEST');
$b = Log::singleton('console', '', 'TEST');

if ($a === $b) {
    echo '$a and $b point to the same Log instance.' . "\n";
}

/* Different construction parameters */
$c = Log::singleton('console', '', 'TEST1');
$d = Log::singleton('console', '', 'TEST2');

if ($c !== $d) {
    echo '$c and $d point to different Log instances.' . "\n";
}

1.1.3   直接实例化

也可以直接实例化具体的 Log 处理器实例。然而,这种方法并不推荐,因为它比必要的程度在您的应用程序代码和 Log 包之间创建了更紧密的耦合。建议使用 工厂方法单例方法

1.2   配置处理器

日志处理器的配置由其构建时使用的参数决定。以下是这些参数的概述

/* Using the factory method ... */
Log::factory($handler, $name, $ident, $conf, $maxLevel);

/* Using the singleton method ... */
Log::singleton($handler, $name, $ident, $conf, $maxLevel);

/* Using direct instantiation ... */
new Log_handler($name, $ident, $conf, $maxLevel);

1.3   记录事件

使用 log() 方法记录事件

$logger->log('Message', PEAR_LOG_NOTICE);

第一个参数包含日志事件的消息。尽管事件总是以字符串的形式记录,但可以将对象传递给 log() 方法。如果对象实现了 getString() 方法、toString() 方法或 Zend Engine 2 的特殊 __toString() 转换方法,它将被用来确定对象的字符串表示形式。否则,将记录对象的 序列化 形式。

第二个可选参数指定日志事件的优先级。有关优先级的完整列表,请参阅 日志级别 表。默认优先级为 PEAR_LOG_INFO。

如果事件成功记录,log() 方法将返回 true

还提供了用于在特定日志级别记录事件的“快捷”方法。有关完整的列表,请参阅 日志级别 表。

1.4   日志级别

此表按最高优先级(PEAR_LOG_EMERG)到最低优先级(PEAR_LOG_DEBUG)排序。

1.5   日志级别掩码

定义日志级别掩码允许您包括和/或排除特定级别的事件记录。构造参数 $level(参见 配置处理器)使用此机制来排除低于一定优先级的日志事件,并且在构建 Log 对象之后还可以定义更复杂的掩码。

每个优先级都有一个特定的掩码与之关联。要计算优先级的掩码,请使用静态 Log::MASK() 方法

$mask = Log::MASK(PEAR_LOG_INFO);

要计算直到并包括一定级别的所有优先级的掩码,请使用静态方法 Log::MAX()

$mask = Log::MAX(PEAR_LOG_INFO);

要计算大于或等于一定级别的所有优先级的掩码,请使用静态方法 Log::MIN()

$mask = Log::MIN(PEAR_LOG_INFO);

应用掩码时,请使用 setMask() 方法

$logger->setMask($mask);

可以使用位运算组合掩码。为了限制日志记录只记录标记为 PEAR_LOG_NOTICEPEAR_LOG_DEBUG 的事件

$mask = Log::MASK(PEAR_LOG_NOTICE) | Log::MASK(PEAR_LOG_DEBUG);
$logger->setMask($mask);

为了方便,预先定义了两个特殊掩码:PEAR_LOG_NONEPEAR_LOG_ALL。其中 PEAR_LOG_ALL 对于排除特定优先级特别有用

$mask = PEAR_LOG_ALL ^ Log::MASK(PEAR_LOG_NOTICE);
$logger->setMask($mask);

还可以检索和修改现有日志对象的掩码

$mask = $logger->getMask() | Log::MASK(PEAR_LOG_INFO);
$logger->setMask($mask);

1.6   日志行格式

大多数日志处理器支持可配置的行格式。以下是一张运行时将扩展为与日志事件相关的上下文信息的特殊标记的列表。每个标记还有一个简写符号。

1.7   刷新日志事件

一些日志处理器(如 控制台处理器)支持显式的“缓冲”。当启用缓冲时,日志事件实际上不会写入输出流,直到处理器关闭。其他处理器(如 文件处理器)由于使用了操作系统的IO例程,可能会缓冲输出,因此支持隐式缓冲。

然而,可以通过调用它们的 flush() 方法来强制这些处理器刷新其输出

$conf = array('buffering' => true);
$logger = Log::singleton('console', '', 'test', $conf);

for ($i = 0; $i < 10; $i++) {
    $logger->log('This event will be buffered.');
}

/* Flush all of the buffered log events. */
$logger->flush();

for ($i = 0; $i < 10; $i++) {
    $logger->log('This event will be buffered.');
}

/* Implicitly flush the buffered events on close. */
$logger->close();

目前,只有 控制台处理器文件处理器Firebug处理器邮件处理器 实现了 flush() 方法。

2   标准日志处理器

2.1   控制台处理器

控制台处理器将日志事件直接输出到控制台。它支持输出缓冲和可配置的字符串格式。

2.1.1   配置

2.1.2   示例

$logger = Log::singleton('console', '', 'ident');
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.2   显示处理器

显示处理器简单地将日志事件打印回浏览器。它尊重 error_prepend_stringerror_append_string 错误处理值,当从标准错误处理器 记录 时很有用。

2.2.1   配置

2.2.2   示例

$conf = array('error_prepend' => '<font color="#ff0000"><tt>',
              'error_append'  => '</tt></font>');
$logger = Log::singleton('display', '', '', $conf, PEAR_LOG_DEBUG);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.3   错误日志处理器

Error_Log处理器将日志事件发送到PHP的 error_log() 函数。

2.3.1   配置

2.3.2   错误日志类型

所有可用的日志类型在PHP手册的 error_log() 部分中详细说明。为了方便,日志包还定义了以下常量,可用于 $name 处理器构造参数。

2.3.3   示例

$logger = Log::singleton('error_log', PEAR_LOG_TYPE_SYSTEM, 'ident');
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.4   文件处理器

文件处理器使用可配置的字符串格式将日志事件写入文本文件。

2.4.1   配置

文件处理器只有在负责创建文件时才会尝试设置 mode 值。

2.4.2   示例

$conf = array('mode' => 0600, 'timeFormat' => '%X %x');
$logger = Log::singleton('file', 'out.log', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.5   Firebug 处理器

Firebug处理器将日志事件输出到 Firebug 控制台。它支持输出缓冲和可配置的字符串格式。

2.5.1   配置

2.5.2   示例

$logger = Log::singleton('firebug', '', 'ident');
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.6   邮件处理器

邮件处理器聚合一个会话的日志事件,并使用 PEAR Mail 包或PHP的本地 mail() 函数将它们发送到邮件消息的主体中。

如果指定了空的 mailBackend 值,则将使用 mail() 函数而不是 PEAR Mail 包。

可以通过在 $name 构造参数中用逗号分隔电子邮件地址来指定多个收件人。

2.6.1   配置

2.6.2   示例

$conf = array('subject' => 'Important Log Events');
$logger = Log::singleton('mail', 'webmaster@example.com', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.7   MDB2 处理器

MDB2处理器类似于 SQL(DB)处理器,但它不是使用PEAR DB包,而是使用 MDB2数据库抽象包

2.7.1   配置

2.8   空处理器

空处理器简单地消费日志事件(类似于将它们发送到/dev/null)。尊重日志级别掩码,事件仍然会被发送到任何已注册的日志观察者

2.8.1   示例

$logger = Log::singleton('null');
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.9   SQL (DB) 处理器

SQL处理器使用PEAR的DB抽象层将日志事件发送到数据库。

注意:由于默认数据库模式限制,SQL处理器将$ident字符串的长度限制为十六(16)个字符。此限制可以通过identLimit配置参数进行调整。

2.9.1   日志表

此处理器默认使用的SQL表如下所示

CREATE TABLE log_table (
    id          INT NOT NULL,
    logtime     TIMESTAMP NOT NULL,
    ident       CHAR(16) NOT NULL,
    priority    INT NOT NULL,
    message     VARCHAR(200),
    PRIMARY KEY (id)
);

这是在所有SQL兼容数据库中都应该工作的“最低公共分母”。但是,您可能需要为此模式进行数据库或站点特定的更改,以满足您的特定需求。例如,PostgreSQL用户可能更喜欢将message字段的类型更改为TEXT

2.9.2   配置

将日志条目写入的数据库表名称使用$name构造参数指定(参见配置处理器)。

2.9.3   示例

使用数据源名称创建新的数据库连接

$conf = array('dsn' => 'pgsql://jon@localhost+unix/logs');
$logger = Log::singleton('sql', 'log_table', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

使用现有的DB对象

require_once 'DB.php';
$db = &DB::connect('pgsql://jon@localhost+unix/logs');

$conf['db'] = $db;
$logger = Log::singleton('sql', 'log_table', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.10   SQLite 处理器

SQLite处理器使用原生PHP sqlite函数将日志事件发送到SQLite数据库。

它比SQL(DB)处理器更快,因为它直接向数据库发出请求,而不使用抽象层。值得注意的是,SQLite数据库文件可以在您的系统上移动、复制和删除,就像任何其他文件一样,这使得日志管理变得更容易。最后但并非最不重要的是,使用数据库记录事件允许您使用SQL查询创建报告和统计信息。

当使用数据库并记录大量事件时,建议将数据库分割成更小的数据库。SQLite允许这样做,并且您可以稍后使用SQLite的ATTACH语句全局查询日志数据库文件。

当日志打开时,如果数据库不存在,sqlite将尝试自动创建它。如果日志表不存在,它也将自动创建。表创建使用以下SQL请求

CREATE TABLE log_table (
    id          INTEGER PRIMARY KEY NOT NULL,
    logtime     NOT NULL,
    ident       CHAR(16) NOT NULL,
    priority    INT NOT NULL,
    message
);

2.10.1   配置

也可以将已打开的数据库连接作为参数传递,而不是上面的配置。在这种情况下,关闭数据库连接取决于用户。

2.10.2   示例

使用配置创建新的数据库连接

$conf = array('filename' => 'log.db', 'mode' => 0666, 'persistent' => true);
$logger = Log::factory('sqlite', 'log_table', 'ident', $conf);
$logger->log('logging an event', PEAR_LOG_WARNING);

使用现有连接

$db = sqlite_open('log.db', 0666, $error);
$logger = Log::factory('sqlite', 'log_table', 'ident', $db);
$logger->log('logging an event', PEAR_LOG_WARNING);
sqlite_close($db);

2.11   Syslog 处理器

Syslog处理器将日志事件发送到系统日志服务(在类Unix环境中为syslog或在Windows系统上的事件日志)。事件是通过PHP的syslog()函数发送的。

2.11.1   配置

2.11.2   设施

2.11.3   示例

$logger = Log::singleton('syslog', LOG_LOCAL0, 'ident');
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

2.12   窗口处理器

Window处理器将日志事件发送到单独的浏览器窗口。此处理器的原始想法受到了Craig Davis在"JavaScript Power PHP Debugging"一文中所述的启发。

2.12.1   配置

注意:当PHP的输出缓冲系统启用时,Window处理器可能无法可靠地工作。

2.12.2   示例

$conf = array('title' => 'Sample Log Output');
$logger = Log::singleton('win', 'LogWindow', 'ident', $conf);
for ($i = 0; $i < 10; $i++) {
    $logger->log("Log entry $i");
}

3   组合处理器

将事件记录到多个处理器通常是很有用的。日志包提供了一个组合系统,使得这项任务变得非常简单。

首先创建单个日志处理器

$console = Log::factory('console', '', 'TEST');
$file = Log::factory('file', 'out.log', 'TEST');

然后,构建一个组合处理器,并将单个处理器作为组合的子项添加

$composite = Log::singleton('composite');
$composite->addChild($console);
$composite->addChild($file);

复合处理器实现了标准 Log 接口,因此您可以使用它就像使用其他任何处理器一样。

$composite->log('This event will be logged to both handlers.');

当不再需要时,可以从复合中移除子元素。

$composite->removeChild($file);

4   日志观察者

日志观察者提供了一个 观察者模式 的实现。在 Log 包的内容中,它们提供了一种机制,可以检查(即观察)每个日志事件。这允许根据日志事件的 内容实现特殊行为。例如,观察者代码可以在日志事件包含字符串 PANIC 时发送警报电子邮件。

创建日志观察者涉及实现 Log_observer 类的子类。子类必须重写基类的 notify() 方法。该方法接收一个包含事件优先级和事件的哈希表。子类的实现可以自由地以任何方式处理这些信息。

日志观察者通过 attach() 方法附加到 Log 实例。

$observer = Log_observer::factory('yourType');
$logger->attach($observer);

可以使用 detach() 方法断开观察者。

$logger->detach($observer);

目前,Log 包中没有提供具体的 Log_observer 实现。

5   从标准错误处理器进行日志记录

5.1   记录 PHP 错误

可以使用 set_error_handler() 函数覆盖 PHP 的默认错误处理器。自定义错误处理函数可以使用全局 Log 实例来记录 PHP 错误。

注意: 目前无法通过自定义错误处理器处理致命的 PHP 错误。

function errorHandler($code, $message, $file, $line)
{
    global $logger;

    /* Map the PHP error to a Log priority. */
    switch ($code) {
    case E_WARNING:
    case E_USER_WARNING:
        $priority = PEAR_LOG_WARNING;
        break;
    case E_NOTICE:
    case E_USER_NOTICE:
        $priority = PEAR_LOG_NOTICE;
        break;
    case E_ERROR:
    case E_USER_ERROR:
        $priority = PEAR_LOG_ERR;
        break;
    default:
        $priority = PEAR_LOG_INFO;
    }

    $logger->log($message . ' in ' . $file . ' at line ' . $line,
                 $priority);
}

set_error_handler('errorHandler');
trigger_error('This is an information log message.', E_USER_NOTICE);

5.2   记录 PHP 断言

PHP 允许用户定义 assert() 回调处理器。使用 assert_options() 函数配置断言回调。

function assertCallback($file, $line, $message)
{
    global $logger;

    $logger->log($message . ' in ' . $file . ' at line ' . $line,
                 PEAR_LOG_ALERT);
}

assert_options(ASSERT_CALLBACK, 'assertCallback');
assert(false);

5.3   记录 PHP 异常

PHP 5 及以后的版本支持 异常 的概念。可以使用 set_exception_handler() 函数分配自定义异常处理器。

function exceptionHandler($exception)
{
    global $logger;

    $logger->log($exception->getMessage(), PEAR_LOG_ALERT);
}

set_exception_handler('exceptionHandler');
throw new Exception('Uncaught Exception');

5.4   记录 PEAR 错误

Log 包可以通过 PEAR::setErrorHandling()PEAR_ERROR_CALLBACK 机制使用,通过编写使用全局 Log 实例的错误处理函数。以下是一个示例

function errorHandler($error)
{
    global $logger;

    $message = $error->getMessage();

    if (!empty($error->backtrace[1]['file'])) {
        $message .= ' (' . $error->backtrace[1]['file'];
        if (!empty($error->backtrace[1]['line'])) {
            $message .= ' at line ' . $error->backtrace[1]['line'];
        }
        $message .= ')';
    }

    $logger->log($message, $error->code);
}

PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'errorHandler');
PEAR::raiseError('This is an information log message.', PEAR_LOG_INFO);

6   自定义处理器

有时标准处理器并不能完全满足您的需求。在这些情况下,解决方案可能是编写一个自定义处理器。

6.1   使用自定义处理器

使用自定义 Log 处理器非常简单。一旦编写完成(请参阅下面的 编写新处理器扩展现有处理器),您可以选择将文件放在 PEAR 安装的主 Log/ 目录中(通常是类似 /usr/local/lib/php/LogC:\php\pear\Log 的位置),这样它就可以被系统上的任何 PHP 应用程序找到和使用,或者将其放置在应用程序的本地层次结构的某个位置,并在构建自定义 Log 对象之前包含它。

6.1.1   方法 1:标准位置的处理程序

将处理器文件复制到 PEAR 安装的 Log/ 目录后,只需将处理器视为标准分发的一部分。如果您的处理器名为 custom(因此由名为 Log_custom 的类实现)

require_once 'Log.php';

$logger = Log::factory('custom', '', 'CUSTOM');

6.1.2   方法 2:自定义位置的处理程序

如果您更喜欢将处理器存储在应用程序的本地层次结构中,您需要在该文件之前包含它,然后才能基于它创建 Log 实例。

require_once 'Log.php';
require_once 'LocalHandlers/custom.php';

$logger = Log::factory('custom', '', 'CUSTOM');

6.2   编写新处理器

编写新的 Log 处理器就像编写一个新的类,该类扩展了 Log 类,并实现了相对较小的一组标准方法。

每个处理器类的名称必须以Log_开头,以便Log包能够识别。

class Log_custom extends Log

处理器的构造函数将使用四个参数调用。这些值在配置处理器部分有详细讨论。

Log_custom($name, $ident = '', $conf = array(), $level = PEAR_LOG_DEBUG)

构造函数负责根据这些值配置处理器。处理器特定的参数作为$conf数组的一部分传递。至少,处理器的构造函数必须设置由Log基类定义的以下值

$this->_id = md5(microtime().rand());
$this->_name = $name;
$this->_ident = $ident;
$this->_mask = Log::UPTO($level);

下面的处理器方法部分详细说明了日志处理器可以实现的多种标准方法。《实用方法》部分描述了由Log基类提供的某些有用的实用方法,这些方法在实现日志处理器时可能很有用。

6.3   扩展现有处理器

扩展现有处理器与编写新处理器非常相似,除了处理器不是直接继承自Log基类,而是扩展现有处理器的类。这是一种在不编写全新类的情况下(按照面向对象编程的精神)向现有处理器添加一些自定义行为的有用方式。

例如,邮件处理器可以通过创建一个新的具有符合规定的构造函数和自定义的log()方法的Log_mail_mime类来扩展以支持发送带有MIME编码附件的消息。其余的标准方法将回退到Log_mail基类的实现。

显然,扩展现有处理器的具体细节需要对该处理器的实现有很好的了解。

6.4   处理器方法

6.4.1   bool open()

调用open()方法以打开日志资源进行输出。处理器可以在构造时立即调用open()或在运行时(例如接收到第一个日志事件时)懒惰地调用。

Log基类提供了一个受保护的$_opened成员变量,当日志处理器打开时将其设置为true,当关闭时将其设置为false。处理器方法可以检查此值以确定处理器是否当前打开并准备好输出。

如果open()方法未能使处理器准备好输出,它应返回false并将$this->_opened设置为false

6.4.2   bool close()

调用close()方法以关闭日志资源。此方法与open()方法类似。即使在处理器未打开以进行输出时,也可以安全地调用close()

如果close()方法未能关闭处理器,它应返回false。否则,它应返回true。应适当更新$this->_opened标志。

6.4.3   bool flush()

flush()方法清除任何缓冲的日志事件,如清除日志事件中所述。此方法的具体实现将主要取决于处理器。如果处理器不支持缓冲输出,则不需要实现此方法;将调用Log类的flush()方法。

6.4.4   bool log($message, $priority = null)

log()方法是每个日志处理器的核心。每当用户希望记录一个事件时,都会调用它。此方法的实现非常特定于处理器。它应根据处理器是否成功记录了消息返回truefalse

log()实现应在成功记录事件时始终调用_announce()

6.5   实用方法

这些实用方法由Log基类提供,并为处理器实现提供常见的有用功能。

6.5.1   string _extractMessage($message)

此方法返回提供的消息数据的字符串表示形式。

如果$message是一个对象,_extractMessage()将尝试使用已知方法(例如,使用PEAR_Error对象的getMessage()方法)提取消息文本。如果找不到已知方法,将返回对象的序列化表示。

如果消息数据已经是一个字符串,它将被原样返回。

6.5.2   string _format($format, $timestamp, $priority, $message)

此方法基于格式字符串和表示当前日志记录和状态的标记集生成格式化的日志行。

6.5.3   bool _isMasked($priority)

此方法检查给定的优先级是否包含在处理器的当前级别掩码中。这对于确定是否将日志事件写入处理器的日志很有用。

6.5.4   void _announce($event)

此方法通知任何已注册的日志观察者已记录新事件。$event是一个包含两个命名元素的数组

array('priority' => $priority, 'message' => $message)

_announce()应该在事件成功记录时从处理器的log()方法中调用。否则,注册的观察者将永远不会意识到该事件。