Skip to content

Commit 1ce02db

Browse files
committed
feat(logger): Add a custom logger, allow output redirection
BREAKING CHANGE
1 parent 3f625c3 commit 1ce02db

25 files changed

+783
-423
lines changed

.gitlab-ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ host_tests:
9393
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_merge_bin.py
9494
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_image_info.py
9595
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_modules.py
96+
- coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_logger.py
9697
# some .coverage files in sub-directories are not collected on some runners, move them firs
9798
- find . -mindepth 2 -type f -name ".coverage*" -print -exec mv --backup=numbered {} . \;
9899

@@ -135,6 +136,7 @@ host_tests_latest_python:
135136
- pytest ${CI_PROJECT_DIR}/test/test_merge_bin.py
136137
- pytest ${CI_PROJECT_DIR}/test/test_image_info.py
137138
- pytest ${CI_PROJECT_DIR}/test/test_modules.py
139+
- pytest ${CI_PROJECT_DIR}/test/test_logger.py
138140
- pytest ${CI_PROJECT_DIR}/test/test_espefuse.py --chip esp32
139141

140142
# A new job "host_test_hsm" is created for the test "test_espsecure_hsm.py" which runs an ubuntu image,
@@ -471,6 +473,7 @@ host_tests_windows:
471473
- python -m coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_image_info.py
472474
- python -m coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_modules.py
473475
- python -m coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_merge_bin.py
476+
- python -m coverage run --parallel-mode -m pytest ${CI_PROJECT_DIR}/test/test_logger.py
474477

475478
target_tests_windows:
476479
extends: .windows_test

docs/en/esptool/flashing-firmware.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ Change ``PORT`` to the name of :ref:`actually used serial port <serial-port>` an
100100
Compressed 169232 bytes to 89490...
101101
Wrote 169232 bytes (89490 compressed) at 0x00010000 in 2.6 seconds (effective 513.0 kbit/s)...
102102
Hash of data verified.
103-
104-
Leaving...
105103
Hard resetting via RTS pin...
106104

107105
It is now possible to unplug the flashed device and repeat the process by connecting another one and running the command again.

docs/en/esptool/scripting.rst

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
.. _scripting:
22

33
Embedding into Custom Scripts
4-
-----------------------------
4+
=============================
55

66
``esptool.py``, ``espefuse.py``, and ``espsecure.py`` can easily be integrated into Python applications or called from other Python scripts.
77

88
While it currently does have a poor Python API, something which `#208 <https://github.com/espressif/esptool/issues/208>`_ will address, it allows for passing CLI arguments to ``esptool.main()``. This workaround makes integration very straightforward as you can pass exactly the same arguments as you would on the CLI:
99

1010
.. code-block:: python
1111
12+
import esptool
13+
1214
command = ['--baud', '460800', 'read_flash', '0', '0x200000', 'flash_contents.bin']
13-
print('Using command %s' % ' '.join(command))
15+
print("Using command ", " ".join(command))
1416
esptool.main(command)
1517
1618
@@ -63,3 +65,63 @@ The following is an example on how to use esptool as a Python module and leverag
6365
6466
# Reset the chip out of bootloader mode
6567
esp.hard_reset()
68+
69+
70+
.. _logging:
71+
72+
Redirecting Output with a Custom Logger
73+
---------------------------------------
74+
75+
Esptool allows redirecting output by implementing a custom logger class. This can be useful when integrating esptool with graphical user interfaces or other systems where the default console output is not appropriate. Below is an example demonstrating how to create and use a custom logger:
76+
77+
.. code-block:: python
78+
79+
from esptool.logger import log, TemplateLogger
80+
81+
class CustomLogger(TemplateLogger):
82+
log_to_file = True
83+
log_file = "esptool.log"
84+
85+
def print(self, message, *args, **kwargs):
86+
# Print to console
87+
print(f"[CustomLogger]: {message}", *args, **kwargs)
88+
# Optionally log to a file
89+
if self.log_to_file:
90+
with open(self.log_file, "a") as log:
91+
log.write(f"{message}\n")
92+
93+
def note(self, message):
94+
self.print(f"NOTE: {message}")
95+
96+
def warning(self, message):
97+
self.print(f"WARNING: {message}")
98+
99+
def error(self, message):
100+
self.print(message, file=sys.stderr)
101+
102+
def print_overwrite(self, message):
103+
# Overwriting not needed, print normally
104+
self.print(message)
105+
106+
def set_progress(self, percentage):
107+
# Progress updates not needed, pass
108+
pass
109+
110+
# Replace the default logger with the custom logger
111+
log.set_logger(CustomLogger())
112+
113+
# From now on, all esptool output will be redirected through the custom logger
114+
# Your code here ...
115+
116+
In this example, the ``CustomLogger`` class provides additional functionality such as logging messages to a file, which the original ``EsptoolLogger`` (imported from ``esptool.logger`` as an initiated object ``log``) doesn't. The ``EsptoolLogger.set_logger()`` method is used to replace the default logger with the custom logger.
117+
118+
To ensure compatibility with esptool, the custom logger should re-implement (or inherit) the following methods from the original ``EsptoolLogger`` class (see the reference implementation `here <https://github.com/espressif/esptool/blob/master/esptool/logger.py>`__), this is enforced by the ``TemplateLogger`` abstract class:
119+
120+
- ``print``: Handles plain message logging.
121+
- ``note``: Logs informational messages.
122+
- ``warning``: Logs warning messages.
123+
- ``error``: Logs error messages.
124+
- ``print_overwrite``: Handles message overwriting (can be a simple ``print()`` if overwriting is not needed).
125+
- ``set_progress``: Handles percentage updates of long-running operations - ``write_flash``, ``read_flash``, and ``dump_mem`` (useful for GUI visualisation, e.g. as a progress bar).
126+
127+
These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all calls to the logger should be made using ``log.print()`` (or the respective method, such as ``log.info()`` or ``log.warning()``) instead of the standard ``print()`` function to ensure the output is routed through the custom logger. This ensures consistency and allows the custom logger to handle all output appropriately. You can further customize this logger to fit your application's needs, such as integrating with GUI components or advanced logging frameworks.

docs/en/migration-guide.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,22 @@ The output format of the :ref:`image_info <image-info>` command has been **updat
2121

2222
1. Update any scripts or tools that parse the ``image_info`` output to use the new format
2323
2. Remove any ``--version`` arguments from ``image_info`` commands
24+
25+
Output Logging
26+
**************
27+
28+
The esptool ``v5`` release introduces a centralized logging mechanism to improve output management and allow redirection.
29+
30+
**Key Changes:**
31+
32+
- All esptool output is now routed through an ``EsptoolLogger`` class.
33+
- The output can include ANSI color codes for better readability.
34+
- Custom loggers can be implemented to redirect output to files or other destinations.
35+
36+
**Migration Steps:**
37+
38+
1. If your scripts rely on direct ``print()`` statements, update them to use the centralized logger for consistent output. Calls to the logger should be made using ``log.print()`` (or the respective method, such as ``log.info()``, ``log.warning()``, or ``log.error()``).
39+
2. Refer to the provided documentation to implement custom loggers as needed.
40+
3. Update GUIs or tools to leverage the progress tracking API for better user feedback during lengthy operations.
41+
42+
See the :ref:`logging <logging>` section for more details on available logger methods and custom logger implementation.

0 commit comments

Comments
 (0)