Skip to main content

IncidentLogging

A Python logging handler that stays silent during normal operation and only writes logs when something goes wrong.

The problem

Verbose debug logging helps diagnose issues, but writing every DEBUG and INFO message to a file or console creates noise that obscures what matters. The usual workaround — raising the log level to WARNING — means you lose the context that would have explained why the warning happened.

How it works

IncidentHandler wraps any standard logging.Handler. It buffers DEBUG and INFO records silently. The moment a WARNING, ERROR, or CRITICAL is emitted, it flushes the buffered context followed by the triggering message — then clears the buffer and starts over.

Normal operation:        DEBUG INFO DEBUG INFO DEBUG INFO  →  (nothing written)
Something goes wrong:    DEBUG INFO DEBUG INFO ERROR       →  DEBUG INFO DEBUG INFO ERROR

The buffer holds the most recent N records (default 30). Older records are dropped as new ones arrive, so the buffer always contains the last N lines of context leading up to the problem.

Installation

Via pip (recommended)

pip install incident-logging

Manual

No dependencies outside the standard library. Copy incident_logging.py directly into your project.

Requires Python 3.8+.

Usage

Basic — wrap the default stderr handler

import logging
from incident_logging import IncidentHandler

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)
logger.addHandler(IncidentHandler())

logger.debug("connecting to database")   # buffered
logger.info("query executed in 4 ms")    # buffered
logger.error("connection pool exhausted") # flushes both lines above, then this

With a custom handler and buffer size

import logging
from incident_logging import IncidentHandler

stream = logging.StreamHandler()
stream.setFormatter(logging.Formatter("%(levelname)s %(name)s: %(message)s"))

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)
logger.addHandler(IncidentHandler(target_handler=stream, buffer_size=50))

With RotatingFileHandler

The log file stays empty during normal operation and only grows when an incident occurs — keeping file sizes minimal while preserving full diagnostic context when you need it.

import logging
from logging.handlers import RotatingFileHandler
from incident_logging import IncidentHandler

rotating = RotatingFileHandler("app.log", maxBytes=1024 * 1024, backupCount=5)
rotating.setFormatter(logging.Formatter("%(asctime)s %(levelname)-8s %(name)s: %(message)s"))

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)
logger.addHandler(IncidentHandler(target_handler=rotating, buffer_size=30))

API

IncidentHandler(target_handler=None, buffer_size=30)

Parameter Type Default Description
target_handler logging.Handler StreamHandler() The handler that receives flushed records
buffer_size int 30 Maximum number of DEBUG/INFO records to buffer; oldest are dropped when exceeded

The handler passes through WARNING, ERROR, and CRITICAL records immediately (after flushing the buffer). DEBUG and INFO records are only ever written as part of a flush.

Comparison to MemoryHandler

Python's standard library includes logging.handlers.MemoryHandler, which is the closest built-in equivalent. Here's how they differ:

MemoryHandler IncidentHandler
Flush trigger ERROR (default) or buffer full WARNING (default)
Buffer full behaviour Flushes the entire buffer immediately Drops the oldest record, keeps the newest N
After a flush Buffer cleared Buffer cleared
Most recent context guaranteed No — a busy logger flushes everything on capacity Yes — you always get the last N lines before the incident

The practical difference: MemoryHandler doesn't miss anything in the log. IncidentHandler behaves like a ring buffer — it silently discards unimportant records that are too old to matter and always preserves the most recent context window.

Recommended pattern: combine both

Use a regular handler for full bookkeeping and an IncidentHandler for focused incident output. The regular handler captures everything for audit trails or offline analysis; the IncidentHandler surfaces only what's relevant when something goes wrong.

import logging
from logging.handlers import RotatingFileHandler
from incident_logging import IncidentHandler

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)

# Full audit log — every record, always
audit = RotatingFileHandler("audit.log", maxBytes=10 * 1024 * 1024, backupCount=5)
audit.setFormatter(logging.Formatter("%(asctime)s %(levelname)-8s %(message)s"))
logger.addHandler(audit)

# Incident log — only emits when WARNING or above fires, with recent context
incident = RotatingFileHandler("incidents.log", maxBytes=1024 * 1024, backupCount=3)
incident.setFormatter(logging.Formatter("%(asctime)s %(levelname)-8s %(message)s"))
logger.addHandler(IncidentHandler(target_handler=incident, buffer_size=30))

audit.log grows continuously and is the source of truth. incidents.log stays small and contains only the context windows around each problem — easy to tail in production or attach to a bug report.

Running the demos

python3 demo.py

Running the tests

python3 -m unittest test_incident_logging -v

Release files for incident-logging 0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for incident-logging 0.5
File Size Uploaded
incident_logging-0.5.tar.gz 4.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for incident-logging 0.5
File Interpreter ABI Platform
incident_logging-0.5-py3-none-any.whl Python 3 none any Details

Total release size: 9.1 kB

Release files / incident_logging-0.5.tar.gz

Download URL incident_logging-0.5.tar.gz
Size 4.4 kB
Tags Source
SHA-256 checksum
How to use checksums
5eed1a57d24c51cdeabe223857bb82a2ea197d7aeb0c39fc1d8df358eceab2ac
BLAKE2b-256 checksum
How to use checksums
a7362c2c411fe8f00cba126996d343e14497bffb22f1bef712386d4ddd1df122
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / incident_logging-0.5-py3-none-any.whl

Download URL incident_logging-0.5-py3-none-any.whl
Size 4.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a09f27eb15ea6febafd4847d8a8a6a28e79bba5e6008d5c138b4ebfe56db53c
BLAKE2b-256 checksum
How to use checksums
f136c2f2938036b51efbc650cb36765569f56c6dd3a758c780cfc386ffc5911d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

0.5 This release

2 release files

0.4

2 release files

0.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page