Reference
Public API documentation.
Settings
- class log_outgoing_requests.conf.LogOutgoingRequestsConf(**kwargs)
Settings for django-log-outgoing-requests.
To override a setting, prefix it with
LOG_OUTGOING_REQUESTS_in your own settings file.- CONTENT_TYPES = [ContentType(pattern='application/json', default_encoding='utf-8'), ContentType(pattern='application/soap+xml', default_encoding='utf-8'), ContentType(pattern='application/xml', default_encoding='utf-8'), ContentType(pattern='text/xml', default_encoding='iso-8859-1'), ContentType(pattern='text/*', default_encoding='utf-8')]
Allowlist of content types for which the body may be saved to the database.
Use
log_outgoing_requests.datastructures.ContentTypeto configure this setting.
- DB_SAVE = True
Whether request logs should be saved to the database.
This can be overridden at runtime via configuration in the admin.
- DB_SAVE_BODY = True
Whether request/response bodies should be saved to the database.
This can be overridden at runtime via configuration in the admin.
- EMIT_BODY = True
Whether request/response body may be emitted in the logs.
- HANDLER_ON_ERROR: Callable[[Exception], None] | None = None
Callback function to invoke if an exception happens during the
emitphase.
- HANDLER_USE_QUEUE: bool = True
Set to
Falseto write the log record in the main thread.By default, django-log-outgoing-requests expects to spin up a background thread to write logs.
- MAX_AGE = 1
The maximum age (in days) of request logs, after which they are deleted (via a Celery task, Django management command, or the like).
- MAX_CONTENT_LENGTH = 524288
The maximum size of request/response bodies for saving to the database, in bytes.
If the body is larger than this treshold, the log record will still be saved to the database, but the body will be missing.
- RESET_DB_SAVE_AFTER = 60
If the config has been updated, reset the database logging after the specified number of minutes.
To protect against unintended logging of potentially sensitive data after debugging, this helps in resetting the “save to DB” configuration option. It resets back to “use the default from settings”.
If the value is falsy (including zero), then no reset takes place at all.
Note
this requires Celery to be installed, an optional dependency.
Formatters
- class log_outgoing_requests.formatters.HttpFormatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)
Display request and response (meta) details of python requests library log records.
Depending on the configuration, either only the metadata or metadata + body of requests and matching responses is emitted.
Metadata:
HTTP method
Request URL
Request headers (masking auth details)
Response status
Response reason
Response headers
Handlers
Provide robust and opinionated logging handlers.
Log handlers are responsible for sending the actual log records produced by loggers to their final destination.
django-log-outgoing-requests provides a handler that knows how to send log records from
structlog to the database and save them into the
log_outgoing_requests.models.OutgoingRequestsLog model.
Our handler implementations take care of packages/modules that rely on process-forking behaviour, notably:
uwsgi
celery workers with prefork pool
Much of this implementation was taken from django-timeline-logger.
- class log_outgoing_requests.handlers.DatabaseOutgoingRequestsHandler(*, use_queue_mode: bool = False, buffer_size: int = 5, flush_interval: float = 3.0, **kwargs)
Save the log record to the database if conditions are met.
The handler checks if saving to the database is desired. If not, nothing happens. Next, request and response body are each checked if:
saving to database is desired
the content type is appropriate
the size of the body does not exceed the configured treshold
If any of the conditions don’t match, then the body is omitted.
- close()
Tidy up any resources used by the handler.
This version removes the handler from an internal map of handlers, _handlers, which is used for handler lookup by name. Subclasses should ensure that this gets called from overridden close() methods.
- emit(record: LogRecord)
Do whatever it takes to actually log the specified logging record.
This version is intended to be implemented by subclasses and so raises a NotImplementedError.
- class log_outgoing_requests.handlers.QueueHandler(queue)
Keep the raw log record and drop streaming responses.
The stdlib implementation by default formats the log record to a string and clears most attributes to make them pickleable.
- filter(record: LogRecord | RequestLogRecord | ErrorRequestLogRecord) bool | LogRecord
Prevent unconsumed response bodies from being passed to the actual handler.
The response body must be consumed in the main thread to avoid thread-safety issues when reading GZIP’ed responses in multiple threads, especially with chunked transfer encodings where there’s no content-length header available where we check the size of response.content to decide if we can save the body to the database or not.
- prepare(record: LogRecord)
Prepare a record for queuing. The object returned by this method is enqueued.
The base implementation formats the record to merge the message and arguments, and removes unpickleable items from the record in-place. Specifically, it overwrites the record’s msg and message attributes with the merged message (obtained by calling the handler’s format method), and sets the args, exc_info and exc_text attributes to None.
You might want to override this method if you want to convert the record to a dict or JSON string, or send a modified copy of the record while leaving the original intact.
- log_outgoing_requests.handlers.ensure_listener(*handlers: Handler, _defer: bool) Queue
Ensure a listener thread is running for
QueueHandler.Creates a queue if it doesn’t exist yet, and starts the background thread to listen to the queue to actually process the log records.
We don’t bother with preventing a background thread in the main runserver process that reloads the code and restarts the server - we don’t expect any audit logs to be created there, and trying to detect these situations is too fragile compared to real uwsgi servers & management command situations. The idle background thread should not cause significant overhead.
- Parameters:
_defer – Defer starting the background tread or not - by default on uwsgi we defer the startup and call te actual startup in te post fork hook.
- log_outgoing_requests.handlers.outgoing_requests_handler_factory(*, buffer_size: int = 5, flush_interval: float = 3.0) QueueHandler | DatabaseOutgoingRequestsHandler
Create a logging handler instance suitable for production or testing.
By default, a queue-based handler is configured so that the actual logging of outgoing requests can be offloaded to a worker thread. This improves performance by taking database queries out of the main thread, and improves log integrity because the log insertions run in a separate thread and associated database transaction.
However, in (unit) test setups, you want to force the logs to be written in the same test transaction so that at the end of the test, the log record creation is rolled back by the test transaction, otherwise you break test isolation substantially and/or slow down test suites considerably by forcing them to be
django.test.TransactionTestCase.The appropriate handler is selected based on the
LOG_OUTGOING_REQUESTS_HANDLER_USE_QUEUEDjango setting.Note that you cannot change this setup at runtime in tests through
django.test.override_settings(), as the logging config does not get reinitialized when settings change (as it should be!). You should configure CI/local test environments to apply conditional configurations.- Parameters:
buffer_size – Maximum size for the internal buffer. Passed along to the
DatabaseOutgoingRequestsHandlerinitializer.flush_interval – Maximum age between database writes. Passed along to the
DatabaseOutgoingRequestsHandlerinitializer.
uWSGI/Celery integration
When integrating the queue-based logging handler (via
log_outgoing_requests.handlers.outgoing_requests_handler_factory()) in uWSGI
and/or Celery environments, some additional care is required.
Celery workers
Celery workers that use the prefork (the default) concurrency model must take care to
defer the background thread initialization by setting an environment variable to
true:
export _LOG_OUTGOING_REQUESTS_LOGGER_DEFER_LISTENER=true
Failing to do so will lead to deadlocks.
Celery beat
Celery beat shouldn’t need the functionality of this library, but to be safe, the deferred initialization is automatically taken care of.
uWSGI
uWSGI also uses a forking process model with the same deadlock risks like the Celery workers. However, it’s automatically taken care of when a uWSGI environment is detected and the background thread initialization is deferred until after the worker processes have forked.