@@ -167,31 +167,147 @@ protected function _attemptToCreateIfNecessary()
167
167
@touch ($ this ->full_log_file );
168
168
}
169
169
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
+
170
186
/**
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
174
197
*/
175
- private function getTraceString ( )
198
+ private function extendedPrintR ( $ data , $ maxLevel = 5 , $ eol = PHP_EOL , $ trimChars = 100 , $ currLevel = 1 , $ numSpaces = 2 )
176
199
{
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 ) {
177
260
$ 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 = '' ;
186
301
}
187
- $ ret .= "\n" ;
188
302
return $ ret ;
189
303
}
190
304
191
305
/**
192
306
* Show log
307
+ *
193
308
* 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.
195
311
* see LoggerTemplate::log()
196
312
*/
197
313
public function log (
@@ -211,7 +327,6 @@ public function log(
211
327
$ this ->fp = fopen ($ this ->full_log_file , 'ab ' );
212
328
}
213
329
214
-
215
330
// change to a string if there is just one entry
216
331
if (is_array ($ message ) && count ($ message ) == 1 ) {
217
332
$ message = array_shift ($ message );
@@ -221,19 +336,35 @@ public function log(
221
336
$ message = print_r ($ message , true );
222
337
}
223
338
224
-
225
- if (isset ($ sugar_config ['show_log_trace ' ]) && $ sugar_config ['show_log_trace ' ]) {
226
- $ trace = $ this ->getTraceString ();
227
- $ message .= ("\n" . $ trace );
228
- }
229
-
230
339
//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
231
340
fwrite (
232
341
$ 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
234
346
);
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
+ }
235
365
}
236
366
367
+
237
368
/**
238
369
* rolls the logger file to start using a new file
239
370
*/
0 commit comments