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