Skip to content

Commit d0a97ae

Browse files
greg0irexabbuh
authored andcommitted
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 ebbd092 commit d0a97ae

File tree

5 files changed

+195
-1
lines changed

5 files changed

+195
-1
lines changed
737 KB
Loading

contributing/code/bugs.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ 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 and you would like to report it, it is
37+
valuable to provide the :doc:`stack trace
38+
</contributing/code/stack_trace>` for that exception.
39+
If you want to provide a stack trace you got on an HTML page, be sure to
3740
provide the plain text version, which should appear at the bottom of the
3841
page. *Do not* provide it as a screenshot, since search engines will not be
3942
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

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