8 |
8 |
9 from __future__ import absolute_import |
9 from __future__ import absolute_import |
10 |
10 |
11 import errno |
11 import errno |
12 |
12 |
13 from . import ( |
13 from . import pycompat |
14 pycompat, |
|
15 ) |
|
16 |
14 |
17 from .utils import ( |
15 from .utils import ( |
18 dateutil, |
16 dateutil, |
19 procutil, |
17 procutil, |
20 stringutil, |
18 stringutil, |
21 ) |
19 ) |
22 |
20 |
|
21 |
23 def openlogfile(ui, vfs, name, maxfiles=0, maxsize=0): |
22 def openlogfile(ui, vfs, name, maxfiles=0, maxsize=0): |
24 """Open log file in append mode, with optional rotation |
23 """Open log file in append mode, with optional rotation |
25 |
24 |
26 If maxsize > 0, the log file will be rotated up to maxfiles. |
25 If maxsize > 0, the log file will be rotated up to maxfiles. |
27 """ |
26 """ |
|
27 |
28 def rotate(oldpath, newpath): |
28 def rotate(oldpath, newpath): |
29 try: |
29 try: |
30 vfs.unlink(newpath) |
30 vfs.unlink(newpath) |
31 except OSError as err: |
31 except OSError as err: |
32 if err.errno != errno.ENOENT: |
32 if err.errno != errno.ENOENT: |
33 ui.debug("warning: cannot remove '%s': %s\n" % |
33 ui.debug( |
34 (newpath, err.strerror)) |
34 "warning: cannot remove '%s': %s\n" |
|
35 % (newpath, err.strerror) |
|
36 ) |
35 try: |
37 try: |
36 if newpath: |
38 if newpath: |
37 vfs.rename(oldpath, newpath) |
39 vfs.rename(oldpath, newpath) |
38 except OSError as err: |
40 except OSError as err: |
39 if err.errno != errno.ENOENT: |
41 if err.errno != errno.ENOENT: |
40 ui.debug("warning: cannot rename '%s' to '%s': %s\n" % |
42 ui.debug( |
41 (newpath, oldpath, err.strerror)) |
43 "warning: cannot rename '%s' to '%s': %s\n" |
|
44 % (newpath, oldpath, err.strerror) |
|
45 ) |
42 |
46 |
43 if maxsize > 0: |
47 if maxsize > 0: |
44 try: |
48 try: |
45 st = vfs.stat(name) |
49 st = vfs.stat(name) |
46 except OSError: |
50 except OSError: |
47 pass |
51 pass |
48 else: |
52 else: |
49 if st.st_size >= maxsize: |
53 if st.st_size >= maxsize: |
50 path = vfs.join(name) |
54 path = vfs.join(name) |
51 for i in pycompat.xrange(maxfiles - 1, 1, -1): |
55 for i in pycompat.xrange(maxfiles - 1, 1, -1): |
52 rotate(oldpath='%s.%d' % (path, i - 1), |
56 rotate( |
53 newpath='%s.%d' % (path, i)) |
57 oldpath='%s.%d' % (path, i - 1), |
54 rotate(oldpath=path, |
58 newpath='%s.%d' % (path, i), |
55 newpath=maxfiles > 0 and path + '.1') |
59 ) |
|
60 rotate(oldpath=path, newpath=maxfiles > 0 and path + '.1') |
56 return vfs(name, 'a', makeparentdirs=False) |
61 return vfs(name, 'a', makeparentdirs=False) |
|
62 |
57 |
63 |
58 def _formatlogline(msg): |
64 def _formatlogline(msg): |
59 date = dateutil.datestr(format=b'%Y/%m/%d %H:%M:%S') |
65 date = dateutil.datestr(format=b'%Y/%m/%d %H:%M:%S') |
60 pid = procutil.getpid() |
66 pid = procutil.getpid() |
61 return b'%s (%d)> %s' % (date, pid, msg) |
67 return b'%s (%d)> %s' % (date, pid, msg) |
62 |
68 |
|
69 |
63 def _matchevent(event, tracked): |
70 def _matchevent(event, tracked): |
64 return b'*' in tracked or event in tracked |
71 return b'*' in tracked or event in tracked |
|
72 |
65 |
73 |
66 class filelogger(object): |
74 class filelogger(object): |
67 """Basic logger backed by physical file with optional rotation""" |
75 """Basic logger backed by physical file with optional rotation""" |
68 |
76 |
69 def __init__(self, vfs, name, tracked, maxfiles=0, maxsize=0): |
77 def __init__(self, vfs, name, tracked, maxfiles=0, maxsize=0): |
77 return _matchevent(event, self._trackedevents) |
85 return _matchevent(event, self._trackedevents) |
78 |
86 |
79 def log(self, ui, event, msg, opts): |
87 def log(self, ui, event, msg, opts): |
80 line = _formatlogline(msg) |
88 line = _formatlogline(msg) |
81 try: |
89 try: |
82 with openlogfile(ui, self._vfs, self._name, |
90 with openlogfile( |
83 maxfiles=self._maxfiles, |
91 ui, |
84 maxsize=self._maxsize) as fp: |
92 self._vfs, |
|
93 self._name, |
|
94 maxfiles=self._maxfiles, |
|
95 maxsize=self._maxsize, |
|
96 ) as fp: |
85 fp.write(line) |
97 fp.write(line) |
86 except IOError as err: |
98 except IOError as err: |
87 ui.debug(b'cannot write to %s: %s\n' |
99 ui.debug( |
88 % (self._name, stringutil.forcebytestr(err))) |
100 b'cannot write to %s: %s\n' |
|
101 % (self._name, stringutil.forcebytestr(err)) |
|
102 ) |
|
103 |
89 |
104 |
90 class fileobjectlogger(object): |
105 class fileobjectlogger(object): |
91 """Basic logger backed by file-like object""" |
106 """Basic logger backed by file-like object""" |
92 |
107 |
93 def __init__(self, fp, tracked): |
108 def __init__(self, fp, tracked): |