1import time 2import logging 3 4from .prefix import addUserDataPrefix 5 6newName = time.strftime("%Y-%m-%d_%H-%M-%S") + ".log" 7logformat = "%(asctime)s.%(msecs)03d %(task)s %(levelname)s: %(message)s" 8 9# delay=True argument prevents creating empty .log files 10file_handler = logging.FileHandler( 11 addUserDataPrefix(newName), 12 delay=True, 13 encoding="utf-8") 14 15 16class TaskFormatter(logging.Formatter): 17 def __init__(self, fmt=None, datefmt=None): 18 logging.Formatter.__init__(self, fmt, datefmt) 19 20 def format(self, record): 21 if not hasattr(record, "task"): 22 record.task = "unknown" 23 24 record.message = record.getMessage() 25 record.asctime = self.formatTime(record, self.datefmt) 26 27 s = self._fmt % record.__dict__ 28 29 if record.exc_info: 30 # Cache the traceback text to avoid converting it multiple times 31 # (it's constant anyway) 32 if not record.exc_text: 33 record.exc_text = self.formatException(record.exc_info) 34 if record.exc_text: 35 if s[-1:] != "\n": 36 s = s + "\n" 37 s = s + record.exc_text 38 39 return s 40 41 42formatter = TaskFormatter(fmt=logformat, datefmt='%H:%M:%S') 43file_handler.setFormatter(formatter) 44 45logger = logging.getLogger() 46logger.addHandler(file_handler) 47 48 49class ExtraAdapter(logging.LoggerAdapter): 50 def process(self, msg, kwargs): 51 kwargs["extra"] = kwargs.get("extra", {"task": "Default"}) 52 return msg, kwargs 53 54 55log = ExtraAdapter(logger, {}) 56 57 58class LoggerWriter: 59 def __init__(self, logger, level): 60 self.logger = logger 61 self.level = level 62 63 def write(self, message): 64 if message != '\n': 65 self.logger.log(self.level, message) 66 67 def flush(self): 68 pass 69 70 71def setup_glib_logging(): 72 """ Code from https://github.com/GNOME/meld/blob/master/bin/meld """ 73 from gi.repository import GLib 74 levels = { 75 GLib.LogLevelFlags.LEVEL_DEBUG: logging.DEBUG, 76 GLib.LogLevelFlags.LEVEL_INFO: logging.INFO, 77 GLib.LogLevelFlags.LEVEL_MESSAGE: logging.INFO, 78 GLib.LogLevelFlags.LEVEL_WARNING: logging.WARNING, 79 GLib.LogLevelFlags.LEVEL_ERROR: logging.ERROR, 80 GLib.LogLevelFlags.LEVEL_CRITICAL: logging.CRITICAL, 81 } 82 83 # Just to make sphinx happy... 84 try: 85 level_flag = ( 86 GLib.LogLevelFlags.LEVEL_WARNING | 87 GLib.LogLevelFlags.LEVEL_ERROR | 88 GLib.LogLevelFlags.LEVEL_CRITICAL 89 ) 90 except TypeError: 91 level_flag = GLib.LogLevelFlags.LEVEL_INFO 92 93 log_domain = "Gtk" 94 log = logging.getLogger(log_domain) 95 96 def silence(message): 97 if "gtk_container_remove: assertion" in message: 98 # Looks like it was some hackish code in GTK+ which is now removed: 99 # https://git.gnome.org/browse/gtk+/commit/?id=a3805333361fee37a3b1a974cfa6452a85169f08 100 return True 101 elif "GdkPixbuf" in message: 102 return True 103 return False 104 105 # This logging handler is for "old" glib logging using a simple 106 # syslog-style API. 107 def log_adapter(domain, level, message, user_data): 108 if not silence(message): 109 log.log(levels.get(level, logging.WARNING), message) 110 111 try: 112 GLib.log_set_handler(log_domain, level_flag, log_adapter, None) 113 except AttributeError: 114 # Only present in glib 2.46+ 115 pass 116 117 # This logging handler is for new glib logging using a structured 118 # API. Unfortunately, it was added in such a way that the old 119 # redirection API became a no-op, so we need to hack both of these 120 # handlers to get it to work. 121 def structured_log_adapter(level, fields, field_count, user_data): 122 try: 123 message = GLib.log_writer_format_fields(level, fields, True) 124 except UnicodeDecodeError: 125 for field in fields: 126 print(field.key, field.value) 127 return GLib.LogWriterOutput.HANDLED 128 129 if not silence(message): 130 log.log(levels.get(level, logging.WARNING), message) 131 return GLib.LogWriterOutput.HANDLED 132 133 try: 134 GLib.log_set_writer_func(structured_log_adapter, None) 135 except AttributeError: 136 # Only present in glib 2.50+ 137 pass 138