~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/logfile.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_logfile -*-
 
2
 
 
3
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
"""
 
7
A rotating, browsable log file.
 
8
"""
 
9
 
 
10
# System Imports
 
11
import os, glob, time, stat
 
12
 
 
13
from twisted.python import threadable
 
14
 
 
15
 
 
16
 
 
17
class BaseLogFile:
 
18
    """
 
19
    The base class for a log file that can be rotated.
 
20
    """
 
21
 
 
22
    synchronized = ["write", "rotate"]
 
23
 
 
24
    def __init__(self, name, directory, defaultMode=None):
 
25
        """
 
26
        Create a log file.
 
27
 
 
28
        @param name: name of the file
 
29
        @param directory: directory holding the file
 
30
        @param defaultMode: permissions used to create the file. Default to
 
31
        current permissions of the file if the file exists.
 
32
        """
 
33
        self.directory = directory
 
34
        assert os.path.isdir(self.directory)
 
35
        self.name = name
 
36
        self.path = os.path.join(directory, name)
 
37
        if defaultMode is None and os.path.exists(self.path):
 
38
            self.defaultMode = stat.S_IMODE(os.stat(self.path)[stat.ST_MODE])
 
39
        else:
 
40
            self.defaultMode = defaultMode
 
41
        self._openFile()
 
42
 
 
43
    def fromFullPath(cls, filename, *args, **kwargs):
 
44
        """
 
45
        Construct a log file from a full file path.
 
46
        """
 
47
        logPath = os.path.abspath(filename)
 
48
        return cls(os.path.basename(logPath),
 
49
                   os.path.dirname(logPath), *args, **kwargs)
 
50
    fromFullPath = classmethod(fromFullPath)
 
51
 
 
52
    def shouldRotate(self):
 
53
        """
 
54
        Override with a method to that returns true if the log
 
55
        should be rotated.
 
56
        """
 
57
        raise NotImplementedError
 
58
 
 
59
    def _openFile(self):
 
60
        """
 
61
        Open the log file.
 
62
        """
 
63
        self.closed = False
 
64
        if os.path.exists(self.path):
 
65
            self._file = file(self.path, "r+", 1)
 
66
            self._file.seek(0, 2)
 
67
        else:
 
68
            if self.defaultMode is not None:
 
69
                # Set the lowest permissions
 
70
                oldUmask = os.umask(0777)
 
71
                try:
 
72
                    self._file = file(self.path, "w+", 1)
 
73
                finally:
 
74
                    os.umask(oldUmask)
 
75
            else:
 
76
                self._file = file(self.path, "w+", 1)
 
77
        if self.defaultMode is not None:
 
78
            try:
 
79
                os.chmod(self.path, self.defaultMode)
 
80
            except OSError:
 
81
                # Probably /dev/null or something?
 
82
                pass
 
83
 
 
84
    def __getstate__(self):
 
85
        state = self.__dict__.copy()
 
86
        del state["_file"]
 
87
        return state
 
88
 
 
89
    def __setstate__(self, state):
 
90
        self.__dict__ = state
 
91
        self._openFile()
 
92
 
 
93
    def write(self, data):
 
94
        """
 
95
        Write some data to the file.
 
96
        """
 
97
        if self.shouldRotate():
 
98
            self.flush()
 
99
            self.rotate()
 
100
        self._file.write(data)
 
101
 
 
102
    def flush(self):
 
103
        """
 
104
        Flush the file.
 
105
        """
 
106
        self._file.flush()
 
107
 
 
108
    def close(self):
 
109
        """
 
110
        Close the file.
 
111
 
 
112
        The file cannot be used once it has been closed.
 
113
        """
 
114
        self.closed = True
 
115
        self._file.close()
 
116
        self._file = None
 
117
 
 
118
 
 
119
    def reopen(self):
 
