feat(verbosity): Allow setting silent or verbose output levels

This commit is contained in:
Radim Karniš
2025-05-21 16:05:53 +02:00
parent 61464101c6
commit 90e3770c67
6 changed files with 114 additions and 24 deletions

View File

@@ -468,11 +468,11 @@ Here is a sample extract, showing a READ_REG command and response:
:: ::
TRACE +0.000 command op=0x0a data len=4 wait_response=1 timeout=3.000 data=1400f43f TRACE +0.000 --- Cmd READ_REG (0x0a) | data_len 4 | wait_response 1 | timeout 3.000 | data 00100040 ---
TRACE +0.000 Write 14 bytes: c0000a0400000000001400f43fc0 TRACE +0.000 Write 14 bytes: c0000a04000000000000100040c0
TRACE +0.005 Read 1 bytes: c0 TRACE +0.046 Read 1 bytes: c0
TRACE +0.000 Read 11 bytes: 010a0200620100000000c0 TRACE +0.000 Read 11 bytes: 010a0200090000000000c0
TRACE +0.000 Received full packet: 010a0200620100000000 TRACE +0.000 Received full packet: 010a0200090000000000
The +X.XXX value is the time delta (in seconds) since the last trace line. The +X.XXX value is the time delta (in seconds) since the last trace line.
@@ -485,18 +485,18 @@ Here is a second example showing part of the initial synchronization sequence (l
:: ::
TRACE +0.000 Write 46 bytes: TRACE +0.000 Write 46 bytes:
c000082400000000 0007071220555555 | ...$........ UUU c000082400000000 0007071220555555 | ...$........ UUU
5555555555555555 5555555555555555 | UUUUUUUUUUUUUUUU 5555555555555555 5555555555555555 | UUUUUUUUUUUUUUUU
5555555555555555 5555555555c0 | UUUUUUUUUUUUU. 5555555555555555 5555555555c0 | UUUUUUUUUUUUU.
TRACE +0.011 Read 1 bytes: c0 TRACE +0.012 Read 1 bytes: c0
TRACE +0.000 Read 63 bytes: TRACE +0.000 Read 63 bytes:
0108040007122055 00000000c0c00108 | ...... U........ 0108040007071220 00000000c0c00108 | ....... ........
0400071220550000 0000c0c001080400 | .... U.......... 0400070712200000 0000c0c001080400 | ..... ..........
0712205500000000 c0c0010804000712 | .. U............ 0707122000000000 c0c0010804000707 | ... ............
205500000000c0c0 01080400071220 | U............ 122000000000c0c0 01080400070712 | . .............
TRACE +0.000 Received full packet: 010804000712205500000000 TRACE +0.000 Received full packet: 010804000707122000000000
TRACE +0.000 Received full packet: 010804000712205500000000 TRACE +0.000 Received full packet: 010804000707122000000000
.. important:: .. important::

View File

@@ -162,3 +162,30 @@ at least one FilterValue for each specified FilterType to be considered. Example
* ``--port-filter serial=7c98d1065267ee11bcc4c8ab93cd958c`` matches ports where the serial number contains the specified text. * ``--port-filter serial=7c98d1065267ee11bcc4c8ab93cd958c`` matches ports where the serial number contains the specified text.
See also the `Espressif USB customer-allocated PID repository <https://github.com/espressif/usb-pids>`_ See also the `Espressif USB customer-allocated PID repository <https://github.com/espressif/usb-pids>`_
Output Verbosity
----------------
Output verbosity can be controlled using the ``--verbose`` and ``--silent`` flags.
Verbose output: ``--verbose``, ``-v``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _verbose:
The ``--verbose``, ``-v`` flag can be used to show all output without any overwriting or collapsing stages into a single line:
.. code-block:: bash
esptool.py --verbose flash-id
See :ref:`the trace option <tracing-communications>` if you want to dump all serial interactions to the standard output for debugging purposes.
Silent output: ``--silent``, ``-s``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _silent:
The ``--silent``, ``-s`` flag can be used to limit the output to errors only:
.. code-block:: bash
esptool.py -s write-flash 0x0 image.bin

View File

@@ -328,6 +328,10 @@ Esptool allows redirecting output by implementing a custom logger class. This ca
percent = f"{100 * (cur_iter / float(total_iters)):.1f}" percent = f"{100 * (cur_iter / float(total_iters)):.1f}"
self.print(f"Finished: {percent}%") self.print(f"Finished: {percent}%")
def set_verbosity(self, verbosity):
# Set verbosity level not needed in this example
pass
# Replace the default logger with the custom logger # Replace the default logger with the custom logger
log.set_logger(CustomLogger()) log.set_logger(CustomLogger())
@@ -344,9 +348,10 @@ To ensure compatibility with esptool, the custom logger should re-implement (or
- ``error``: Logs error messages. - ``error``: Logs error messages.
- ``stage``: Starts or ends a collapsible output stage. - ``stage``: Starts or ends a collapsible output stage.
- ``progress_bar``: Displays a progress bar. - ``progress_bar``: Displays a progress bar.
- ``set_verbosity``: Sets the verbosity level for logging.
.. autoclass:: esptool.logger.EsptoolLogger .. autoclass:: esptool.logger.EsptoolLogger
:members: print, note, warning, error, stage, progress_bar :members: print, note, warning, error, stage, progress_bar, set_verbosity
:member-order: bysource :member-order: bysource
These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all output printing 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. These methods are essential for maintaining proper integration and behavior with esptool. Additionally, all output printing 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.

View File

@@ -364,6 +364,18 @@ def check_flash_size(esp: ESPLoader, address: int, size: int) -> None:
is_flag=True, is_flag=True,
help="Enable trace-level output of esptool.py interactions.", help="Enable trace-level output of esptool.py interactions.",
) )
@click.option(
"--verbose",
"-v",
is_flag=True,
help="Print all output, disable collapsing output stages.",
)
@click.option(
"--silent",
"-s",
is_flag=True,
help="Silence all output except for errors.",
)
@click.option( @click.option(
"--override-vddsdio", "--override-vddsdio",
type=click.Choice(ESP32ROM.OVERRIDE_VDDSDIO_CHOICES), type=click.Choice(ESP32ROM.OVERRIDE_VDDSDIO_CHOICES),
@@ -383,6 +395,18 @@ def cli(
): ):
ctx.ensure_object(dict) ctx.ensure_object(dict)
ctx.obj.update(kwargs) ctx.obj.update(kwargs)
if ctx.obj["verbose"] and ctx.obj["silent"]:
raise FatalError(
"Cannot use both --verbose and --silent options at the same time."
)
if ctx.obj["trace"] and ctx.obj["silent"]:
raise FatalError(
"Cannot use both --trace and --silent options at the same time."
)
if ctx.obj["verbose"]:
log.set_verbosity("verbose")
elif ctx.obj["silent"]:
log.set_verbosity("silent")
ctx.obj["invoked_subcommand"] = ctx.invoked_subcommand ctx.obj["invoked_subcommand"] = ctx.invoked_subcommand
ctx.obj["esp"] = getattr(ctx, "esp", None) ctx.obj["esp"] = getattr(ctx, "esp", None)
log.print(f"esptool.py v{__version__}") log.print(f"esptool.py v{__version__}")

View File

@@ -57,6 +57,13 @@ class TemplateLogger(ABC):
""" """
pass pass
@abstractmethod
def set_verbosity(self, verbosity: str):
"""
Set the verbosity level.
"""
pass
class EsptoolLogger(TemplateLogger): class EsptoolLogger(TemplateLogger):
ansi_red: str = "" ansi_red: str = ""
@@ -72,6 +79,8 @@ class EsptoolLogger(TemplateLogger):
_kept_lines: list[str] = [] _kept_lines: list[str] = []
_smart_features: bool = False _smart_features: bool = False
_verbosity: str | None = None
_print_anyway: bool = False
def __new__(cls): def __new__(cls):
""" """
@@ -79,7 +88,7 @@ class EsptoolLogger(TemplateLogger):
""" """
if not hasattr(cls, "instance"): if not hasattr(cls, "instance"):
cls.instance = super(EsptoolLogger, cls).__new__(cls) cls.instance = super(EsptoolLogger, cls).__new__(cls)
cls.instance._set_smart_features() cls.instance.set_verbosity("auto")
return cls.instance return cls.instance
@classmethod @classmethod
@@ -138,6 +147,8 @@ class EsptoolLogger(TemplateLogger):
""" """
Log a plain message. Count newlines if in a collapsing stage. Log a plain message. Count newlines if in a collapsing stage.
""" """
if self._verbosity == "silent" and not self._print_anyway:
return
if self._stage_active: if self._stage_active:
# Count the number of newlines in the message # Count the number of newlines in the message
message = "".join(map(str, args)) message = "".join(map(str, args))
@@ -145,12 +156,12 @@ class EsptoolLogger(TemplateLogger):
if kwargs.get("end", "\n") == "\n": if kwargs.get("end", "\n") == "\n":
self._newline_count += 1 self._newline_count += 1
print(*args, **kwargs) print(*args, **kwargs)
self._print_anyway = False
def note(self, message: str): def note(self, message: str):
""" """
Log a Note: message in blue and white. Log a Note: message in blue and white.
""" """
formatted_message = f"{self.ansi_blue}Note:{self.ansi_normal} {message}" formatted_message = f"{self.ansi_blue}Note:{self.ansi_normal} {message}"
if self._stage_active: if self._stage_active:
self._kept_lines.append(formatted_message) self._kept_lines.append(formatted_message)
@@ -160,7 +171,6 @@ class EsptoolLogger(TemplateLogger):
""" """
Log a Warning: message in yellow and white. Log a Warning: message in yellow and white.
""" """
formatted_message = f"{self.ansi_yellow}Warning:{self.ansi_normal} {message}" formatted_message = f"{self.ansi_yellow}Warning:{self.ansi_normal} {message}"
if self._stage_active: if self._stage_active:
self._kept_lines.append(formatted_message) self._kept_lines.append(formatted_message)
@@ -170,8 +180,8 @@ class EsptoolLogger(TemplateLogger):
""" """
Log an error message in red to stderr. Log an error message in red to stderr.
""" """
formatted_message = f"{self.ansi_red}{message}{self.ansi_normal}" formatted_message = f"{self.ansi_red}{message}{self.ansi_normal}"
self._print_anyway = True
self.print(formatted_message, file=sys.stderr) self.print(formatted_message, file=sys.stderr)
def stage(self, finish: bool = False): def stage(self, finish: bool = False):
@@ -182,7 +192,6 @@ class EsptoolLogger(TemplateLogger):
Warnings and notes will be saved and printed at the end of the stage. Warnings and notes will be saved and printed at the end of the stage.
If terminal doesn't support ANSI escape codes, no collapsing happens. If terminal doesn't support ANSI escape codes, no collapsing happens.
""" """
if finish: if finish:
if not self._stage_active: if not self._stage_active:
return return
@@ -219,7 +228,6 @@ class EsptoolLogger(TemplateLogger):
Call in a loop to print a progress bar overwriting itself in place. Call in a loop to print a progress bar overwriting itself in place.
If terminal doesn't support ANSI escape codes, no overwriting happens. If terminal doesn't support ANSI escape codes, no overwriting happens.
""" """
filled = int(bar_length * cur_iter // total_iters) filled = int(bar_length * cur_iter // total_iters)
if filled == bar_length: if filled == bar_length:
bar = "=" * bar_length bar = "=" * bar_length
@@ -238,5 +246,28 @@ class EsptoolLogger(TemplateLogger):
def set_logger(self, new_logger): def set_logger(self, new_logger):
self.__class__ = new_logger.__class__ self.__class__ = new_logger.__class__
def set_verbosity(self, verbosity: str):
"""
Set the verbosity level to one of the following:
- "auto": Enable smart terminal features and colors if supported by the terminal
- "verbose": Enable verbose output (no collapsing output)
- "silent": Disable all output except errors
- "compact": Enable smart terminal features and colors even if not supported
"""
if verbosity == self._verbosity:
return
self._verbosity = verbosity
if verbosity == "auto":
self._set_smart_features()
elif verbosity == "verbose":
self._set_smart_features(override=False)
elif verbosity == "silent":
pass
elif verbosity == "compact":
self._set_smart_features(override=True)
else:
raise ValueError(f"Invalid verbosity level: {verbosity}")
log = EsptoolLogger() log = EsptoolLogger()

View File

@@ -42,6 +42,9 @@ class CustomLogger(TemplateLogger):
): ):
pass pass
def set_verbosity(self, verbosity: str):
pass
# Custom logger that doesn't implement all methods # Custom logger that doesn't implement all methods
class CustomLoggerIncomplete: class CustomLoggerIncomplete: