Skip to main content

Structured logging with Slack integration, rate limiting, and deduplication

Project description

pylogkit

PyPI CI Python 3.11+ License: MIT

Structured logging with Slack integration, rate limiting, and deduplication.

Built on top of Python's standard logging module — no custom APIs to learn.

Installation

# Core (JSON + colored formatters)
pip install pylogkit

# With Slack support
pip install pylogkit[slack]

# With colored output
pip install pylogkit[color]

# Everything
pip install pylogkit[all]

Or in pyproject.toml:

dependencies = ["pylogkit[all]"]

Quick Start

import os
from pylogkit import setup_logging, get_logger

setup_logging(
    level="INFO",
    service_name="my-api",
    slack_token=os.environ.get("SLACK_BOT_TOKEN"),
    slack_channel="#errors",
)

logger = get_logger(__name__)

logger.info("Server started", extra={"port": 8000})
logger.error("Request failed", extra={"endpoint": "/users", "status": 500})

Features

Auto-detect output format

  • TTY (development): colored human-readable output with extras
  • Non-TTY (production/Docker): structured JSON

Force a specific format:

setup_logging(json_format=True)   # always JSON
setup_logging(json_format=False)  # always colored

Structured logging with extra fields

Extra fields are included in both JSON and colored output:

logger.info("Deal processed", extra={
    "deal_id": "123",
    "pipeline": "laba_czech",
    "duration": 1.23,
})

JSON output:

{"timestamp": "2026-03-18T12:00:00+00:00", "level": "INFO", "logger": "myapp.deals", "message": "Deal processed", "deal_id": "123", "pipeline": "laba_czech", "duration": 1.23}

Colored output:

2026-03-18 12:00:00 INFO     myapp.deals — Deal processed  [deal_id=123 pipeline=laba_czech duration=1.23]

Logger with static context

Bind context fields to a logger — they appear in every message:

logger = get_logger(__name__, pipeline="laba_czech", env="production")

logger.info("Course synced")
# All messages from this logger will include pipeline= and env=

Calling get_logger() again with the same name updates the context:

logger = get_logger(__name__, pipeline="v2")
# pipeline is now "v2", env remains from the previous call

Request-scoped context with contextvars

Inject request-scoped fields (request_id, user_id, etc.) without passing extra={} to every log call:

import contextvars
from pylogkit import setup_logging

log_context: contextvars.ContextVar[dict] = contextvars.ContextVar(
    "log_context", default={}
)

setup_logging(
    service_name="my-api",
    ctx_var=log_context,
)

# In your request handler / middleware:
log_context.set({"request_id": "abc-123", "user_id": "42"})
logger.info("Processing request")
# Output includes request_id and user_id automatically

Works with async frameworks (FastAPI, aiohttp) — each coroutine gets its own context.

Slack notifications

ERROR and CRITICAL logs go to Slack with:

  • Color-coded attachments (yellow/red/dark red)
  • Module and line number
  • Extra fields as "Context" block
  • Formatted traceback
setup_logging(
    slack_token="xoxb-...",
    slack_channel="#alerts",
    slack_level="ERROR",          # minimum level (default: ERROR)
)

Or use incoming webhook (simpler, scoped to one channel):

setup_logging(
    slack_webhook_url="https://hooks.slack.com/services/T.../B.../xxx",
)

How it works internally:

  • Messages are queued and sent from a background thread (non-blocking)
  • Respects Slack's 1 msg/sec rate limit
  • Exponential backoff on consecutive failures (up to 5 min)
  • If queue is full (100 messages) — drops silently, never blocks the app
  • If Slack is unreachable — prints to stderr, never crashes
  • Flushes remaining messages on shutdown

Rate limiting

Prevents Slack spam. Each unique message can fire at most N times per period:

setup_logging(
    slack_token="xoxb-...",
    slack_channel="#alerts",
    slack_rate_limit=1,       # max 1 message per period (default)
    slack_rate_period=60.0,   # per 60 seconds (default)
)

If the same error fires 500 times in a minute — only the first one goes to Slack.

Deduplication

Suppresses identical errors within a time window. When the window expires, the next message includes the suppressed count:

setup_logging(
    slack_token="xoxb-...",
    slack_channel="#alerts",
    slack_dedupe_window=300.0,  # 5 minutes (default)
)

Example: "Connection refused (suppressed 47 duplicates)"

Set to 0 to disable deduplication.

Per-logger level overrides

Silence noisy third-party libraries:

setup_logging(
    level="INFO",
    loggers={
        "httpx": "WARNING",
        "sqlalchemy.engine": "WARNING",
        "celery": "INFO",
    },
)

Static context fields

Add fields to every log record in the application:

setup_logging(
    service_name="my-api",
    extra_context={
        "env": "production",
        "region": "eu-west-1",
    },
)

Advanced: using components directly

All components can be used independently without setup_logging():

import logging
from pylogkit import JsonFormatter, ColoredFormatter, RateLimitFilter, DeduplicationFilter
from pylogkit import SlackHandler  # lazy import, requires slack-sdk

# Custom handler with JSON
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())

# Slack with custom filters
slack = SlackHandler(token="xoxb-...", channel="#alerts")
slack.addFilter(RateLimitFilter(rate=3, period=120))
slack.addFilter(DeduplicationFilter(window=600))

logging.getLogger().addHandler(handler)
logging.getLogger().addHandler(slack)

Celery integration

Prevent Celery from hijacking your logging setup:

from celery.signals import setup_logging as celery_setup_logging
from pylogkit import setup_logging

@celery_setup_logging.connect
def configure_celery_logging(**kwargs):
    setup_logging(
        service_name="worker",
        slack_token=os.environ.get("SLACK_BOT_TOKEN"),
        slack_channel="#worker-errors",
    )

setup_logging() parameters

Parameter Type Default Description
level str "INFO" Root log level
service_name str None Added to every record as service field
json_format bool auto True = JSON, False = colored, None = auto-detect
slack_token str None Slack Bot token (xoxb-...)
slack_channel str None Slack channel (required with token)
slack_webhook_url str None Incoming webhook URL (alternative to token)
slack_level str "ERROR" Minimum level for Slack
slack_rate_limit int 1 Max messages per period
slack_rate_period float 60.0 Rate limit window (seconds)
slack_dedupe_window float 300.0 Deduplication window (seconds), 0 to disable
extra_context dict None Static fields for every record
loggers dict None Per-logger level overrides
ctx_var ContextVar None Request-scoped context variable

Development

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=pylogkit --cov-report=term-missing

# Lint
ruff check src/ tests/
ruff format src/ tests/

# Type check
mypy src/pylogkit/

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pylogkit-0.2.0-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file pylogkit-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pylogkit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for pylogkit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1fe54929bcf31bb9502860ec8b9720e7c0a904a6b2f514ce6f49a420ed64fcd1
MD5 9ca2371c60ee47a64aa2be1e72d09b43
BLAKE2b-256 349af51973cdbe000c0d405b6a5da86e7ac21a9fce9a773e78b45693cc55fa29

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page