120
        """
 
121
        Reopen the log file. This is mainly useful if you use an external log
 
122
        rotation tool, which moves under your feet.
 
123
 
 
124
        Note that on Windows you probably need a specific API to rename the
 
125
        file, as it's not supported to simply use os.rename, for example.
 
126
        """
 
127
        self.close()
 
128
        self._openFile()
 
129
 
 
130
 
 
131
    def getCurrentLog(self):
 
132
        """
 
133
        Return a LogReader for the current log file.
 
134
        """
 
135
        return LogReader(self.path)
 
136
 
 
137
 
 
138
class LogFile(BaseLogFile):
 
139
    """
 
140
    A log file that can be rotated.
 
141
 
 
142
    A rotateLength of None disables automatic log rotation.
 
143
    """
 
144
    def __init__(self, name, directory, rotateLength=1000000, defaultMode=None,
 
145
                 maxRotatedFiles=None):
 
146
        """
 
147
        Create a log file rotating on length.
 
148
 
 
149
        @param name: file name.
 
150
        @type name: C{str}
 
151
        @param directory: path of the log file.
 
152
        @type directory: C{str}
 
153
        @param rotateLength: size of the log file where it rotates. Default to
 
154
            1M.
 
155
        @type rotateLength: C{int}
 
156
        @param defaultMode: mode used to create the file.
 
157
        @type defaultMode: C{int}
 
158
        @param maxRotatedFiles: if not None, max number of log files the class
 
159
            creates. Warning: it removes all log files above this number.
 
160
        @type maxRotatedFiles: C{int}
 
161
        """
 
162
        BaseLogFile.__init__(self, name, directory, defaultMode)
 
163
        self.rotateLength = rotateLength
 
164
        self.maxRotatedFiles = maxRotatedFiles
 
165
 
 
166
    def _openFile(self):
 
167
        BaseLogFile._openFile(self)
 
168
        self.size = self._file.tell()
 
169
 
 
170
    def shouldRotate(self):
 
171
        """
 
172
        Rotate when the log file size is larger than rotateLength.
 
173
        """
 
174
        return self.rotateLength and self.size >= self.rotateLength
 
175
 
 
176
    def getLog(self, identifier):
 
177
        """
 
178
        Given an integer, return a LogReader for an old log file.
 
179
        """
 
180
        filename = "%s.%d" % (self.path, identifier)
 
181
        if not os.path.exists(filename):
 
182
            raise ValueError, "no such logfile exists"
 
183
        return LogReader(filename)
 
184
 
 
185
    def write(self, data):
 
186
        """
 
187
        Write some data to the file.
 
188
        """
 
189
        BaseLogFile.write(self, data)
 
190
        self.size += len(data)
 
191
 
 
192
    def rotate(self):
 
193
        """
 
194
        Rotate the file and create a new one.
 
195
 
 
196
        If it's not possible to open new logfile, this will fail silently,
 
197
        and continue logging to old logfile.
 
198
        """
 
199
        if not (os.access(self.directory, os.W_OK) and os.access(self.path, os.W_OK)):
 
200
            return
 
201
        logs = self.listLogs()
 
202
        logs.reverse()
 
203
        for i in logs:
 
204
            if self.maxRotatedFiles is not None and i >= self.maxRotatedFiles:
 
205
                os.remove("%s.%d" % (self.path, i))
 
206
            else:
 
207
                os.rename("%s.%d" % (self.path, i), "%s.%d" % (self.path, i + 1))
 
208
        self._file.close()
 
209
        os.rename(self.path, "%s.1" % self.path)
 
210
        self._openFile()
 
211
 
 
212
    def listLogs(self):
 
213
        """
 
214
        Return sorted list of integers - the old logs' identifiers.
 
215
        """
 
216
        result = []
 
217
        for name in glob.glob("%s.*" % self.path):
 
218
            try:
 
219
                counter = int(name.split('.')[-1])
 
220
                if counter:
 
221
                    result.append(counter)
 
222
            except ValueError:
 
