Skip to content

Commit f4213ea

Browse files
committed
Properly format log record & report logged extras to rollbar
This change passes the log record through the configured formatter, if any, and reports any non blocklisted logging record keys as extra data. This allows supporting Python logging's `extra` keyword argument which sets additional properties on the log record.
1 parent 7a74332 commit f4213ea

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

rollbar/logger.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
_checkLevel = lambda lvl: lvl
3333

3434

35+
EXCLUDE_RECORD_KEYS = {
36+
# Attributes that are disallowed in `logging.Logger.makeRecord`
37+
'asctime',
38+
'message',
39+
# Attributes that are used internally by pyrollbar
40+
'extra_data',
41+
'payload_data',
42+
'request',
43+
*vars(logging.makeLogRecord({})).keys(),
44+
}
45+
46+
3547
def resolve_logging_types(obj):
3648
if isinstance(obj, (dict, ConvertingDict)):
3749
return {k: resolve_logging_types(v) for k, v in obj.items()}
@@ -116,9 +128,10 @@ def emit(self, record):
116128
'thread': record.thread,
117129
'threadName': record.threadName
118130
}
119-
}
120-
121-
extra_data.update(getattr(record, 'extra_data', {}))
131+
} | { # include any extras
132+
k: v for k, v in vars(record).items()
133+
if k not in EXCLUDE_RECORD_KEYS
134+
} | getattr(record, 'extra_data', {}) # include historical extra_data
122135

123136
payload_data = getattr(record, 'payload_data', {})
124137

@@ -133,6 +146,15 @@ def emit(self, record):
133146
# load the request
134147
request = getattr(record, "request", None) or rollbar.get_request()
135148

149+
# Rather than copy the log record and disable exception and stack trace
150+
# formatting, this does the same steps to prepare the log record
151+
# as `logging.Formatter.format` does before calling
152+
# `logging.Formatter.formatMessage`.
153+
formatter = self.formatter or logging._defaultFormatter
154+
record = record.getMessage()
155+
if formatter.usesTime():
156+
record.asctime = formatter.formatTime(record, formatter.datefmt)
157+
136158
uuid = None
137159
try:
138160
# when not in an exception handler, exc_info == (None, None, None)
@@ -142,7 +164,9 @@ def emit(self, record):
142164
'body': {
143165
'trace': {
144166
'exception': {
145-
'description': record.getMessage()
167+
'description': formatter.formatMessage(
168+
record
169+
)
146170
}
147171
}
148172
}
@@ -156,7 +180,7 @@ def emit(self, record):
156180
extra_data=extra_data,
157181
payload_data=payload_data)
158182
else:
159-
uuid = rollbar.report_message(record.getMessage(),
183+
uuid = rollbar.report_message(formatter.formatMessage(record),
160184
level=level,
161185
request=request,
162186
extra_data=extra_data,

rollbar/test/test_loghandler.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,21 @@ def _raise_ex():
165165
self.assertEqual('Bad time', payload['data']['custom']['exception']['description'])
166166
if trace is not None:
167167
self.assertEqual('Bad time', trace['exception']['description'])
168+
169+
@mock.patch('rollbar.send_payload')
170+
def test_logging_custom_extra(self, send_payload):
171+
logger = self._create_logger()
172+
logger.error("Test error", extra=dict(test_attribute=1, test_other='test'))
173+
174+
payload = send_payload.call_args[0][0]
175+
self.assertEqual(payload['data']['body'].get('test_attribute'), 1)
176+
self.assertEqual(payload['data']['body'].get('test_other'), 'test')
177+
178+
@mock.patch('rollbar.send_payload')
179+
def test_logging_extra_data(self, send_payload):
180+
logger = self._create_logger()
181+
logger.error("Test error", extra_data=dict(test_attribute=1, test_other='test'))
182+
183+
payload = send_payload.call_args[0][0]
184+
self.assertEqual(payload['data']['body'].get('test_attribute'), 1)
185+
self.assertEqual(payload['data']['body'].get('test_other'), 'test')

0 commit comments

Comments
 (0)