tail support, log filtering, executable, and splitlines bug fix

Change-Id: I81e928fb5222d055f248d50a5332ea39d4b9ebc1
This commit is contained in:
Kevin Rasmussen 2017-01-23 16:47:50 -07:00
parent aed513015c
commit c6379afce0

67
oslo_log/cmds/convert_json.py Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env python
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
# a copy of the License at # a copy of the License at
@ -16,15 +17,16 @@ import argparse
import collections import collections
import functools import functools
import sys import sys
import time
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import importutils from oslo_utils import importutils
import six import six
termcolor = importutils.try_import('termcolor')
from oslo_log import log from oslo_log import log
termcolor = importutils.try_import('termcolor')
_USE_COLOR = False _USE_COLOR = False
@ -33,9 +35,29 @@ def main():
global _USE_COLOR global _USE_COLOR
args = parse_args() args = parse_args()
_USE_COLOR = args.color _USE_COLOR = args.color
formatter = functools.partial(console_format, args.prefix, args.locator) formatter = functools.partial(
for line in reformat_json(args.file, formatter): console_format,
print(line) args.prefix,
args.locator,
loggers=args.loggers,
levels=args.levels,
)
if args.lines:
# Read backward until we find all of our newline characters
# or reach the beginning of the file
args.file.seek(0, 2)
newlines = 0
pos = args.file.tell()
while newlines <= args.lines and pos > 0:
pos = pos - 1
args.file.seek(pos)
if args.file.read(1) == '\n':
newlines = newlines + 1
try:
for line in reformat_json(args.file, formatter, args.follow):
print(line)
except KeyboardInterrupt:
sys.exit(0)
def parse_args(): def parse_args():
@ -55,6 +77,21 @@ def parse_args():
parser.add_argument("-c", "--color", parser.add_argument("-c", "--color",
action='store_true', default=False, action='store_true', default=False,
help="Color log levels (requires `termcolor`)") help="Color log levels (requires `termcolor`)")
parser.add_argument("-f", "--follow",
action='store_true', default=False,
help="Continue parsing new data until"
" KeyboardInterrupt")
parser.add_argument("-n", "--lines",
required=False, type=int,
help="Last N number of records to view."
" (May show less than N records when used"
" in conjuction with --loggers or --levels)")
parser.add_argument("--loggers",
nargs='*', default=[],
help="only return results matching given logger(s)")
parser.add_argument("--levels",
nargs='*', default=[],
help="Only return lines matching given log level(s)")
args = parser.parse_args() args = parser.parse_args()
if args.color and not termcolor: if args.color and not termcolor:
raise ImportError("Coloring requested but `termcolor` is not" raise ImportError("Coloring requested but `termcolor` is not"
@ -85,12 +122,16 @@ def warn(prefix, msg):
return "%s: %s" % (colorise('exc', prefix), msg) return "%s: %s" % (colorise('exc', prefix), msg)
def reformat_json(fh, formatter): def reformat_json(fh, formatter, follow=False):
# using readline allows interactive stdin to respond to every line # using readline allows interactive stdin to respond to every line
while True: while True:
line = fh.readline() line = fh.readline()
if not line: if not line:
break if follow:
time.sleep(0.1)
continue
else:
break
line = line.strip() line = line.strip()
if not line: if not line:
continue continue
@ -103,11 +144,19 @@ def reformat_json(fh, formatter):
yield out_line yield out_line
def console_format(prefix, locator, record): def console_format(prefix, locator, record, loggers=[], levels=[]):
# Provide an empty string to format-specifiers the record is # Provide an empty string to format-specifiers the record is
# missing, instead of failing. Doesn't work for non-string # missing, instead of failing. Doesn't work for non-string
# specifiers. # specifiers.
record = collections.defaultdict(str, record) record = collections.defaultdict(str, record)
# skip if the record doesn't match a logger we are looking at
if loggers:
name = record.get('name')
if not any(name.startswith(n) for n in loggers):
return
if levels:
if record.get('levelname') not in levels:
return
levelname = record.get('levelname') levelname = record.get('levelname')
if levelname: if levelname:
record['levelname'] = colorise(levelname) record['levelname'] = colorise(levelname)
@ -130,7 +179,7 @@ def console_format(prefix, locator, record):
tb = record.get('traceback') tb = record.get('traceback')
if tb: if tb:
for tb_line in tb.splitlines(): for tb_line in tb:
yield ' '.join([prefix, tb_line]) yield ' '.join([prefix, tb_line])