blare

The demonstration Quickstart The spec GitHub
A stacked public warning siren on a tall mast against a clear blue sky, behind a red palisade fence.
  • Apache-2.0Free, forever
  • Zero dependenciesStandard library only
  • No serverNo database, no dashboard
  • 369 testsShared across every port
  • Spec-firstOne behaviour, many languages
OperationalError: connection pool exhausted
Service
checkout-api · production
Occurrences
847 times in the last 5 min
Where
app/payments.py:142 in charge
Fingerprint
9f2a41c7b0e3d518
app/payments.py:142 in charge
app/routes.py:88 in post_pay
web-3 · 2026-10-23T14:21:09.481Z

Production failures, in the channel you already watch.

One message per problem, not per crash
Slack, Discord, Teams, any webhook
Secrets stripped before they leave
Python and TypeScript today
Install it

The demonstration

Six real problems, one message each. Press the button to see the same five minutes without blare.

OperationalError: connection pool exhausted

847 occurrences in the last five minutes, delivered as this one message. Fingerprinted from the type, the line in your code, and the message with its variable parts removed, so User 4821 not found and User 9173 not found are one problem, not two.

The same event, rendered for each provider

OperationalError: connection pool exhausted
Service
checkout-api · production
Occurrences
847 times in the last 5 min
Where
app/payments.py:142 in charge
Request
POST /pay → 500
app/payments.py:142 in charge
app/routes.py:88 in post_pay
app/middleware.py:24 in dispatch
web-3 · the same canonical event, rendered as a Discord embed
$ python -m blare --send
blare 0.1.0 (spec 1)
  service      checkout-api
  environment  production
  level        ERROR
  dedupe       300s window, max 20/hour
  state        file
  sinks
    - slack    hooks.slack.com
    - discord  discord.com

sending test alert...
  ok   https://hooks.slack.com/…
  ok   https://discord.com/…
# the webhook path is never printed, and never logged

Capture, decide, deliver

Two of those are easy. The middle one is why this is a library and not a snippet.

Capture
A logging handler and a crash hook. No application code.
Decide
Fingerprint, group, throttle, cap, redact. Nearly all of the work.
Deliver
Render for the provider, post on a worker thread, retry twice.

First, it attaches to the logging calls you already wrote, and to the hook that fires when a process dies.

Then it answers the only question that matters at 3 a.m.: is this worth waking someone for, or is it the four hundredth copy of something already reported? Suppressed occurrences ride along as a count on the next alert, so nothing is lost and no background timer is needed.

Last, it posts on a worker thread behind a bounded queue, so a chat outage can never become back-pressure on production traffic.

What it captures and where it sends

Captures

Your existing log calls

Nothing to change. The call you already have becomes the alert.

logger.error("payment failed")
Unhandled crashes

Alerts, then re-raises. Swallowing a crash in order to report it would be worse than the crash.

sys.excepthook · threading.excepthook
FastAPI, Starlette, Litestar, Quart
app.add_middleware(BlareASGIMiddleware)

Adds the method, the path and the templated route, so one broken endpoint is one alert rather than one per user id.

Flask
from blare.integrations.flask import install
install(app)

Hooks the framework signal rather than WSGI, because Flask turns an exception into a 500 before any WSGI wrapper sees it.

Django
from blare.integrations.django import install
install()

Call it once from your AppConfig.ready, and every view exception is covered.

Celery

A failed job produces no 500 and nobody reads a worker log. This is the silence blare exists for.

from blare.integrations.celery import install
install()

Sends to

Slack

Block Kit, with a fallback line that stands alone for notifications and screen readers.

BLARE_SINKS=https://hooks.slack.com/services/…
Discord

Embeds, with every field kept inside the 1024-character limit the API enforces.

Microsoft Teams

Teams has two incompatible webhook shapes, and sending the wrong one is accepted and then silently shows nothing. blare picks by hostname, so you never find that out during an incident.

A Power Automate workflow URL takes an Adaptive Card; an older Office 365 connector takes a MessageCard. blare sends whichever the hostname calls for.

Anything else

The generic sink posts the canonical event unchanged, so PagerDuty, n8n, a Lambda or your own relay all work today without blare knowing about them.

{"fingerprint":"9f2a41c7b0e3d518",
 "occurrences":{"since_last_alert":847}}
No webhook at all

The console sink renders to stderr, so the demonstration runs with nothing configured.

BLARE_SINKS=console python examples/plain_script.py

An annunciator panel

A fire alarm annunciator panel in a lobby: a black board printed with the white floor plans of every level, each one labelled.
An annunciator is the older version of this idea: one board, in the place people already stand, that says which floor is on fire. It does not keep a history and it has no dashboard. Photograph by MarylandGeoffrey, CC0, via Wikimedia Commons.

One spec, every language

Ink means shipping, pale means planned. A Go service and a Python service in the same fleet group the same failure under the same fingerprint, because both pass the fixtures below.

Python

TypeScript

Go

Java

Ruby

PHP

.NET

An 1876 telegraph key and sounder mounted on a pale wooden board, wired together with a coiled copper lead.
{ "id": "varying-number-groups-together-a",
  "message": "User 4821 not found",
  "fingerprint": "b0a5839f239ce91c" }

{ "id": "varying-number-groups-together-b",
  "message": "User 9173 not found",
  "fingerprint": "b0a5839f239ce91c" }

What it won't do

No history, no search, no dashboard

blare is a library. It tells you something broke, and then forgets. If you need to look a failure up next week, run an error tracker (Sentry, GlitchTip or Bugsink) and let blare stay out of it. Being the thin thing is the point.

No browser or React support yet

A webhook URL in frontend JavaScript is a public secret: anyone can read it and spam your channel. Browser capture needs a relay through your own backend, which is a different shape of product. It comes after the server-side ports, not before.

It will not send your request bodies

Local variables, request bodies, response bodies and environment variable values are never collected, in any configuration. What does leave is scanned for bearer tokens, JWTs, AWS keys, GitHub and Slack tokens, PEM private keys, passwords inside connection strings, and card numbers that pass a Luhn check.

It will not alert four times for one crash

Dedupe state lives in a lock-guarded file shared by every process on the host, so four Gunicorn workers seeing one failure send one message. Across several hosts, point BLARE_STATE at Redis and the whole fleet deduplicates together.

It will not break your deploy

No runtime dependencies at all, so there is no version conflict to drag in. Delivery never raises into the host, the queue is bounded, and a webhook that has started refusing connections costs you alerts, never requests.

Alert in one line

pip install blare
npm install @geekvb/blare
import blare
blare.init()

Then set BLARE_SINKS to the webhook URL your chat tool gave you. Confirm it is wired up before you need it with python -m blare --send.

Read it on GitHub