企业应用后端【免费下载链接】dolibarrDolibarr ERP CRM is a modern software package to manage your company or foundations activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, ...). its an open source Web application (written in PHP) designed for businesses of any sizes, foundations and freelancers.项目地址https://gitcode.com/gh_mirrors/do/dolibarr点击查看免费下载PSR-3PHP Standard Recommendation 3定义了 PHP 应用与日志库之间统一的日志接口规范。本篇文章以 Dolibarr 仓库内置的psr/log包为对象系统讲解Psr\Log命名空间下各个接口、基类、Trait 与测试工具的设计意图和用法并展示它如何在仓库内的 sabre/dav WebDAV 组件中被实际消费。读完本文你将掌握如何基于 PSR-3 编写一个符合规范的日志器、如何在业务类中安全地注入并使用日志器以及如何借助包内测试设施验证实现的合规性。一、psr/log 是什么一套日志接口而非日志器本身psr/log是一个被 PHP-FIG 维护的标准接口包它的定位在包描述中写得很清楚Common interface for logging libraries日志库的公共接口。该包在 composer.json 中声明了自己的名字为psr/log许可证为 MIT最低 PHP 版本要求为5.3.0。需要特别强调的是psr/log 本身不是一个日志器logger。它不负责写文件、输出到终端或发送邮件而是纯粹定义了一个日志器应该长什么样。真正的日志实现如 Monolog、Log4php 等通过实现这套接口来接入任何遵循 PSR-3 的代码库从而保证调用方写日志的方式与日志库具体怎么落盘完全解耦。正如其 README.md 所述它只是描述日志器的接口具体行为细节需参阅 PSR-3 规范原文。二、安装与包结构2.1 通过 Composer 安装在任意 PHP 项目中安装psr/log只需一条命令composer require psr/log安装后包通过 PSR-4 自动加载规则将命名空间Psr\Log\映射到Psr/Log/目录见 composer.json 中的autoload配置。在 Dolibarr 仓库中这个包被 vendor 化托管在 htdocs/includes/sabre/psr/log与 sabre/dav 等其他第三方依赖一起随仓库分发。2.2 目录与文件清单包内源码位于Psr/Log/子目录共包含 7 个核心 PHP 文件文件类型作用LoggerInterface.php接口定义日志器的全部 8 个级别方法 通用log()方法LogLevel.php类定义 8 个日志级别常量AbstractLogger.php抽象类把级别方法统一委托给log()减少样板代码LoggerTrait.phpTrait与 AbstractLogger 等价供无法继承抽象类的类使用NullLogger.php类空实现日志器丢弃一切日志LoggerAwareInterface.php接口定义setLogger()注入方法LoggerAwareTrait.phpTraitLoggerAwareInterface的便捷实现InvalidArgumentException.php类日志级别非法时抛出的异常此外Psr/Log/Test/目录还提供了用于验证实现合规性的测试基类与测试日志器详见第五节。三、核心接口 LoggerInterface 逐方法解析LoggerInterface.php 是整个规范的心脏。它声明了 8 个按严重程度排序的级别方法外加 1 个通用的log($level, $message, $context)方法interface LoggerInterface { public function emergency($message, array $context array()); public function alert($message, array $context array()); public function critical($message, array $context array()); public function error($message, array $context array()); public function warning($message, array $context array()); public function notice($message, array $context array()); public function info($message, array $context array()); public function debug($message, array $context array()); public function log($level, $message, array $context array()); }每个方法在源码中都带有规范的语义注释用于指导实现者与调用方emergency系统不可用System is unusable需要立即人工介入alert必须立即采取行动例如整个网站宕机、数据库不可用规范注释中甚至建议此时应触发短信告警把人叫醒critical严重状况例如应用组件不可用、出现未预期的异常error运行期错误不要求立即处理但应当被记录并监控warning例外但非错误的状况例如使用了已废弃的 API、API 使用不当、出现了不希望但不算错误的情况notice正常但重要的事件info有意义的事件例如用户登录、SQL 日志debug详细的调试信息。接口层面还约定了三条对实现方有约束力的规则写在接口的类注释中消息类型$message必须是字符串或实现了__toString()的对象占位符替换消息中可包含{foo}形式的占位符实现方应将其替换为$context数组中键为foo的值异常约定$context数组可以携带任意数据但唯一可被实现方依赖的假设是——如果需要记录异常以生成堆栈追踪该异常必须放在键名为exception的位置。非法级别调用log()时应抛出 InvalidArgumentException.php 定义的异常该异常类直接继承 PHP 内置的\InvalidArgumentException因此对调用方而言可以用标准的异常捕获机制处理。四、日志级别 LogLevel八个常量的统一命名LogLevel.php 将 8 个级别以常量形式固化级别字符串是各实现之间互操作的基础class LogLevel { const EMERGENCY emergency; const ALERT alert; const CRITICAL critical; const ERROR error; const WARNING warning; const NOTICE notice; const INFO info; const DEBUG debug; }使用LogLevel::ERROR这样的常量而非手写字符串可以避免拼写错误也能让 IDE 提供自动补全。注意规范规定log()方法接收的$level必须是这 8 个值之一传入其他值即触发InvalidArgumentException。五、减少样板代码的三件利器5.1 抽象类 AbstractLogger统一委托到 log()AbstractLogger.php 实现了LoggerInterface的全部 8 个级别方法但它们不做事只是把调用原样转发给唯一的抽象方法log()abstract class AbstractLogger implements LoggerInterface { public function emergency($message, array $context array()) { $this-log(LogLevel::EMERGENCY, $message, $context); } // alert/critical/error/warning/notice/info/debug 结构完全相同 abstract public function log($level, $message, array $context array()); }这意味着如果你的日志器对所有级别采用相同的处理逻辑例如统一格式化后写入文件那么只需继承AbstractLogger并实现log()一个方法即可8 个级别方法全部免费获得。5.2 Trait LoggerTrait给无法继承的类用场景中经常有我的类已经继承了某个基类无法再继承 AbstractLogger的情况。此时可改用 LoggerTrait.php它的方法与AbstractLogger完全等价只是以 Trait 形式提供并通过abstract public function log(...)强制宿主类实现log()trait LoggerTrait { public function emergency($message, array $context array()) { $this-log(LogLevel::EMERGENCY, $message, $context); } // ... abstract public function log($level, $message, array $context array()); }5.3 NullLogger消灭 if 判空NullLogger.php 继承AbstractLogger把log()实现为空操作// noop。它的存在意义是日志应该是可选能力与其在代码里到处写if ($this-logger) { ... }不如在拿不到真实日志器时塞一个NullLogger实例让调用方代码保持干净。这正是 README 示例中Foo类那种构造函数接收可选 logger 使用时判空模式的替代方案详见第六节。六、使用方法从 README 示例到可落地实践6.1 README 提供的经典用法README.md 给出了需要日志但不想绑定具体日志库的标准写法——依赖注入接口而非具体实现?php use Psr\Log\LoggerInterface; class Foo { private $logger; public function __construct(LoggerInterface $logger null) { $this-logger $logger; } public function doSomething() { if ($this-logger) { $this-logger-info(Doing work); } try { $this-doSomethingElse(); } catch (Exception $exception) { $this-logger-error(Oh no!, array(exception $exception)); } // do something useful } }这段代码展示了三个要点构造函数的类型提示是LoggerInterface因此任何 PSR-3 实现Monolog、自研日志器、甚至NullLogger都可以被注入记录异常时把异常对象放进$context数组的exception键符合接口注释中的约定便于实现方生成堆栈追踪$logger允许为 null调用前先判空——这是日志可选的朴素实现。调用方拿到Foo后再自行挑选一个接口的实现来获得真实日志能力You can then pick one of the implementations of the interface to get a logger.6.2 用 NullLogger 替代判空如果不想在doSomething()里写if可以在构造函数里做一次兜底public function __construct(LoggerInterface $logger null) { $this-logger $logger ?: new NullLogger(); }之后所有$this-logger-info(...)都无需判空没有真实日志器时日志被静默丢弃。这正是 NullLogger.php 类注释所提倡的用法。6.3 实现自己的 LoggerInterface 实现如需为项目编写一个 PSR-3 兼容的日志器推荐路径是继承AbstractLogger并只实现log()use Psr\Log\AbstractLogger; class FileLogger extends AbstractLogger { public function log($level, $message, array $context array()) { // 将 {foo} 占位符替换为 context 中的值规范建议实现方负责替换 // 将 $level 与 $message 格式化后写入日志文件 // 若 $context 中存在 exception 键且为 \Exception可追加其堆栈追踪 } }若你的类已继承其他基类则改用use Psr\Log\LoggerTrait;同样只需实现log()。实现完成后可参考第五节介绍LoggerAwareInterface/LoggerAwareTrait把日志器注入到组件中。七、让组件可注入日志LoggerAware 机制为了让需要日志的类与日志器之间的装配更规范psr/log 还提供了注入协议LoggerAwareInterface.php 只声明一个方法setLogger(LoggerInterface $logger)LoggerAwareTrait.php 提供默认实现持有一个protected $logger属性并通过setLogger()赋值。组件类只需implements LoggerAwareInterface并use LoggerAwareTrait;就拥有了标准的日志注入入口容器或调用方可以通过setLogger()把任意 PSR-3 日志器交给它。这是框架层如 Symfony 的依赖注入容器在装配时自动注入日志器的通用协议。八、仓库内真实集成案例sabre/dav 的 Server 类在 Dolibarr 仓库中psr/log 并非孤立存在而是被 WebDAV 组件 sabre/dav 实际消费。htdocs/includes/sabre/sabre/dav/lib/DAV/Server.php 的头部就同时引入了四个 PSR-3 相关符号use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger;随后Server类声明implements LoggerAwareInterface, EmitterInterface并使用LoggerAwareTrait见该文件第 2629 行。这意味着 Dolibarr 的 WebDAV 服务端支持通过setLogger()注入任意 PSR-3 日志器来记录 DAV 请求处理过程在未注入任何日志器时可以回退到NullLogger保持静默。这是一个接口定义、Trait 提供默认实现、NullLogger 兜底三件套配合使用的完整范例也印证了第六节介绍的注入模式在真实框架组件中的落地方式。九、测试设施如何验证你的实现符合规范包内自带两套测试辅助设施位于 Psr/Log/Test9.1 TestLogger内存版记录日志器TestLogger.php 继承AbstractLogger把log()收到的每条记录level、message、context追加到$records与$recordsByLevel两个公开属性中。它还通过魔术方法__call()动态派生出大量便捷断言方法例如hasError($record)、hasInfoThatContains($message)、hasWarningThatMatches($regex)、hasCriticalThatPasses($callback)等方便在单元测试中快速断言是否记录过某条日志。reset()方法用于清空已收集的记录。9.2 LoggerInterfaceTest合规性测试基类LoggerInterfaceTest.php 是一个抽象 PHPUnit 测试基类。你自己的日志器只需继承它并实现两个抽象方法——getLogger()返回被测日志器实例与getLogs()按顺序返回格式化后的日志记录格式约定为LOG LEVEL MESSAGE即可自动运行整套规范合规测试覆盖以下关键行为testImplements被测对象必须是Psr\Log\LoggerInterface实例testLogsAtAllLevels8 个级别方法各自产生与log()调用等价的两条记录且消息中的{user}占位符被 context 正确替换testThrowsOnInvalidLevel对log()传入非法级别必须抛出Psr\Log\InvalidArgumentExceptiontestContextReplacement验证{foo.bar}这类带点的占位符键也能正确替换testObjectCastToString消息传入实现了__toString()的对象时会被正确转成字符串testContextCanContainAnythingcontext 中可包含布尔、null、数组、对象、资源甚至已关闭的资源句柄实现不得因 context 内容崩溃testContextExceptionKeyCanBeExceptionOrOtherValuesexception键可以是任意值字符串或异常对象实现都应容忍。这套测试是衡量我的实现是否真的符合 PSR-3的权威标尺强烈建议自研日志器时直接继承使用。十、小结psr/log 用极小的代码量定义了 PHP 生态日志互操作的标准LoggerInterface是契约LogLevel统一级别命名AbstractLogger/LoggerTrait降低实现成本NullLogger提供空实现兜底LoggerAwareInterface/LoggerAwareTrait打通注入链路而Test目录下的设施让合规性可被自动验证。在 Dolibarr 仓库中这套接口已被 sabre/dav 组件直接使用是接口与实现分离这一工程原则的典型示范。无论你是要在自己的 PHP 项目中引入日志库还是想为 Dolibarr 相关的扩展组件补充日志能力从实现一个LoggerInterface开始都是成本最低、兼容性最好的路径。赞分享企业应用后端【免费下载链接】dolibarrDolibarr ERP CRM is a modern software package to manage your company or foundations activity (contacts, suppliers, invoices, orders, stocks, agenda, accounting, ...). its an open source Web application (written in PHP) designed for businesses of any sizes, foundations and freelancers.项目地址https://gitcode.com/gh_mirrors/do/dolibarr点击查看免费下载相关推荐PSR-3 日志接口规范与 psr/log 包实战解析 ShowDoc 仓库中的 LoggerInterface、NullLogger 与测试体系PSR 3 日志接口规范与 psr/log 包实战解析 ShowDoc 仓库中的 LoggerInterface、NullLogger 与测试体系 PSR 3文档知识库后端前端sql-server-samples 中的 Laravel 日志基石深入解析 psr/log 与 PSR-3 接口规范sql server samples 中的 Laravel 日志基石深入解析 psr/log 与 PSR 3 接口规范 在 sql server sample示例工程数据库教程后端深入解析 psr/log 与 PSR-3 日志接口从接口设计到 mailcow 中的实战应用深入解析 psr/log 与 PSR 3 日志接口从接口设计到 mailcow 中的实战应用 PSR 3Logger Interface是 PHP FIG后端企业应用上一篇Skia GM 测试中的可移植字体机制CreatePortableTypeface 原理与实战下一篇终极Soundcloud音乐下载器5步搞定免费音乐收藏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考