#!/usr/bin/env python # # Copyright (c) 2021 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Required modules: # pyserial # coloredlogs # # Example usage to show only warning and above messages: # # ./scripts/esp32_log_cat.py --log-level WARNING # import argparse import logging import coloredlogs import serial class LogPrinter: """Converts raw bytes from a serial output to python log outputs.""" def __init__(self, logger): # a severity state is kept to account for multiline messages self.severity = 'I' self.logger = logger def ExtractSeverity(self, log_line: str): if len(log_line) < 2: return # Log line expected to start like 'E ', 'I ', ... if log_line[1] != ' ': return if log_line[0] in 'EWIV': self.severity = log_line[0] def Log(self, raw): """Converts raw bytes from serial output into python logging. Strips out any color encoding from the line and uses the prefix to decide log type. """ # ESP32 serial lines already contain color encoding information and look like: # b'\x1b[0;32mI (4921) app-devicecallbacks: Current free heap: 125656\r\n' # b'\x1b[0;31mE (1491) chip[DL]: Long dispatch time: 635 ms\x1b[0m' if raw.startswith(b'\x1b['): raw = raw[raw.find(b'm')+1:] raw = raw.decode('utf8').strip() self.ExtractSeverity(raw) if self.severity == 'E': self.logger.error('%s', raw) elif self.severity == 'W': self.logger.warning('%s', raw) elif self.severity == 'I': self.logger.info('%s', raw) elif self.severity == 'V': self.logger.debug('%s', raw) def main(): """Main task if executed standalone.""" parser = argparse.ArgumentParser( description='Output nicely colored logs from esp32') parser.add_argument( '--device', default='/dev/ttyUSB0', type=str, help='What serial device to open.') parser.add_argument( '--baudrate', default=115200, type=int, help='Baudrate for the serial device.') parser.add_argument( '--log-level', default=logging.DEBUG, type=lambda x: getattr(logging, x), help='Log filtering to apply.') args = parser.parse_args() # Ensures somewhat pretty logging of what is going on logging.basicConfig(level=args.log_level) coloredlogs.install(fmt='%(asctime)s %(name)s %(levelname)-7s %(message)s') logger = logging.getLogger(args.device) logger.setLevel(args.log_level) printer = LogPrinter(logger) ser = serial.Serial(args.device, args.baudrate) while True: data = ser.readline() printer.Log(data) if __name__ == '__main__': # execute only if run as a script main()