Google Cloud Logging with FastAPI and custom log formatters
ntoshev opened this issue · 4 comments
Hi!
I'm running a Docker container with FastAPI in Google Cloud Run and would like to get full structured logging and nested logs with correlation_ids. The way to do it is described in https://cloud.google.com/run/docs/logging#writing_structured_logs
I get the correlation id from the request header and enable a custom formatter to make Cloud logging nest the logs. I couldn't get the custom formatter from the documentation to work (https://github.com/bobbui/json-logging-python/tree/dc80372b4981ddb035768a861f596a28f7fe2133#26-custom-log-formatter, I get request_response_data
is not a property of record
). My code looks like this:
PROJECT = '....'
class CloudRunJSONLog(json_logging.JSONLogWebFormatter):
'Logger for Google Cloud Run correlating with the requests'
def _format_log_object(self, record, request_util):
json_log_object = super(CloudRunJSONLog, self)._format_log_object(record, request_util)
if 'correlation_id' in json_log_object and len(json_log_object['correlation_id'])>1: # Missing correlation id actually contains '-'
trace = json_log_object['correlation_id'].split('/')
json_log_object["logging.googleapis.com/trace"]=f"projects/{PROJECT}/traces/{trace[0]}"
return json_log_object
# ...
json_logging.CORRELATION_ID_HEADERS = ['X-Cloud-Trace-Context']
json_logging.init_fastapi(custom_formatter=CloudRunJSONLog, enable_json=True)
This works with my own logs, however at least some logs made by FastAPI don't use the custom formatter. For example, if the data I pass to the handler fail validation, the resulting log is not handled via the custom log handler and does not appear nested in the logs explorer.
Is there a proper way to do this?
could u pls provide fully working python file
Sure
import uvicorn, json_logging, logging, os, sys
from fastapi import FastAPI
PROJECT = 'myproject'
class CloudRunJSONLog(json_logging.JSONLogWebFormatter):
'Logger for Google Cloud Run correlating with the requests'
def _format_log_object(self, record, request_util):
json_log_object = super(CloudRunJSONLog, self)._format_log_object(record, request_util)
if 'correlation_id' in json_log_object and len(json_log_object['correlation_id'])>1:
trace = json_log_object['correlation_id'].split('/')
json_log_object["logging.googleapis.com/trace"]=f"projects/{PROJECT}/traces/{trace[0]}"
return json_log_object
app = FastAPI()
logging.basicConfig(level=logging.INFO)
json_logging.CORRELATION_ID_HEADERS = ['X-Cloud-Trace-Context']
json_logging.init_fastapi(custom_formatter=CloudRunJSONLog, enable_json=True)
json_logging.init_request_instrument(app)
json_logging.config_root_logger()
logging.info('Started in environment', extra=dict(props=dict(env=dict(**os.environ))) )
@app.get("/")
async def root(url: str):
logging.info('Request!!!', extra=dict(props={"url":url}))
return True
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
@ntoshev sorry i has been busy taking care of some personal stuff. Are u still having some problem?
closed as no response from author