Coverage for src/km3flux/logger.py: 59%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python3
2from hashlib import sha256
3import os
4import re
5import sys
6import logging
7import logging.handlers
9loggers = {} # this holds all the registered loggers
12ATTRIBUTES = dict(
13 list(
14 zip(
15 ["bold", "dark", "", "underline", "blink", "", "reverse", "concealed"],
16 list(range(1, 9)),
17 )
18 )
19)
20del ATTRIBUTES[""]
22ATTRIBUTES_RE = r"\033\[(?:%s)m" % "|".join(["%d" % v for v in ATTRIBUTES.values()])
24HIGHLIGHTS = dict(
25 list(
26 zip(
27 [
28 "on_grey",
29 "on_red",
30 "on_green",
31 "on_yellow",
32 "on_blue",
33 "on_magenta",
34 "on_cyan",
35 "on_white",
36 ],
37 list(range(40, 48)),
38 )
39 )
40)
42HIGHLIGHTS_RE = r"\033\[(?:%s)m" % "|".join(["%d" % v for v in HIGHLIGHTS.values()])
44COLORS = dict(
45 list(
46 zip(
47 [
48 "grey",
49 "red",
50 "green",
51 "yellow",
52 "blue",
53 "magenta",
54 "cyan",
55 "white",
56 ],
57 list(range(30, 38)),
58 )
59 )
60)
62COLORS_RE = r"\033\[(?:%s)m" % "|".join(["%d" % v for v in COLORS.values()])
64RESET = r"\033[0m"
65RESET_RE = r"\033\[0m"
68def supports_color():
69 """Checks if the terminal supports color."""
70 if isnotebook():
71 return True
72 supported_platform = sys.platform != "win32" or "ANSICON" in os.environ
73 is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
75 if not supported_platform or not is_a_tty:
76 return False
78 return True
81def colored(text, color=None, on_color=None, attrs=None, ansi_code=None):
82 """Colorize text, while stripping nested ANSI color sequences.
84 Author: Konstantin Lepa <konstantin.lepa@gmail.com> / termcolor
86 Available text colors:
87 red, green, yellow, blue, magenta, cyan, white.
88 Available text highlights:
89 on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white.
90 Available attributes:
91 bold, dark, underline, blink, reverse, concealed.
92 Example:
93 colored('Hello, World!', 'red', 'on_grey', ['blue', 'blink'])
94 colored('Hello, World!', 'green')
95 """
96 if os.getenv("ANSI_COLORS_DISABLED") is None:
97 if ansi_code is not None:
98 return "\033[38;5;{}m{}\033[0m".format(ansi_code, text)
99 fmt_str = "\033[%dm%s"
100 if color is not None:
101 text = re.sub(COLORS_RE + "(.*?)" + RESET_RE, r"\1", text)
102 text = fmt_str % (COLORS[color], text)
103 if on_color is not None:
104 text = re.sub(HIGHLIGHTS_RE + "(.*?)" + RESET_RE, r"\1", text)
105 text = fmt_str % (HIGHLIGHTS[on_color], text)
106 if attrs is not None:
107 text = re.sub(ATTRIBUTES_RE + "(.*?)" + RESET_RE, r"\1", text)
108 for attr in attrs:
109 text = fmt_str % (ATTRIBUTES[attr], text)
110 return text + RESET
111 else:
112 return text
115def hash_coloured_escapes(text):
116 """Return the ANSI hash colour prefix and suffix for a given text"""
117 ansi_code = int(sha256(text.encode("utf-8")).hexdigest(), 16) % 230
118 prefix, suffix = colored("SPLIT", ansi_code=ansi_code).split("SPLIT")
119 return prefix, suffix
122def isnotebook():
123 """Check if running within a Jupyter notebook"""
124 try:
125 shell = get_ipython().__class__.__name__
126 if shell == "ZMQInteractiveShell":
127 return True # Jupyter notebook or qtconsole
128 elif shell == "TerminalInteractiveShell":
129 return False # Terminal running IPython
130 else:
131 return False # Other type (?)
132 except NameError:
133 return False
136def get_logger(name, filename=None, stream_loglevel="INFO", file_loglevel="DEBUG"):
137 """Helper function to get a logger"""
138 if name in loggers:
139 return loggers[name]
140 logger = logging.getLogger(name)
141 logger.propagate = False
143 with_color = supports_color()
145 pre1, suf1 = hash_coloured_escapes(name) if with_color else ("", "")
146 pre2, suf2 = hash_coloured_escapes(name + "salt") if with_color else ("", "")
147 formatter = logging.Formatter(
148 "%(asctime)s %(levelname)s {}+{}+{} "
149 "%(name)s: %(message)s".format(pre1, pre2, suf1),
150 datefmt="%Y-%m-%d %H:%M:%S",
151 )
152 if filename is not None:
153 ch_file = logging.handlers.RotatingFileHandler(
154 filename, maxBytes=5 * 1024 * 1024, backupCount=10
155 )
156 ch_file.setLevel(file_loglevel)
157 ch_file.setFormatter(formatter)
158 logger.addHandler(ch_file)
159 ch = logging.StreamHandler()
160 ch.setLevel(stream_loglevel)
161 ch.setFormatter(formatter)
162 logger.addHandler(ch)
164 loggers[name] = logger
166 logger.once_dict = {}
168 return logger
171def set_level(log_or_name, level):
172 """Set the log level for given logger"""
173 if isinstance(log_or_name, str):
174 log = get_logger(log_or_name)
175 else:
176 log = log_or_name
177 log.setLevel(level)
178 for handler in log.handlers:
179 handler.setLevel(level)
182if supports_color():
183 logging.addLevelName(
184 logging.INFO, "\033[1;32m%s\033[1;0m" % logging.getLevelName(logging.INFO)
185 )
186 logging.addLevelName(
187 logging.DEBUG, "\033[1;34m%s\033[1;0m" % logging.getLevelName(logging.DEBUG)
188 )
189 logging.addLevelName(
190 logging.WARNING, "\033[1;33m%s\033[1;0m" % logging.getLevelName(logging.WARNING)
191 )
192 logging.addLevelName(
193 logging.ERROR, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.ERROR)
194 )
195 logging.addLevelName(
196 logging.CRITICAL,
197 "\033[1;101m%s\033[1;0m" % logging.getLevelName(logging.CRITICAL),
198 )
201log = get_logger("km3flux")
202set_level(log, "WARNING")