Smarter Logging Middleware
Smarter.lib.logging.middleware
Middleware for Per-Request Logging Context
This module provides Django middleware that injects a per-request logging context into Python’s
contextvars system. This enables:
Request-scoped logging correlation: Each HTTP request receives a unique logging context, allowing logs to be traced per request.
Async-safe logging context propagation: Works seamlessly with both synchronous and asynchronous Django views, ensuring context is preserved across async boundaries.
Real-time log filtering: Supports real-time log filtering in the Smarter Terminal Emulator for improved debugging and monitoring.
User-scoped or anonymous request tracing: Authenticated users are identified in logs by their model and username; anonymous users receive a UUID-based identifier.
Features
Integrates with Django’s middleware stack.
Uses context variables for safe, per-request logging context.
Supports both sync and async Django request handling.
Controlled by a Waffle switch (
SmarterWaffleSwitches.ENABLE_WEB_CONSOLE_SERVER_LOGS).
Usage
Add SmarterRequestLogContextMiddleware to your Django MIDDLEWARE settings to enable per-request logging context. Ensure the required Waffle switch is configured to activate the middleware.
MIDDLEWARE = [
# ...
'smarter.lib.logging.middleware.SmarterRequestLogContextMiddleware',
# ...
]
Dependencies
Django
asgiref
smarter.common.helpers.logger_helpers
smarter.common.mixins
smarter.lib.logging.redis_log_handler
smarter.lib.django.waffle
- class smarter.lib.logging.middleware.SmarterRequestLogContextMiddleware(get_response, *args, **kwargs)[source]
Bases:
SmarterMiddlewareMixinMiddleware that injects request identity into logging contextvars.
- Authenticated users receive:
“<ModelClass>.<username>”
- Anonymous users receive:
UUID-based request identifiers.
- __init__(get_response, *args, **kwargs)
Note: this needs to exist; something in the Python MRO requires it, even if it does nothing.
If you remove this, you will get a mysterious error about something downstream expecting exactly one object.
- _is_private_ip(ip)
Check if IP is in private/internal ranges.
- async_capable = True
- bool_environment_variable(var_name, default=False)
Retrieves a boolean value from an environment variable.
This method checks the specified environment variable and returns its value as a boolean. It recognizes common truthy values such as “true”, “1”, “yes”, and “on”. If the variable is not set or cannot be interpreted as a boolean, it returns the provided default value.
- data_to_dict(data)
Converts data to a dictionary, handling different types of input.
This method accepts either a dictionary or a string. If a string is provided, it will attempt to parse it as JSON first, and if that fails, as YAML. If parsing fails or the data type is unsupported, a SmarterValueError is raised.
- deserves_amnesty(slug)
Determines if a given URL deserves amnesty based on the amnesty URLs list.
This excuses certain endpoints (like health checks) from select middleware checks.
- dict_is_contained_in(dict1, dict2)
Checks if one dictionary is contained within another.
This method determines if all key-value pairs in dict1 are present in dict2.
- dict_is_subset(small, big)
Checks if one dictionary is a subset of another.
This method determines if all key-value pairs in the small dictionary are present in the big dictionary. It returns True if the small dictionary is a subset of the big dictionary, and False otherwise.
- property formatted_class_name: str
str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to ‘utf-8’. errors defaults to ‘strict’.
- formatted_json(json_obj)
Formats a JSON object as a pretty-printed string with ANSI color codes for logging.
- property formatted_state_not_ready: str
Returns the not-ready state formatted for logging.
- Returns:
The formatted not-ready state as a string.
- Return type:
- property formatted_state_ready: str
Returns the readiness state formatted for logging.
- Returns:
The formatted readiness state as a string.
- Return type:
- formatted_text(text, color_code='\\x1b[1;31m')
Formats text with ANSI color codes for logging.
- formatted_text_blue(text)
Formats text in bold dark blue for logging.
- formatted_text_green(text)
Formats text in bright green for logging.
- formatted_text_red(text)
Formats text in dark red for logging.
- generate_fernet_encryption_key()
Generates a Fernet encryption key.
This method creates a new Fernet encryption key, which can be used for secure encryption and decryption of data. The generated key is returned as a URL-safe base64-encoded string.
- Returns:
A new Fernet encryption key.
- Return type:
- async get_async_context(request)[source]
Resolve logging context for async requests.
- Return type:
- get_client_ip(request)
Get client IP address from request.
This method attempts to determine the original client IP address, accounting for proxies, load balancers, and CDNs. It checks common headers set by proxies and falls back to
REMOTE_ADDR.Notes
In AWS CLB → Kubernetes Nginx setups, the client IP flow is: Client → CLB → Nginx Ingress → Django. - CLB adds
X-Forwarded-Forwith the original client IP. - Nginx may addX-Real-IPor modifyX-Forwarded-For. - Django seesREMOTE_ADDRas the Nginx IP (not the client IP).If using Cloudflare, it adds the
CF-Connecting-IPheader.Always validate IPs to avoid trusting spoofed headers.
- get_readonly_csv_file(file_path)
Reads a CSV file and returns its contents as a list of dictionaries, one per row.
The file is opened read-only, so reading it never modifies it.
- get_readonly_yaml_file(file_path)
Reads a YAML file and returns its contents as a dictionary.
The file is opened read-only, so reading it never modifies it.
- has_auth_indicators(request)
Check if request has authentication indicators (cookies, headers, etc.).
This method inspects the request for common authentication signals, such as session cookies, CSRF tokens, authorization headers, API keys, or Django’s built-in authentication.
- Parameters:
request (HttpRequest) – The Django request object.
- Returns:
True if authentication indicators are present, False otherwise.
- Return type:
- property health_check_urls: list[str]
Returns a list of URL paths that are considered health check endpoints.
- static is_authenticated(user)[source]
Safely determine whether a user is authenticated.
- Return type:
- mask_string(string='', mask_char='*', mask_length=4, string_length=8)
Masks a string for secure logging.
This utility function masks all but the last unmasked_chars characters of the input string, replacing them with asterisks. It is useful for logging sensitive information like API keys or passwords.
- property ready: bool
Indicates whether the object is ready for use.
This is a placeholder that should be overridden in subclasses.
- Returns:
True if ready, False otherwise.
- Return type:
- recursive_sort_dict(data)
Recursively sorts a dictionary by its keys.
This method takes a dictionary and returns a new dictionary with all keys sorted in ascending order. If any values are also dictionaries, they will be sorted recursively as well.
- rfc1034_compliant_str(name)
Converts a string to an RFC 1034 compliant format.
This method takes a string and converts it to a format that complies with RFC 1034, which is commonly used for domain names. It replaces invalid characters with hyphens and ensures the resulting string is lowercase.
- rfc1034_compliant_to_snake(name)
Converts an RFC 1034 compliant string to snake_case.
This method takes a string in RFC 1034 compliant format and converts it to snake_case. It replaces hyphens with underscores and ensures the resulting string is lowercase.
- set_smarter_process_id(request)
Set smarter_process_id on request if not already set. This has to consider the pipeline of middlewares. Each middleware class sets its own unique smarter_process_id, but if a previous middleware has already set the request object’s smarter_process_id then we should not overwrite it. This allows all middlewares in the pipeline to share the same process ID for logging and caching purposes.
- Return type:
- smarter_build_absolute_uri(request)
Attempts to get the absolute URI from a request object.
This utility function tries to retrieve the request URL from any valid child class of
django.http.HttpRequest. It is especially useful in unit tests or scenarios where the request object may not implementbuild_absolute_uri().- Parameters:
request (
HttpRequest) – The request object.- Returns:
The absolute request URL.
- Return type:
- Raises:
SmarterValueError – If the URI cannot be built from the request.
- smarter_process_id: int
- sorted_dict(data)
Returns a new dictionary with keys sorted.
- sync_capable = True
- to_camel_case(data, convert_values=False)
Converts a snake_case string to camelCase.
This method takes a string in snake_case format and converts it to camelCase. It is useful for standardizing naming conventions across different formats.
- Parameters:
- Returns:
The converted string in camelCase.
- Return type:
- to_snake_case(data, convert_values=False)
Converts a camelCase or PascalCase string to snake_case.
This method takes a string in camelCase or PascalCase format and converts it to snake_case. It is useful for standardizing naming conventions across different formats.
- Parameters:
- Returns:
The converted string in snake_case.
- Return type: