From b195a266f48adc3f69e33e5817ecc734984b7e3a Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Thu, 26 Sep 2024 14:12:59 +0200 Subject: [PATCH] LSlog: add configuration parameters to allow to log error context --- doc/src/conf/global/LSlog.md | 20 ++++++ src/conf/config.inc.php | 110 +++++++++++++++++------------ src/includes/class/class.LSlog.php | 76 ++++++++++++++++---- 3 files changed, 148 insertions(+), 58 deletions(-) diff --git a/doc/src/conf/global/LSlog.md b/doc/src/conf/global/LSlog.md index 0a672959..1f751489 100644 --- a/doc/src/conf/global/LSlog.md +++ b/doc/src/conf/global/LSlog.md @@ -6,6 +6,9 @@ Cette section décrit le tableau de configuration de la journalisation de l'appl $GLOBALS['LSlog'] = array( 'enable' => [booléen], 'level' => '[niveau]', + 'log_errors_context' => [booléen], + 'log_errors_context_with_args' => [booléen], + 'log_errors_context_args_max_length' => [entier], 'handlers' => array( '[handler 1]', array ( @@ -53,6 +56,23 @@ $GLOBALS['LSlog'] = array( - `ERROR` - `FATAL` +- `log_errors_context` + + Booléen permatant de définir si le contexte _(=backtrace)_ doit être inclus lors de la + journalisation d'une erreurs. + +- `log_errors_context_with_args` + + Booléen permatant de définir si les arguments des méthodes/fonctions appelées doivent être + inclus lors de la journalisation du contexte des erreurs. + __Note :__ ce paramètre n'as aucun effet si le paramètre `log_errors_context` n'est pas activé. + +- `log_errors_context_args_max_length` + + Ce paramètre permet de définir à partir de quelle longueur les arguments des méthodes/fonctions + appelées et journalisés seront tronqués (par défaut : `1000`). __Note :__ pour désactiver le + troncage, mettre ce paramètre à zéro. + - `handlers` Tableau permettant de configurer les *handlers* de la journalisation. Chaque *handler* gère les diff --git a/src/conf/config.inc.php b/src/conf/config.inc.php index 951701e1..64a9d9bb 100644 --- a/src/conf/config.inc.php +++ b/src/conf/config.inc.php @@ -204,60 +204,80 @@ define('LS_CSS_DIR', 'css'); define('LSdebug',false); // Logging -$GLOBALS['LSlog']['handlers'] = array ( - array ( - 'handler' => 'file', - 'path' => 'tmp/LS.log', - //'level' => 'DEBUG', - //'enabled' => false, // Uncomment to disable this handler - // Filter on specific loggers - //'loggers' => array('LSurl', 'LSlang'), - 'excluded_loggers' => array('generate_lang_file', 'generate_ldapsaisie_pot'), - // Default formats - //'format' => '%{requesturi} - %{remoteaddr} - %{ldapservername} - %{authuser} - %{level} - %{message}', - //'cli_format' => '%{clibinpath} - %{level} - %{message}', - //'datetime_prefix' => true, // Prefix message with datetime ? - //'datetime_format' => 'Y/m/d H:i:s', // Datetime prefix format (see php date() function) +$GLOBALS['LSlog'] = array ( + // Enable/disable logs + 'enable' => true, + + // Global logs level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL) + 'level' => 'INFO', + + // Log errors's context (=backtrace, default: false) + 'log_errors_context' => true, + + // Log errors's context with arguments of called method/functions (default: false) + // 'log_errors_context_with_args' => false, + + // Truncate logged arguments in errors's context (default: 1000) + // 'log_errors_context_args_max_length' => 1000, + + /** + * Logs handlers are components that logged message emitted by the application. + * Each handlers handle emitted message as its own way (storing it in file/database, send it via + * email or to an external backend, ...). + */ + 'handlers' => array ( + array( + 'handler' => 'file', + 'path' => 'tmp/LS.log', + //'level' => 'DEBUG', + //'enabled' => false, // Uncomment to disable this handler + // Filter on specific loggers + //'loggers' => array('LSurl', 'LSlang'), + 'excluded_loggers' => array('generate_lang_file', 'generate_ldapsaisie_pot'), + // Default formats + //'format' => '%{requesturi} - %{remoteaddr} - %{ldapservername} - %{authuser} - %{level} - %{message}', + //'cli_format' => '%{clibinpath} - %{level} - %{message}', + //'datetime_prefix' => true, // Prefix message with datetime ? + //'datetime_format' => 'Y/m/d H:i:s', // Datetime prefix format (see php date() function) + ), + array ( + 'handler' => 'email', // Email handler (each logged message generated an email) + 'level' => 'FATAL', + 'recipient' => 'root@localhost', // Email recipient + ), + /* + array ( + 'handler' => 'syslog', // Syslog handler + //'priority' => 'WARNING', // Force priority : EMERG, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG + ), + */ + /* + array ( + 'handler' => 'system', // System logging (using PHP error_log) + 'level' => 'ERROR', + ), + */ ), - array ( - 'handler' => 'email', // Email handler (each logged message generated an email) - 'level' => 'FATAL', - 'recipient' => 'root@localhost', // Email recipient - ), - /* - array ( - 'handler' => 'syslog', // Syslog handler - //'priority' => 'WARNING', // Force priority : EMERG, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG - ), - */ - /* - array ( - 'handler' => 'system', // System logging (using PHP error_log) - 'level' => 'ERROR', - ), - */ -); -$GLOBALS['LSlog']['loggers'] = array ( /** * Loggers permit to define different log parameters for specific components * of LdapSaisie (a class, an addon, ...). You could : * - Enabled/disabled logs for this component with 'enabled' parameter * - Set a specific log level for this component with 'enabled' parameter **/ - /* - 'LSurl' => array ( - 'level' => 'DEBUG', + "loggers" => array( + /* + 'LSurl' => array ( + 'level' => 'DEBUG', + ), + 'LSldap' => array ( + 'level' => 'DEBUG', + ), + 'LSlang' => array ( + 'enabled' => false, + ), + */ ), - 'LSldap' => array ( - 'level' => 'DEBUG', - ), - 'LSlang' => array ( - 'enabled' => false, - ), - */ ); -$GLOBALS['LSlog']['level'] = 'INFO'; // TRACE, DEBUG, INFO, WARNING, ERROR, FATAL -$GLOBALS['LSlog']['enable'] = true; define('NB_LSOBJECT_LIST',30); define('NB_LSOBJECT_LIST_SELECT',20); diff --git a/src/includes/class/class.LSlog.php b/src/includes/class/class.LSlog.php index 344d363f..d20f4352 100644 --- a/src/includes/class/class.LSlog.php +++ b/src/includes/class/class.LSlog.php @@ -33,6 +33,24 @@ class LSlog { */ private static $enabled = false; + /** + * Log errors context + * @var bool + */ + private static $log_errors_context = false; + + /** + * Log errors context with arguments + * @var bool + */ + private static $log_errors_context_with_args = false; + + /** + * Log errors context with arguments + * @var int + */ + private static $log_errors_context_args_max_length = 1000; + /** * Configured handlers * @see self::start() @@ -91,6 +109,9 @@ class LSlog { public static function start() { // Load configuration self :: $enabled = self :: getConfig('enable', false, 'bool'); + self :: $log_errors_context = self :: getConfig('log_errors_context', false, 'bool'); + self :: $log_errors_context_with_args = self :: getConfig('log_errors_context_with_args', false, 'bool'); + self :: $log_errors_context_args_max_length = self :: getConfig('log_errors_context_args_max_length', 1000, 'int'); self :: setLevel(); // Load default handlers class @@ -232,10 +253,11 @@ class LSlog { * @param string $level The message level * @param string $message The message * @param string|null $logger The logger name (optional, default: null) + * @param bool $do_not_include_context Set to true to disable context inclusion (optional, default: false) * * @return void **/ - public static function logging($level, $message, $logger=null) { + public static function logging($level, $message, $logger=null, $do_not_include_context=false) { // Check LSlog is enabled if (!self :: $enabled) return; @@ -252,6 +274,19 @@ class LSlog { $message = varDump($message); } + // Append context to message (if enabled) + $context = ""; + if ( + !$do_not_include_context + && self :: $log_errors_context + && self :: checkLevel($level, "ERROR") + ) + $context = "\n ".self :: get_debug_backtrace_context( + self :: $log_errors_context_with_args, + 2, + " " + ); + foreach (self :: $handlers as $handler) { // Check handler level if (!$handler -> checkLevel($level)) @@ -261,12 +296,12 @@ class LSlog { continue; // Logging on this handler - call_user_func(array($handler, 'logging'), $level, $message, $logger); + call_user_func(array($handler, 'logging'), $level, $message.$context, $logger); } if ($level == 'FATAL') { if (php_sapi_name() == "cli") - die($message); + die($message.$context); elseif (class_exists('LStemplate')) LStemplate :: fatal_error($message); else @@ -321,7 +356,7 @@ class LSlog { $prefix = is_string($prefix)?$prefix:""; $traces = debug_backtrace(); if (!is_array($traces) || count($traces) < ($ignore_last_frames + 1)) - return $prefix."unknown context"; + return "unknown context"; $msg = array(); $j=0; @@ -332,7 +367,11 @@ class LSlog { $trace[] = $traces[$i]['file'].(isset($traces[$i]['line'])?":".$traces[$i]['line']:""); $args = ( $with_args && isset($traces[$i]["args"])? - format_callable_args($traces[$i]["args"], $prefix): + format_callable_args( + $traces[$i]["args"], + $prefix, + self :: $log_errors_context_args_max_length + ): "" ); if (isset($traces[$i]['class']) && isset($traces[$i]['function'])) @@ -342,10 +381,10 @@ class LSlog { ); elseif (isset($traces[$i]['function'])) $trace[] = sprintf("%s(%s)", $traces[$i]['function'], $args); - $msg[] = $prefix.implode(" - ", $trace); + $msg[] = implode(" - ", $trace); } - return $prefix.implode("$prefix\n", $msg); + return implode("\n$prefix", $msg); } /** @@ -364,12 +403,23 @@ class LSlog { public static function exception($exception, $prefix=null, $fatal=true, $logger=null) { $message = ($prefix?"$prefix :\n":"An exception occured :\n"). - self :: get_debug_backtrace_context(). "\n" . - "## ".$exception->getFile().":".$exception->getLine(). " : ". $exception->getMessage(); - if (is_null($logger)) - self :: logging(($fatal?'FATAL':'ERROR'), $message); - else - self :: logging(($fatal?'FATAL':'ERROR'), $message, $logger); + implode( + "\n", + array_map( + function($line) { return " $line"; }, + array_slice( + array_reverse( + explode( + "\n", + $exception->getTraceAsString() + ), + 1 + ), + 1 + ) + ) + )."\n => ". $exception->getMessage(); + self :: logging(($fatal?'FATAL':'ERROR'), $message, $logger, true); } /**