優れた使いやすい日誌を設計します.

7247 ワード

この文章は数年前に個人プロジェクトを開発する時に作ったものです.
ログ記録は、アプリケーションの保守、特に実行環境に配備されたアプリケーションのデバッグにとって重要な意義を持っています.
アプリケーションのログシステムについては、まずログオブジェクトが必要です.同時にこの情報は、コンソール、ファイル、またはネットワークなど、異なる位置に出力されてもよい.情報のフォーマットについては、必要に応じて、普通のテキスト、XMLまたはHTML形式に出力できます.また、ログ情報を異なるレベルで分類する必要があります.このような利点は、冗長情報をフィルタリングして、肝心なログだけを残してもいいです.ログフレームに対しては、ログオブジェクトは設定可能である必要があり、設定に従って指定されたターゲットに出力することができ、設定に従って出力のフォーマットを決定し、どのレベル以上のログを出力するかを決定することができる.
PHPプログラマーになってから、多くのPHPフレームを使ったことがあります.大同小易の日記類を使いすぎました.抑制や自分で一つのログクラスを実現するのもとても簡単です.例えば、これ、これ.
これらはすべてログを記録することができますが、これは本当に私たちが必要なログ機能ですか?
次に、ソフトウェアテストの担当者(プログラマではない)に聞いてみましたが、あなたが理解しているログ機能はいったい何がありますか?彼がくれた答えは大体次の通りです.
記録情報:出力結果のアウトラインを一箇所で確認することができます.指定されたレベルのログ記録フォーマットをフィルタして出力することができます.ファイル、xml、txtなどの様々な形で出力できます.エラーは毎回検出できません.いくつかのエラーに対して、アプリケーションのメンテナに注意することができます.
上記の4つによれば、ほとんどのフレームの中で1-3という部分の機能がほぼ実現されています.
  • ローカルファイル、SAE環境などの情報を記録します.
  • 特定レベル情報をフィルタリングする
  • フォーマット出力は、ほとんどの使用シーンがtxt形式であり、他のスタイルを拡張するのも難しくないはずです.
    警察への通報については、この一つは基本的に何も反映されていません.この点について考えてみますと、実はとても重要な一環だと思います.私達の日常開発から言えば、この場面を仮定します.カスタマーサービスの子供靴はオンラインのバグが突然現れたことを反映しています.早く解決してください.
    私達の解決の構想は大体このようです.カスタマーサービスの子供靴からもらったバグによって、スクリーンショットや訪問先などの情報でこのバグを再現します.もし再現できたら、おめでとうございます.しかし、ほとんどのオンラインバグは再現しにくいです.あるいは特定の環境で再現できます.
    この時、私達はアプリケーションログを調べます.(記録していないなら、へへへ、あなた)、私達は膨大なログファイルから記録の情報を測ります.(大きさによって分割したら「しかも多く」があります.ここを見たら、「警報」はどれほど役に立つかと思いましたか?メールでもsmsでも、WeChatでも….銀弾ではないですが、でもたくさんの時間が節約できます.
    私の提案で有名な高性能ログコンポーネントSeaslogsはアラームメカニズムを追加しました.
    ここでは、その時の実現コードを提供します.ここでは、機能を別の機能コンポーネントに整理して、WS-logsを実現します.
    class Aert_Log
    {
        const TRACE = 1;
        const DEBUG = 2;
        const INFO  = 3;
        const WARN  = 4;
        const ERROR = 5;
        const FATAL = 6;
            
        private $enable = false;
        private $level;
            
        /**
         *      
         * @var Aert_LogAppender
         */
        private $appender;
        
        /**
         *      
         * @var Aert_LogAppender
         */
        private $alert;
        private $alertLevel;
        private $enableAlert = false;
            
        private static $levelNames = array(
            1 => 'TRACE',
            2 => 'DBEUG',
            3 => 'INFO',
            4 => 'WARN',
            5 => 'ERROR',
            6 => 'FATAL',
        );
        
        /**
         *              
         *
         * @param string $name
         * @param array $config
         *
         * @return Aert_Log
         */
        static function instance($name, array $config=array())
        {
            static $instances = array();
            if (!isset($instances[$name]))
            {
                $instances[$name] = new self($config);
            }
            return $instances[$name];
        }
        
        private function __construct(array $config)
        {
            $this->level = intval(val($config, 'level', self::WARN));
            $this->enable = (bool) val($config ,'enable' ,TRUE);
            
            if ($this->enable)
            {
                do {
                    if ( empty($config['appender']) || empty($config['appender']['class']) )
                    {
                        $this->enable = false;
                        break;
                    }
                    $class = $config['appender']['class'];
                    $params = (array) val($config['appender'], 'config', NULL);
                    
                    $this->appender = new $class($params);
                    
                    if ( !empty($config['alert']) || !empty($config['alert']['class']) )
                    {
                        $this->enableAlert = TRUE;                  
                        $this->alertLevel = (int) val($config['alert'] ,'level' ,self::ERROR);
                        
                        $class = $config['alert']['class'];
                        $params = (array) val($config['alert'], 'config', NULL);
                        
                        $this->alert = new $class($params);
                    }
                    
                } while (false);
            }
            
        }
        
        function log($level, $msg)
        {
            if (!$this->enable) return;
            if ($this->enableAlert && ($level >= $this->alertLevel))
            {
                $this->alert($level, $msg);
            }
            if ($level < $this->level) return;
            $this->appender->append(self::$levelNames[$level], $msg);
        }
        
        private function alert($level, $msg)
        {
            $this->alert->alert(self::$levelNames[$level], $msg);
        }
    }
    
    /**
     *      
     */
    class Aert_LogAppender
    {
        function __construct(array $config)
        {
            $this->init($config);   
        }
        
        protected function init(array $config)
        {
            
        }
        
        function append($level, $msg)
        {
            
        }
    }
    
    /**
     *      
     */
    class Aert_LogAlert
    {
        function __construct(array $config)
        {
            $this->init($config);   
        }
        
        protected function init(array $config)
        {
            
        }
        
        function alert($level, $msg)
        {
            
        }
    }
    
    ログの保存と警報を分離して、カスタマイズログ処理の複雑さと強化処理の多様性を大いに簡略化することができます.例えば、Fileメモリ、SAEメモリなどを単独で実現することができます.アラームに対しては、コンソール出力、メール、SMS、QQ、WeChat、SMSなど様々です.
    以下は2つの実現形態を示します.他はみんなで抽象的にします.
    コンソール(一般ブラウザ)出力実現
    [$level]: " . print_r($msg,true);
                }
                else
                {
                    dump($msg,"[{$level}]");                    
                }               
            }       
        }
    }
    
    ファイアフォックスプラグインFirePHP実現
    getCaption();
                if (empty($caption)) $caption = $level;
                
                FirePHP::getInstance(true)->table($caption, $msg->combingRows());
                return;
            }
            
            switch ($level)
            {
                case 'TRACE':
                    FirePHP::getInstance(true)->log($msg,$level);
                    break;
                case 'DBEUG':
                    FirePHP::getInstance(true)->log($msg,$level);               
                    break;
                case 'INFO':
                    FirePHP::getInstance(true)->info($msg);
                    break;
                case 'WARN':
                    FirePHP::getInstance(true)->warn($msg);
                    break;
                case 'ERROR':
                    FirePHP::getInstance(true)->error($msg);
                    break;
                case 'FATAL':
                    FirePHP::getInstance(true)->error($msg,$level);
                    break;
            }
        }
    }