kiwi.logger
1# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved. 2# 3# This file is part of kiwi. 4# 5# kiwi is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# kiwi is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with kiwi. If not, see <http://www.gnu.org/licenses/> 17# 18from typing import ( 19 Dict, Optional, List 20) 21import logging 22from kiwi.logger_socket import PlainTextSocketHandler 23import sys 24 25# project 26from kiwi.logger_color_formatter import ColorFormatter 27from kiwi.logger_filter import ( 28 LoggerSchedulerFilter, 29 InfoFilter, 30 DebugFilter, 31 ErrorFilter, 32 WarningFilter 33) 34 35from kiwi.exceptions import ( 36 KiwiLogFileSetupFailed, 37 KiwiLogSocketSetupFailed 38) 39 40 41class Logger(logging.Logger): 42 """ 43 **Extended logging facility based on Python logging** 44 45 :param str name: name of the logger 46 """ 47 def __init__(self, name: str): 48 logging.Logger.__init__(self, name) 49 self.log_handlers: Dict = {} 50 self.logfile: Optional[str] = None 51 # log INFO to stdout 52 self._add_stream_handler( 53 'info', 54 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 55 [InfoFilter(), LoggerSchedulerFilter()] 56 ) 57 # log WARNING messages to stdout 58 self._add_stream_handler( 59 'warning', 60 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 61 [WarningFilter()] 62 ) 63 # log DEBUG messages to stdout 64 self._add_stream_handler( 65 'debug', 66 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 67 [DebugFilter()] 68 ) 69 # log ERROR messages to stderr 70 self._add_stream_handler( 71 'error', 72 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 73 [ErrorFilter()], 74 sys.__stderr__ 75 ) 76 self.log_level = self.level 77 self.log_flags: Dict[str, bool] = {} 78 79 def getLogLevel(self) -> int: 80 """ 81 Return currently used log level 82 83 :return: log level number 84 85 :rtype: int 86 """ 87 return self.log_level 88 89 def getLogFlags(self) -> Dict[str, bool]: 90 """ 91 Return logging flags 92 93 :return: 94 Dictionary with flags and their activation status 95 96 :rtype: dict 97 """ 98 return self.log_flags 99 100 def setLogLevel( 101 self, level: int, except_for: List[str] = [], only_for: List[str] = [] 102 ) -> None: 103 """ 104 Set custom log level for all console handlers 105 106 :param int level: log level number 107 :param list except_for: 108 set log level to all handlers except for the given list 109 :param list only_for: 110 set log level to the given handlers only 111 112 if both except_for and only_for handlers are specified, 113 the except_for list will be ignored 114 """ 115 self.log_level = level 116 if only_for: 117 for handler_type in self.log_handlers: 118 if handler_type in only_for: 119 self.log_handlers[handler_type].setLevel(level) 120 return 121 for handler_type in self.log_handlers: 122 if handler_type not in except_for: 123 self.log_handlers[handler_type].setLevel(level) 124 125 def setLogFlag(self, flag: str, value: bool = True) -> None: 126 """ 127 Set logging flag for further properties of the logging facility 128 Available flags are: 129 130 * run-scripts-in-screen 131 132 :param str flag: name 133 """ 134 self.log_flags[flag] = value 135 136 def set_color_format(self) -> None: 137 """ 138 Set color format for all console handlers 139 """ 140 for handler_type in self.log_handlers: 141 message_format = None 142 if handler_type == 'debug': 143 message_format = \ 144 '$LIGHTCOLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 145 elif handler_type == 'warning' or handler_type == 'error': 146 message_format = \ 147 '$COLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 148 149 if message_format: 150 self.log_handlers[handler_type].setFormatter( 151 ColorFormatter(message_format, '%H:%M:%S') 152 ) 153 154 def set_logfile(self, filename: str) -> None: 155 """ 156 Set logfile handler 157 158 :param str filename: logfile file path 159 """ 160 try: 161 if filename == 'stdout': 162 # special case, log usual log file contents to stdout 163 handler = logging.StreamHandler(sys.__stdout__) 164 else: 165 handler = logging.FileHandler( 166 filename=filename, encoding='utf-8' 167 ) 168 self.logfile = filename 169 handler.setFormatter( 170 logging.Formatter( 171 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 172 '%H:%M:%S' 173 ) 174 ) 175 handler.addFilter(LoggerSchedulerFilter()) 176 self.addHandler(handler) 177 self.log_handlers['file'] = handler 178 except Exception as e: 179 raise KiwiLogFileSetupFailed( 180 f'{type(e).__name__}: {format(e)}' 181 ) 182 183 def set_log_socket(self, filename: str) -> None: 184 """ 185 Set log socket handler 186 187 :param str filename: 188 UDS socket file path. Note if there is no server 189 listening on the socket the log handler setup 190 will fail 191 """ 192 try: 193 handler = PlainTextSocketHandler(filename, None) 194 handler.makeSocket() 195 handler.setFormatter( 196 logging.Formatter( 197 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 198 '%H:%M:%S' 199 ) 200 ) 201 handler.addFilter(LoggerSchedulerFilter()) 202 self.addHandler(handler) 203 self.log_handlers['socket'] = handler 204 except Exception as e: 205 raise KiwiLogSocketSetupFailed( 206 'UDS socket: {0}:{1}: {2}'.format(filename, type(e).__name__, e) 207 ) 208 209 def get_logfile(self) -> Optional[str]: 210 """ 211 Return file path name of logfile 212 213 :return: file path 214 215 :rtype: str 216 """ 217 return self.logfile 218 219 @staticmethod 220 def progress( 221 current: int, total: int, prefix: str, bar_length: int = 40 222 ) -> None: 223 """ 224 Custom progress log information. progress information is 225 intentionally only logged to stdout and will bypass any 226 handlers. We don't want this information to show up in 227 the log file 228 229 :param int current: current item 230 :param int total: total number of items 231 :param string prefix: prefix name 232 :param int bar_length: length of progress bar 233 """ 234 try: 235 percent = float(current) / total 236 except Exception: 237 # we don't want the progress to raise an exception 238 # In case of any error e.g division by zero the current 239 # way out is to skip the progress update 240 return 241 hashes = '#' * int(round(percent * bar_length)) 242 spaces = ' ' * (bar_length - len(hashes)) 243 sys.stdout.write('\r{0}: [{1}] {2}%'.format( 244 prefix, hashes + spaces, int(round(percent * 100)) 245 )) 246 if current == 100: 247 sys.stdout.write('\n') 248 sys.stdout.flush() 249 250 def _add_stream_handler( 251 self, handler_type, message_format, message_filter, 252 channel=sys.__stdout__ 253 ): 254 handler = logging.StreamHandler(channel) 255 handler.setFormatter( 256 logging.Formatter(message_format, '%H:%M:%S') 257 ) 258 for rule in message_filter: 259 handler.addFilter(rule) 260 self.addHandler(handler) 261 self.log_handlers[handler_type] = handler
class
Logger(logging.Logger):
42class Logger(logging.Logger): 43 """ 44 **Extended logging facility based on Python logging** 45 46 :param str name: name of the logger 47 """ 48 def __init__(self, name: str): 49 logging.Logger.__init__(self, name) 50 self.log_handlers: Dict = {} 51 self.logfile: Optional[str] = None 52 # log INFO to stdout 53 self._add_stream_handler( 54 'info', 55 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 56 [InfoFilter(), LoggerSchedulerFilter()] 57 ) 58 # log WARNING messages to stdout 59 self._add_stream_handler( 60 'warning', 61 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 62 [WarningFilter()] 63 ) 64 # log DEBUG messages to stdout 65 self._add_stream_handler( 66 'debug', 67 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 68 [DebugFilter()] 69 ) 70 # log ERROR messages to stderr 71 self._add_stream_handler( 72 'error', 73 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 74 [ErrorFilter()], 75 sys.__stderr__ 76 ) 77 self.log_level = self.level 78 self.log_flags: Dict[str, bool] = {} 79 80 def getLogLevel(self) -> int: 81 """ 82 Return currently used log level 83 84 :return: log level number 85 86 :rtype: int 87 """ 88 return self.log_level 89 90 def getLogFlags(self) -> Dict[str, bool]: 91 """ 92 Return logging flags 93 94 :return: 95 Dictionary with flags and their activation status 96 97 :rtype: dict 98 """ 99 return self.log_flags 100 101 def setLogLevel( 102 self, level: int, except_for: List[str] = [], only_for: List[str] = [] 103 ) -> None: 104 """ 105 Set custom log level for all console handlers 106 107 :param int level: log level number 108 :param list except_for: 109 set log level to all handlers except for the given list 110 :param list only_for: 111 set log level to the given handlers only 112 113 if both except_for and only_for handlers are specified, 114 the except_for list will be ignored 115 """ 116 self.log_level = level 117 if only_for: 118 for handler_type in self.log_handlers: 119 if handler_type in only_for: 120 self.log_handlers[handler_type].setLevel(level) 121 return 122 for handler_type in self.log_handlers: 123 if handler_type not in except_for: 124 self.log_handlers[handler_type].setLevel(level) 125 126 def setLogFlag(self, flag: str, value: bool = True) -> None: 127 """ 128 Set logging flag for further properties of the logging facility 129 Available flags are: 130 131 * run-scripts-in-screen 132 133 :param str flag: name 134 """ 135 self.log_flags[flag] = value 136 137 def set_color_format(self) -> None: 138 """ 139 Set color format for all console handlers 140 """ 141 for handler_type in self.log_handlers: 142 message_format = None 143 if handler_type == 'debug': 144 message_format = \ 145 '$LIGHTCOLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 146 elif handler_type == 'warning' or handler_type == 'error': 147 message_format = \ 148 '$COLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 149 150 if message_format: 151 self.log_handlers[handler_type].setFormatter( 152 ColorFormatter(message_format, '%H:%M:%S') 153 ) 154 155 def set_logfile(self, filename: str) -> None: 156 """ 157 Set logfile handler 158 159 :param str filename: logfile file path 160 """ 161 try: 162 if filename == 'stdout': 163 # special case, log usual log file contents to stdout 164 handler = logging.StreamHandler(sys.__stdout__) 165 else: 166 handler = logging.FileHandler( 167 filename=filename, encoding='utf-8' 168 ) 169 self.logfile = filename 170 handler.setFormatter( 171 logging.Formatter( 172 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 173 '%H:%M:%S' 174 ) 175 ) 176 handler.addFilter(LoggerSchedulerFilter()) 177 self.addHandler(handler) 178 self.log_handlers['file'] = handler 179 except Exception as e: 180 raise KiwiLogFileSetupFailed( 181 f'{type(e).__name__}: {format(e)}' 182 ) 183 184 def set_log_socket(self, filename: str) -> None: 185 """ 186 Set log socket handler 187 188 :param str filename: 189 UDS socket file path. Note if there is no server 190 listening on the socket the log handler setup 191 will fail 192 """ 193 try: 194 handler = PlainTextSocketHandler(filename, None) 195 handler.makeSocket() 196 handler.setFormatter( 197 logging.Formatter( 198 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 199 '%H:%M:%S' 200 ) 201 ) 202 handler.addFilter(LoggerSchedulerFilter()) 203 self.addHandler(handler) 204 self.log_handlers['socket'] = handler 205 except Exception as e: 206 raise KiwiLogSocketSetupFailed( 207 'UDS socket: {0}:{1}: {2}'.format(filename, type(e).__name__, e) 208 ) 209 210 def get_logfile(self) -> Optional[str]: 211 """ 212 Return file path name of logfile 213 214 :return: file path 215 216 :rtype: str 217 """ 218 return self.logfile 219 220 @staticmethod 221 def progress( 222 current: int, total: int, prefix: str, bar_length: int = 40 223 ) -> None: 224 """ 225 Custom progress log information. progress information is 226 intentionally only logged to stdout and will bypass any 227 handlers. We don't want this information to show up in 228 the log file 229 230 :param int current: current item 231 :param int total: total number of items 232 :param string prefix: prefix name 233 :param int bar_length: length of progress bar 234 """ 235 try: 236 percent = float(current) / total 237 except Exception: 238 # we don't want the progress to raise an exception 239 # In case of any error e.g division by zero the current 240 # way out is to skip the progress update 241 return 242 hashes = '#' * int(round(percent * bar_length)) 243 spaces = ' ' * (bar_length - len(hashes)) 244 sys.stdout.write('\r{0}: [{1}] {2}%'.format( 245 prefix, hashes + spaces, int(round(percent * 100)) 246 )) 247 if current == 100: 248 sys.stdout.write('\n') 249 sys.stdout.flush() 250 251 def _add_stream_handler( 252 self, handler_type, message_format, message_filter, 253 channel=sys.__stdout__ 254 ): 255 handler = logging.StreamHandler(channel) 256 handler.setFormatter( 257 logging.Formatter(message_format, '%H:%M:%S') 258 ) 259 for rule in message_filter: 260 handler.addFilter(rule) 261 self.addHandler(handler) 262 self.log_handlers[handler_type] = handler
Extended logging facility based on Python logging
Parameters
- str name: name of the logger
Logger(name: str)
48 def __init__(self, name: str): 49 logging.Logger.__init__(self, name) 50 self.log_handlers: Dict = {} 51 self.logfile: Optional[str] = None 52 # log INFO to stdout 53 self._add_stream_handler( 54 'info', 55 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 56 [InfoFilter(), LoggerSchedulerFilter()] 57 ) 58 # log WARNING messages to stdout 59 self._add_stream_handler( 60 'warning', 61 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 62 [WarningFilter()] 63 ) 64 # log DEBUG messages to stdout 65 self._add_stream_handler( 66 'debug', 67 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 68 [DebugFilter()] 69 ) 70 # log ERROR messages to stderr 71 self._add_stream_handler( 72 'error', 73 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 74 [ErrorFilter()], 75 sys.__stderr__ 76 ) 77 self.log_level = self.level 78 self.log_flags: Dict[str, bool] = {}
Initialize the logger with a name and an optional level.
def
getLogLevel(self) -> int:
80 def getLogLevel(self) -> int: 81 """ 82 Return currently used log level 83 84 :return: log level number 85 86 :rtype: int 87 """ 88 return self.log_level
Return currently used log level
Returns
log level number
def
getLogFlags(self) -> Dict[str, bool]:
90 def getLogFlags(self) -> Dict[str, bool]: 91 """ 92 Return logging flags 93 94 :return: 95 Dictionary with flags and their activation status 96 97 :rtype: dict 98 """ 99 return self.log_flags
Return logging flags
Returns
Dictionary with flags and their activation status
def
setLogLevel( self, level: int, except_for: List[str] = [], only_for: List[str] = []) -> None:
101 def setLogLevel( 102 self, level: int, except_for: List[str] = [], only_for: List[str] = [] 103 ) -> None: 104 """ 105 Set custom log level for all console handlers 106 107 :param int level: log level number 108 :param list except_for: 109 set log level to all handlers except for the given list 110 :param list only_for: 111 set log level to the given handlers only 112 113 if both except_for and only_for handlers are specified, 114 the except_for list will be ignored 115 """ 116 self.log_level = level 117 if only_for: 118 for handler_type in self.log_handlers: 119 if handler_type in only_for: 120 self.log_handlers[handler_type].setLevel(level) 121 return 122 for handler_type in self.log_handlers: 123 if handler_type not in except_for: 124 self.log_handlers[handler_type].setLevel(level)
Set custom log level for all console handlers
Parameters
- int level: log level number
- list except_for: set log level to all handlers except for the given list
- list only_for: set log level to the given handlers only
if both except_for and only_for handlers are specified, the except_for list will be ignored
def
setLogFlag(self, flag: str, value: bool = True) -> None:
126 def setLogFlag(self, flag: str, value: bool = True) -> None: 127 """ 128 Set logging flag for further properties of the logging facility 129 Available flags are: 130 131 * run-scripts-in-screen 132 133 :param str flag: name 134 """ 135 self.log_flags[flag] = value
Set logging flag for further properties of the logging facility Available flags are:
- run-scripts-in-screen
Parameters
- str flag: name
def
set_color_format(self) -> None:
137 def set_color_format(self) -> None: 138 """ 139 Set color format for all console handlers 140 """ 141 for handler_type in self.log_handlers: 142 message_format = None 143 if handler_type == 'debug': 144 message_format = \ 145 '$LIGHTCOLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 146 elif handler_type == 'warning' or handler_type == 'error': 147 message_format = \ 148 '$COLOR[ %(levelname)-8s]: %(asctime)-8s | %(message)s' 149 150 if message_format: 151 self.log_handlers[handler_type].setFormatter( 152 ColorFormatter(message_format, '%H:%M:%S') 153 )
Set color format for all console handlers
def
set_logfile(self, filename: str) -> None:
155 def set_logfile(self, filename: str) -> None: 156 """ 157 Set logfile handler 158 159 :param str filename: logfile file path 160 """ 161 try: 162 if filename == 'stdout': 163 # special case, log usual log file contents to stdout 164 handler = logging.StreamHandler(sys.__stdout__) 165 else: 166 handler = logging.FileHandler( 167 filename=filename, encoding='utf-8' 168 ) 169 self.logfile = filename 170 handler.setFormatter( 171 logging.Formatter( 172 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 173 '%H:%M:%S' 174 ) 175 ) 176 handler.addFilter(LoggerSchedulerFilter()) 177 self.addHandler(handler) 178 self.log_handlers['file'] = handler 179 except Exception as e: 180 raise KiwiLogFileSetupFailed( 181 f'{type(e).__name__}: {format(e)}' 182 )
Set logfile handler
Parameters
- str filename: logfile file path
def
set_log_socket(self, filename: str) -> None:
184 def set_log_socket(self, filename: str) -> None: 185 """ 186 Set log socket handler 187 188 :param str filename: 189 UDS socket file path. Note if there is no server 190 listening on the socket the log handler setup 191 will fail 192 """ 193 try: 194 handler = PlainTextSocketHandler(filename, None) 195 handler.makeSocket() 196 handler.setFormatter( 197 logging.Formatter( 198 '[ %(levelname)-8s]: %(asctime)-8s | %(message)s', 199 '%H:%M:%S' 200 ) 201 ) 202 handler.addFilter(LoggerSchedulerFilter()) 203 self.addHandler(handler) 204 self.log_handlers['socket'] = handler 205 except Exception as e: 206 raise KiwiLogSocketSetupFailed( 207 'UDS socket: {0}:{1}: {2}'.format(filename, type(e).__name__, e) 208 )
Set log socket handler
Parameters
- str filename: UDS socket file path. Note if there is no server listening on the socket the log handler setup will fail
def
get_logfile(self) -> Optional[str]:
210 def get_logfile(self) -> Optional[str]: 211 """ 212 Return file path name of logfile 213 214 :return: file path 215 216 :rtype: str 217 """ 218 return self.logfile
Return file path name of logfile
Returns
file path
@staticmethod
def
progress(current: int, total: int, prefix: str, bar_length: int = 40) -> None:
220 @staticmethod 221 def progress( 222 current: int, total: int, prefix: str, bar_length: int = 40 223 ) -> None: 224 """ 225 Custom progress log information. progress information is 226 intentionally only logged to stdout and will bypass any 227 handlers. We don't want this information to show up in 228 the log file 229 230 :param int current: current item 231 :param int total: total number of items 232 :param string prefix: prefix name 233 :param int bar_length: length of progress bar 234 """ 235 try: 236 percent = float(current) / total 237 except Exception: 238 # we don't want the progress to raise an exception 239 # In case of any error e.g division by zero the current 240 # way out is to skip the progress update 241 return 242 hashes = '#' * int(round(percent * bar_length)) 243 spaces = ' ' * (bar_length - len(hashes)) 244 sys.stdout.write('\r{0}: [{1}] {2}%'.format( 245 prefix, hashes + spaces, int(round(percent * 100)) 246 )) 247 if current == 100: 248 sys.stdout.write('\n') 249 sys.stdout.flush()
Custom progress log information. progress information is intentionally only logged to stdout and will bypass any handlers. We don't want this information to show up in the log file
Parameters
- int current: current item
- int total: total number of items
- string prefix: prefix name
- int bar_length: length of progress bar