feature 3221 Add Logger class

git-svn-id: http://piwigo.org/svn/trunk@31102 68402e56-0260-453c-a942-63ccdbb3a9ee
This commit is contained in:
mistic100
2015-04-24 14:00:50 +00:00
parent becc0117f0
commit 0c576ea19d
7 changed files with 542 additions and 96 deletions
+33 -36
View File
@@ -30,6 +30,17 @@ defined('PWG_DERIVATIVE_DIR') or define('PWG_DERIVATIVE_DIR', $conf['data_locati
@include(PHPWG_ROOT_PATH.PWG_LOCAL_DIR .'config/database.inc.php');
include(PHPWG_ROOT_PATH . 'include/Logger.class.php');
$logger = new Logger(array(
'directory' => PHPWG_ROOT_PATH . $conf['data_location'] . $conf['log_dir'],
'severity' => $conf['log_level'],
// we use an hashed filename to prevent direct file access, and we salt with
// the db_password instead of secret_key because the log must be usable in i.php
// (secret_key is in the database)
'filename' => 'log_' . date('Y-m-d') . '_' . sha1(date('Y-m-d') . $conf['db_password']) . '.txt',
));
function trigger_notify() {}
function get_extension( $filename )
@@ -66,33 +77,9 @@ function mkgetdir($dir)
// end fast bootstrap
function ilog()
{
global $conf;
if (!$conf['enable_i_log']) return;
$line = date("c");
foreach( func_get_args() as $arg)
{
$line .= ' ';
if (is_array($arg))
{
$line .= implode(' ', $arg);
}
else
{
$line .= $arg;
}
}
$file=PHPWG_ROOT_PATH.$conf['data_location'].'tmp/i.log';
if (false == file_put_contents($file, $line."\n", FILE_APPEND))
{
mkgetdir(dirname($file));
}
}
function ierror($msg, $code)
{
global $logger;
if ($code==301 || $code==302)
{
if (ob_get_length () !== FALSE)
@@ -101,10 +88,12 @@ function ierror($msg, $code)
}
// default url is on html format
$url = html_entity_decode($msg);
$logger->warning($code . ' ' . $url, 'i.php', array(
'url' => $_SERVER['REQUEST_URI'],
));
header('Request-URI: '.$url);
header('Content-Location: '.$url);
header('Location: '.$url);
ilog('WARN', $code, $url, $_SERVER['REQUEST_URI']);
exit;
}
if ($code>=400)
@@ -117,7 +106,9 @@ function ierror($msg, $code)
}
//todo improve
echo $msg;
ilog('ERROR', $code, $msg, $_SERVER['REQUEST_URI']);
$logger->error($code . ' ' . $msg, 'i.php', array(
'url' => $_SERVER['REQUEST_URI'],
));
exit;
}
@@ -404,7 +395,7 @@ try
}
catch (Exception $e)
{
ilog("db error", $e->getMessage());
$logger->error($e->getMessage(), 'i.php');
}
pwg_db_check_charset();
@@ -501,7 +492,7 @@ SELECT *
}
catch (Exception $e)
{
ilog("db error", $e->getMessage());
$logger->error($e->getMessage(), 'i.php');
}
}
else
@@ -621,10 +612,16 @@ $timing['save'] = time_step($step);
send_derivative($expires);
$timing['send'] = time_step($step);
ilog('perf',
basename($page['src_path']), $o_size, $o_size[0]*$o_size[1],
basename($page['derivative_path']), $d_size, $d_size[0]*$d_size[1],
function_exists('memory_get_peak_usage') ? round( memory_get_peak_usage()/(1024*1024), 1) : '',
time_step($begin),
'|', $timing);
?>
$timing['total'] = time_step($begin);
if ($logger->severity() >= Logger::INFO)
{
$logger->info('perf', 'i.php', array(
'src_path' => basename($page['src_path']),
'derivative_path' => basename($page['derivative_path']),
'o_size' => $o_size[0] . ' ' . $o_size[1] . ' ' . ($o_size[0]*$o_size[1]),
'd_size' => $d_size[0] . ' ' . $d_size[1] . ' ' . ($d_size[0]*$d_size[1]),
'mem_usage' => function_exists('memory_get_peak_usage') ? round( memory_get_peak_usage()/(1024*1024), 1) : '',
'timing' => $timing,
));
}
+453
View File
@@ -0,0 +1,453 @@
<?php
// +-----------------------------------------------------------------------+
// | Piwigo - a PHP based photo gallery |
// +-----------------------------------------------------------------------+
// | Copyright(C) 2008-2015 Piwigo Team http://piwigo.org |
// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
// +-----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or modify |
// | it under the terms of the GNU General Public License as published by |
// | the Free Software Foundation |
// | |
// | This program is distributed in the hope that it will be useful, but |
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | General Public License for more details. |
// | |
// | You should have received a copy of the GNU General Public License |
// | along with this program; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
// | USA. |
// +-----------------------------------------------------------------------+
/**
* Modified version of KLogger 0.2.0
*
* @author Kenny Katzgrau <katzgrau@gmail.com>
*
* @package logger
*/
class Logger
{
/**
* Error severity, from low to high. From BSD syslog RFC, section 4.1.1
* @link http://www.faqs.org/rfcs/rfc3164.html
*/
const EMERGENCY = 0; // Emergency: system is unusable
const ALERT = 1; // Alert: action must be taken immediately
const CRITICAL = 2; // Critical: critical conditions
const ERROR = 3; // Error: error conditions
const WARNING = 4; // Warning: warning conditions
const NOTICE = 5; // Notice: normal but significant condition
const INFO = 6; // Informational: informational messages
const DEBUG = 7; // Debug: debug messages
/**
* Custom "disable" level.
*/
const OFF = -1; // Log nothing at all
/**
* Internal status codes.
*/
const STATUS_LOG_OPEN = 1;
const STATUS_OPEN_FAILED = 2;
const STATUS_LOG_CLOSED = 3;
/**
* Disable archive purge.
*/
const ARCHIVE_NO_PURGE = -1;
/**
* Standard messages produced by the class.
* @var array
*/
private static $_messages = array(
'writefail' => 'The file could not be written to. Check that appropriate permissions have been set.',
'opensuccess' => 'The log file was opened successfully.',
'openfail' => 'The file could not be opened. Check permissions.',
);
/**
* Instance options.
* @var array
*/
private $options = array(
'directory' => null, // Log files directory
'filename' => null, // Path to the log file
'globPattern' => 'log_*.txt', // Pattern to select all log files with glob()
'severity' => self::DEBUG, // Current minimum logging threshold
'dateFormat' => 'Y-m-d G:i:s', // Date format
'archiveDays' => self::ARCHIVE_NO_PURGE, // Number of files to keep
);
/**
* Current status of the logger.
* @var integer
*/
private $_logStatus = self::STATUS_LOG_CLOSED;
/**
* File handle for this instance's log file.
* @var resource
*/
private $_fileHandle = null;
/**
* Class constructor.
*
* @param array $options
* @return void
*/
public function __construct($options)
{
$this->options = array_merge($this->options, $options);
if (is_string($this->options['severity'])) {
$this->options['severity'] = self::codeToLevel($this->options['severity']);
}
if ($this->options['severity'] === self::OFF) {
return;
}
$this->options['directory'] = rtrim($this->options['directory'], '\\/') . DIRECTORY_SEPARATOR;
if ($this->options['filename'] == null) {
$this->options['filename'] = 'log_' . date('Y-m-d') . '.txt';
}
$this->options['filePath'] = $this->options['directory'] . $this->options['filename'];
if (!file_exists($this->options['directory'])) {
mkgetdir($this->options['directory'], MKGETDIR_DEFAULT|MKGETDIR_PROTECT_HTACCESS);
}
if (file_exists($this->options['filePath']) && !is_writable($this->options['filePath'])) {
$this->_logStatus = self::STATUS_OPEN_FAILED;
throw new RuntimeException(self::$_messages['writefail']);
return;
}
if (($this->_fileHandle = fopen($this->options['filePath'], 'a'))) {
$this->_logStatus = self::STATUS_LOG_OPEN;
}
else {
$this->_logStatus = self::STATUS_OPEN_FAILED;
throw new RuntimeException(self::$_messages['openfail']);
}
if ($this->options['archiveDays'] != self::ARCHIVE_NO_PURGE && rand() % 97 == 0) {
$this->purge();
}
}
/**
* Class destructor.
*/
public function __destruct()
{
if ($this->_fileHandle) {
fclose($this->_fileHandle);
}
}
/**
* Returns logger status.
*
* @return int
*/
public function status()
{
return $this->_logStatus;
}
/**
* Returns logger severity threshold.
*
* @return int
*/
public function severity()
{
return $this->options['severity'];
}
/**
* Writes a $line to the log with a severity level of DEBUG.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function debug($line, $cat = null, $args = array())
{
$this->log(self::DEBUG, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of INFO.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function info($line, $cat = null, $args = array())
{
$this->log(self::INFO, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of NOTICE.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function notice($line, $cat = null, $args = array())
{
$this->log(self::NOTICE, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of WARNING.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function warn($line, $cat = null, $args = array())
{
$this->log(self::WARNING, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of ERROR.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function error($line, $cat = null, $args = array())
{
$this->log(self::ERROR, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of ALERT.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function alert($line, $cat = null, $args = array())
{
$this->log(self::ALERT, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of CRITICAL.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function critical($line, $cat = null, $args = array())
{
$this->log(self::CRITICAL, $line, $cat, $args);
}
/**
* Writes a $line to the log with a severity level of EMERGENCY.
*
* @param string $line
* @param string $cat
* @param array $args
*/
public function emergency($line, $cat = null, $args = array())
{
$this->log(self::EMERGENCY, $line, $cat, $args);
}
/**
* Writes a $line to the log with the given severity.
*
* @param integer $severity
* @param string $line
* @param string $cat
* @param array $args
*/
public function log($severity, $message, $cat = null, $args = array())
{
if ($this->severity() >= $severity) {
if (is_array($cat)) {
$args = $cat;
$cat = null;
}
$line = $this->formatMessage($severity, $message, $cat, $args);
$this->write($line);
}
}
/**
* Directly writes a line to the log without adding level and time.
*
* @param string $line
*/
public function write($line)
{
if ($this->_logStatus == self::STATUS_LOG_OPEN) {
if (fwrite($this->_fileHandle, $line) === false) {
throw new RuntimeException(self::$_messages['writefail']);
}
}
}
/**
* Purges files matching 'globPattern' older than 'archiveDays'.
*/
public function purge() {
$files = glob($this->options['directory'] . $this->options['globPattern']);
$limit = time() - $this->options['archiveDays'] * 86400;
foreach ($files as $file) {
if (@filemtime($file) < $limit) {
@unlink($file);
}
}
}
/**
* Formats the message for logging.
*
* @param string $level
* @param string $message
* @param array $context
* @return string
*/
private function formatMessage($level, $message, $cat, $context)
{
if (!empty($context)) {
$message .= "\n" . $this->indent($this->contextToString($context));
}
$line = "[" . $this->getTimestamp() . "]\t[" . self::levelToCode($level) . "]\t";
if ($cat != null) {
$line .= "[" . $cat . "]\t";
}
return $line . $message . "\n";
}
/**
* Gets the formatted Date/Time for the log entry.
*
* PHP DateTime is dumb, and you have to resort to trickery to get microseconds
* to work correctly, so here it is.
*
* @return string
*/
private function getTimestamp()
{
$originalTime = microtime(true);
$micro = sprintf("%06d", ($originalTime - floor($originalTime)) * 1000000);
$date = new DateTime(date('Y-m-d H:i:s.'.$micro, $originalTime));
return $date->format($this->options['dateFormat']);
}
/**
* Takes the given context and converts it to a string.
*
* @param array $context
* @return string
*/
private function contextToString($context)
{
$export = '';
foreach ($context as $key => $value) {
$export .= "{$key}: ";
$export .= preg_replace(array(
'/=>\s+([a-zA-Z])/im',
'/array\(\s+\)/im',
'/^ |\G /m'
), array(
'=> $1',
'array()',
' '
), str_replace('array (', 'array(', var_export($value, true)));
$export .= PHP_EOL;
}
return str_replace(array('\\\\', '\\\''), array('\\', '\''), rtrim($export));
}
/**
* Indents the given string with the given indent.
*
* @param string $string The string to indent
* @param string $indent What to use as the indent.
* @return string
*/
private function indent($string, $indent = ' ')
{
return $indent.str_replace("\n", "\n".$indent, $string);
}
/**
* Converts level constants to string name.
*
* @param int $level
* @return string
*/
static function levelToCode($level)
{
switch ($level) {
case self::EMERGENCY:
return 'EMERGENCY';
case self::ALERT:
return 'ALERT';
case self::CRITICAL:
return 'CRITICAL';
case self::NOTICE:
return 'NOTICE';
case self::INFO:
return 'INFO';
case self::WARNING:
return 'WARNING';
case self::DEBUG:
return 'DEBUG';
case self::ERROR:
return 'ERROR';
default:
throw new RuntimeException('Unknown severity level ' . $level);
}
}
/**
* Converts level names to constant.
*
* @param string $code
* @return int
*/
static function codeToLevel($code)
{
switch (strtoupper($code)) {
case 'EMERGENCY':
return self::EMERGENCY;
case 'ALERT':
return self::ALERT;
case 'CRITICAL':
return self::CRITICAL;
case 'NOTICE':
return self::NOTICE;
case 'INFO':
return self::INFO;
case 'WARNING':
return self::WARNING;
case 'DEBUG':
return self::DEBUG;
case 'ERROR':
return self::ERROR;
default:
throw new RuntimeException('Unknown severity code ' . $code);
}
}
}
+14
View File
@@ -104,6 +104,9 @@ if(isset($conf['show_php_errors']) && !empty($conf['show_php_errors']))
include(PHPWG_ROOT_PATH . 'include/constants.php');
include(PHPWG_ROOT_PATH . 'include/functions.inc.php');
include(PHPWG_ROOT_PATH . 'include/template.class.php');
include(PHPWG_ROOT_PATH . 'include/cache.class.php');
include(PHPWG_ROOT_PATH . 'include/Logger.class.php');
$persistent_cache = new PersistentFileCache();
@@ -122,6 +125,17 @@ pwg_db_check_charset();
load_conf_from_db();
$logger = new Logger(array(
'directory' => PHPWG_ROOT_PATH . $conf['data_location'] . $conf['log_dir'],
'severity' => $conf['log_level'],
// we use an hashed filename to prevent direct file access, and we salt with
// the db_password instead of secret_key because the log must be usable in i.php
// (secret_key is in the database)
'filename' => 'log_' . date('Y-m-d') . '_' . sha1(date('Y-m-d') . $conf['db_password']) . '.txt',
'globPattern' => 'log_*.txt',
'archiveDays' => $conf['log_archive_days'],
));
if (!$conf['check_upgrade_feed'])
{
if (!isset($conf['piwigo_db_version']) or $conf['piwigo_db_version'] != get_branch_from_version(PHPWG_VERSION))
+13 -9
View File
@@ -462,8 +462,6 @@ $conf['template_combine_files'] = true;
// gives an empty value '' to deactivate
$conf['show_php_errors'] = E_ALL;
// enable log for i derivative script
$conf['enable_i_log'] = false;
// +-----------------------------------------------------------------------+
// | authentication |
@@ -653,12 +651,6 @@ $conf['enable_plugins']=true;
// Web services are allowed (true) or completely forbidden (false)
$conf['allow_web_services'] = true;
// enable log for web services
$conf['ws_enable_log'] = false;
// web services log file path
$conf['ws_log_filepath'] = '/tmp/piwigo_ws.log';
// Maximum number of images to be returned foreach call to the web service
$conf['ws_max_images_per_page'] = 500;
@@ -812,4 +804,16 @@ $conf['upload_form_all_types'] = false;
// "ffmpeg" is not visible by the web user, you can define the full path of
// the directory where "ffmpeg" executable is.
$conf['ffmpeg_dir'] = '';
?>
// +-----------------------------------------------------------------------+
// | log |
// +-----------------------------------------------------------------------+
// Logs directory, relative to $conf['data_location']
$conf['log_dir'] = '/logs';
// Log level (OFF, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG)
// development = DEBUG, production = ERROR
$conf['log_level'] = 'DEBUG';
// Keep logs file during X days
$conf['log_archive_days'] = 30;
-2
View File
@@ -36,8 +36,6 @@ include_once( PHPWG_ROOT_PATH .'include/functions_url.inc.php' );
include_once( PHPWG_ROOT_PATH .'include/derivative_params.inc.php');
include_once( PHPWG_ROOT_PATH .'include/derivative_std_params.inc.php');
include_once( PHPWG_ROOT_PATH .'include/derivative.inc.php');
include_once( PHPWG_ROOT_PATH .'include/template.class.php');
include_once( PHPWG_ROOT_PATH .'include/cache.class.php');
/**
-19
View File
@@ -204,25 +204,6 @@ function ws_std_get_tag_xml_attributes()
);
}
/**
* Writes info to the log file
*/
function ws_logfile($string)
{
global $conf;
if (!$conf['ws_enable_log'])
{
return true;
}
file_put_contents(
$conf['ws_log_filepath'],
'['.date('c').'] '.$string."\n",
FILE_APPEND
);
}
/**
* create a tree from a flat list of categories, no recursivity for high speed
*/
+29 -30
View File
@@ -182,9 +182,9 @@ SELECT category_id, MAX(rank) AS max_rank
*/
function merge_chunks($output_filepath, $original_sum, $type)
{
global $conf;
global $conf, $logger;
ws_logfile('[merge_chunks] input parameter $output_filepath : '.$output_filepath);
$logger->debug('[merge_chunks] input parameter $output_filepath : '.$output_filepath, 'WS');
if (is_file($output_filepath))
{
@@ -206,7 +206,7 @@ function merge_chunks($output_filepath, $original_sum, $type)
{
if (preg_match($pattern, $file))
{
ws_logfile($file);
$logger->debug($file, 'WS');
$chunks[] = $upload_dir.'/'.$file;
}
}
@@ -216,7 +216,7 @@ function merge_chunks($output_filepath, $original_sum, $type)
sort($chunks);
if (function_exists('memory_get_usage')) {
ws_logfile('[merge_chunks] memory_get_usage before loading chunks: '.memory_get_usage());
$logger->debug('[merge_chunks] memory_get_usage before loading chunks: '.memory_get_usage(), 'WS');
}
$i = 0;
@@ -226,7 +226,7 @@ function merge_chunks($output_filepath, $original_sum, $type)
$string = file_get_contents($chunk);
if (function_exists('memory_get_usage')) {
ws_logfile('[merge_chunks] memory_get_usage on chunk '.++$i.': '.memory_get_usage());
$logger->debug('[merge_chunks] memory_get_usage on chunk '.++$i.': '.memory_get_usage(), 'WS');
}
if (!file_put_contents($output_filepath, $string, FILE_APPEND))
@@ -238,7 +238,7 @@ function merge_chunks($output_filepath, $original_sum, $type)
}
if (function_exists('memory_get_usage')) {
ws_logfile('[merge_chunks] memory_get_usage after loading chunks: '.memory_get_usage());
$logger->debug('[merge_chunks] memory_get_usage after loading chunks: '.memory_get_usage(), 'WS');
}
}
@@ -824,7 +824,7 @@ UPDATE '. IMAGE_CATEGORY_TABLE .'
*/
function ws_images_add_chunk($params, $service)
{
global $conf;
global $conf, $logger;
foreach ($params as $param_key => $param_value)
{
@@ -832,13 +832,12 @@ function ws_images_add_chunk($params, $service)
{
continue;
}
ws_logfile(
sprintf(
'[ws_images_add_chunk] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
)
);
$logger->debug(sprintf(
'[ws_images_add_chunk] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
), 'WS');
}
$upload_dir = $conf['upload_dir'].'/buffer';
@@ -856,7 +855,7 @@ function ws_images_add_chunk($params, $service)
$params['position']
);
ws_logfile('[ws_images_add_chunk] data length : '.strlen($params['data']));
$logger->debug('[ws_images_add_chunk] data length : '.strlen($params['data']), 'WS');
$bytes_written = file_put_contents(
$upload_dir.'/'.$filename,
@@ -881,9 +880,9 @@ function ws_images_add_chunk($params, $service)
*/
function ws_images_addFile($params, $service)
{
ws_logfile(__FUNCTION__.', input : '.var_export($params, true));
global $conf, $logger;
global $conf;
$logger->debug(__FUNCTION__, 'WS', $params);
// what is the path and other infos about the photo?
$query = '
@@ -974,17 +973,15 @@ SELECT
*/
function ws_images_add($params, $service)
{
global $conf, $user;
global $conf, $user, $logger;
foreach ($params as $param_key => $param_value)
{
ws_logfile(
sprintf(
'[pwg.images.add] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
)
);
$logger->debug(sprintf(
'[pwg.images.add] input param "%s" : "%s"',
$param_key,
is_null($param_value) ? 'NULL' : $param_value
), 'WS');
}
if ($params['image_id'] > 0)
@@ -1398,9 +1395,9 @@ SELECT
*/
function ws_images_exist($params, $service)
{
ws_logfile(__FUNCTION__.' '.var_export($params, true));
global $conf, $logger;
global $conf;
$logger->debug(__FUNCTION__, 'WS', $params);
$split_pattern = '/[\s,;\|]/';
$result = array();
@@ -1471,7 +1468,9 @@ SELECT id, file
*/
function ws_images_checkFiles($params, $service)
{
ws_logfile(__FUNCTION__.', input : '.var_export($params, true));
global $logger;
$logger->debug(__FUNCTION__, 'WS', $params);
$query = '
SELECT path
@@ -1509,7 +1508,7 @@ SELECT path
if (isset($compare_type))
{
ws_logfile(__FUNCTION__.', md5_file($path) = '.md5_file($path));
$logger->debug(__FUNCTION__.', md5_file($path) = '.md5_file($path), 'WS');
if (md5_file($path) != $params[$compare_type.'_sum'])
{
$ret[$compare_type] = 'differs';
@@ -1520,7 +1519,7 @@ SELECT path
}
}
ws_logfile(__FUNCTION__.', output : '.var_export($ret, true));
$logger->debug(__FUNCTION__, 'WS', $ret);
return $ret;
}