Test Failed
Push — 1.0.0-dev ( 52e7b8...740ad8 )
by nguereza
02:33
created

Log::debug()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
    defined('ROOT_PATH') || exit('Access denied');
3
    /**
4
     * TNH Framework
5
     *
6
     * A simple PHP framework using HMVC architecture
7
     *
8
     * This content is released under the MIT License (MIT)
9
     *
10
     * Copyright (c) 2017 TNH Framework
11
     *
12
     * Permission is hereby granted, free of charge, to any person obtaining a copy
13
     * of this software and associated documentation files (the "Software"), to deal
14
     * in the Software without restriction, including without limitation the rights
15
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
     * copies of the Software, and to permit persons to whom the Software is
17
     * furnished to do so, subject to the following conditions:
18
     *
19
     * The above copyright notice and this permission notice shall be included in all
20
     * copies or substantial portions of the Software.
21
     *
22
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
     * SOFTWARE.
29
     */
30
31
    class Log {
32
		
33
        /**
34
         * The defined constante for Log level
35
         */
36
        const NONE = 99999999;
37
        const FATAL = 500;
38
        const ERROR = 400;
39
        const WARNING = 300;
40
        const INFO = 200;
41
        const DEBUG = 100;
42
        const ALL = -99999999;
43
44
        /**
45
         * The logger name
46
         * @var string
47
         */
48
        private $logger = 'ROOT_LOGGER';
49
		
50
        /**
51
         * List of valid log level to be checked for the configuration
52
         * @var array
53
         */
54
        private static $validConfigLevel = array('off', 'none', 'fatal', 'error', 'warning', 'warn', 'info', 'debug', 'all');
55
56
        /**
57
         * Create new Log instance
58
         */
59
        public function __construct() {
60
        }
61
62
        /**
63
         * Set the logger to identify each message in the log
64
         * @param string $logger the logger name
65
         */
66
        public  function setLogger($logger) {
67
            $this->logger = $logger;
68
        }
69
70
        /**
71
         * get the logger name
72
         *
73
         * @return string
74
         */
75
        public  function getLogger() {
76
            return $this->logger;
77
        }
78
79
        /**
80
         * Save the fatal message in the log
81
         * @see Log::writeLog for more detail
82
         * @param  string $message the log message to save
83
         */
84
        public function fatal($message) {
85
            $this->writeLog($message, self::FATAL);
86
        } 
87
		
88
        /**
89
         * Save the error message in the log
90
         * @see Log::writeLog for more detail
91
         * @param  string $message the log message to save
92
         */
93
        public function error($message) {
94
            $this->writeLog($message, self::ERROR);
95
        } 
96
97
        /**
98
         * Save the warning message in the log
99
         * @see Log::writeLog for more detail
100
         * @param  string $message the log message to save
101
         */
102
        public function warning($message) {
103
            $this->writeLog($message, self::WARNING);
104
        } 
105
		
106
        /**
107
         * Save the info message in the log
108
         * @see Log::writeLog for more detail
109
         * @param  string $message the log message to save
110
         */
111
        public function info($message) {
112
            $this->writeLog($message, self::INFO);
113
        } 
114
		
115
        /**
116
         * Save the debug message in the log
117
         * @see Log::writeLog for more detail
118
         * @param  string $message the log message to save
119
         */
120
        public function debug($message) {
121
            $this->writeLog($message, self::DEBUG);
122
        } 
123
		
124
		
125
        /**
126
         * Save the log message
127
         * @param  string $message the log message to be saved
128
         * @param  integer|string $level   the log level in integer or string format, 
129
         * if is string will convert into integer
130
         * to allow check the log level threshold.
131
         */
132
        public function writeLog($message, $level = self::INFO) {
133
            $configLogLevel = get_config('log_level');
134
            if (!$configLogLevel) {
135
                //so means no need log just stop here
136
                return;
137
            }
138
            //check config log level
139
            if (!self::isValidConfigLevel($configLogLevel)) {
140
                //NOTE: here need put the show_error() "logging" to false 
141
                //to prevent self function loop call
142
                show_error('Invalid config log level [' . $configLogLevel . '], '
143
                           . 'the value must be one of the following: ' 
144
                           . implode(', ', array_map('strtoupper', self::$validConfigLevel))
145
                           , 'Log Config Error', 
146
                           $logging = false
147
                       );
148
                return;	
149
            }
150
			
151
            //check if config log_logger_name and current log can save log data
152
            if (!$this->canSaveLogDataForLogger()) {
153
                return;
154
            }
155
			
156
            //if $level is not an integer
157
            if (!is_numeric($level)) {
158
                $level = self::getLevelValue($level);
159
            }
160
			
161
            //check if can logging regarding the log level config
162
            $configLevel = self::getLevelValue($configLogLevel);
163
            if ($configLevel > $level) {
164
                //can't log
165
                return;
166
            }
167
            //check log file and directory
168
            $path = $this->checkAndSetLogFileDirectory();
169
            //save the log data
170
            $this->saveLogData($path, $level, $message);
0 ignored issues
show
Bug introduced by
It seems like $level can also be of type string; however, parameter $level of Log::saveLogData() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
            $this->saveLogData($path, /** @scrutinizer ignore-type */ $level, $message);
Loading history...
171
        }	
172
173
        /**
174
         * Save the log data into file
175
         * @param  string $path    the path of the log file
176
         * @param  integer $level   the log level in integer format.
177
         * @param  string $message the log message to save
178
         * @return void
179
         */
180
        protected function saveLogData($path, $level, $message) {
181
            //may be at this time helper user_agent not yet included
182
            require_once CORE_FUNCTIONS_PATH . 'function_user_agent.php';
183
			
184
            ///////////////////// date //////////////
185
            $timestampWithMicro = microtime(true);
186
            $microtime = sprintf('%06d', ($timestampWithMicro - floor($timestampWithMicro)) * 1000000);
187
            $dateTime = new DateTime(date('Y-m-d H:i:s.' . $microtime, $timestampWithMicro));
188
            $logDate = $dateTime->format('Y-m-d H:i:s.u'); 
189
            //ip
190
            $ip = get_ip();
191
192
            //level name
193
            $levelName = self::getLevelName($level);
194
			
195
            //debug info
196
            $fileInfo = $this->getLogDebugBacktraceInfo();
197
198
            $str = $logDate . ' [' . str_pad($levelName, 7 /*warning len*/) . '] ' 
199
                            . ' [' . str_pad($ip, 15) . '] ' . $this->logger . ': ' 
200
                            . $message . ' ' . '[' . $fileInfo['file'] . '::' . $fileInfo['line'] . ']' . "\n";
201
            $fp = fopen($path, 'a+');
202
            if (is_resource($fp)) {
203
                flock($fp, LOCK_EX); // exclusive lock, will get released when the file is closed
204
                fwrite($fp, $str);
205
                fclose($fp);
206
            }
207
        }	
208
209
210
        /**
211
         * Return the debug backtrace information
212
         * @return array the line number and file path
213
         */
214
        protected function getLogDebugBacktraceInfo() {
215
            $dtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
216
            $fileInfo = $dtrace[0];
0 ignored issues
show
Unused Code introduced by
The assignment to $fileInfo is dead and can be removed.
Loading history...
217
            $i = 0;
218
            while ($dtrace[$i]['file'] == __FILE__ ) {
219
                $i++;
220
            } 
221
            $fileInfo = $dtrace[$i];
222
            
223
            $line = -1;
224
            $file = -1;
225
226
            if (isset($fileInfo['file'])) {
227
                $file = $fileInfo['file'];
228
            }
229
230
            if (isset($fileInfo['line'])) {
231
                $line = $fileInfo['line'];
232
            }
233
            return array(
234
                'file' => $file,
235
                'line' => $line
236
            );
237
        }
238
239
        /**
240
         * Check if the current logger can save log data regarding the configuration
241
         * of logger filter
242
         * @return boolean
243
         */
244
        protected function canSaveLogDataForLogger() {
245
                $configLoggersName = get_config('log_logger_name', array());
246
                if (!empty($configLoggersName)) {
247
                    //for best comparaison put all string to lowercase
248
                    $configLoggersName = array_map('strtolower', $configLoggersName);
249
                    if (!in_array(strtolower($this->logger), $configLoggersName)) {
250
                        return false;
251
                    }
252
                }
253
            return true;
254
        }
255
256
        /**
257
         * Check the file and directory 
258
         * @return string|null the log file path
259
         */
260
        protected function checkAndSetLogFileDirectory() {
261
            $logSavePath = get_config('log_save_path');
262
            if (!$logSavePath) {
263
                $logSavePath = LOGS_PATH;
264
            }
265
			
266
            if (!is_dir($logSavePath) || !is_writable($logSavePath)) {
267
                //NOTE: here need put the show_error() "logging" to false 
268
                //to prevent self function loop call
269
                show_error('Error : the log dir does not exist or is not writable', 'Log directory error', $logging = false);
270
            }
271
			
272
            $path = $logSavePath . 'logs-' . date('Y-m-d') . '.log';
273
            if (!file_exists($path)) {
274
                touch($path);
275
            }
276
            return $path;
277
        }
278
		
279
        /**
280
         * Check if the given log level is valid
281
         *
282
         * @param  string  $level the log level
283
         *
284
         * @return boolean        true if the given log level is valid, false if not
285
         */
286
        protected static function isValidConfigLevel($level) {
287
            $level = strtolower($level);
288
            return in_array($level, self::$validConfigLevel);
289
        }
290
291
        /**
292
         * Get the log level number for the given level string
293
         * @param  string $level the log level in string format
294
         * 
295
         * @return int the log level in integer format using the predefined constants
296
         */
297
        protected static function getLevelValue($level) {
298
            $level = strtolower($level);
299
            $levelMaps = array(
300
                'fatal'   => self::FATAL,
301
                'error'   => self::ERROR,
302
                'warning' => self::WARNING,
303
                'warn'    => self::WARNING,
304
                'info'    => self::INFO,
305
                'debug'   => self::DEBUG,
306
                'all'     => self::ALL
307
            );
308
            //the default value is NONE, so means no need test for NONE
309
            $value = self::NONE;
310
            if (isset($levelMaps[$level])) {
311
                $value = $levelMaps[$level];
312
            }
313
            return $value;
314
        }
315
316
        /**
317
         * Get the log level string for the given log level integer
318
         * @param  integer $level the log level in integer format
319
         * @return string        the log level in string format
320
         */
321
        protected static function getLevelName($level) {
322
            $levelMaps = array(
323
                self::FATAL   => 'FATAL',
324
                self::ERROR   => 'ERROR',
325
                self::WARNING => 'WARNING',
326
                self::INFO    => 'INFO',
327
                self::DEBUG   => 'DEBUG'
328
            );
329
            $value = '';
330
            if (isset($levelMaps[$level])) {
331
                $value = $levelMaps[$level];
332
            }
333
            return $value;
334
        }
335
336
    }
337