31

I'm using getTraceAsString() to get a stack trace but the string is being truncated for some reason.

Example, an exception is thrown and I log the string using:

catch (SoapFault $e) {
error_log( $e->getTraceAsString() )
}

The string thats prints out is:

#0 C:\Somedirectory\Somedirectory\Somedirectory\Somedir\SomeScript.php(10): SoapClient->SoapClient('http://www.ex...')

How can I get the full string to print?

Gumbo
  • 643,351
  • 109
  • 780
  • 844
User
  • 62,498
  • 72
  • 186
  • 247

9 Answers9

40

I created this function to return a stack trace with no truncated strings:

function getExceptionTraceAsString($exception) {
    $rtn = "";
    $count = 0;
    foreach ($exception->getTrace() as $frame) {
        $args = "";
        if (isset($frame['args'])) {
            $args = array();
            foreach ($frame['args'] as $arg) {
                if (is_string($arg)) {
                    $args[] = "'" . $arg . "'";
                } elseif (is_array($arg)) {
                    $args[] = "Array";
                } elseif (is_null($arg)) {
                    $args[] = 'NULL';
                } elseif (is_bool($arg)) {
                    $args[] = ($arg) ? "true" : "false";
                } elseif (is_object($arg)) {
                    $args[] = get_class($arg);
                } elseif (is_resource($arg)) {
                    $args[] = get_resource_type($arg);
                } else {
                    $args[] = $arg;
                }   
            }   
            $args = join(", ", $args);
        }
        $rtn .= sprintf(
            "#%s %s(%s): %s%s%s(%s)\n",
            $count,
            $frame['file'],
            $frame['line'],
            isset($frame['class']) ? $frame['class'] : '',
            isset($frame['type']) ? $frame['type'] : '', // "->" or "::"
            $frame['function'],
            $args
        );
        $count++;
    }
    return $rtn;
}

Alternatively, you could edit the php source where it is truncating the output: https://github.com/php/php-src/blob/master/Zend/zend_exceptions.c#L392

stollr
  • 6,534
  • 4
  • 43
  • 59
Steve
  • 1,084
  • 11
  • 17
  • 1
    Thanks for this function. Very useful. The truncation of backtrace in Exception was bugging me for years but I never had time to do anything about it. I added your function to my core Exception class on http://www.lampcms.com now. – Dmitri Jul 03 '11 at 13:55
  • 1
    Thanks Steve. I added in some spans for color coding purposes, and we're in business, EG: $rtn .= sprintf( "#%s %s(%s): %s(%s)\n", – Andrew Jun 11 '16 at 15:06
  • 2
    Thanks for this. When I tried it, however, I got two notices: `Notice: Undefined index: file` and `Notice: Undefined index: line`. – Alexandre R. Janini Jun 22 '16 at 14:05
  • 1
    Why not use just `json_encode()` for exception trace array? – Mehdi Rahimi May 08 '19 at 12:32
  • Please note, that there is a new INI directive (`zend.exception_ignore_args`) for including or excluding arguments from stack traces generated from exceptions. Source: https://www.php.net/manual/de/migration74.other-changes.php Make sure to set it to `0`, e.g. like so: `ini_set('zend.exception_ignore_args', 0);` – Alfred Bez Jan 21 '20 at 10:46
7

That solution is good but in my case it threw an error because my trace has internal functions in it. I added a few lines of code to check for that so the trace functions still works.

