printf like log function for PHP
Oktober 9th, 2011
The following functions are good friends for logging. You can use format strings parsed by printf and several log levels.
Usage example : ( Download: example.php)
<?php /** * Require l.php before parsing the ini file * so that L_* contants can be used in the ini file * to define the L_LEVEL */ require_once 'l.php'; $config = parse_ini_file('config.ini'); define('l_reporting', intval($config['l_reporting'])); l ('test', L_NOTICE); l (1); l (3.4); l (FALSE); l (NULL, L_DEPRECATED); l (array(1,2,3)); l (new DOMDocument()); lf ('file: %s', @file_get_contents('sada'), L_ERROR); lf ('hello %s! The time is %s', 'thorsten', date('Y:m:i')); lf ('%s in binary is %1$032b', rand()); |
Assuming this config file:
;... l_reporting = L_ALL; ;... |
The output will look like
[2011:10:03][L_NOTICE] test [2011:10:03][L_ERROR] file: FALSE |
When using the following config file:
; ... l_reporting = L_VDEBUG; ; ... |
Then the output will look like:
[2011:10:29]test [2011:10:29]1 [2011:10:29]3.4 [2011:10:29]FALSE [2011:10:29]Array ( [0] => 1 [1] => 2 [2] => 3 ) [2011:10:29]DOMDocument Object ( ) [2011:10:29]file: FALSE [2011:10:29]hello thorsten! The time is 2011:10:29 [2011:10:29]1296685703 in binary is 01001101010010011101101010000111 |
<?php /** * Printf like log function for php * * @example /** * Parse current log level from ini file. If doing * is AFTER define('L_*', ..) contants, one can use * this constants in the ini file. * * Example ini file: * ... * l_reporting = E_ALL | DEBUG; * ... *\/ * $config = parse_ini_file('config.ini'); * define('L_LEVEL', intval($config['l_reporting'])); * * l ('test', L_NOTICE); * l (1); * l (3.4,); * l (FALSE); * l (NULL, L_DEPRECATED); * l (array()); * l (new DOMDocument()); * lf ('file: %s', @file_get_contents('sada'), L_ERROR); * lf ('hello %s! The time is %s', $argv[1], new DateTime()); * lf ('SOME_INT_VALUE is %1$016b %1$s', SOME_INT_VALUE); * * @author Thorsten Heymann <info@metashock.net> */ /** * Common log levels * define them before parsing the ini file */ define('L_ERROR', 1); define('L_WARNING', 2); define('L_NOTICE', 4); define('L_ALL', L_ERROR | L_WARNING | L_NOTICE); define('L_DEBUG', 8 | L_ALL); define('L_VDEBUG', 16 | L_DEBUG); define('L_DEPRECATED', 32); /** * Custom log levels are after bit 8 */ define('L_MODEL_DEBUG', 256); /** * A log function like printf. * * For the flexibiltity that $level is an optional parameter, * we have to pay the the price of "must parse the fmt string" * to find out how many arguments in arglist are used as fmt args, * and then detect if the log level was was specifed and get its value. * So try to use this method with $fmt strings as short as possible * to avoid overhead. * * l($mixed [, $level = L_DEBUG]); * l($fmt_str, $fmt_arg0, ..., $fmt_arg [, $level = L_DEBUG]); * * @return string | FALSE */ function lf ($message /* , $args ..., $level = L_DEBUG*/) { // get number of arguments $nargs = func_num_args(); if($nargs < 1) { user_error(sprintf('%s expected at least one argument')); return FALSE; } $nfmtargs = 0; // check if first argument is a string if ( is_string ( func_get_arg (0) )) { // then check how much format arguments // the string consumes $fmt = func_get_arg(0); $args = array(); for($i = 0; $i < strlen($fmt); $i++) { if($fmt[$i] !== '%') { continue; } $argindex = ''; while(ord($fmt[$i+1]) > 0x29 && ord($fmt[$i+1]) < 0x40) { $argindex .= $fmt[$i+1]; $i++; } if($argindex === '' || $fmt[$i+1] !== '$' ) { $nfmtargs++; } else if (!isset($args[$argindex])) { $nfmtargs++; $args[$argindex] = TRUE; } } } // $level is the last argument in list // if $level was not set we assume L_DEBUG as the default value if($nargs < $nfmtargs + 2) { $level = L_DEBUG; } else { $level = func_get_arg($nargs -1); } $printfargs = array(); // check if there are arguments to the ftm string for($i = 0; $i < max(1, $nargs); $i++) { $arg = func_get_arg($i); // convert none printf arguments to printf arguments. // if they are willing or not! if(is_string($arg) || is_float($arg) || is_int($arg) || (is_object($arg) && method_exists($arg, '__toString')) ) { $printfargs []= $arg; } else if(is_bool($arg)) { $printfargs []= $arg ? 'TRUE' : 'FALSE'; } else if(is_null($arg)) { $printfargs []= 'NULL'; } else { $printfargs []= print_r($arg, TRUE); } } // call printf $msg = call_user_func_array ('sprintf', $printfargs); return l($msg, $level); } /** * Logs a message to stderr * * @param mixed $message * @param integer $level=L_DEBUG * @return string the log message */ function l($message, $level = L_DEBUG) { // check if $level passes current L_LEVEL filter if(($level & l_reporting) !== $level) { return ''; } // convert $message to a string - willing or not! if(is_bool($message)) { $message = $message ? 'TRUE' : 'FALSE'; } else if(is_null($message)) { $message = 'NULL'; } else if(is_object($message) || is_array($message)) { $message = print_r($message, TRUE); } $prefix = '[' . date('Y:m:i') . '][' . l_reporting_to_string($level) . '] '; $message = $prefix . $message; // just using STDERR here. Use your own logger .. fwrite(STDERR, $message . PHP_EOL); return $message . PHP_EOL; } /** * Gives a string representation of the common * log levels * * @param integer * @return string */ function l_reporting_to_string($level) { $switches = array(); if(($level & L_ERROR) === L_ERROR) { $switches []= 'L_ERROR'; } if(($level & L_WARNING) === L_WARNING) { $switches []= 'L_WARNING'; } if(($level & L_NOTICE) === L_NOTICE) { $switches []= 'L_NOTICE'; } if(($level & L_DEBUG) === L_DEBUG) { $switches []= 'L_DEBUG'; } if(($level & L_VDEBUG) === L_VDEBUG) { $switches []= 'L_VDEBUG'; } if(($level & L_DEPRECATED) === L_DEPRECATED) { $switches []= 'L_DEPRECATED'; } return implode(' | ', $switches); } |
Play around with various combinations of l_reporting levels. (And report bugs )
Leave a Reply