Wydrukuj stos wywołań PHP


Odpowiedzi:


123

Jeśli chcesz wygenerować ślad, szukasz debug_backtracei / lub debug_print_backtrace.


Pierwszy, na przykład, da ci tablicę taką jak ta (cytując instrukcję) :

array(2) {
[0]=>
array(4) {
    ["file"] => string(10) "/tmp/a.php"
    ["line"] => int(10)
    ["function"] => string(6) "a_test"
    ["args"]=>
    array(1) {
      [0] => &string(6) "friend"
    }
}
[1]=>
array(4) {
    ["file"] => string(10) "/tmp/b.php"
    ["line"] => int(2)
    ["args"] =>
    array(1) {
      [0] => string(10) "/tmp/a.php"
    }
    ["function"] => string(12) "include_once"
  }
}


Najwyraźniej nie będą opróżniać bufora we / wy, ale możesz to zrobić samodzielnie, za pomocą flushi / lub ob_flush.

(zobacz stronę podręcznika pierwszego, aby dowiedzieć się, dlaczego „i / lub” ;-))


7
regularnie powoduje to brak pamięci w php. Polecam rozwiązanie Tobiasza.
peedee

Jeśli trudno ci przeczytać / zrozumieć, polecam również rozwiązanie
Tobiasza

1
@peedee wystarczy podać jeden z opcjonalnych DEBUG_BACKTRACE_IGNORE_ARGSparametrów; dzięki czemu są funkcjonalnie równoważne z(new \Exception())->getTraceAsString()

565

Bardziej czytelny niż debug_backtrace():

$e = new \Exception;
var_dump($e->getTraceAsString());

#2 /usr/share/php/PHPUnit/Framework/TestCase.php(626): SeriesHelperTest->setUp()
#3 /usr/share/php/PHPUnit/Framework/TestResult.php(666): PHPUnit_Framework_TestCase->runBare()
#4 /usr/share/php/PHPUnit/Framework/TestCase.php(576): PHPUnit_Framework_TestResult->run(Object(SeriesHelperTest))
#5 /usr/share/php/PHPUnit/Framework/TestSuite.php(757): PHPUnit_Framework_TestCase->run(Object(PHPUnit_Framework_TestResult))
#6 /usr/share/php/PHPUnit/Framework/TestSuite.php(733): PHPUnit_Framework_TestSuite->runTest(Object(SeriesHelperTest), Object(PHPUnit_Framework_TestResult))
#7 /usr/share/php/PHPUnit/TextUI/TestRunner.php(305): PHPUnit_Framework_TestSuite->run(Object(PHPUnit_Framework_TestResult), false, Array, Array, false)
#8 /usr/share/php/PHPUnit/TextUI/Command.php(188): PHPUnit_TextUI_TestRunner->doRun(Object(PHPUnit_Framework_TestSuite), Array)
#9 /usr/share/php/PHPUnit/TextUI/Command.php(129): PHPUnit_TextUI_Command->run(Array, true)
#10 /usr/bin/phpunit(53): PHPUnit_TextUI_Command::main()
#11 {main}"

50
Cholera, jest o wiele lepiej, dlaczego nie mogliby zrobić z tego domyślnego wyjścia dla debug_print_backtrace ()? Mógłbym dodać parametr boolowski „returnTrace” dla tych, którzy chcą go w zmiennej, bez echa, i byłoby idealnie!
jurchiks

1
Nie wiem, ile miesięcy próbowałem wymyślić, jak to zrobić, nigdy nie myślałem, że to zadziała
WojonsTech

To rozwiązanie wydaje się również zajmować mniej pamięci niż przechwytywanie danych wyjściowych debug_backtrace () jako tablicy, a następnie drukowanie ich za pomocą print_r (), co robiłem, dopóki tego nie zobaczyłem!
Peter

5
Szukałem sposobu ograniczenia, debug_backtraceaby zwrócić tylko pierwszy poziom w stacktrace - to rozwiązanie działa dla mnie. Dziękuję Ci!
ankr

3
@Andrew print_rzachowa wszystkie wiadomości.
mopo922,

41

Aby zarejestrować ślad

$e = new Exception;
error_log(var_export($e->getTraceAsString(), true));

Dzięki @Tobiasz


35

Backtrace zrzuca całe śmieci, których nie potrzebujesz. Trwa bardzo długo, trudny do odczytania. Wszystko, czego zwykle pragniesz, to „jak się nazywa, skąd?” Oto proste rozwiązanie funkcji statycznej. Zwykle umieszczam go w klasie o nazwie „debugowanie”, która zawiera wszystkie moje funkcje narzędzia do debugowania.

class debugUtils {
    public static function callStack($stacktrace) {
        print str_repeat("=", 50) ."\n";
        $i = 1;
        foreach($stacktrace as $node) {
            print "$i. ".basename($node['file']) .":" .$node['function'] ."(" .$node['line'].")\n";
            $i++;
        }
    } 
}

Nazywasz to tak:

debugUtils::callStack(debug_backtrace());

I produkuje takie dane wyjściowe:

