| #!/usr/bin/env python |
| |
| # Copyright 2017 The Fuchsia Authors |
| # |
| # Use of this source code is governed by a MIT-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/MIT |
| |
| """ |
| |
| This tool will color the lines from loglistener |
| |
| Example usage #1: |
| # Colorize log lines, with all messages from the same thread in the same color |
| loglistener | scripts/colorize_logs -t |
| |
| Example usage #2: |
| # Colorize log lines, with all messages from the same process in the same color |
| loglistener | scripts/colorize_logs |
| |
| Example usage #3: |
| # Print the colorization of log.txt to stdout |
| # Identical to `scripts/colorize_logs < log.txt` |
| scripts/colorize_logs log.txt |
| |
| Example usage #4: |
| # Colorize all ERROR and INFO lines in log.txt |
| scripts/colorize_logs -r ERROR -r INFO log.txt |
| |
| Example usage #5: |
| # Colorize all lines with drv='<something>' in log.txt with distinct colors |
| # for each <something> |
| scripts/colorize_logs -r "drv='[^']*'" log.txt |
| |
| """ |
| |
| import argparse |
| import re |
| import sys |
| |
| BASE_COLORS = [ |
| #'\033[40m', # black |
| '\033[91m', # red |
| '\033[92m', # green |
| '\033[93m', # yellow |
| '\033[94m', # blue |
| '\033[95m', # magenta |
| '\033[66m', # cyan |
| #'\033[47m', # white |
| ] |
| RESET_BG = '\033[49m' |
| RESET_FG = '\033[39m' |
| |
| class ColorAssigner(object): |
| def __init__(self, colors): |
| self.lru = list(colors) |
| self.task_colors = { } |
| |
| def get_bg_color(self, task_id): |
| if task_id not in self.task_colors: |
| c = self.lru.pop(0) |
| self.task_colors[task_id] = c |
| else: |
| c = self.task_colors[task_id] |
| self.lru.remove(c) |
| |
| self.lru.append(c) |
| return c |
| |
| |
| PROCESS_RE = r'^\[\d+\.\d+] (\d+)[\.:]\d+> .*$' |
| THREAD_RE = r'^\[\d+\.\d+] (\d+[\.:]\d+)> .*$' |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter) |
| parser.add_argument("--process", "-p", dest="patterns", action="append_const", |
| const=PROCESS_RE, help="Color code by process (default)") |
| parser.add_argument("--thread", "-t", dest="patterns", action="append_const", |
| const=THREAD_RE, help="Color code by thread") |
| parser.add_argument("--regex", "-r", dest="patterns", action="append", |
| help="Color by matching regexp") |
| parser.add_argument("input", nargs='?', action="store", default=None, |
| help="The file to colorize. Defaults to stdin") |
| args = parser.parse_args() |
| |
| if args.input: |
| f = open(args.input, 'r') |
| else: |
| f = sys.stdin |
| |
| # If no patterns were specified, use the process pattern. |
| if not args.patterns: |
| args.patterns = [PROCESS_RE] |
| |
| # Define the identifier extractor. It should be in group 1. |
| patterns = [] |
| for pattern in args.patterns: |
| regex = re.compile(pattern) |
| if not regex.groups: |
| # if there's no group, wrap the pattern |
| regex = re.compile(r'^.*(' + pattern + r').*$') |
| patterns.append(regex) |
| |
| assigner = ColorAssigner(BASE_COLORS); |
| |
| while True: |
| line = f.readline() |
| if not line: |
| break |
| |
| line = line.strip() |
| matched = False |
| for line_re in patterns: |
| m = line_re.match(line) |
| if m: |
| matched = True |
| task_id = m.group(1) |
| color = assigner.get_bg_color(task_id) |
| |
| # Use join to avoid python putting a space between each value being |
| # printed. |
| print ''.join([color, line, RESET_BG, RESET_FG]) |
| sys.stdout.flush() |
| break |
| |
| if not matched: |
| print line |
| sys.stdout.flush() |
| |
| if __name__ == '__main__': |
| main() |