aboutsummaryrefslogtreecommitdiffstats
path: root/dogtail/logging.py
blob: 7e73f163b832303aee05d1fbbe9a885ea8d8d98d (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# -*- coding: utf-8 -*-
"""
Logging facilities

Authors: Ed Rousseau <rousseau@redhat.com>, Zack Cerza <zcerza@redhat.com, David Malcolm <dmalcolm@redhat.com>
"""

__author__ = """Ed Rousseau <rousseau@redhat.com>,
Zack Cerza <zcerza@redhat.com,
David Malcolm <dmalcolm@redhat.com>
"""
import os
import sys
import time
from config import config
import codecs

# Timestamp class for file logs


class TimeStamp(object):

    """
    Generates timestamps tempfiles and log entries
    """

    def __init__(self):
        self.now = "0"
        self.timetup = time.localtime()

    def zeroPad(self, int, width=2):
        """
        Pads an integer 'int' with zeroes, up to width 'width'.

        Returns a string.

        It will not truncate. If you call zeroPad(100, 2), '100' will be returned.
        """
        if int < 10 ** width:
            return ("0" * (width - len(str(int)))) + str(int)
        else:
            return str(int)

    # file stamper
    def fileStamp(self, filename, addTime=True):
        """
        Generates a filename stamp in the format of filename_YYYYMMDD-hhmmss.
        A format of filename_YYYYMMDD can be used instead by specifying addTime = False.
        """
        self.now = filename.strip() + "_"
        self.timetup = time.localtime()

        # Should produce rel-eng style filestamps
        # format it all pretty by chopping the tuple
        fieldCount = 3
        if addTime:
            fieldCount = fieldCount + 3
        for i in range(fieldCount):
            if i == 3:
                self.now = self.now + '-'
            self.now = self.now + self.zeroPad(self.timetup[i])
        return self.now

    # Log entry stamper
    def entryStamp(self):
        """
        Generates a logfile entry stamp of YYYY.MM.DD HH:MM:SS
        """
        self.timetup = time.localtime()

        # This will return a log entry formatted string in YYYY.MM.DD HH:MM:SS
        for i in range(6):
            # put in the year
            if i == 0:
                self.now = str(self.timetup[i])
            # Format Month and Day
            elif i == 1 or i == 2:
                self.now = self.now + "." + self.zeroPad(self.timetup[i])
            else:
                # make the " " between Day and Hour and put in the hour
                if i == 3:
                    self.now = self.now + " " + self.zeroPad(self.timetup[i])
                # Otherwise Use the ":" divider
                else:
                    self.now = self.now + ":" + self.zeroPad(self.timetup[i])
        return self.now


class Logger(object):

    """
    Writes entries to standard out.
    """
    stamper = TimeStamp()

    def __init__(self, logName, file=False, stdOut=True):
        """
        name: the name of the log
        file: The file object to log to.
        stdOut: Whether to log to standard out.
        """
        self.logName = logName
        self.stdOut = stdOut
        self.file = file  # Handle to the logfile
        if not self.file:
            return

        scriptName = config.scriptName
        if not scriptName:
            scriptName = 'log'
        self.fileName = scriptName

        # check to see if we can write to the logDir
        if os.path.isdir(config.logDir):
            self.findUniqueName()
        else:
            # If path doesn't exist, raise an exception
            raise IOError(
                "Log path %s does not exist or is not a directory" % config.logDir)

    def findUniqueName(self):
        # generate a logfile name and check if it already exists
        self.fileName = config.logDir + self.stamper.fileStamp(self.fileName) \
            + '_' + self.logName
        i = 0
        while os.path.exists(self.fileName):
            # Append the pathname
            if i == 0:
                self.fileName = self.fileName + "." + str(i)
            else:
                logsplit = self.fileName.split(".")
                logsplit[-1] = str(i)
                self.fileName = ".".join(logsplit)
            i += 1

    def createFile(self):
        # Try to create the file and write the header info
        print("Creating logfile at %s ..." % self.fileName)
        self.file = codecs.open(self.fileName, mode='wb', encoding=
                                'utf-8')
        self.file.write("##### " + os.path.basename(self.fileName) + '\n')
        self.file.flush()

    def log(self, message, newline=True, force=False):
        """
        Hook used for logging messages. Might eventually be a virtual
        function, but nice and simple for now.

        If force is True, log to a file irrespective of config.logDebugToFile.
        """
        try:
            message = message.decode('utf-8', 'replace')
        except UnicodeEncodeError:
            pass


        # Try to open and write the result to the log file.
        if isinstance(self.file, bool) and (force or config.logDebugToFile):
            self.createFile()

        if force or config.logDebugToFile:
            if newline:
                self.file.write(message + '\n')
            else:
                self.file.write(message + ' ')
            self.file.flush()

        if self.stdOut and config.logDebugToStdOut:
            if newline:
                print(message)
            else:
                print(message)


class ResultsLogger(Logger):

    """
    Writes entries into the Dogtail log
    """

    def __init__(self, stdOut=True):
        Logger.__init__(self, 'results', file=True, stdOut=stdOut)

    # Writes the result of a test case comparison to the log
    def log(self, entry):
        """
        Writes the log entry. Requires a 1 {key: value} pair dict for an argument or else it will throw an exception.
        """
        # We require a 1 key: value dict
        # Strip all leading and trailing witespace from entry dict and convert
    # to string for writing

        if len(entry) == 1:
            key = entry.keys()
            value = entry.values()
            key = key[0]
            value = value[0]
            entry = str(key) + ":      " + str(value)
        else:
            raise ValueError(entry)
            print(
                "Method argument requires a 1 {key: value} dict. Supplied argument not one {key: value}")

        Logger.log(self, self.stamper.entryStamp() + "      " + entry,
                   force=True)

debugLogger = Logger('debug', config.logDebugToFile)

import traceback


def exceptionHook(exc, value, tb):  # pragma: no cover
    tbStringList = traceback.format_exception(exc, value, tb)
    tbString = ''.join(tbStringList)
    debugLogger.log(tbString)
    sys.exc_clear()

sys.excepthook = exceptionHook