==================================================
 1. DatabaseDriver.php::getSequenceTable(169)
 2. ClassMetadataFactory.php::loadMetadataForClass(284)
 3. ClassMetadataFactory.php::loadMetadata(177)
 4. ClassMetadataFactory.php::getMetadataFor(124)
 5. Import.php::getAllMetadata(188)
 6. Command.php::execute(187)
 7. Application.php::run(194)
 8. Application.php::doRun(118)
 9. doctrine.php::run(99)
 10. doctrine::include(4)
==================================================


33

Dziwne, że nikt nie opublikował w ten sposób:

debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

To faktycznie drukuje ślad bez śmieci - tylko jaka metoda została wywołana i gdzie.


2
Rzeczywiście, naprawdę równoważne głównemu głosowanemu rozwiązaniu i krótsze. Dzięki
brunetton,

9

Jeśli chcesz śledzenia stosu, który wygląda bardzo podobnie do tego, jak php formatuje wyjątek śledzenia stosu, niż użyj tej funkcji, napisałem:

function debug_backtrace_string() {
    $stack = '';
    $i = 1;
    $trace = debug_backtrace();
    unset($trace[0]); //Remove call to this function from stack trace
    foreach($trace as $node) {
        $stack .= "#$i ".$node['file'] ."(" .$node['line']."): "; 
        if(isset($node['class'])) {
            $stack .= $node['class'] . "->"; 
        }
        $stack .= $node['function'] . "()" . PHP_EOL;
        $i++;
    }
    return $stack;
} 

Zwróci to ślad stosu sformatowany w następujący sposób:

#1 C:\Inetpub\sitename.com\modules\sponsors\class.php(306): filePathCombine()
#2 C:\Inetpub\sitename.com\modules\sponsors\class.php(294): Process->_deleteImageFile()
#3 C:\Inetpub\sitename.com\VPanel\modules\sponsors\class.php(70): Process->_deleteImage()
#4 C:\Inetpub\sitename.com\modules\sponsors\process.php(24): Process->_delete() 

2
lub po prostu$e = new Exception; echo $e->getTraceAsString();
Brad Kent

Brad, to rozwiązanie nie usuwa ostatniego elementu ze śledzenia stosu, więc nie wyświetlasz elementu śledzenia spowodowanego przez nowy wyjątek
TroySteven



4

phptrace to świetne narzędzie do drukowania stosu PHP w dowolnym momencie, bez potrzeby instalowania żadnych rozszerzeń.

Istnieją dwie główne funkcje phptrace: po pierwsze, stos wywołań drukowania PHP, które nie muszą niczego instalować, po drugie, śledzenie przepływów wykonania php, które wymaga zainstalowania rozszerzenia, które dostarcza.

następująco:

$ ./phptrace -p 3130 -s             # phptrace -p <PID> -s
phptrace 0.2.0 release candidate, published by infra webcore team
process id = 3130
script_filename = /home/xxx/opt/nginx/webapp/block.php
[0x7f27b9a99dc8]  sleep /home/xxx/opt/nginx/webapp/block.php:6
[0x7f27b9a99d08]  say /home/xxx/opt/nginx/webapp/block.php:3
[0x7f27b9a99c50]  run /home/xxx/opt/nginx/webapp/block.php:10 

Czy jest wersja Windows?
Johnny

Podoba mi się, że pokazany jest tutaj adres pamięci. Może to być pomocne
Tyler Miles,

3

Służy debug_backtracedo uzyskania informacji o tym, jakie funkcje i metody zostały wywołane oraz jakie pliki zostały zawarte, które doprowadziły do ​​momentu, w którym debug_backtracezostały wywołane.





1

Rozwiązanie Walltearer jest doskonałe, szczególnie jeśli jest umieszczone w tagu „pre”:

<pre>
<?php debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); ?>
</pre>

- który określa połączenia na osobnych liniach, starannie ponumerowanych


0

Zaadaptowałem powyższą odpowiedź Dona Briggsa, aby używać wewnętrznego rejestrowania błędów zamiast drukowania publicznego, co może stanowić poważny problem podczas pracy na serwerze na żywo. Ponadto dodano kilka dodatkowych modyfikacji, takich jak opcja włączenia pełnej ścieżki pliku zamiast podstawowej nazwy (ponieważ mogą istnieć pliki o tej samej nazwie w różnych ścieżkach), a także (dla tych, którzy tego wymagają) kompletny plik wyjściowy stosu węzłów:

class debugUtils {
    public static function callStack($stacktrace) {
        error_log(str_repeat("=", 100));
        $i = 1;
        foreach($stacktrace as $node) {
            // uncomment next line to debug entire node stack
            // error_log(print_r($node, true));
            error_log( $i . '.' . ' file: ' .$node['file'] . ' | ' . 'function: ' . $node['function'] . '(' . ' line: ' . $node['line'] . ')' );
            $i++;
        }
        error_log(str_repeat("=", 100));
    } 
}

// call debug stack
debugUtils::callStack(debug_backtrace());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.