aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/windows/exelogging.py
blob: 9f3e0c2d2be0bad23c1b6b25f49b4673fc891d87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""lvc.windows.exelogging -- handle logging inside an exe file

Most of this is copied from the Miro code.
"""

import logging
import os
import sys
import tempfile
from StringIO import StringIO
from logging.handlers import RotatingFileHandler


class ApatheticRotatingFileHandler(RotatingFileHandler):
    """The whole purpose of this class is to prevent rotation errors
    from percolating up into stdout/stderr and popping up a dialog
    that's not particularly useful to users or us.
    """
    def doRollover(self):
        # If you shut down LibreVideoConverter then
        # start it up again immediately afterwards,
        # then we get in this squirrely situation where
        # the log is opened by another process.  We ignore the
        # exception, but make sure we have an open file.  (bug #11228)
        try:
            RotatingFileHandler.doRollover(self)
        except WindowsError:
            if not self.stream or self.stream.closed:
                self.stream = open(self.baseFilename, "a")
            try:
                RotatingFileHandler.doRollover(self)
            except WindowsError:
                pass

    def shouldRollover(self, record):
        # if doRollover doesn't work, then we don't want to find
        # ourselves in a situation where we're trying to do things on
        # a closed stream.
        if self.stream.closed:
            self.stream = open(self.baseFilename, "a")
        return RotatingFileHandler.shouldRollover(self, record)

    def handleError(self, record):
        # ignore logging errors that occur rather than printing them to
        # stdout/stderr which isn't helpful to us
        pass


class AutoLoggingStream(StringIO):
    """Create a stream that intercepts write calls and sends them to
    the log.
    """
    def __init__(self, logging_callback, prefix):
        StringIO.__init__(self)
        # We init from StringIO to give us a bunch of stream-related
        # methods, like closed() and read() automatically.
        self.logging_callback = logging_callback
        self.prefix = prefix

    def write(self, data):
        if isinstance(data, unicode):
            data = data.encode('ascii', 'backslashreplace')
        if data.endswith("\n"):
            data = data[:-1]
        if data:
            self.logging_callback(self.prefix + data)

FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s"


def setup_logging():
    """Setup logging for when we're running inside a windows exe.

    The object here is to avoid logging anything to stderr since
    windows will consider that an error.

    We also catch things written to sys.stdout and forward that to the logging
    system.

    Finally we also copy the log output to stdout so that when MVC is run in
    console mode we see the logs
    """

    log_path = os.path.join(tempfile.gettempdir(), "MVC.log")
    rotater = ApatheticRotatingFileHandler(
        log_path, mode="a", maxBytes=100000, backupCount=5)

    formatter = logging.Formatter(FORMAT)
    rotater.setFormatter(formatter)
    logger = logging.getLogger('')
    logger.addHandler(rotater)
    logger.addHandler(logging.StreamHandler(sys.stdout))
    logger.setLevel(logging.INFO)
    rotater.doRollover()
    sys.stdout = AutoLoggingStream(logging.warn, '(from stdout) ')
    sys.stderr = AutoLoggingStream(logging.error, '(from stderr) ')