-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimple_observers.py
142 lines (103 loc) · 3.82 KB
/
simple_observers.py
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
import json
import sys
from twisted.python import log, util
from twisted.logger import formatEvent
class SimpleFileObserver(object):
def __init__(self, out, err):
self.out = out
self.err = err
def emit(self, eventDict):
text = log.textFromEventDict(eventDict)
if text is None:
return
text = text.replace("\n", "\n\t")
if eventDict.get('isError'):
f = self.err
else:
f = self.out
util.untilConcludes(f.write, text + '\n')
util.untilConcludes(f.flush)
def start(self):
"""
Start observing log events.
"""
log.addObserver(self.emit)
def stop(self):
"""
Stop observing log events.
"""
log.removeObserver(self.emit)
class KeyValueFileObserver(SimpleFileObserver):
''' Returns a string of formatted key=value pairs.
The string will include level, msg, and exc_*. Also includes any arbitrary
data that was included in the eventDict except for certain common and
uninteresting fields. All values in the key-value string are json-encoded.
Complex objects like lists and dicts are ignored.
For readability purposes the order of the fields is:
* level
* msg
* exc_type, if present
* exc_value, if present
* arbitrary additional data sorted by key
'''
def emit(self, event):
is_error = event.get('isError', False)
try:
text = self.format_kv(event)
except Exception, e:
text = 'Parse error: %s. Original message: %s' % \
(repr(e), formatEvent(event))
is_error = True
f = self.err if is_error else self.out
util.untilConcludes(f.write, text + '\n')
util.untilConcludes(f.flush)
def format_kv(self, event):
log_format = event.pop('log_format', None)
failure = event.pop('failure', None)
exc_type = failure and '%s.%s' % (failure.type.__module__, failure.type.__name__)
exc_value = failure and failure.getErrorMessage()
msg = log_format and log_format.format(**event)
message = event.pop('message')
if failure:
msg = message if message else '%s: %s' % (exc_type, exc_value)
if not msg:
msg = message
out = []
if event.get('isError'):
level = 'error'
else:
level = 'info'
out = [
('level', level),
('msg', msg),
]
if failure:
out.extend([
('exc_type', exc_type),
('exc_value', exc_value),
])
# Remove everything else that's prefixed with log_.
event = {k: v for k, v in event.items() if not k.startswith('log_')}
event.pop('message', None)
event.pop('system', None)
event.pop('format', None)
event.pop('time', None)
event.pop('isError', None)
event.pop('failure', None)
out.extend(sorted(event.items()))
return ' '.join(["%s=%s" % (k, json.dumps(v)) for (k, v) in out if not self.is_complex(v)])
def is_complex(self, v):
return isinstance(v, list) or isinstance(v, dict)
def SimpleStdoutLogger():
''' Writes everything to stdout. '''
return SimpleFileObserver(sys.stdout, sys.stdout).emit
def SimpleStreamLogger():
''' Writes output to stdout and errors to stderr. '''
return SimpleFileObserver(sys.stdout, sys.stderr).emit
def KeyValueStdoutLogger():
''' Writes output to stdout as structured key=value logs (logfmt). '''
return KeyValueFileObserver(sys.stdout, sys.stdout).emit
def KeyValueStreamLogger():
''' Writes output to stdout and errors to stderr as structured key=value
logs (logfmt). '''
return KeyValueFileObserver(sys.stdout, sys.stderr).emit