File: log.py

package info (click to toggle)
bootstrap-vz 0.9.11%2B20180121git-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,244 kB
  • sloc: python: 8,800; sh: 813; makefile: 16
file content (104 lines) | stat: -rw-r--r-- 3,931 bytes parent folder | download | duplicates (2)
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
97
98
99
100
101
102
103
104
"""This module holds functions and classes responsible for formatting the log output
both to a file and to the console.
"""
import logging


def get_console_handler(debug, colorize):
    """Returns a log handler for the console
    The handler color codes the different log levels

    :params bool debug: Whether to set the log level to DEBUG (otherwise INFO)
    :params bool colorize: Whether to colorize console output
    :return: The console logging handler
    """
    # Create a console log handler
    import sys
    console_handler = logging.StreamHandler(sys.stderr)
    if colorize:
        # We want to colorize the output to the console, so we add a formatter
        console_handler.setFormatter(ColorFormatter())
    # Set the log level depending on the debug argument
    if debug:
        console_handler.setLevel(logging.DEBUG)
    else:
        console_handler.setLevel(logging.INFO)
    return console_handler


def get_file_handler(path, debug):
    """Returns a log handler for the given path
    If the parent directory of the logpath does not exist it will be created
    The handler outputs relative timestamps (to when it was created)

    :params str path: The full path to the logfile
    :params bool debug: Whether to set the log level to DEBUG (otherwise INFO)
    :return: The file logging handler
    """
    import os.path
    if not os.path.exists(os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
    # Create the log handler
    file_handler = logging.FileHandler(path)
    # Absolute timestamps are rather useless when bootstrapping, it's much more interesting
    # to see how long things take, so we log in a relative format instead
    file_handler.setFormatter(FileFormatter('[%(relativeCreated)s] %(levelname)s: %(message)s'))
    # The file log handler always logs everything
    file_handler.setLevel(logging.DEBUG)
    return file_handler


def get_log_filename(manifest_path):
    """Returns the path to a logfile given a manifest
    The logfile name is constructed from the current timestamp and the basename of the manifest

    :param str manifest_path: The path to the manifest
    :return: The path to the logfile
    :rtype: str
    """
    import os.path
    from datetime import datetime

    manifest_basename = os.path.basename(manifest_path)
    manifest_name, _ = os.path.splitext(manifest_basename)
    timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
    filename = '{timestamp}_{name}.log'.format(timestamp=timestamp, name=manifest_name)
    return filename


class SourceFormatter(logging.Formatter):
    """Adds a [source] tag to the log message if it exists
    The python docs suggest using a LoggingAdapter, but that would mean we'd
    have to use it everywhere we log something (and only when called remotely),
    which is not feasible.
    """

    def format(self, record):
        extra = getattr(record, 'extra', {})
        if 'source' in extra:
            record.msg = '[{source}] {message}'.format(source=record.extra['source'],
                                                       message=record.msg)
        return super(SourceFormatter, self).format(record)


class ColorFormatter(SourceFormatter):
    """Colorizes log messages depending on the loglevel
    """
    level_colors = {logging.ERROR: 'red',
                    logging.WARNING: 'magenta',
                    logging.INFO: 'blue',
                    }

    def format(self, record):
        # Colorize the message if we have a color for it (DEBUG has no color)
        from termcolor import colored
        record.msg = colored(record.msg, self.level_colors.get(record.levelno, None))
        return super(ColorFormatter, self).format(record)


class FileFormatter(SourceFormatter):
    """Formats log statements for output to file
    Currently this is just a stub
    """
    def format(self, record):
        return super(FileFormatter, self).format(record)