Passed
Push — 1.0.0-dev ( 247c52...b3c42a )
by nguereza
02:31
created

Log::info()   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 GNU GPL License (GPL)
9
     *
10
     * Copyright (C) 2017 Tony NGUEREZA
11
     *
12
     * This program is free software; you can redistribute it and/or
13
     * modify it under the terms of the GNU General Public License
14
     * as published by the Free Software Foundation; either version 3
15
     * of the License, or (at your option) any later version.
16
     *
17
     * This program is distributed in the hope that it will be useful,
18
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
     * GNU General Public License for more details.
21
     *
22
     * You should have received a copy of the GNU General Public License
23
     * along with this program; if not, write to the Free Software
24
     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25
     */
26
27
    class Log {
28
		
29
        /**
30
         * The defined constante for Log level
31
         */
32
        const NONE = 99999999;
33
        const FATAL = 500;
34
        const ERROR = 400;
35
        const WARNING = 300;
36
        const INFO = 200;
37
        const DEBUG = 100;
38
        const ALL = -99999999;
39
40
        /**
41
         * The logger name
42
         * @var string
43
         */
44
        private $logger = 'ROOT_LOGGER';
45
		
46
        /**
47
         * List of valid log level to be checked for the configuration
48
         * @var array
49
         */
50
        private static $validConfigLevel = array('off', 'none', 'fatal', 'error', 'warning', 'warn', 'info', 'debug', 'all');
51
52
        /**
53
         * Create new Log instance
54
         */
55
        public function __construct() {
56
        }
57
58
        /**
59
         * Set the logger to identify each message in the log
60
         * @param string $logger the logger name
61
         */
62
        public  function setLogger($logger) {
63
            $this->logger = $logger;
64
        }
65
66
        /**
67
         * get the logger name
68
         *
69
         * @return string
70
         */
71
        public  function getLogger() {
72
            return $this->logger;
73
        }
74
75
        /**
76
         * Save the fatal message in the log
77
         * @see Log::writeLog for more detail
78
         * @param  string $message the log message to save
79
         */
80
        public function fatal($message) {
81
            $this->writeLog($message, self::FATAL);
82
        } 
83
		
84
        /**
85
         * Save the error message in the log
86
         * @see Log::writeLog for more detail
87
         * @param  string $message the log message to save
88
         */
89
        public function error($message) {
90
            $this->writeLog($message, self::ERROR);
91
        } 
92
93
        /**
94
         * Save the warning message in the log
95
         * @see Log::writeLog for more detail
96
         * @param  string $message the log message to save
97
         */
98
        public function warning($message) {
99
            $this->writeLog($message, self::WARNING);
100
        } 
101
		
102
        /**
103
         * Save the info message in the log
104
         * @see Log::writeLog for more detail
105
         * @param  string $message the log message to save
106
         */
107
        public function info($message) {
108
            $this->writeLog($message, self::INFO);
109
        } 
110
		
111
        /**
112
         * Save the debug message in the log
113
         * @see Log::writeLog for more detail
114
         * @param  string $message the log message to save
115
         */
116
        public function debug($message) {
117
            $this->writeLog($message, self::DEBUG);
118
        } 
119
		
120
		
121
        /**
122
         * Save the log message
123
         * @param  string $message the log message to be saved
124
         * @param  integer|string $level   the log level in integer or string format, if is string will convert into integer
125
         * to allow check the log level threshold.
126
         */
127
        public function writeLog($message, $level = self::INFO) {
128
            $configLogLevel = get_config('log_level');
129
            if (!$configLogLevel) {
130
                //so means no need log just stop here
131
                return;
132
            }
133
            //check config log level
134
            if (!self::isValidConfigLevel($configLogLevel)) {
135
                //NOTE: here need put the show_error() "logging" to false to prevent loop
136
                show_error('Invalid config log level [' . $configLogLevel . '], the value must be one of the following: ' . implode(', ', array_map('strtoupper', self::$validConfigLevel)), $title = 'Log Config Error', $logging = false);	
137
            }
138
			
139
            //check if config log_logger_name and current log can save log data
140
            if (!$this->canSaveLogDataForLogger()) {
141
                return;
142
            }
143
			
144
            //if $level is not an integer
145
            if (!is_numeric($level)) {
146
                $level = self::getLevelValue($level);
147
            }
148
			
149
            //check if can logging regarding the log level config
150
            $configLevel = self::getLevelValue($configLogLevel);
151
            if ($configLevel > $level) {
152
                //can't log
153
                return;
154
            }
155
            //check log file and directory
156
            $path = $this->checkAndSetLogFileDirectory();
157
            //save the log data
158
            $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

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