Skip to content

Commit dfdb8a8

Browse files
committed
Improved logging, squashed
1 parent 422bd2e commit dfdb8a8

File tree

1 file changed

+153
-22
lines changed

1 file changed

+153
-22
lines changed

include/SugarLogger/SugarLogger.php

+153-22
Original file line numberDiff line numberDiff line change
@@ -167,31 +167,147 @@ protected function _attemptToCreateIfNecessary()
167167
@touch($this->full_log_file);
168168
}
169169

170+
private function getArguments($className, $funcName) {
171+
// https://stackoverflow.com/a/42600677/1189711
172+
$f = null;
173+
try {
174+
$f = (new ReflectionFunction($funcName))->getParameters();
175+
} catch(ReflectionException $funcExc) {
176+
try {
177+
$f = (new ReflectionClass($className))->getMethod($funcName)->getParameters();
178+
} catch (ReflectionException $classExc) {
179+
return [];
180+
}
181+
}
182+
return array_map( function( $parameter ) { return $parameter->name; },
183+
($funcName!='__callNOT') ? $f : [] );
184+
}
185+
170186
/**
171-
* for log() function, it shows a backtrace information in log when
172-
* the 'show_log_trace' config variable is set and true
173-
* @return string a readable trace string
187+
* A fancy replacement for PHP's print_r, allowing depth-level limits and other formatting options
188+
*
189+
* @param mixed $data The data to be printed into text
190+
* @param int $maxLevel The maximum depth level for recursion into arrays or objects [optional, defaults to 5]
191+
* @param string $eol Use either '' or PHP_EOL to control whether newlines get added [optional, defaults to PHP_EOL]
192+
* @param int $trimChars Not a strict char limit, just a performance enhancement; avoid recursion when generating excess chars [optional, defaults to 100]
193+
* @param int $currLevel the current depth level into an object or array, used for recursion [optional, defaults to 1]
194+
* @param int $numSpaces Total spaces to indent, will be increased by 2 in each recursion [optional, defaults to 2]
195+
*
196+
* @return string Text representation of $data input
174197
*/
175-
private function getTraceString()
198+
private function extendedPrintR($data, $maxLevel = 5, $eol = PHP_EOL, $trimChars = 100, $currLevel = 1, $numSpaces = 2)
176199
{
200+
$type = gettype($data);
201+
$spaces = str_repeat(' ', $numSpaces + 1);
202+
$print = '';
203+
$elements = array();
204+
205+
if (in_array($type, array('object', 'array'))) {
206+
if ($type === 'object') {
207+
$print = get_class($data) . ' ' . ucfirst($type);
208+
$ref = new \ReflectionObject($data);
209+
210+
foreach ($ref->getProperties() as $property) {
211+
$property->setAccessible(true);
212+
$pType = $property->getName();
213+
$elements[$pType] = $property->getValue($data);
214+
}
215+
}
216+
elseif ($type === 'array') {
217+
$print = 'Array';
218+
$elements = $data;
219+
}
220+
221+
if ($maxLevel === 0 || $currLevel < $maxLevel) {
222+
$print .= " ("; // start of obj or arr
223+
foreach ($elements as $key => $element) {
224+
$print .= $eol.($eol==PHP_EOL? " {$spaces}" : '')."[{$key}] => ";
225+
if (strlen($print) < $trimChars) {
226+
$print .= in_array(gettype($element), array('object', 'array')) ?
227+
$this->extendedPrintR($element, $maxLevel, $eol, $trimChars, $currLevel + 1, $numSpaces + 2) :
228+
(is_string($element) ? ("'" . $element . "'") : $element);
229+
} else {
230+
return ($print . ''); // excess chars, finish everything early
231+
}
232+
}
233+
} else {
234+
$print .= ''; // excess levels, no more recursion in depth, but continue working in breadth
235+
}
236+
} else {
237+
// non-complex types, end of recursion:
238+
$print = is_string($data) ? ("'".$data."'") : $data;
239+
}
240+
return $print;
241+
}
242+
243+
/**
244+
* Print an entire exception trace as a string
245+
*
246+
* This is an advanced function allowing multiple formatting configurations,
247+
* and combining two subtly different sources of information:
248+
* - function names and argument values come in the dynamic information of
249+
* the Exception object itself (where we are in the execution at the moment)
250+
* - but since that doesn't include argument names,
251+
* we use also Reflection to get those (static information about what is in the files)
252+
*
253+
* @param Exception $Exception The Exception object we want to print, containing the message, stack trace, etc
254+
* @param boolean $eol Use true to include newlines in output, false to keep each frame to just one line [optional, defaults to false]
255+
* @param integer $trimChars Char limit for text output of each frame [optional, defaults to 100]
256+
*
257+
* @return string Text representation of Exception stack trace
258+
*/
259+
private function getExceptionTraceAsString($exception, $eol = false, $trimChars = 100) {
177260
$ret = '';
178-
$trace = debug_backtrace();
179-
foreach ($trace as $call) {
180-
$file = isset($call['file']) ? $call['file'] : '???';
181-
$line = isset($call['line']) ? $call['line'] : '???';
182-
$class = isset($call['class']) ? $call['class'] : '';
183-
$type = isset($call['type']) ? $call['type'] : '';
184-
$function = isset($call['function']) ? $call['function'] : '???';
185-
$ret .= "\nCall in {$file} at #{$line} from {$class}{$type}{$function}(...)";
261+
$count = 0;
262+
$eol = $eol ? PHP_EOL : '';
263+
$dumpArgs = '';
264+
//$eol = PHP_EOL; // '';
265+
$frames = $exception->getTrace();
266+
foreach ($frames as $frame) {
267+
if (isset($frame['class']) && (($frame['class']==='SugarLogger') || $frame['class']==='LoggerManagerNOT')) {
268+
continue; // skip repetitive entries common to everything logged
269+
}
270+
$args = "";
271+
try {
272+
if (isset($frame['args'])) {
273+
$args = array();
274+
//$argNames = array();php does string contain newline
275+
$argNames = $this->getArguments(isset($frame['class']) ? $frame['class'] : null, $frame['function']);
276+
277+
foreach ($frame['args'] as $paramsCount => $arg) {
278+
$argName = '';
279+
if (isset($argNames[$paramsCount]) and strlen($argNames[$paramsCount]) !== 0) {
280+
$argName = "$argNames[$paramsCount]";
281+
}
282+
/** @noinspection PhpComposerExtensionStubsInspection */
283+
$text = mb_strimwidth(trim($this->extendedPrintR($arg, 7, $eol, $trimChars)), 0, $trimChars, ' …)');
284+
$args[] = ($eol===PHP_EOL ? ' ' : '').(strlen($argName)>0 ? "$argName: " : '')."$text";
285+
}
286+
$dumpArgs = join(', '.$eol, $args);
287+
}
288+
} catch(Exception $e) {
289+
// if something fails when getting an arg while we're dumping a backtrace, we just skip it
290+
$dumpArgs .= "### Exception in reflection: $e ###";
291+
}
292+
293+
$ret .= sprintf(PHP_EOL."#%s %s(%s): %s(%s%s)",
294+
$count++,
295+
isset($frame['file']) ? str_replace(SUGAR_PATH, '', $frame['file']) : 'unknown file',
296+
isset($frame['line']) ? $frame['line'] : 'unknown line',
297+
(isset($frame['class'])) ? $frame['class'] . $frame['type'] . $frame['function'] : $frame['function'],
298+
strstr($dumpArgs, PHP_EOL) ? $eol : '',
299+
$dumpArgs);
300+
$dumpArgs= '';
186301
}
187-
$ret .= "\n";
188302
return $ret;
189303
}
190304

191305
/**
192306
* Show log
307+
*
193308
* and show a backtrace information in log when
194-
* the 'show_log_trace' config variable is set and true
309+
* the 'show_log_trace' config variable is set and true, or a string value to match a message.
310+
* Further configuration possible with 'show_log_trace_with_eol' and 'show_log_trace_trim' config vars.
195311
* see LoggerTemplate::log()
196312
*/
197313
public function log(
@@ -211,7 +327,6 @@ public function log(
211327
$this->fp = fopen($this->full_log_file, 'ab');
212328
}
213329

214-
215330
// change to a string if there is just one entry
216331
if (is_array($message) && count($message) == 1) {
217332
$message = array_shift($message);
@@ -221,19 +336,35 @@ public function log(
221336
$message = print_r($message, true);
222337
}
223338

224-
225-
if (isset($sugar_config['show_log_trace']) && $sugar_config['show_log_trace']) {
226-
$trace = $this->getTraceString();
227-
$message .= ("\n" . $trace);
228-
}
229-
230339
//write out to the file including the time in the dateFormat the process id , the user id , and the log level as well as the message
231340
fwrite(
232341
$this->fp,
233-
strftime($this->dateFormat) . ' [' . getmypid() . '][' . $userID . '][' . strtoupper($level) . '] ' . $message . "\n"
342+
strftime($this->dateFormat) . ' [' .
343+
getmypid () . '][' . $userID . '][' .
344+
str_pad(strtoupper($level), 5) . '] ' .
345+
$message . PHP_EOL
234346
);
347+
348+
if (isset($sugar_config['show_log_trace']) &&
349+
(($sugar_config['show_log_trace'] === true) ||
350+
(is_string($sugar_config['show_log_trace']) && (strpos($message, $sugar_config['show_log_trace'])!== false)))) {
351+
352+
$eolConfig = isset($sugar_config['show_log_trace_with_eol']) && $sugar_config['show_log_trace_with_eol'];
353+
if (isset($sugar_config['show_log_trace_trim'])) {
354+
$trimConfig = intval($sugar_config['show_log_trace_trim']);
355+
}
356+
$trimConfig = (is_int($trimConfig) && $trimConfig > 0) ? $trimConfig : 100;
357+
358+
$e = new \Exception;
359+
fwrite(
360+
$this->fp,
361+
PHP_EOL . strftime($this->dateFormat) . ' [' . getmypid () . '][' . $userID . '][TRACE] ' .
362+
$this->getExceptionTraceAsString($e, $eolConfig, $trimConfig) . PHP_EOL . PHP_EOL
363+
);
364+
}
235365
}
236366

367+
237368
/**
238369
* rolls the logger file to start using a new file
239370
*/

0 commit comments

Comments
 (0)