Current traceback seems optimized for terminal. See this example.
Output from traceback.print_exception(e)
:
Traceback (most recent call last):
File "/Users/inada-n/notes/2024/mini_traceback/sample.py", line 8, in main
conn = engine.connect()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 3280, in connect
return self._connection_cls(self)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 148, in __init__
Connection._handle_dbapi_exception_noconnection(
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 2444, in _handle_dbapi_exception_noconnection
raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 146, in __init__
self._dbapi_connection = engine.raw_connection()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py", line 3304, in raw_connection
return self.pool.connect()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 449, in connect
return _ConnectionFairy._checkout(self)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 1263, in _checkout
fairy = _ConnectionRecord.checkout(pool)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 712, in checkout
rec = pool._do_get()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/impl.py", line 179, in _do_get
with util.safe_reraise():
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
raise exc_value.with_traceback(exc_tb)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/impl.py", line 177, in _do_get
return self._create_connection()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 390, in _create_connection
return _ConnectionRecord(self)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 674, in __init__
self.__connect()
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 900, in __connect
with util.safe_reraise():
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
raise exc_value.with_traceback(exc_tb)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py", line 896, in __connect
self.dbapi_connection = connection = pool._invoke_creator(self)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/create.py", line 643, in connect
return dialect.connect(*cargs, **cparams)
File "/Users/inada-n/notes/2024/mini_traceback/.venv/lib/python3.12/site-packages/sqlalchemy/engine/default.py", line 617, in connect
return self.loaded_dbapi.connect(*cargs, **cparams)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file
(Background on this error at: https://sqlalche.me/e/20/e3q8)
Note that:
- If I don’t set
chain=False
, this traceback becomes two times longer. - I used
PYTHONNODEBUGRANGES=1
to stop showing range markers.
Although this traceback is good for terminal, this is too large for structured logging + web based log viewer because:
- On web-based log viewer, users see the top first and scroll down. So the exception and the most recent call should be first.
- On textural log, one line in traceback is one line in the log. But structured logging with JSON lines can creates more than 8000bytes when traceback in the log record.
So I tried to create toy project named minitraceback. It creates shorter traceback with reversed order:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file
(Background on this error at: https://sqlalche.me/e/20/e3q8)
Traceback (most recent call first):
sqlalchemy/engine/default.py:617 connect
sqlalchemy/engine/create.py:643 connect
sqlalchemy/pool/base.py:896 __connect
sqlalchemy/util/langhelpers.py:146 __exit__
sqlalchemy/pool/base.py:900 __connect
sqlalchemy/pool/base.py:674 __init__
sqlalchemy/pool/base.py:390 _create_connection
sqlalchemy/pool/impl.py:177 _do_get
sqlalchemy/util/langhelpers.py:146 __exit__
sqlalchemy/pool/impl.py:179 _do_get
sqlalchemy/pool/base.py:712 checkout
sqlalchemy/pool/base.py:1263 _checkout
sqlalchemy/pool/base.py:449 connect
sqlalchemy/engine/base.py:3304 raw_connection
sqlalchemy/engine/base.py:146 __init__
sqlalchemy/engine/base.py:2444 _handle_dbapi_exception_noconnection
sqlalchemy/engine/base.py:148 __init__
sqlalchemy/engine/base.py:3280 connect
sample_mini.py:8 main
This looks good for my personal use. But I think shorter&reversed traceback in stdlib traceback would be better because some PaaS/Observability platforms may parse the traceback.
How do you think adding these options to print/format traceback functions in the traceback module?
- Add
reverse=False
option.- If it is true, traceback is shown in “most recent call first” order.
- Add
showsource=True
option.- If it is false, source code is not shown.
- Add
remove_path_prefix=False
option.- If it is true, remove the longest
sys.path
prefix from the file path.
- If it is true, remove the longest