Skip to content

Commit 1f9e19f

Browse files
michaelkedarandrewpollock
authored andcommitted
Allow for json fields in python logs (google#2052)
The way Error Reporting information was being added to logs was preventing us from adding additional `json_fields` to our logs. Changed from inserting Error Reporting information in a Log Record Factory to a Log Filter. See: https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information Also put the API logged fields back into the json data. --------- Co-authored-by: Andrew Pollock <[email protected]>
1 parent 8bb4916 commit 1f9e19f

File tree

2 files changed

+38
-29
lines changed

2 files changed

+38
-29
lines changed

gcp/api/server.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,18 @@ def QueryAffected(self, request, context: grpc.ServicerContext):
130130
# Log some information about the query with structured logging
131131
qtype, ecosystem, versioned = query_info(request.query)
132132
if ecosystem is not None:
133-
# TODO(michaelkedar): work out how to combine json_fields with osv/logs.py
134-
import json
135133
logging.info(
136-
'QueryAffected for %s "%s"\n%s', qtype, ecosystem,
137-
json.dumps({
138-
'details': {
139-
'ecosystem': ecosystem,
140-
'versioned': versioned == 'versioned'
134+
'QueryAffected for %s "%s"',
135+
qtype,
136+
ecosystem,
137+
extra={
138+
'json_fields': {
139+
'details': {
140+
'ecosystem': ecosystem,
141+
'versioned': versioned == 'versioned'
142+
}
141143
}
142-
}))
144+
})
143145
else:
144146
logging.info('QueryAffected for %s', qtype)
145147

@@ -217,11 +219,12 @@ def QueryAffectedBatch(self, request, context: grpc.ServicerContext):
217219
# Filter out empty fields
218220
query_details = {k: v for k, v in query_details.items() if v}
219221

220-
# TODO(michaelkedar): work out how to combine json_fields with osv/logs.py
221-
import json
222-
logging.info('QueryAffectedBatch with %d queries\n%s',
223-
len(request.query.queries),
224-
json.dumps({'details': query_details}))
222+
logging.info(
223+
'QueryAffectedBatch with %d queries',
224+
len(request.query.queries),
225+
extra={'json_fields': {
226+
'details': query_details
227+
}})
225228

226229
if len(request.query.queries) > _MAX_BATCH_QUERY:
227230
context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Too many queries.')

osv/logs.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,29 @@
1717
from google.cloud import logging as google_logging
1818

1919

20-
def setup_gcp_logging(service_name):
21-
"""Set up GCP logging and error reporting."""
20+
class _ErrorReportingFilter:
21+
"""
22+
A logging filter that adds necessary JSON fields to error logs so that they
23+
can be picked up by Error Reporting.
24+
25+
https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-text
26+
https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information
27+
"""
2228

23-
logging_client = google_logging.Client()
24-
logging_client.setup_logging()
29+
def __init__(self, service_name: str) -> None:
30+
self.service_name = service_name
2531

26-
old_factory = logging.getLogRecordFactory()
27-
28-
def record_factory(*args, **kwargs):
29-
"""Insert jsonPayload fields to all logs."""
30-
31-
record = old_factory(*args, **kwargs)
32+
def filter(self, record: logging.LogRecord) -> bool:
33+
"""Add the error reporting fields to json_fields."""
3234
if not hasattr(record, 'json_fields'):
3335
record.json_fields = {}
3436

35-
# Add jsonPayload fields to logs that don't contain stack traces to enable
36-
# capturing and grouping by error reporting.
37-
# https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-text
3837
if record.levelno >= logging.ERROR and not record.exc_info:
3938
record.json_fields.update({
4039
'@type':
4140
'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent', # pylint: disable=line-too-long
4241
'serviceContext': {
43-
'service': service_name,
42+
'service': self.service_name,
4443
},
4544
'context': {
4645
'reportLocation': {
@@ -51,9 +50,16 @@ def record_factory(*args, **kwargs):
5150
},
5251
})
5352

54-
return record
53+
return True
54+
55+
56+
def setup_gcp_logging(service_name):
57+
"""Set up GCP logging and error reporting."""
58+
59+
logging_client = google_logging.Client()
60+
logging_client.setup_logging()
5561

56-
logging.setLogRecordFactory(record_factory)
62+
logging.getLogger().addFilter(_ErrorReportingFilter(service_name))
5763
logging.getLogger().setLevel(logging.INFO)
5864

5965
# Suppress noisy logs in some of our dependencies.

0 commit comments

Comments
 (0)