Passed
Push — 1.0.0-dev ( 459011...73c7ea )
by nguereza
03:08
created

Log::canSaveLogDataForLogger()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 0
dl 0
loc 12
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);
159
        }	
160
161
        /**
162
         * Save the log data into file
163
         * @param  string $path    the path of the log file
164
         * @param  integer|string $level   the log level in integer or string format, if is string will convert into integer
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
            //if $level is not an integer
181
            if (!is_numeric($level)) {
182
                $level = self::getLevelValue($level);
183
            }
184
185
            //level name
186
            $levelName = self::getLevelName($level);
0 ignored issues
show
Bug introduced by
It seems like $level can also be of type string; however, parameter $level of Log::getLevelName() 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

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