Shared logging + Slack-alerting for BetShareMarket Python services
Project description
betshare-infra-logging
Shared logging + Slack-alerting for BetShareMarket Python services. Extracted
from the near-identical per-service conf_logger modules
(consumer, joiner-events, betfair-exchange-scraper).
Import name: betshare_infra_logging. Distribution name: betshare-infra-logging.
Why
The per-service copies drifted in accidental ways that were bugs:
- Slack POST hang.
joiner-eventsandbetfair-exchange-scrapercalledrequests.post(webhook, ...)with no timeout, so a hung Slack call could freeze their single-threaded loops. This package bakes in a finite timeout (SLACK_TIMEOUT = 10) with no public parameter to override it toNone. - Level env var schism (
LOGGER_LEVELvsLOG_LEVEL). - Slack channel drift (
#errorvs#errors). - Idempotency, noise suppression, and correlation IDs were each reinvented or missing.
Install
Production (private registry, version-pinned per service):
# in the service's pyproject.toml
betshare-infra-logging = "^0.1.0"
Local development (PATH dependency, used by the pilot migration):
betshare-infra-logging = { path = "../../packages/py-infra-logging", develop = true }
Or with pip: pip install -e packages/py-infra-logging.
Usage
One-liner at service startup. A service's thin conf_logger.py shim collapses to:
from betshare_infra_logging import configure_logging
logger = configure_logging("joiner") # Slack timeout now bounded => hang bug fixed
configure_logging configures the ROOT logger, so existing
logging.getLogger(__name__) call sites keep working unchanged via propagation.
Correlation-id / context binding:
log = logger.child(correlation_id=cid, bookmaker="bet365")
log.info("processing message")
# -> "... processing message [correlation_id=... bookmaker=bet365]"
# context shows in console output AND in the Slack payload.
get_logger(name) returns a Logger wrapping logging.getLogger(name).
Notes
- No import-time side effects. Importing the package does NOT configure
logging (unlike the old modules which called
configure_logger()at import). Callconfigure_logging(...)explicitly. The package does NOT callload_dotenv()— that stays in the service entrypoint. - Level resolution priority (Python majority order):
levelarg ??LOGGER_LEVELenv ??LOG_LEVELenv ??"INFO". Cutover does not change any service's effective level and no.envis edited. - Slack channel defaults to
#errors(unified from the Python services'#error). The channel field is cosmetic for modern Slack incoming webhooks — the destination is fixed by the webhook URL itself, so this change is safe and does not reroute alerts. - Idempotent. Calling
configure_loggingtwice does not stack duplicate console/Slack handlers; the package tracks and clears the handlers it owns. - Slack failures are swallowed. A failing Slack POST never propagates into the caller's loop.
Public interface
DEFAULT_QUIET_LOGGERS = (
"azure", "azure.servicebus", "azure.core",
"motor", "pymongo", "crawl4ai", "playwright",
)
SLACK_TIMEOUT = 10 # seconds, baked in
def configure_logging(service, *, level=None, slack_webhook=None,
quiet_loggers=None) -> Logger: ...
def get_logger(name=None) -> Logger: ...
class Logger:
def debug(self, message, **context) -> None
def info(self, message, **context) -> None
def warning(self, message, **context) -> None
def error(self, message, exc=None, **context) -> None
def child(self, **bindings) -> "Logger"
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters