]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/munet/mulog.py
Merge pull request #13455 from sri-mohan1/srib-ldpd
[mirror_frr.git] / tests / topotests / munet / mulog.py
1 # -*- coding: utf-8 eval: (blacken-mode 1) -*-
2 # SPDX-License-Identifier: GPL-2.0-or-later
3 #
4 # December 4 2022, Christian Hopps <chopps@labn.net>
5 #
6 # Copyright (c) 2022, LabN Consulting, L.L.C.
7 #
8 """Utilities for logging in munet."""
9
10 import logging
11
12 from pathlib import Path
13
14
15 class MultiFileHandler(logging.FileHandler):
16 """A logging handler that logs to new files based on the logger name.
17
18 The MultiFileHandler operates as a FileHandler with additional functionality. In
19 addition to logging to the specified logging file MultiFileHandler also creates new
20 FileHandlers for child loggers based on a root logging name path.
21
22 The ``root_path`` determines when to create a new FileHandler. For each received log
23 record, ``root_path`` is removed from the logger name of the record if present, and
24 the resulting channel path (if any) determines the directory for a new log file to
25 also emit the record to. The new file path is constructed by starting with the
26 directory ``filename`` resides in, then joining the path determined above after
27 converting "." to "/" and finally by adding back the basename of ``filename``.
28
29 record logger path => mutest.output.testingfoo
30 root_path => mutest.output
31 base filename => /tmp/mutest/mutest-exec.log
32 new logfile => /tmp/mutest/testingfoo/mutest-exec.log
33
34 All messages are also emitted to the common FileLogger for ``filename``.
35
36 If a log record is from a logger that does not start with ``root_path`` no file is
37 created and the normal emit occurs.
38
39 Args:
40 root_path: the logging path of the root level for this handler.
41 new_handler_level: logging level for newly created handlers
42 log_dir: the log directory to put log files in.
43 filename: the base log file.
44 """
45
46 def __init__(self, root_path, filename=None, **kwargs):
47 self.__root_path = root_path
48 self.__basename = Path(filename).name
49 if root_path[-1] != ".":
50 self.__root_path += "."
51 self.__root_pathlen = len(self.__root_path)
52 self.__kwargs = kwargs
53 self.__log_dir = Path(filename).absolute().parent
54 self.__log_dir.mkdir(parents=True, exist_ok=True)
55 self.__filenames = {}
56 self.__added = set()
57
58 if "new_handler_level" not in kwargs:
59 self.__new_handler_level = logging.NOTSET
60 else:
61 new_handler_level = kwargs["new_handler_level"]
62 del kwargs["new_handler_level"]
63 self.__new_handler_level = new_handler_level
64
65 super().__init__(filename=filename, **kwargs)
66
67 if self.__new_handler_level is None:
68 self.__new_handler_level = self.level
69
70 def __log_filename(self, name):
71 if name in self.__filenames:
72 return self.__filenames[name]
73
74 if not name.startswith(self.__root_path):
75 newname = None
76 else:
77 newname = name[self.__root_pathlen :]
78 newname = Path(newname.replace(".", "/"))
79 newname = self.__log_dir.joinpath(newname)
80 newname = newname.joinpath(self.__basename)
81 self.__filenames[name] = newname
82
83 self.__filenames[name] = newname
84 return newname
85
86 def emit(self, record):
87 newname = self.__log_filename(record.name)
88 if newname:
89 if newname not in self.__added:
90 self.__added.add(newname)
91 h = logging.FileHandler(filename=newname, **self.__kwargs)
92 h.setLevel(self.__new_handler_level)
93 h.setFormatter(self.formatter)
94 logging.getLogger(record.name).addHandler(h)
95 h.emit(record)
96 super().emit(record)
97
98
99 class ColorFormatter(logging.Formatter):
100 """A formatter that adds color sequences based on level."""
101
102 def __init__(self, fmt=None, datefmt=None, style="%", **kwargs):
103 grey = "\x1b[90m"
104 yellow = "\x1b[33m"
105 red = "\x1b[31m"
106 bold_red = "\x1b[31;1m"
107 reset = "\x1b[0m"
108 # basefmt = " ------| %(message)s "
109
110 self.formatters = {
111 logging.DEBUG: logging.Formatter(grey + fmt + reset),
112 logging.INFO: logging.Formatter(grey + fmt + reset),
113 logging.WARNING: logging.Formatter(yellow + fmt + reset),
114 logging.ERROR: logging.Formatter(red + fmt + reset),
115 logging.CRITICAL: logging.Formatter(bold_red + fmt + reset),
116 }
117 # Why are we even bothering?
118 super().__init__(fmt, datefmt, style, **kwargs)
119
120 def format(self, record):
121 formatter = self.formatters.get(record.levelno)
122 return formatter.format(record)