blob: a2b464cf7a3d89d766fbdf7284c9300920ad09ed [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02005 * An open source application development framework for PHP
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Andrey Andreev76cc9202011-12-22 20:02:04 +02008 *
Andrey Andreevcce6bd12018-01-09 11:32:02 +02009 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
Andrey Andreev76cc9202011-12-22 20:02:04 +020010 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020011 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
Derek Jonesf4a4bd82011-10-20 12:18:42 -050017 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020018 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 * @package CodeIgniter
30 * @author EllisLab Dev Team
Andrey Andreev1924e872016-01-11 12:55:34 +020031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
Andrey Andreevcce6bd12018-01-09 11:32:02 +020032 * @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
Andrey Andreevbd202c92016-01-11 12:50:18 +020034 * @link https://codeigniter.com
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020035 * @since Version 1.0.0
Derek Allard2067d1a2008-11-13 22:59:24 +000036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +000039
Derek Allard2067d1a2008-11-13 22:59:24 +000040/**
41 * Logging Class
42 *
43 * @package CodeIgniter
44 * @subpackage Libraries
45 * @category Logging
Derek Jonesf4a4bd82011-10-20 12:18:42 -050046 * @author EllisLab Dev Team
Andrey Andreevbd202c92016-01-11 12:50:18 +020047 * @link https://codeigniter.com/user_guide/general/errors.html
Derek Allard2067d1a2008-11-13 22:59:24 +000048 */
49class CI_Log {
50
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040051 /**
52 * Path to save log files
53 *
54 * @var string
55 */
Greg Aker2eaa4072010-12-21 11:44:08 -060056 protected $_log_path;
Andrey Andreev56454792012-05-17 14:32:19 +030057
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040058 /**
Andrey Andreev45965742014-08-27 20:40:11 +030059 * File permissions
60 *
61 * @var int
62 */
63 protected $_file_permissions = 0644;
64
65 /**
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040066 * Level of logging
67 *
68 * @var int
69 */
Andrey Andreev45965742014-08-27 20:40:11 +030070 protected $_threshold = 1;
Andrey Andreev56454792012-05-17 14:32:19 +030071
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040072 /**
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040073 * Array of threshold levels to log
74 *
75 * @var array
76 */
Andrey Andreev45965742014-08-27 20:40:11 +030077 protected $_threshold_array = array();
Andrey Andreev56454792012-05-17 14:32:19 +030078
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040079 /**
80 * Format of timestamp for log files
81 *
82 * @var string
83 */
Andrey Andreev45965742014-08-27 20:40:11 +030084 protected $_date_fmt = 'Y-m-d H:i:s';
Andrey Andreev56454792012-05-17 14:32:19 +030085
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040086 /**
Andrey Andreeva107a0f2013-02-15 22:30:31 +020087 * Filename extension
88 *
89 * @var string
90 */
91 protected $_file_ext;
92
93 /**
Timothy Warrenb82bc3a2012-04-27 09:12:58 -040094 * Whether or not the logger can write to the log files
95 *
96 * @var bool
97 */
Andrey Andreev45965742014-08-27 20:40:11 +030098 protected $_enabled = TRUE;
Andrey Andreev56454792012-05-17 14:32:19 +030099
Timothy Warrenb82bc3a2012-04-27 09:12:58 -0400100 /**
101 * Predefined logging levels
102 *
103 * @var array
104 */
Andrey Andreev45965742014-08-27 20:40:11 +0300105 protected $_levels = array('ERROR' => 1, 'DEBUG' => 2, 'INFO' => 3, 'ALL' => 4);
Derek Allard2067d1a2008-11-13 22:59:24 +0000106
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300107 /**
Andrey Andreevc0c74d52017-01-19 15:26:35 +0200108 * mbstring.func_overload flag
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300109 *
110 * @var bool
111 */
Andrey Andreevc0c74d52017-01-19 15:26:35 +0200112 protected static $func_overload;
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300113
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200114 // --------------------------------------------------------------------
115
Timothy Warrenb82bc3a2012-04-27 09:12:58 -0400116 /**
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200117 * Class constructor
Timothy Warrenb82bc3a2012-04-27 09:12:58 -0400118 *
Andrey Andreev56454792012-05-17 14:32:19 +0300119 * @return void
Timothy Warrenb82bc3a2012-04-27 09:12:58 -0400120 */
Greg Akera9263282010-11-10 15:26:43 -0600121 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +0000122 {
123 $config =& get_config();
Barry Mienydd671972010-10-04 16:33:58 +0200124
Andrey Andreevc0c74d52017-01-19 15:26:35 +0200125 isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300126
Alex Bilbied261b1e2012-06-02 11:12:16 +0100127 $this->_log_path = ($config['log_path'] !== '') ? $config['log_path'] : APPPATH.'logs/';
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200128 $this->_file_ext = (isset($config['log_file_extension']) && $config['log_file_extension'] !== '')
129 ? ltrim($config['log_file_extension'], '.') : 'php';
Chris Passas0bd6b282013-02-13 14:16:18 -0500130
Andrey Andreev45965742014-08-27 20:40:11 +0300131 file_exists($this->_log_path) OR mkdir($this->_log_path, 0755, TRUE);
Andrey Andreev9f690f12013-01-21 15:30:25 +0200132
Greg Aker2eaa4072010-12-21 11:44:08 -0600133 if ( ! is_dir($this->_log_path) OR ! is_really_writable($this->_log_path))
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 {
135 $this->_enabled = FALSE;
136 }
Barry Mienydd671972010-10-04 16:33:58 +0200137
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 if (is_numeric($config['log_threshold']))
139 {
Andrey Andreev76cc9202011-12-22 20:02:04 +0200140 $this->_threshold = (int) $config['log_threshold'];
Derek Allard2067d1a2008-11-13 22:59:24 +0000141 }
Nithin333f9f92011-08-21 16:52:06 -0400142 elseif (is_array($config['log_threshold']))
143 {
Andrey Andreevfd08d022015-03-03 12:36:11 +0200144 $this->_threshold = 0;
Nithin333f9f92011-08-21 16:52:06 -0400145 $this->_threshold_array = array_flip($config['log_threshold']);
146 }
Barry Mienydd671972010-10-04 16:33:58 +0200147
Andrey Andreev45965742014-08-27 20:40:11 +0300148 if ( ! empty($config['log_date_format']))
Derek Allard2067d1a2008-11-13 22:59:24 +0000149 {
150 $this->_date_fmt = $config['log_date_format'];
151 }
Andrey Andreev45965742014-08-27 20:40:11 +0300152
Andrey Andreevdc0ad202014-09-17 10:34:04 +0300153 if ( ! empty($config['log_file_permissions']) && is_int($config['log_file_permissions']))
Andrey Andreev45965742014-08-27 20:40:11 +0300154 {
155 $this->_file_permissions = $config['log_file_permissions'];
156 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000157 }
Barry Mienydd671972010-10-04 16:33:58 +0200158
Derek Allard2067d1a2008-11-13 22:59:24 +0000159 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200160
Derek Allard2067d1a2008-11-13 22:59:24 +0000161 /**
162 * Write Log File
163 *
164 * Generally this function will be called using the global log_message() function
165 *
Andrey Andreev1ccc8be2016-02-09 21:15:10 +0200166 * @param string $level The error level: 'error', 'debug' or 'info'
167 * @param string $msg The error message
Derek Allard2067d1a2008-11-13 22:59:24 +0000168 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200169 */
Andrey Andreev838c9a92013-09-13 14:05:13 +0300170 public function write_log($level, $msg)
Barry Mienydd671972010-10-04 16:33:58 +0200171 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000172 if ($this->_enabled === FALSE)
173 {
174 return FALSE;
175 }
Barry Mienydd671972010-10-04 16:33:58 +0200176
Derek Allard2067d1a2008-11-13 22:59:24 +0000177 $level = strtoupper($level);
Barry Mienydd671972010-10-04 16:33:58 +0200178
Andrey Andreev76cc9202011-12-22 20:02:04 +0200179 if (( ! isset($this->_levels[$level]) OR ($this->_levels[$level] > $this->_threshold))
Andrey Andreev443bbd92012-04-03 15:49:11 +0300180 && ! isset($this->_threshold_array[$this->_levels[$level]]))
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 {
Andrey Andreev76cc9202011-12-22 20:02:04 +0200182 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 }
Barry Mienydd671972010-10-04 16:33:58 +0200184
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200185 $filepath = $this->_log_path.'log-'.date('Y-m-d').'.'.$this->_file_ext;
186 $message = '';
Barry Mienydd671972010-10-04 16:33:58 +0200187
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 if ( ! file_exists($filepath))
189 {
Jeroen van der Gulik2835e082011-12-04 14:19:38 +0100190 $newfile = TRUE;
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200191 // Only add protection to php files
192 if ($this->_file_ext === 'php')
Chris Passasfb8de242013-02-14 09:01:24 -0500193 {
Andrey Andreeva107a0f2013-02-15 22:30:31 +0200194 $message .= "<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>\n\n";
Chris Passasfb8de242013-02-14 09:01:24 -0500195 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000196 }
Barry Mienydd671972010-10-04 16:33:58 +0200197
Andrey Andreev7cf682a2014-03-13 14:55:45 +0200198 if ( ! $fp = @fopen($filepath, 'ab'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 {
200 return FALSE;
201 }
202
Andrey Andreeve1f36e32016-03-01 13:33:37 +0200203 flock($fp, LOCK_EX);
204
Gabriel Potkány53e83032014-12-20 00:00:23 +0100205 // Instantiating DateTime with microseconds appended to initial date is needed for proper support of this format
206 if (strpos($this->_date_fmt, 'u') !== FALSE)
207 {
208 $microtime_full = microtime(TRUE);
209 $microtime_short = sprintf("%06d", ($microtime_full - floor($microtime_full)) * 1000000);
210 $date = new DateTime(date('Y-m-d H:i:s.'.$microtime_short, $microtime_full));
211 $date = $date->format($this->_date_fmt);
212 }
213 else
214 {
215 $date = date($this->_date_fmt);
216 }
Gabriel Potkányf83262e2014-12-19 12:35:00 +0100217
Andrey Andreev1ccc8be2016-02-09 21:15:10 +0200218 $message .= $this->_format_line($level, $date, $msg);
Barry Mienydd671972010-10-04 16:33:58 +0200219
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300220 for ($written = 0, $length = self::strlen($message); $written < $length; $written += $result)
Andrey Andreevd8b1ad32014-01-15 17:42:52 +0200221 {
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300222 if (($result = fwrite($fp, self::substr($message, $written))) === FALSE)
Andrey Andreevd8b1ad32014-01-15 17:42:52 +0200223 {
224 break;
225 }
226 }
227
Derek Allard2067d1a2008-11-13 22:59:24 +0000228 flock($fp, LOCK_UN);
229 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200230
Andrey Andreev443bbd92012-04-03 15:49:11 +0300231 if (isset($newfile) && $newfile === TRUE)
Jeroen van der Gulik2835e082011-12-04 14:19:38 +0100232 {
Andrey Andreev45965742014-08-27 20:40:11 +0300233 chmod($filepath, $this->_file_permissions);
Jeroen van der Gulik2835e082011-12-04 14:19:38 +0100234 }
Andrey Andreev443bbd92012-04-03 15:49:11 +0300235
Andrey Andreevd8b1ad32014-01-15 17:42:52 +0200236 return is_int($result);
Derek Allard2067d1a2008-11-13 22:59:24 +0000237 }
238
Andrey Andreev1ccc8be2016-02-09 21:15:10 +0200239 // --------------------------------------------------------------------
240
241 /**
242 * Format the log line.
243 *
244 * This is for extensibility of log formatting
245 * If you want to change the log format, extend the CI_Log class and override this method
246 *
247 * @param string $level The error level
248 * @param string $date Formatted date string
Andrey Andreeve13fa9f2016-05-20 17:30:07 +0300249 * @param string $message The log message
Andrey Andreev1ccc8be2016-02-09 21:15:10 +0200250 * @return string Formatted log line with a new line character '\n' at the end
251 */
252 protected function _format_line($level, $date, $message)
253 {
254 return $level.' - '.$date.' --> '.$message."\n";
255 }
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300256
257 // --------------------------------------------------------------------
258
259 /**
260 * Byte-safe strlen()
261 *
262 * @param string $str
263 * @return int
264 */
265 protected static function strlen($str)
266 {
Andrey Andreevc0c74d52017-01-19 15:26:35 +0200267 return (self::$func_overload)
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300268 ? mb_strlen($str, '8bit')
269 : strlen($str);
270 }
271
272 // --------------------------------------------------------------------
273
274 /**
275 * Byte-safe substr()
276 *
277 * @param string $str
278 * @param int $start
279 * @param int $length
280 * @return string
281 */
282 protected static function substr($str, $start, $length = NULL)
283 {
Andrey Andreevc0c74d52017-01-19 15:26:35 +0200284 if (self::$func_overload)
Andrey Andreev4e2cdec2016-10-28 14:19:08 +0300285 {
286 // mb_substr($str, $start, null, '8bit') returns an empty
287 // string on PHP 5.3
288 isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);
289 return mb_substr($str, $start, $length, '8bit');
290 }
291
292 return isset($length)
293 ? substr($str, $start, $length)
294 : substr($str, $start);
295 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000296}