__main__.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """pyzmq log watcher.
  2. Easily view log messages published by the PUBHandler in zmq.log.handlers
  3. Designed to be run as an executable module - try this to see options:
  4. python -m zmq.log -h
  5. Subscribes to the '' (empty string) topic by default which means it will work
  6. out-of-the-box with a PUBHandler object instantiated with default settings.
  7. If you change the root topic with PUBHandler.setRootTopic() you must pass
  8. the value to this script with the --topic argument.
  9. Note that the default formats for the PUBHandler object selectively include
  10. the log level in the message. This creates redundancy in this script as it
  11. always prints the topic of the message, which includes the log level.
  12. Consider overriding the default formats with PUBHandler.setFormat() to
  13. avoid this issue.
  14. """
  15. # encoding: utf-8
  16. # Copyright (C) PyZMQ Developers
  17. # Distributed under the terms of the Modified BSD License.
  18. import argparse
  19. from datetime import datetime
  20. import zmq
  21. from zmq.utils.strtypes import u, cast_bytes
  22. parser = argparse.ArgumentParser('ZMQ Log Watcher')
  23. parser.add_argument('zmq_pub_url',type=str,
  24. help='URL to a ZMQ publisher socket.')
  25. parser.add_argument('-t', '--topic', type=str, default='',
  26. help='Only receive messages that start with this topic.')
  27. parser.add_argument('--timestamp', action='store_true',
  28. help='Append local time to the log messages.')
  29. parser.add_argument('--separator', type=str, default=' | ',
  30. help='String to print between topic and message.')
  31. parser.add_argument('--dateformat', type=str, default='%Y-%d-%m %H:%M',
  32. help='Set alternative date format for use with --timestamp.')
  33. parser.add_argument('--align', action='store_true', default=False,
  34. help='Try to align messages by the width of their topics.')
  35. parser.add_argument('--color', action='store_true', default=False,
  36. help='Color the output based on the error level. Requires the colorama module.')
  37. args = parser.parse_args()
  38. if args.color:
  39. import colorama
  40. colorama.init()
  41. colors = {
  42. 'DEBUG' : colorama.Fore.LIGHTCYAN_EX,
  43. 'INFO' : colorama.Fore.LIGHTWHITE_EX,
  44. 'WARNING' : colorama.Fore.YELLOW,
  45. 'ERROR' : colorama.Fore.LIGHTRED_EX,
  46. 'CRITICAL' : colorama.Fore.LIGHTRED_EX,
  47. '__RESET__': colorama.Fore.RESET,
  48. }
  49. else:
  50. colors = { }
  51. ctx = zmq.Context()
  52. sub = ctx.socket(zmq.SUB)
  53. sub.subscribe(cast_bytes(args.topic))
  54. sub.connect(args.zmq_pub_url)
  55. topic_widths = {}
  56. while(1):
  57. try:
  58. if sub.poll(10, zmq.POLLIN):
  59. topic, msg = sub.recv_multipart()
  60. topics = topic.decode('utf8').strip().split('.')
  61. if args.align:
  62. topics.extend(' ' for extra in range(len(topics), len(topic_widths)))
  63. aligned_parts = []
  64. for key, part in enumerate(topics):
  65. topic_widths[key] = max(len(part), topic_widths.get(key,0))
  66. fmt = ''.join(('{:<',str(topic_widths[key]),'}'))
  67. aligned_parts.append(fmt.format(part))
  68. if len(topics)==1:
  69. level = topics[0]
  70. else:
  71. level = topics[1]
  72. fields = {
  73. 'msg' : msg.decode('utf8').strip(),
  74. 'ts' : datetime.now().strftime(args.dateformat)+' ' if args.timestamp else '',
  75. 'aligned' : '.'.join(aligned_parts) if args.align else topic.decode('utf8').strip(),
  76. 'color' : colors.get(level,''),
  77. 'color_rst' : colors.get('__RESET__',''),
  78. 'sep' : args.separator,
  79. }
  80. print('{ts}{color}{aligned}{sep}{msg}{color_rst}'.format(**fields))
  81. except KeyboardInterrupt:
  82. break
  83. sub.disconnect(args.zmq_pub_url)
  84. if args.color:
  85. print(colorama.Fore.RESET)