Skip to content

Commit 357a6b6

Browse files
committed
Describe how to get a stack trace with Symfony
As a maintainer, I often struggle to get stack traces in bug reports. I miss a good resource to describe how to do it with Symfony. This aims at showing developers how to do that, but also to help them understand more about stack traces.
1 parent 510ed2d commit 357a6b6

File tree

5 files changed

+187
-1
lines changed

5 files changed

+187
-1
lines changed
737 KB
Loading

contributing/code/bugs.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ If your problem definitely looks like a bug, report it using the official bug
3333
* Give as much detail as possible about your environment (OS, PHP version,
3434
Symfony version, enabled extensions, ...);
3535

36-
* If you want to provide a stack trace you got on an HTML page, be sure to
36+
* If there was an exception, please provide the :doc:`stack trace
37+
</contributing/code/stack_trace>` for that exception.
38+
If you want to provide a stack trace you got on an HTML page, be sure to
3739
provide the plain text version, which should appear at the bottom of the
3840
page. *Do not* provide it as a screenshot, since search engines will not be
3941
able to index the text inside them. Same goes for errors encountered in a

contributing/code/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Contributing Code
55
:maxdepth: 2
66

77
bugs
8+
stack_trace
89
reproducer
910
pull_requests
1011
maintenance

contributing/code/stack_trace.rst

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
Getting a Stack Trace
2+
=====================
3+
4+
When :doc:`reporting a bug </contributing/code/bugs>` for an
5+
exception, it is instrumental that you provide one or several stack
6+
traces. To understand why, you first have to understand what a stack
7+
trace is, and how it can be useful to you as a developer.
8+
9+
Anatomy of a Stack Trace
10+
------------------------
11+
12+
A stack trace is called that way because it allows one to see a trail of
13+
function calls leading to a point in code since the beginning of the
14+
program. That point is not necessarily an exception. For instance, you
15+
can use the native PHP function ``debug_print_backtrace()`` to get such
16+
a trace. For each line in the trace, you get a file and a function or
17+
method call, and the line number for that call. This is often of great
18+
help for understanding the flow of your program and how it can end up in
19+
unexpected places, such as lines of code where exceptions are thrown.
20+
21+
Stack Traces and Exceptions
22+
---------------------------
23+
24+
In PHP, every exception comes with it own stack trace, which is
25+
displayed by default if the exception is not caught. When using Symfony,
26+
such exceptions go through a custom exception handler, which enhances
27+
them in various ways before displaying them according to the current
28+
Server API (CLI or not).
29+
This means a better way to get a stack trace when you do need the
30+
program to continue is to throw an exception, as follows:
31+
``throw new \Exception();``
32+
33+
Nested Exceptions
34+
-----------------
35+
36+
When programs get bigger, complexity is often tackled with layers of
37+
architecture that need to be kept separate. For instance, if you have an
38+
web application that makes a call to a remote API, it might be good to
39+
wrap exceptions thrown when making that call with exceptions that have
40+
special meaning in your domain, and to build appropriate HTTP exceptions
41+
from those. Exceptions can be nested by using the ``$previous``
42+
argument that appears last in the signature of the ``Exception`` class:
43+
``public __construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )``
44+
This means that sometimes, when you get an exception from an
45+
application, you might actually get several of them.
46+
47+
What to look for in a Stack Trace
48+
---------------------------------
49+
50+
When using a library, you will call code that you did not write. When
51+
using a framework, it is the opposite: because you follow the
52+
conventions of the framework, `the framework finds your code and calls
53+
it <https://en.wikipedia.org/wiki/Inversion_of_control>`_, and does
54+
things for you beforehand, like routing or access control.
55+
Symfony being both a framework and library of components, it calls your
56+
code and then your code might call it. This means you will always have
57+
at least 2 parts, very often 3 in your stack traces when using Symfony:
58+
a part that starts in one of the entrypoints of the framework
59+
(``bin/console`` or ``public/index.php`` in most cases), and ends when
60+
reaching your code, most times in a command or in an action found under
61+
``src``. Then, either the exception is thrown in your code or in
62+
libraries you call. If it is the latter, there should be a third part in
63+
the stack trace with calls made in files under ``vendor``. Before
64+
landing in that directory, code goes through numerous review processes
65+
and CI pipelines, which means it should be less likely to be the source
66+
of the issue than code from your application, so it is important that
67+
you focus first on lines starting with ``src``, and look for anything
68+
suspicious or unexpected, like method calls that are not supposed to
69+
happen.
70+
71+
Next, you can have a look at what packages are involved. Files under
72+
``vendor`` are organized by Composer in the following way:
73+
``vendor/acme/router`` where ``acme`` is the vendor, ``router`` the
74+
library and ``acme/router`` the Composer package. If you plan on
75+
reporting the bug, make sure to report it to the library throwing the
76+
exception. ``composer home acme/router`` should lead you to the right
77+
place for that. As Symfony is a monorepository, use ``composer home
78+
symfony/symfony`` when reporting a bug for any component.
79+
80+
Getting Stack Traces with Symfony
81+
---------------------------------
82+
83+
Now that we have all this in mind, let us see how to get a Stack trace
84+
with Symfony.
85+
86+
Stack Traces in your Web Browser
87+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88+
89+
Several things need to be paid attention to when picking a stack trace
90+
from your web browser:
91+
92+
1. Are there several exceptions? If yes, the most interesting one is
93+
often exception 1/n which, is shown _last_ in the example below (it
94+
is the one marked as exception [1/2]).
95+
2. Top right is a "Stack Traces" button. It shows exceptions in plain
96+
text, so that you can easily share them in e.g. bug reports. Make
97+
sure to remove any sensitive information before doing so.
98+
3. You may notice there is a logs tab too; this tab does not have to do
99+
with stack traces, it only contains logs produced in arbitrary places
100+
in your application. They may or may not relate to the exception you
101+
are getting, but are not what the term "stack trace" refers to.
102+
103+
.. image:: /_images/contributing/code/stack-trace.gif
104+
:align: center
105+
:class: with-browser
106+
107+
Stack Traces in the CLI
108+
~~~~~~~~~~~~~~~~~~~~~~~
109+
110+
Exceptions might occur when running a Symfony command. By default, only
111+
the message is shown because it is often enough to understand what is
112+
going on:
113+
114+
.. code-block:: terminal
115+
116+
$ php bin/console debug:exception
117+
118+
119+
Command "debug:exception" is not defined.
120+
121+
Did you mean one of these?
122+
debug:autowiring
123+
debug:config
124+
debug:container
125+
debug:event-dispatcher
126+
debug:form
127+
debug:router
128+
debug:translation
129+
debug:twig
130+
131+
132+
If that is not the case, you can obtain a stack trace by increasing the
133+
verbosity with ``--verbose``:
134+
135+
.. code-block:: terminal
136+
137+
$ php bin/console --verbose debug:exception
138+
139+
In Application.php line 644:
140+
141+
[Symfony\Component\Console\Exception\CommandNotFoundException]
142+
Command "debug:exception" is not defined.
143+
144+
Did you mean one of these?
145+
debug:autowiring
146+
debug:config
147+
debug:container
148+
debug:event-dispatcher
149+
debug:form
150+
debug:router
151+
debug:translation
152+
debug:twig
153+
154+
155+
Exception trace:
156+
at /home/greg/dev/sf-demo/vendor/symfony/console/Application.php:644
157+
Symfony\Component\Console\Application->find() at /home/greg/dev/sf-demo/vendor/symfony/framework-bundle/Console/Application.php:116
158+
Symfony\Bundle\FrameworkBundle\Console\Application->find() at /home/greg/dev/sf-demo/vendor/symfony/console/Application.php:228
159+
Symfony\Component\Console\Application->doRun() at /home/greg/dev/sf-demo/vendor/symfony/framework-bundle/Console/Application.php:82
160+
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /home/greg/dev/sf-demo/vendor/symfony/console/Application.php:140
161+
Symfony\Component\Console\Application->run() at /home/greg/dev/sf-demo/bin/console:42
162+
163+
Stack Traces and API Calls
164+
~~~~~~~~~~~~~~~~~~~~~~~~~~
165+
166+
When getting an exception from an API, you might not get a stack trace,
167+
or it might be displayed in a way that is not suitable for sharing.
168+
Luckily, you can obtain a plain text stack trace by using the profiler.
169+
To find the profile, you can have a look at ``X-Debug-Token-Link``
170+
response headers:
171+
172+
.. code-block:: terminal
173+
174+
$ curl --head http://localhost:8000/api/posts/1
175+
… more headers
176+
X-Debug-Token: 110e1e
177+
X-Debug-Token-Link: http://localhost:8000/_profiler/110e1e
178+
X-Robots-Tag: noindex
179+
X-Previous-Debug-Token: 209101
180+
181+
Following that link will lead you to a page very similar to the one
182+
described above in `Stack Traces in your Web Browser`_.

contributing/map.rst.inc

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* **Code**
99

1010
* :doc:`Bugs </contributing/code/bugs>`
11+
* :doc:`Getting a Stack Trace </contributing/code/stack_trace>`
1112
* :doc:`Pull Requests </contributing/code/pull_requests>`
1213
* :doc:`Reviewing Issues and Pull Requests </contributing/community/reviews>`
1314
* :doc:`Maintenance </contributing/code/maintenance>`

0 commit comments

Comments
 (0)