esp32_log_cat.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2021 Project CHIP Authors
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. #
  18. # Required modules:
  19. # pyserial
  20. # coloredlogs
  21. #
  22. # Example usage to show only warning and above messages:
  23. #
  24. # ./scripts/esp32_log_cat.py --log-level WARNING
  25. #
  26. import argparse
  27. import logging
  28. import coloredlogs
  29. import serial
  30. class LogPrinter:
  31. """Converts raw bytes from a serial output to python log outputs."""
  32. def __init__(self, logger):
  33. # a severity state is kept to account for multiline messages
  34. self.severity = 'I'
  35. self.logger = logger
  36. def ExtractSeverity(self, log_line: str):
  37. if len(log_line) < 2:
  38. return
  39. # Log line expected to start like 'E ', 'I ', ...
  40. if log_line[1] != ' ':
  41. return
  42. if log_line[0] in 'EWIV':
  43. self.severity = log_line[0]
  44. def Log(self, raw):
  45. """Converts raw bytes from serial output into python logging.
  46. Strips out any color encoding from the line and uses the prefix to decide
  47. log type.
  48. """
  49. # ESP32 serial lines already contain color encoding information and look like:
  50. # b'\x1b[0;32mI (4921) app-devicecallbacks: Current free heap: 125656\r\n'
  51. # b'\x1b[0;31mE (1491) chip[DL]: Long dispatch time: 635 ms\x1b[0m'
  52. if raw.startswith(b'\x1b['):
  53. raw = raw[raw.find(b'm')+1:]
  54. raw = raw.decode('utf8').strip()
  55. self.ExtractSeverity(raw)
  56. if self.severity == 'E':
  57. self.logger.error('%s', raw)
  58. elif self.severity == 'W':
  59. self.logger.warning('%s', raw)
  60. elif self.severity == 'I':
  61. self.logger.info('%s', raw)
  62. elif self.severity == 'V':
  63. self.logger.debug('%s', raw)
  64. def main():
  65. """Main task if executed standalone."""
  66. parser = argparse.ArgumentParser(
  67. description='Output nicely colored logs from esp32')
  68. parser.add_argument(
  69. '--device',
  70. default='/dev/ttyUSB0',
  71. type=str,
  72. help='What serial device to open.')
  73. parser.add_argument(
  74. '--baudrate',
  75. default=115200,
  76. type=int,
  77. help='Baudrate for the serial device.')
  78. parser.add_argument(
  79. '--log-level',
  80. default=logging.DEBUG,
  81. type=lambda x: getattr(logging, x),
  82. help='Log filtering to apply.')
  83. args = parser.parse_args()
  84. # Ensures somewhat pretty logging of what is going on
  85. logging.basicConfig(level=args.log_level)
  86. coloredlogs.install(fmt='%(asctime)s %(name)s %(levelname)-7s %(message)s')
  87. logger = logging.getLogger(args.device)
  88. logger.setLevel(args.log_level)
  89. printer = LogPrinter(logger)
  90. ser = serial.Serial(args.device, args.baudrate)
  91. while True:
  92. data = ser.readline()
  93. printer.Log(data)
  94. if __name__ == '__main__':
  95. # execute only if run as a script
  96. main()