function getExceptionTraceAsString($exception) {
    $rtn = "";
    $count = 0;
    foreach ($exception->getTrace() as $frame) {


        $args = "";
        if (isset($frame['args'])) {
            $args = array();
            foreach ($frame['args'] as $arg) {
                if (is_string($arg)) {
                    $args[] = "'" . $arg . "'";
                } elseif (is_array($arg)) {
                    $args[] = "Array";
                } elseif (is_null($arg)) {
                    $args[] = 'NULL';
                } elseif (is_bool($arg)) {
                    $args[] = ($arg) ? "true" : "false";
                } elseif (is_object($arg)) {
                    $args[] = get_class($arg);
                } elseif (is_resource($arg)) {
                    $args[] = get_resource_type($arg);
                } else {
                    $args[] = $arg;
                }
            }
            $args = join(", ", $args);
        }
        $current_file = "[internal function]";
        if(isset($frame['file']))
        {
            $current_file = $frame['file'];
        }
        $current_line = "";
        if(isset($frame['line']))
        {
            $current_line = $frame['line'];
        }
        $rtn .= sprintf( "#%s %s(%s): %s(%s)\n",
            $count,
            $current_file,
            $current_line,
            $frame['function'],
            $args );
        $count++;
    }
    return $rtn;
}
Jason Gallavin
  • 403
  • 4
  • 14
7

Some better version https://stackoverflow.com/a/6076667/194508 is here https://gist.github.com/1437966 added class to output.

Community
  • 1
  • 1
Ladislav Prskavec
  • 4,351
  • 3
  • 19
  • 16
3

Will changing the php.ini setting log_errors_max_len help?

Also, please note that messages are truncated only during the output, you still can get the original error message with call to $exception->getMessage()

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • 3
    Prior to creating this post I bumped the value of log_errors_max_len from 1024 to 4096. But there was no difference. – User Dec 22 '09 at 23:24
  • Regarding getMessage(), I use both getMessage() and getTraceAsString() but they print different things so I need them both. – User Dec 22 '09 at 23:26
  • This is not enough for full `getTraceAsString()` output – andras.tim Mar 03 '17 at 10:40
  • This just sorted out an issue I was having. Uncaught exceptions are subjected to this setting (stringifying the exception does not have this issue though). – Christian Jul 16 '18 at 17:51
2

There is also the excellent jTraceEx recipe by Ernest Vogelsinger at https://www.php.net/manual/exception.gettraceasstring.php#114980, that supports chained exceptions and is formatted in a Java-like manner.

Here is a comparison taken directly from his comment on php.net :

Exception::getTraceAsString :

#0 /var/htdocs/websites/sbdevel/public/index.php(70): seabird\test\C->exc()
#1 /var/htdocs/websites/sbdevel/public/index.php(85): seabird\test\C->doexc()
#2 /var/htdocs/websites/sbdevel/public/index.php(89): seabird\test\fail2()
#3 /var/htdocs/websites/sbdevel/public/index.php(93): seabird\test\fail1()
#4 {main}

jTraceEx :

Exception: Thrown from class C
 at seabird.test.C.exc(index.php:78)
 at seabird.test.C.doexc(index.php:70)
 at seabird.test.fail2(index.php:85)
 at seabird.test.fail1(index.php:89)
 at (main)(index.php:93)
Caused by: Exception: Thrown from class B
 at seabird.test.B.exc(index.php:64)
 at seabird.test.C.exc(index.php:75)
 ... 4 more
Caused by: Exception: Thrown from class A
 at seabird.test.A.exc(index.php:46)
 at seabird.test.B.exc(index.php:61)
 ... 5 more
Charles Okolms
  • 11
  • 1
  • 1
  • 5
Lucas Cimon
  • 1,859
  • 2
  • 24
  • 33
  • I tried this function, and while it produces clean output, it omits the full path of the files in the trace. Which is a problem if you have multiple files with the same name (in different folders). I found the `getExceptionTraceAsString()` method listed in another comment in this thread was more helpful. – dearsina Apr 28 '22 at 06:20
1

You can print the backtrace with

debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

It does not truncate.

Example print would be

#0  W3\\Sausage\\Mushroom->setCredentials() called at [/sausage/common/library/W3/Vzaar/Api.php:40]
#1  W3\\Sausage\\Mushroom->__construct() called at [/sausage/common/modules/video.mod.php:24]
#2  ModVideo->__construct() called at [/sausage/common/core/modules.core.php:133]
#3  Modules->__get() called at [/sausage/common/search/Classified/ESAdapter.php:399]
#4  Base\\Search\\Classified\\ESAdapter->getVideoInfo() called at [/sausage/common/search/Classified/ESAdapter.php:436]
#5  Base\\Search\\Classified\\ESAdapter->fillDataSet() called at [/sausage/common/search/Adapter.php:58]
Oli Girling
  • 605
  • 8
  • 14
