dthh5038 2013-09-07 13:43
浏览 33
已采纳

PHP - 将记录机制实现为几个类中的文件

I would like to implement logging mechanism to file in PHP:

  1. log file path will be in config file config.php
  2. in several classes I would like to log some events into the log file

For example:

    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);
  • 写回答

3条回答 默认 最新

  • dongze8698 2013-09-08 10:52
    关注

    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.

    • Non-invasive logging:

      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.

    Implementing an invasive 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.

    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.

    Now .. the implementation with factory and lazy loading.

    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:

    • it can ensure that only one instance is created without a need for global state
    • provide a seam for use when writing unit-tests

    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).

    Next step would be implementation of the factory:

    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];
        }
    
    }
    

    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.

    And the last step is actually wiring it all up with providers:

    $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 );
    

    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.

    Implementing non-invasive logging

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 运动想象脑电信号数据集.vhdr
  • ¥15 三因素重复测量数据R语句编写,不存在交互作用
  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目