我想在 PHP 中实现日志记录机制:
- 日志文件路径将在配置文件 config.php 中
- 在几个类(class)中,我想将一些事件记录到日志文件中
例如:
Class A {
public function f_A {
log_to_file($message);
}
}
Class B {
public function f_B {
log_to_file($message);
}
}
如果有任何提示,我将不胜感激。我想实现一些简单而优雅的解决方案。
我正在考虑(谢谢你的回答),我想我会这样做(也许,有一些错误,我是从头开始写的):
interface Logger {
public function log_message($message);
}
class LoggerFile implements Logger {
private $log_file;
public function __construct($log_file) {
$this->log_file = $log_file;
}
public function log_message($message) {
if (is_string($message)) {
file_put_contents($this->log_file, date("Y-m-d H:i:s")." ".$message."\n", FILE_APPEND);
}
}
}
//maybe in the future logging into database
class LoggerDb implements Logger {
private $db;
public function __construct($db) {
//some code
}
public function log_message($message) {
//some code
}
}
Class A {
private $logger;
public function __construct(Logger $l) {
$this->logger = $l;
}
public function f_A {
$this->logger->log_message($message);
}
}
Class B {
private $logger;
public function __construct(Logger $l) {
$this->logger = $l;
}
public function f_B {
$this->logger->log_message($message);
}
}
//usage:
//in config.php:
define("CONFIG_LOG_FILE", "log/app_log.log");
//in the index.php or some other files
$logger = new LoggerFile(CONFIG_LOG_FILE);
$instance_a = new A($logger);
$instance_b = new B($logger);
最佳答案
记录器用在什么地方?
一般来说,在代码中使用记录器有两个主要用例:
侵入式日志记录:
大多数人使用这种方法是因为它最容易理解。
实际上,如果日志记录是域逻辑本身的一部分,那么您应该只使用侵入式日志记录。例如 - 在处理支付或敏感信息管理的类中。
非侵入式日志记录:
使用此方法而不是更改您希望记录的类,您将现有实例包装在一个容器中,该容器允许您跟踪实例和应用程序其余部分之间的每次交换。
您还可以临时启用此类日志记录,同时在开发环境之外调试某些特定问题或对用户行为进行一些研究。由于记录实例的类永远不会改变,因此与侵入式日志记录相比,破坏项目行为的风险要低得多。
实现侵入式记录器
为此,您可以使用两种主要方法。您可以注入(inject)一个实现 Logger
接口(interface)的实例,或者为该类提供一个工厂,该工厂仅在必要时才初始化日志系统。
Note:
Since it seems that direct injection is not some hidden mystery for you, I will leave that part out... only I would urge you to avoid using constants outside of a file where they have been defined.
现在 .. 工厂和延迟加载的实现。
您首先要定义您将使用的 API(在完美世界中,您从单元测试开始)。
class Foobar
{
private $loggerFactory;
public function __construct(Creator $loggerFactory, ....)
{
$this->loggerFactory = $loggerFactory;
....
}
....
public function someLoggedMethod()
{
$logger = $this->loggerFactory->provide('simple');
$logger->log( ... logged data .. );
....
}
....
}
这个工厂会有两个额外的好处:
- 它可以确保只创建一个实例而不需要全局状态
- 提供编写单元测试时使用的接缝
Note:
Actually, when written this way the class Foobar only depends on an instance that implements the Creator interface. Usually you will inject either a builder (if you need to type of instance, probably with some setting) or a factory (if you want to create different instance with same interface).
下一步将是工厂的实现:
class LazyLoggerFactory implements Creator
{
private $loggers = [];
private $providers = [];
public function addProvider($name, callable $provider)
{
$this->providers[$name] = $provider;
return $this;
}
public function provide($name)
{
if (array_key_exists($name, $this->loggers) === false)
{
$this->loggers[$name] = call_user_func($this->providers[$name]);
}
return $this->loggers[$name];
}
}
当您调用 $factory->provide('thing');
时,工厂会查找实例是否已创建。如果搜索失败,它会创建一个新实例。
Note: I am actually not entirely sure that this can be called "factory" since the instantiation is really encapsulated in the anonymous functions.
最后一步实际上是与供应商连接:
$config = include '/path/to/config/loggers.php';
$loggerFactory = new LazyLoggerFactory;
$loggerFactory->addProvider('simple', function() use ($config){
$instance = new SimpleFileLogger($config['log_file']);
return $instance;
});
/*
$loggerFactory->addProvider('fake', function(){
$instance = new NullLogger;
return $instance;
});
*/
$test = new Foobar( $loggerFactory );
当然,要完全理解这种方法,您必须了解闭包在 PHP 中的工作原理,但无论如何您都必须学习它们。
实现非侵入式日志记录
这种方法的核心思想是,不是注入(inject)记录器,而是将现有实例放入容器中,容器充当所述实例和应用程序之间的膜。然后,该膜可以执行不同的任务,其中之一就是记录。
class LogBrane
{
protected $target = null;
protected $logger = null;
public function __construct( $target, Logger $logger )
{
$this->target = $target;
$this->logger = $logger;
}
public function __call( $method, $arguments )
{
if ( method_exists( $this->target, $method ) === false )
{
// sometime you will want to log call of nonexistent method
}
try
{
$response = call_user_func_array( [$this->target, $method],
$arguments );
// write log, if you want
$this->logger->log(....);
}
catch (Exception $e)
{
// write log about exception
$this->logger->log(....);
// and re-throw to not disrupt the behavior
throw $e;
}
}
}
这个类也可以和上面描述的惰性工厂一起使用。
要使用此结构,您只需执行以下操作:
$instance = new Foobar;
$instance = new LogBrane( $instance, $logger );
$instance->someMethod();
此时包装实例的容器成为原始容器的全功能替代品。您的应用程序的其余部分可以像处理一个简单对象一样处理它(传递、调用方法)。包装的实例本身并不知道它正在被记录。
如果在某个时候您决定删除日志记录,则无需重写应用程序的其余部分即可完成。
关于PHP - 实现日志记录机制以在多个类中归档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18673941/