0

In case of rethrowing Exceptions it's possible to supply the previous Exception as the third argument. By doing so, it's possible to chain the Exception trace.

try  {
    f('123');
} catch(Throwable $e){
    var_dump($e);
}

function f($arg){
    if(is_string($arg)){
        try {
            g($arg);
        } catch(UnexpectedValueException $e) {
            // Supply a third argument to pass the previous Exception.
            throw new RuntimeException('Error in function g()', $e->getCode(), $e);
        } catch(Throwable $e) {
            // Supply a third argument to pass the previous Exception.
            throw new RuntimeException('Unkown Error in function g()', $e->getCode(), $e);
        }   
    }   
}

function g($string){
    if(strlen($string) < 6){
        try {
            h($string);
        } catch(UnexpectedValueException $e) {
            throw new UnexpectedValueException('String is smaller then 6', $e->getCode(), $e);
        }
    }
    return $string;
}

function h($string){
    if(strlen($string) < 4){
        throw new UnexpectedValueException('String is smaller then 4');
    }
    return $string;
}

OUTPUT:

C:\wamp64\www\julian\index.php:21:
object(RuntimeException)[3]
  protected 'message' => string 'Error in function g()' (length=21)
  private 'string' (Exception) => string '' (length=0)
  protected 'code' => int 0
  protected 'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
  protected 'line' => int 30
  private 'trace' (Exception) => 
    array (size=1)
      0 => 
        array (size=4)
          'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
          'line' => int 19
          'function' => string 'f' (length=1)
          'args' => 
            array (size=1)
              0 => string '123' (length=3)
  private 'previous' (Exception) => 
    object(UnexpectedValueException)[2]
      protected 'message' => string 'String is smaller then 6' (length=24)
      private 'string' (Exception) => string '' (length=0)
      protected 'code' => int 0
      protected 'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
      protected 'line' => int 43
      private 'trace' (Exception) => 
        array (size=2)
          0 => 
            array (size=4)
              'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
              'line' => int 27
              'function' => string 'g' (length=1)
              'args' => 
                array (size=1)
                  0 => string '123' (length=3)
          1 => 
            array (size=4)
              'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
              'line' => int 19
              'function' => string 'f' (length=1)
              'args' => 
                array (size=1)
                  0 => string '123' (length=3)
      private 'previous' (Exception) => 
        object(UnexpectedValueException)[1]
          protected 'message' => string 'String is smaller then 4' (length=24)
          private 'string' (Exception) => string '' (length=0)
          protected 'code' => int 0
          protected 'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
          protected 'line' => int 51
          private 'trace' (Exception) => 
            array (size=3)
              0 => 
                array (size=4)
                  'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
                  'line' => int 41
                  'function' => string 'h' (length=1)
                  'args' => 
                    array (size=1)
                      0 => string '123' (length=3)
              1 => 
                array (size=4)
                  'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
                  'line' => int 27
                  'function' => string 'g' (length=1)
                  'args' => 
                    array (size=1)
                      0 => string '123' (length=3)
              2 => 
                array (size=4)
                  'file' => string 'C:\wamp64\www\julian\index.php' (length=30)
                  'line' => int 19
                  'function' => string 'f' (length=1)
                  'args' => 
                    array (size=1)
                      0 => string '123' (length=3)
          private 'previous' (Exception) => null
          public 'xdebug_message' => string '<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> UnexpectedValueException: String is smaller then 4 in C:\wamp64\www\julian\index.php on line <i>51</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361152</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='C:\wamp64\www\julian\index.php' bgcolor='#eeeeec'>...\index.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361528</td><td bgcolor='#eeeeec'>f(  )</td><td title='C'... (length=1645)
      public 'xdebug_message' => string '<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> UnexpectedValueException: String is smaller then 6 in C:\wamp64\www\julian\index.php on line <i>43</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361152</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='C:\wamp64\www\julian\index.php' bgcolor='#eeeeec'>...\index.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361528</td><td bgcolor='#eeeeec'>f(  )</td><td title='C'... (length=1376)
  public 'xdebug_message' => string '<tr><th align='left' bgcolor='#f57900' colspan="5"><span style='background-color: #cc0000; color: #fce94f; font-size: x-large;'>( ! )</span> RuntimeException: Error in function g() in C:\wamp64\www\julian\index.php on line <i>30</i></th></tr>
