問題描述
我想在 PHP 中實現日志記錄機制:
I would like to implement logging mechanism to file in PHP:
- 日志文件路徑將在配置文件 config.php 中
- 在幾個類中,我想將一些事件記錄到日志文件中
例如:
Class A {
public function f_A {
log_to_file($message);
}
}
Class B {
public function f_B {
log_to_file($message);
}
}
我將非常感謝您提供任何提示.我想實現一些簡單而優雅的解決方案.
I will be very grateful for any tips. I would like to implement some easy and elegant solution.
我正在考慮(謝謝你的回答),我想我會這樣做(也許,有一些錯誤,我是從頭開始寫的):
I was thinking about it (thank you for your answers) and I think I will do it this way (maybe, there are some errors, I was writing it from scratch):
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."
", 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);
推薦答案
記錄器在哪里使用?
一般來說,在代碼中使用記錄器有兩個主要用例:
Where are loggers used?
In general there are two major use-cases for use of loggers within your code:
侵入式日志記錄:
invasive logging:
大多數人使用這種方法是因為它最容易理解.
For the most part people use this approach because it is the easiest to understand.
實際上,如果日志記錄是域邏輯本身的一部分,您應該只使用侵入式日志記錄.例如 - 在處理支付或敏感信息管理的課程中.
In reality you should only use invasive logging if logging is part of the domain logic itself. For example - in classes that deal with payments or management of sensitive information.
非侵入式日志記錄:
使用此方法而不是更改您希望記錄的類,您可以將現有實例包裝在一個容器中,這樣您就可以跟蹤實例與應用程序其余部分之間的每次交換.
With this method instead of altering the class that you wish to log, you wrap an existing instance in a container that lets you track every exchange between instance and rest of application.
您還可以臨時啟用此類日志記錄,同時在開發環境之外調試某些特定問題或對用戶行為進行一些研究時.由于記錄實例的類從未改變,與侵入式記錄相比,破壞項目行為的風險要低得多.
You also gain the ability to enable such logging temporarily, while debugging some specific problem outside of the development environment or when you are conducting some research of user behaviour. Since the class of the logged instance is never altered, the risk of disrupting the project's behaviour is a lot lower when compared to invasive logging.
為此,您可以使用兩種主要方法.您可以注入一個實現 Logger
接口的實例,也可以為類提供一個工廠,而工廠只會在必要時初始化日志系統.
To do this you have two main approaches available. You can either inject an instance that implements the Logger
interface, or provide the class with a factory that in turn will initialize the logging system only when necessary.
注意:
由于似乎直接注入對您來說并不是什么隱藏的秘密,我將省略那部分...只是我會敦促您避免使用常量在已定義它們的文件之外.
現在.. 工廠和延遲加載的實現.
Now .. the implementation with factory and lazy loading.
您首先要定義您將使用的 API(在完美的世界中,您從單元測試開始).
You start by defining the API that you will use (in perfect world you start with unit-tests).
class Foobar
{
private $loggerFactory;
public function __construct(Creator $loggerFactory, ....)
{
$this->loggerFactory = $loggerFactory;
....
}
....
public function someLoggedMethod()
{
$logger = $this->loggerFactory->provide('simple');
$logger->log( ... logged data .. );
....
}
....
}
這個工廠將有兩個額外的好處:
This factory will have two additional benefits:
- 可以保證只創建一個實例,不需要全局狀態
- 在編寫單元測試時提供一個接縫
注意:
實際上,當以這種方式編寫時,類 Foobar 僅依賴于實現 Creator 接口的實例.通常你會注入一個構建器(如果你需要類型的實例,可能需要一些設置)或一個工廠(如果你想用相同的界面創建不同的實例).
下一步將是工廠的實施:
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');
時,工廠會查找實例是否已創建.如果搜索失敗,它會創建一個新實例.
When you call $factory->provide('thing');
, the factory looks up if the instance has already been created. If the search fails it creates a new instance.
注意:我實際上并不完全確定這是否可以稱為工廠",因為實例化確實封裝在匿名函數中.
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 中的工作原理,但無論如何您都必須學習它們.
Of course to fully understand this approach you will have to know how closures work in PHP, but you will have to learn them anyway.
這種方法的核心思想是,不是注入記錄器,而是將現有實例放入容器中,該容器充當所述實例和應用程序之間的隔膜.這種膜可以執行不同的任務,其中之一就是記錄.
The core idea of this approach is that instead of injecting the logger, you put an existing instance in a container which acts as membrane between said instance and application. This membrane can then perform different tasks, one of those is logging.
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;
}
}
}
這個類也可以和上面描述的惰性工廠一起使用.
This class can also be used together with the above described lazy factory.
要使用此結構,您只需執行以下操作:
To use this structure, you simply do the following:
$instance = new Foobar;
$instance = new LogBrane( $instance, $logger );
$instance->someMethod();
此時包裝實例的容器成為原始容器的全功能替代品.應用程序的其余部分可以像處理一個簡單的對象一樣處理它(傳遞、調用方法).并且包裝的實例本身并不知道它正在被記錄.
At this point the container which wraps the instance becomes a fully functional replacement of the original. The rest of your application can handle it as if it is a simple object (pass around, call methods upon). And the wrapped instance itself is not aware that it is being logged.
如果您決定在某個時候刪除日志記錄,則無需重寫應用程序的其余部分即可完成.
And if at some point you decide to remove the logging then it can be done without rewriting the rest of your application.
這篇關于PHP - 在多個類中實現日志記錄機制的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!