223
                pass
 
224
        result.sort()
 
225
        return result
 
226
 
 
227
    def __getstate__(self):
 
228
        state = BaseLogFile.__getstate__(self)
 
229
        del state["size"]
 
230
        return state
 
231
 
 
232
threadable.synchronize(LogFile)
 
233
 
 
234
 
 
235
class DailyLogFile(BaseLogFile):
 
236
    """A log file that is rotated daily (at or after midnight localtime)
 
237
    """
 
238
    def _openFile(self):
 
239
        BaseLogFile._openFile(self)
 
240
        self.lastDate = self.toDate(os.stat(self.path)[8])
 
241
 
 
242
    def shouldRotate(self):
 
243
        """Rotate when the date has changed since last write"""
 
244
        return self.toDate() > self.lastDate
 
245
 
 
246
    def toDate(self, *args):
 
247
        """Convert a unixtime to (year, month, day) localtime tuple,
 
248
        or return the current (year, month, day) localtime tuple.
 
249
 
 
250
        This function primarily exists so you may overload it with
 
251
        gmtime, or some cruft to make unit testing possible.
 
252
        """
 
253
        # primarily so this can be unit tested easily
 
254
        return time.localtime(*args)[:3]
 
255
 
 
256
    def suffix(self, tupledate):
 
257
        """Return the suffix given a (year, month, day) tuple or unixtime"""
 
258
        try:
 
259
            return '_'.join(map(str, tupledate))
 
260
        except:
 
261
            # try taking a float unixtime
 
262
            return '_'.join(map(str, self.toDate(tupledate)))
 
263
 
 
264
    def getLog(self, identifier):
 
265
        """Given a unix time, return a LogReader for an old log file."""
 
266
        if self.toDate(identifier) == self.lastDate:
 
267
            return self.getCurrentLog()
 
268
        filename = "%s.%s" % (self.path, self.suffix(identifier))
 
269
        if not os.path.exists(filename):
 
270
            raise ValueError, "no such logfile exists"
 
271
        return LogReader(filename)
 
272
 
 
273
    def write(self, data):
 
274
        """Write some data to the log file"""
 
275
        BaseLogFile.write(self, data)
 
276
        # Guard against a corner case where time.time()
 
277
        # could potentially run backwards to yesterday.
 
278
        # Primarily due to network time.
 
279
        self.lastDate = max(self.lastDate, self.toDate())
 
280
 
 
281
    def rotate(self):
 
282
        """Rotate the file and create a new one.
 
283
 
 
284
        If it's not possible to open new logfile, this will fail silently,
 
285
        and continue logging to old logfile.
 
286
        """
 
287
        if not (os.access(self.directory, os.W_OK) and os.access(self.path, os.W_OK)):
 
288
            return
 
289
        newpath = "%s.%s" % (self.path, self.suffix(self.lastDate))
 
290
        if os.path.exists(newpath):
 
291
            return
 
292
        self._file.close()
 
293
        os.rename(self.path, newpath)
 
294
        self._openFile()
 
295
 
 
296
    def __getstate__(self):
 
297
        state = BaseLogFile.__getstate__(self)
 
298
        del state["lastDate"]
 
299
        return state
 
300
 
 
301
threadable.synchronize(DailyLogFile)
 
302
 
 
303
 
 
304
class LogReader:
 
305
    """Read from a log file."""
 
306
 
 
307
    def __init__(self, name):
 
308
        self._file = file(name, "r")
 
309
 
 
310
    def readLines(self, lines=10):
 
311
        """Read a list of lines from the log file.
 
312
 
 
313
        This doesn't returns all of the files lines - call it multiple times.
 
314
        """
 
315
        result = []
 
316
        for i in range(lines):
 
317
            line = self._file.readline()
 
318
            if not line:
 
319
                break
 
320
            result.append(line)
 
321
        return result
 
322
 
 
323
    def close(self):
 
324
        self._file.close()