<tr><th align='left' bgcolor='#e9b96e' colspan='5'>Call Stack</th></tr>
<tr><th align='center' bgcolor='#eeeeec'>#</th><th align='left' bgcolor='#eeeeec'>Time</th><th align='left' bgcolor='#eeeeec'>Memory</th><th align='left' bgcolor='#eeeeec'>Function</th><th align='left' bgcolor='#eeeeec'>Location</th></tr>
<tr><td bgcolor='#eeeeec' align='center'>1</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361152</td><td bgcolor='#eeeeec'>{main}(  )</td><td title='C:\wamp64\www\julian\index.php' bgcolor='#eeeeec'>...\index.php<b>:</b>0</td></tr>
<tr><td bgcolor='#eeeeec' align='center'>2</td><td bgcolor='#eeeeec' align='center'>0.0034</td><td bgcolor='#eeeeec' align='right'>361528</td><td bgcolor='#eeeeec'>f(  )</td><td title='C:\wamp64\ww'... (length=1096)
Julian
  • 4,396
  • 5
  • 39
  • 51
0

I (very recently, after long time when I thought how to have exception warnings better and as exact and descriptive as possible) solved problem with incomplete report of getTrace with following (maybe not perfect, but still usable) method:

private function Create_TraceText()
{
    $trace_text = "<b>TRACE:</b> <br /><br />\n";
    $trace = $this -> getTrace();

    for($index = 0; $index < count($trace); $index++)
    {
        $trace_text .= 'STEP ' . ($index + 1) . ":<br />\n";

        foreach($trace[$index] as $trace_name => $trace_value)
        {
            $trace_value = $this -> Convert_StringifyEmpty($trace_value);
            $trace_value = $this -> Convert_StringifyNull($trace_value);
            $trace_value = $this -> Convert_StringifyArray($trace_value);
            $trace_text .= strtoupper($trace_name == 'args' ? 'arguments' : $trace_name) . ': ' . $trace_value . "<br />\n";
        }

        $trace_text .= "<br />\n";
    }

    return $trace_text;
}

Method Create_TraceText calls three other methods that are (also) my own. Those methods have following purpose:

  • insert alternative text in case of no argument was set
  • replace NULL value with NULL string
  • convert arguments' array into string (with comma as glue)
  • make code better readable

I chose private accessibility because it is called internally by method that handles report assembling. But if you would like, you may have it public.

It iterates through trace, takes items (keys and their values) of each step and converts them into string in form that is written below

TRACE:

STEP 1:
FILE: A:\WWW\Kilometrovnik\Kilometrovnik.php
LINE: 166
FUNCTION: Execute
CLASS: VMaX\MarC\Assemblers\CodeGenerator
TYPE: ->
ARGUMENTS: not defined

There is only one step in example above, but it may be repeated (automatically done), if demanded by trace.


Notice:

Method getTrace may be used directly, of course. I chose current way to support make code better readable (and may be faster - if method getTrace is used only once).

Also, if you would like, you may delete replacement of args with arguments (and have written args) or let trace items lowercase, as is in default.

Trace parts class and function may be united into method. But it is not neccessary, of course.

Example comes from my localhost private testing project (and your file name may be different).

Václav
  • 527
  • 7
  • 28
-4

If you can get away with var_dump() an easy solution is:

try {
   ...
} catch (Exception $e)
   var_dump($e->getTrace());
}

Stolen from this great answer by Andre

Community
  • 1
  • 1
Josh Johnson
  • 10,729
  • 12
  • 60
  • 83