Mukul Sharma
Posted on July 14, 2024
Have you ever found your FastAPI deployment cluttered with unnecessary access logs? These logs can create more noise than value, obscuring important information. Let's explore a neat trick to selectively block these access logs in FastAPI.
Access logs can quickly overwhelm your console, making it difficult to spot critical information.
No B.S. Solution
# main.py
import logging
block_endpoints = ["/endpoint1", "/endpoint2"]
class LogFilter(logging.Filter):
def filter(self, record):
if record.args and len(record.args) >= 3:
if record.args[2] in block_endpoints:
return False
return True
uvicorn_logger = logging.getLogger("uvicorn.access")
uvicorn_logger.addFilter(LogFilter())
How It Works
We create a LogFilter
class that inherits from logging.Filter
.
The filter method checks if the log record corresponds to one of our blocked endpoints.
- If the endpoint is in our
block_endpoints
list, we return False, preventing the log from being processed. - By default we return True to log as usual.
We apply this filter to Uvicorn's access logger, which FastAPI uses under the hood.
Going Deep
When working with Python's logging module, each log entry is represented by a LogRecord
object. In our LogFilter
class, the record
parameter is an instance of this LogRecord
class.
The args
attribute of a LogRecord
contains a tuple of arguments which are merged into the log message. In the context of Uvicorn's access logs, these arguments hold valuable information about each request. Here's a breakdown of what each argument typically represents:
# Typical structure of record.args for Uvicorn access logs
remote_address = record.args[0] # IP address of the client
request_method = record.args[1] # HTTP method (GET, POST, etc.)
query_string = record.args[2] # Full path including query parameters
html_version = record.args[3] # HTTP version used
status_code = record.args[4] # HTTP status code of the response
Understanding this structure allows us to create more sophisticated filters. For example, we could filter logs based on status codes, specific IP addresses, or HTTP methods, not just endpoint paths.
Gotchas and Tips
- This method works with Uvicorn's default logging setup. If you've customized your logging configuration, you might need to adjust the logger name.
- At times you might have a separate file for logging config such as
logger.py
. You may define the class LogFilter there but make sure to apply.addFilter
inmain.py
.
Remember that completely blocking logs for certain endpoints might make debugging more challenging. Use this technique judiciously.
As thank you for making it to the end of this post, here is a trick I use for maximum benefit.
# main.py
import logging
block_endpoints = ["/endpoint1", "/endpoint2"]
class LogFilter(logging.Filter):
def filter(self, record):
if record.args and len(record.args) >= 4:
if (record.args[2] in block_endpoints
and record.args[4] == "200"):
return False
return True
uvicorn_logger = logging.getLogger("uvicorn.access")
uvicorn_logger.addFilter(LogFilter())
This will only block the request if the status code is 200. This is super helpful when blocking INFO
endpoints where you only need to be informed if an unexpected status code was returned.
Conclusion
By implementing this simple logging filter, you can significantly reduce noise in your FastAPI application logs. This allows you to focus on the information that truly matters, making your debugging and monitoring processes more efficient.
What to do next?
Posted on July 14, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.