Passed
Push — 1.0.0-dev ( 83bedf...ceb5d8 )
by nguereza
02:34
created

Log::isValidConfigLevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
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
		 * Save the fatal message in the log
68
		 * @see Log::writeLog for more detail
69
		 * @param  string $message the log message to save
70
		 */
71
		public function fatal($message){
72
			$this->writeLog($message, self::FATAL);
73
		} 
74
		
75
		/**
76
		 * Save the error message in the log
77
		 * @see Log::writeLog for more detail
78
		 * @param  string $message the log message to save
79
		 */
80
		public function error($message){
81
			$this->writeLog($message, self::ERROR);
82
		} 
83
84
		/**
85
		 * Save the warning message in the log
86
		 * @see Log::writeLog for more detail
87
		 * @param  string $message the log message to save
88
		 */
89
		public function warning($message){
90
			$this->writeLog($message, self::WARNING);
91
		} 
92
		
93
		/**
94
		 * Save the info message in the log
95
		 * @see Log::writeLog for more detail
96
		 * @param  string $message the log message to save
97
		 */
98
		public function info($message){
99
			$this->writeLog($message, self::INFO);
100
		} 
101
		
102
		/**
103
		 * Save the debug message in the log
104
		 * @see Log::writeLog for more detail
105
		 * @param  string $message the log message to save
106
		 */
107
		public function debug($message){
108
			$this->writeLog($message, self::DEBUG);
109
		} 
110
		
111
		
112
		/**
113
		 * Save the log message
114
		 * @param  string $message the log message to be saved
115
		 * @param  integer|string $level   the log level in integer or string format, if is string will convert into integer
116
		 * to allow check the log level threshold.
117
		 */
118
		public function writeLog($message, $level = self::INFO){
119
			$configLogLevel = get_config('log_level');
120
			if(! $configLogLevel){
121
				//so means no need log just stop here
122
				return;
123
			}
124
			//check config log level
125
			if(! self::isValidConfigLevel($configLogLevel)){
126
				//NOTE: here need put the show_error() "logging" to false to prevent loop
127
				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);	
128
			}
129
			
130
			//check if config log_logger_name is set
131
			if($this->logger){
132
				$configLoggersName = get_config('log_logger_name', array());
133
				if (!empty($configLoggersName)) {
134
					//for best comparaison put all string to lowercase
135
					$configLoggersName = array_map('strtolower', $configLoggersName);
136
					if(! in_array(strtolower($this->logger), $configLoggersName)){
137
						return;
138
					}
139
				}
140
			}
141
			
142
			//if $level is not an integer
143
			if(! is_numeric($level)){
144
				$level = self::getLevelValue($level);
145
			}
146
			
147
			//check if can logging regarding the log level config
148
			$configLevel = self::getLevelValue($configLogLevel);
149
			if($configLevel > $level){
150
				//can't log
151
				return;
152
			}
153
			//check log file and directory
154
			$path = $this->checkAndSetLogFileDirectory();
155
			//save the log data
156
			$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

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