Benjamin Renard
6fdc5447f1
* Code cleaning and fix some small errors using Phpstan * Configure pre-commit to run Phpstan before each commit * Some little improvments and logging, mail, smarty & URL libs * Add Sentry integration * Add Webstat JS code inclusion * Install Smarty dependency using composer Breaking changes: * Rename Event class as HookEvent to avoid conflict with PECL event * URL with refresh GET parameter now automatically trigger redirection without it after page loading to avoid to keep it in URL
256 lines
7 KiB
PHP
256 lines
7 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Configuration :
|
|
* // Log file
|
|
* $log_file='/path/to/app.log';
|
|
*
|
|
* // Log level (DEBUG / INFO / WARNING / ERROR / FATAL)
|
|
* $log_level='INFO';
|
|
*
|
|
* // Log PHP errors levels (as specified to set_error_handler())
|
|
* // $log_php_errors_levels = E_ALL & ~E_STRICT;
|
|
|
|
* // Log PHP errors levels (as specified to set_error_handler())
|
|
* // Default:
|
|
* // - In TRACE or DEBUG: E_ALL & ~E_STRICT
|
|
* // - Otherwise: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
|
|
* // $log_php_errors_levels = E_ALL & ~E_STRICT;
|
|
*/
|
|
|
|
|
|
// Log file descriptor (Do not change !!!)
|
|
$_log_file_fd = null;
|
|
|
|
// Log Levels
|
|
$_log_levels = array(
|
|
'TRACE' => 0,
|
|
'DEBUG' => 1,
|
|
'INFO' => 2,
|
|
'WARNING' => 3,
|
|
'ERROR' => 4,
|
|
'FATAL' => 5,
|
|
);
|
|
|
|
// Custom fatal error handler
|
|
$_fatal_error_handler = null;
|
|
|
|
/**
|
|
* Log a message
|
|
* @param string $level The message level (key of $_log_levels)
|
|
* @param string $message The message to log
|
|
* @return true
|
|
*/
|
|
function logging($level, $message) {
|
|
global $log_file, $_log_file_fd, $_log_levels, $log_level, $argv,
|
|
$_fatal_error_handler, $auth_user;
|
|
|
|
if (!array_key_exists($level, $_log_levels)) $level = 'INFO';
|
|
$level_id = $_log_levels[$level];
|
|
|
|
$log_level_id = $_log_levels[$log_level];
|
|
|
|
if ($level_id < $log_level_id) return true;
|
|
if(is_null($_log_file_fd)) {
|
|
$_log_file_fd = fopen($log_file, 'a');
|
|
}
|
|
|
|
// If more than 2 arguments passed, format message using sprintf
|
|
if (func_num_args() > 2) {
|
|
$message = call_user_func_array(
|
|
'sprintf',
|
|
array_merge(array($message), array_slice(func_get_args(), 2))
|
|
);
|
|
}
|
|
|
|
if (php_sapi_name() == "cli") {
|
|
$msg = implode(' - ', array(
|
|
date('Y/m/d H:i:s'),
|
|
basename($argv[0]),
|
|
$level,
|
|
$message
|
|
))."\n";
|
|
}
|
|
else {
|
|
$msg = array(
|
|
date('Y/m/d H:i:s'),
|
|
$_SERVER['REQUEST_URI'],
|
|
$_SERVER['REMOTE_ADDR'],
|
|
);
|
|
if (isset($auth_user))
|
|
$msg[] = ($auth_user['username']?$auth_user['username']:'anonymous');
|
|
$msg[] = $level;
|
|
$msg[] = $message;
|
|
$msg = implode(' - ', $msg)."\n";
|
|
}
|
|
|
|
fwrite($_log_file_fd, $msg);
|
|
|
|
if ($level == 'FATAL')
|
|
if (!is_null($_fatal_error_handler))
|
|
call_user_func($_fatal_error_handler, $message);
|
|
elseif (function_exists('fatal_error'))
|
|
fatal_error($message);
|
|
else
|
|
die("\n$message\n\n");
|
|
elseif (php_sapi_name() == "cli")
|
|
echo $msg;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Set default log level (if not defined or invalid)
|
|
$default_log_level = 'WARNING';
|
|
if (!isset($log_level)) {
|
|
$log_level = $default_log_level;
|
|
}
|
|
elseif (!array_key_exists($log_level, $_log_levels)) {
|
|
$invalid_value = $log_level;
|
|
$log_level = $default_log_level;
|
|
logging(
|
|
$log_level, "Invalid log level value found in configuration (%s). ".
|
|
"Set as default (%s).", $invalid_value, $log_level);
|
|
}
|
|
|
|
/**
|
|
* Register a contextual fatal error handler
|
|
* @param null|callable $handler The fatal error handler (set as null to reset)
|
|
* @return void
|
|
*/
|
|
function register_fatal_error_handler($handler) {
|
|
// @phpstan-ignore-next-line
|
|
if ($handler && !is_callable($handler))
|
|
logging('FATAL', 'Fatal handler provided is not callable !');
|
|
global $_fatal_error_handler;
|
|
$_fatal_error_handler = ($handler?$handler:null);
|
|
}
|
|
|
|
|
|
/**
|
|
* Change of current log file
|
|
* @param string $file The new log file path
|
|
* @return bool
|
|
*/
|
|
function change_log_file($file) {
|
|
global $log_file, $_log_file_fd;
|
|
if ($file == $log_file) return True;
|
|
if ($_log_file_fd) {
|
|
fclose($_log_file_fd);
|
|
$_log_file_fd = false;
|
|
}
|
|
$log_file = $file;
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
*******************************************************************************
|
|
* Handle exception logging
|
|
*******************************************************************************
|
|
*/
|
|
|
|
/**
|
|
* Get the current backtrace
|
|
* @param int $ignore_last The number of last levels to ignore
|
|
* @return string
|
|
*/
|
|
function get_debug_backtrace_context($ignore_last=0) {
|
|
$traces = debug_backtrace();
|
|
|
|
// Also ignore this function it self
|
|
$ignore_last++;
|
|
|
|
if (!is_array($traces) || count($traces) <= $ignore_last)
|
|
return "";
|
|
|
|
$msg = array();
|
|
for ($i=$ignore_last; $i < count($traces); $i++) {
|
|
$trace = array("#$i");
|
|
if (isset($traces[$i]['file']))
|
|
$trace[] = $traces[$i]['file'].(isset($traces[$i]['line'])?":".$traces[$i]['line']:"");
|
|
// @phpstan-ignore-next-line
|
|
if (isset($traces[$i]['class']) && isset($traces[$i]['function']))
|
|
$trace[] = implode(" ", array(
|
|
$traces[$i]['class'],
|
|
$traces[$i]['type'],
|
|
$traces[$i]['function']. "()"));
|
|
elseif (isset($traces[$i]['function']))
|
|
$trace[] = $traces[$i]['function']. "()";
|
|
$msg[] = implode(" - ", $trace);
|
|
}
|
|
|
|
return implode("\n", $msg);
|
|
}
|
|
|
|
/**
|
|
* Log an exception
|
|
* @param Throwable $exception
|
|
* @param string|null $prefix The prefix of the log message
|
|
* (optional, default: "An exception occured")
|
|
* @return void
|
|
*/
|
|
function log_exception($exception, $prefix=null) {
|
|
if (function_exists('log_in_sentry'))
|
|
log_in_sentry($exception);
|
|
// If more than 2 arguments passed, format prefix message using sprintf
|
|
if ($prefix && func_num_args() > 2) {
|
|
$prefix = call_user_func_array(
|
|
'sprintf',
|
|
array_merge(array($prefix), array_slice(func_get_args(), 2))
|
|
);
|
|
}
|
|
logging(
|
|
"ERROR", "%s:\n%s\n## %s:%d : %s",
|
|
($prefix?$prefix:"An exception occured"),
|
|
get_debug_backtrace_context(1),
|
|
$exception->getFile(), $exception->getLine(),
|
|
$exception->getMessage());
|
|
}
|
|
set_exception_handler('log_exception');
|
|
|
|
/*
|
|
*******************************************************************************
|
|
* Handle PHP error logging
|
|
*******************************************************************************
|
|
*/
|
|
|
|
/**
|
|
* Convert PHP error number to the corresponding label
|
|
* @param int $errno
|
|
* @return string
|
|
*/
|
|
function errno2type($errno) {
|
|
$constants = get_defined_constants();
|
|
if (is_array($constants))
|
|
foreach($constants as $label => $value)
|
|
if ($value == $errno && preg_match('/^E_(.*)$/', $label, $m))
|
|
return $m[1];
|
|
return 'UNKNOWN ERROR #'.$errno;
|
|
}
|
|
|
|
/**
|
|
* Log a PHP error
|
|
* Note: method design to be used as callable by set_error_handler()
|
|
* @param int $errno The error number
|
|
* @param string $errstr The error message
|
|
* @param string $errfile The filename that the error was raised in
|
|
* @param int $errline The line number where the error was raised
|
|
* @return false Return false to let the normal error handler continues.
|
|
*/
|
|
function log_php_error($errno, $errstr, $errfile, $errline) {
|
|
$msg = sprintf(
|
|
"A PHP error occured : [%s] %s\nFile : %s (line : %d)",
|
|
errno2type($errno), $errstr, $errfile, $errline
|
|
);
|
|
logging("ERROR", $msg);
|
|
if (function_exists('log_php_error_in_sentry'))
|
|
log_php_error_in_sentry($errno, $msg);
|
|
return False;
|
|
}
|
|
if (isset($log_php_errors_levels))
|
|
set_error_handler('log_php_error', $log_php_errors_levels);
|
|
elseif (in_array($log_level, array('DEBUG', 'TRACE')))
|
|
set_error_handler('log_php_error', E_ALL & ~E_STRICT);
|
|
else
|
|
set_error_handler('log_php_error', E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
|
|
|
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|