136 lines
3.9 KiB
Python
136 lines
3.9 KiB
Python
|
"""pyzmq log watcher.
|
||
|
|
||
|
Easily view log messages published by the PUBHandler in zmq.log.handlers
|
||
|
|
||
|
Designed to be run as an executable module - try this to see options:
|
||
|
python -m zmq.log -h
|
||
|
|
||
|
Subscribes to the '' (empty string) topic by default which means it will work
|
||
|
out-of-the-box with a PUBHandler object instantiated with default settings.
|
||
|
If you change the root topic with PUBHandler.setRootTopic() you must pass
|
||
|
the value to this script with the --topic argument.
|
||
|
|
||
|
Note that the default formats for the PUBHandler object selectively include
|
||
|
the log level in the message. This creates redundancy in this script as it
|
||
|
always prints the topic of the message, which includes the log level.
|
||
|
Consider overriding the default formats with PUBHandler.setFormat() to
|
||
|
avoid this issue.
|
||
|
|
||
|
"""
|
||
|
|
||
|
# encoding: utf-8
|
||
|
|
||
|
# Copyright (C) PyZMQ Developers
|
||
|
# Distributed under the terms of the Modified BSD License.
|
||
|
|
||
|
import argparse
|
||
|
from datetime import datetime
|
||
|
from typing import Dict
|
||
|
|
||
|
import zmq
|
||
|
|
||
|
parser = argparse.ArgumentParser('ZMQ Log Watcher')
|
||
|
parser.add_argument('zmq_pub_url', type=str, help='URL to a ZMQ publisher socket.')
|
||
|
parser.add_argument(
|
||
|
'-t',
|
||
|
'--topic',
|
||
|
type=str,
|
||
|
default='',
|
||
|
help='Only receive messages that start with this topic.',
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'--timestamp', action='store_true', help='Append local time to the log messages.'
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'--separator',
|
||
|
type=str,
|
||
|
default=' | ',
|
||
|
help='String to print between topic and message.',
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'--dateformat',
|
||
|
type=str,
|
||
|
default='%Y-%d-%m %H:%M',
|
||
|
help='Set alternative date format for use with --timestamp.',
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'--align',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='Try to align messages by the width of their topics.',
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'--color',
|
||
|
action='store_true',
|
||
|
default=False,
|
||
|
help='Color the output based on the error level. Requires the colorama module.',
|
||
|
)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
|
||
|
if args.color:
|
||
|
import colorama
|
||
|
|
||
|
colorama.init()
|
||
|
colors = {
|
||
|
'DEBUG': colorama.Fore.LIGHTCYAN_EX,
|
||
|
'INFO': colorama.Fore.LIGHTWHITE_EX,
|
||
|
'WARNING': colorama.Fore.YELLOW,
|
||
|
'ERROR': colorama.Fore.LIGHTRED_EX,
|
||
|
'CRITICAL': colorama.Fore.LIGHTRED_EX,
|
||
|
'__RESET__': colorama.Fore.RESET,
|
||
|
}
|
||
|
else:
|
||
|
colors = {}
|
||
|
|
||
|
|
||
|
ctx = zmq.Context()
|
||
|
sub = ctx.socket(zmq.SUB)
|
||
|
sub.subscribe(args.topic.encode("utf8"))
|
||
|
sub.connect(args.zmq_pub_url)
|
||
|
|
||
|
topic_widths: Dict[int, int] = {}
|
||
|
|
||
|
while True:
|
||
|
try:
|
||
|
if sub.poll(10, zmq.POLLIN):
|
||
|
topic, msg = sub.recv_multipart()
|
||
|
topics = topic.decode('utf8').strip().split('.')
|
||
|
|
||
|
if args.align:
|
||
|
topics.extend(' ' for extra in range(len(topics), len(topic_widths)))
|
||
|
aligned_parts = []
|
||
|
for key, part in enumerate(topics):
|
||
|
topic_widths[key] = max(len(part), topic_widths.get(key, 0))
|
||
|
fmt = ''.join(('{:<', str(topic_widths[key]), '}'))
|
||
|
aligned_parts.append(fmt.format(part))
|
||
|
|
||
|
if len(topics) == 1:
|
||
|
level = topics[0]
|
||
|
else:
|
||
|
level = topics[1]
|
||
|
|
||
|
fields = {
|
||
|
'msg': msg.decode('utf8').strip(),
|
||
|
'ts': (
|
||
|
datetime.now().strftime(args.dateformat) + ' '
|
||
|
if args.timestamp
|
||
|
else ''
|
||
|
),
|
||
|
'aligned': (
|
||
|
'.'.join(aligned_parts)
|
||
|
if args.align
|
||
|
else topic.decode('utf8').strip()
|
||
|
),
|
||
|
'color': colors.get(level, ''),
|
||
|
'color_rst': colors.get('__RESET__', ''),
|
||
|
'sep': args.separator,
|
||
|
}
|
||
|
print('{ts}{color}{aligned}{sep}{msg}{color_rst}'.format(**fields))
|
||
|
except KeyboardInterrupt:
|
||
|
break
|
||
|
|
||
|
sub.disconnect(args.zmq_pub_url)
|
||
|
if args.color:
|
||
|
print(colorama.Fore.RESET)
|