pw_system: Add no-rpc-logging option for console
- Add --rpc-logging and --no-rpc-logging options to pw-system-console
defaults to True. If False no rpc logging will be started.
- Better --serial-debug logging that shows 3 views of each read/write
event: bytes, characters and lines.
- Fix an exception in console table view if a log message has no extra
metadata.
Change-Id: Ibe507a6a7822c16c91aa36b832ca42d9c8b757b4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/109613
Pigweed-Auto-Submit: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Erik Gilling <konkers@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_console/py/pw_console/pyserial_wrapper.py b/pw_console/py/pw_console/pyserial_wrapper.py
index ac00e05..072d5e4 100644
--- a/pw_console/py/pw_console/pyserial_wrapper.py
+++ b/pw_console/py/pw_console/pyserial_wrapper.py
@@ -24,7 +24,7 @@
_LOG = logging.getLogger('pw_console.serial_debug_logger')
-def _log_hex_strings(data, prefix=''):
+def _log_hex_strings(data: bytes, prefix=''):
"""Create alinged hex number and character view log messages."""
# Make a list of 2 character hex number strings.
hex_numbers = textwrap.wrap(data.hex(), 2)
@@ -51,12 +51,14 @@
hex_numbers_msg,
extra=dict(extra_metadata_fields={
'msg': hex_numbers_msg,
+ 'view': 'hex',
}))
_LOG.debug('%s%s',
prefix,
hex_chars_msg,
extra=dict(extra_metadata_fields={
'msg': hex_chars_msg,
+ 'view': 'chars',
}))
@@ -87,16 +89,28 @@
_LOG.debug('%s%s',
prefix,
data,
- extra=dict(extra_metadata_fields={
- 'mode': 'Read',
- 'bytes': len(data),
- 'msg': str(data),
- }))
+ extra=dict(
+ extra_metadata_fields={
+ 'mode': 'Read',
+ 'bytes': len(data),
+ 'view': 'bytes',
+ 'msg': str(data),
+ }))
_log_hex_strings(data, prefix=prefix)
+ # Print individual lines
+ for line in data.decode(encoding='utf-8',
+ errors='ignore').splitlines():
+ _LOG.debug('%s',
+ line,
+ extra=dict(extra_metadata_fields={
+ 'msg': line,
+ 'view': 'lines',
+ }))
+
return data
- def write(self, data, *args, **kwargs):
+ def write(self, data: bytes, *args, **kwargs):
self.pw_bps_history['write'].log(len(data))
self.pw_bps_history['total'].log(len(data))
@@ -105,11 +119,23 @@
_LOG.debug('%s%s',
prefix,
data,
- extra=dict(extra_metadata_fields={
- 'mode': 'Write',
- 'bytes': len(data),
- 'msg': str(data)
- }))
+ extra=dict(
+ extra_metadata_fields={
+ 'mode': 'Write',
+ 'bytes': len(data),
+ 'view': 'bytes',
+ 'msg': str(data)
+ }))
_log_hex_strings(data, prefix=prefix)
+ # Print individual lines
+ for line in data.decode(encoding='utf-8',
+ errors='ignore').splitlines():
+ _LOG.debug('%s',
+ line,
+ extra=dict(extra_metadata_fields={
+ 'msg': line,
+ 'view': 'lines',
+ }))
+
super().write(data, *args, **kwargs)
diff --git a/pw_console/py/pw_console/widgets/table.py b/pw_console/py/pw_console/widgets/table.py
index 882a0dd..ec863f5 100644
--- a/pw_console/py/pw_console/widgets/table.py
+++ b/pw_console/py/pw_console/widgets/table.py
@@ -94,8 +94,14 @@
return ordered_columns.items()
- def update_metadata_column_widths(self, log: LogLine):
+ def update_metadata_column_widths(self, log: LogLine) -> None:
"""Calculate the max widths for each metadata field."""
+ if log.metadata is None:
+ log.update_metadata()
+ # If extra fields still don't exist, no need to update column widths.
+ if log.metadata is None:
+ return
+
for field_name, value in log.metadata.fields.items():
value_string = str(value)
diff --git a/pw_system/py/pw_system/console.py b/pw_system/py/pw_system/console.py
index fc20df1..6a9fbf9 100644
--- a/pw_system/py/pw_system/console.py
+++ b/pw_system/py/pw_system/console.py
@@ -161,6 +161,11 @@
dest='use_ipython',
help='Use IPython instead of pw_console.')
+ parser.add_argument('--rpc-logging',
+ action=argparse.BooleanOptionalAction,
+ default=True,
+ help='Use pw_rpc based logging.')
+
return parser.parse_args()
@@ -292,6 +297,7 @@
verbose: bool = False,
compiled_protos: Optional[List[ModuleType]] = None,
merge_device_and_host_logs: bool = False,
+ rpc_logging: bool = True,
use_ipython: bool = False) -> int:
"""Starts an interactive RPC console for HDLC."""
# argparse.FileType doesn't correctly handle '-' for binary files.
@@ -370,7 +376,8 @@
# Append compiled log.proto library to avoid include errors when manually
# provided, and shadowing errors due to ordering when the default global
# search path is used.
- compiled_protos.append(log_pb2)
+ if rpc_logging:
+ compiled_protos.append(log_pb2)
compiled_protos.append(unit_test_pb2)
protos.extend(compiled_protos)
protos.append(metric_service_pb2)
@@ -424,7 +431,8 @@
protos,
detokenizer,
timestamp_decoder=timestamp_decoder,
- rpc_timeout_s=5)
+ rpc_timeout_s=5,
+ use_rpc_logging=rpc_logging)
_start_python_terminal(
device=device_client,
diff --git a/pw_system/py/pw_system/device.py b/pw_system/py/pw_system/device.py
index f80aa04..a8bd580 100644
--- a/pw_system/py/pw_system/device.py
+++ b/pw_system/py/pw_system/device.py
@@ -49,7 +49,8 @@
proto_library: List[Union[ModuleType, Path]],
detokenizer: Optional[detokenize.Detokenizer],
timestamp_decoder: Optional[Callable[[int], str]],
- rpc_timeout_s=5):
+ rpc_timeout_s: float = 5,
+ use_rpc_logging: bool = True):
self.channel_id = channel_id
self.protos = proto_library
self.detokenizer = detokenizer
@@ -64,15 +65,27 @@
default_unary_timeout_s=self.rpc_timeout_s,
default_stream_timeout_s=None,
)
- self.client = HdlcRpcClient(
- read,
- self.protos,
- default_channels(write),
- lambda data: self.logger.info("%s", str(data)),
- client_impl=callback_client_impl)
- # Start listening to logs as soon as possible.
- self.listen_to_log_stream()
+ def detokenize_and_log_output(data: bytes, _detokenizer=None):
+ log_messages = data.decode(encoding='utf-8',
+ errors='surrogateescape')
+
+ if self.detokenizer:
+ log_messages = decode_optionally_tokenized(
+ self.detokenizer, data)
+
+ for line in log_messages.splitlines():
+ self.logger.info(line)
+
+ self.client = HdlcRpcClient(read,
+ self.protos,
+ default_channels(write),
+ detokenize_and_log_output,
+ client_impl=callback_client_impl)
+
+ if use_rpc_logging:
+ # Start listening to logs as soon as possible.
+ self.listen_to_log_stream()
def info(self) -> console_tools.ClientInfo:
return console_tools.ClientInfo('device', self.rpcs,
@@ -137,12 +150,12 @@
decode_optionally_tokenized(self.detokenizer,
log_proto.message))
else:
- message = log_proto.message.decode("utf-8")
+ message = log_proto.message.decode('utf-8')
log = pw_log_tokenized.FormatStringWithMetadata(message)
# Handle dropped count.
if log_proto.dropped:
- drop_reason = log_proto.message.decode("utf-8").lower(
+ drop_reason = log_proto.message.decode('utf-8').lower(
) if log_proto.message else 'enqueue failure on device'
self._handle_log_drop_count(log_proto.dropped, drop_reason)
continue