Home

Shuuten — last-stop alerts for Python automations¶
Stop writing boilerplate alert code. Shuuten gives your Python automations structured JSON logging and instant Slack, Microsoft Teams, or email alerts — with zero dependencies and minimal setup.
Built for AWS Lambda and ECS, works anywhere Python runs.
終点 (Shūten) — "final stop" in Japanese. The last line of defense before a silent failure.
Why Shuuten?¶
- Zero dependencies — no SDKs, agents, or background workers
- Structured JSON logs — CloudWatch-friendly out of the box
- Built for failure paths — only
ERROR+alerts are sent by default, no noise - Designed for AWS — Lambda, ECS tasks, and containers work out of the box
Quick start (AWS Lambda)¶
import shuuten
@shuuten.capture
def lambda_handler(event, context):
shuuten.error('domain error') # → sends alert
1 / 0 # → alert with full stack trace
Configure one destination:
# Slack
export SHUUTEN_SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
# OR Microsoft Teams
export SHUUTEN_TEAMS_WEBHOOK_URL="https://xxxxx.webhook.office.com/..."
# OR Email (Amazon SES)
export SHUUTEN_SES_FROM="..."
export SHUUTEN_SES_TO="..."
That's it.
📖 Documentation · ⭐ Star on GitHub
Installation¶
pip install shuuten # core package (logging, Slack, Teams)
pip install "shuuten[email]" # + SES email support (boto3)
Examples¶
Structured logging (logging-style)¶
Note: By default, only
ERRORand above are sent to configured destinations. Lower-severity logs (DEBUG,INFO,WARNING) are emitted locally but are not sent as notifications unlessmin_levelis changed.
import shuuten
def handler(event, context):
shuuten.info('hello') # not sent
shuuten.error('bad input') # sent to configured destinations
Explicit logger + notifications¶
Requires SES env vars (
SHUUTEN_SES_FROM,SHUUTEN_SES_TO). Email is sent via AWS SES if configured.
import shuuten
shuuten.init(shuuten.Config(app='my-app', env='dev'))
log = shuuten.get_logger(__name__)
@shuuten.capture(workflow='my-workflow')
def handler(event, context):
log.info('Here we GOoOooO!')
log.error('Something went wrong') # sent to configured destinations
Deferred delivery¶
Deferred delivery requires
capture()(as a decorator or context manager) so Shuuten knows when to send grouped notifications. It does not catch Lambda hard timeouts or OOM failures.
Use deferred delivery to collect alert-worthy logs during a captured execution and send one grouped notification at the end.
import shuuten
import logging
shuuten.init(shuuten.Config(min_level=logging.INFO))
log = shuuten.get_logger(__name__)
@shuuten.capture(workflow="orders", delivery_mode="deferred")
def handler(event, context):
log.info("starting order sync")
log.error("failed to process order", extra={"data": {"order_id": 123}})
1 / 0
Instead of sending multiple Slack, Teams, or email notifications, Shuuten sends one grouped notification with the captured logs, context, and exception details.
Context manager¶
capture() can also be used as a context manager.
import logging
import shuuten
shuuten.init(shuuten.Config(min_level=logging.INFO))
log = shuuten.get_logger(__name__)
with shuuten.capture(workflow="orders", delivery_mode="deferred"):
log.info("starting order sync")
log.error("failed to process order", extra={"data": {"order_id": 123}})
1 / 0
Manual context control (advanced)¶
import shuuten
def handler(event, context):
token = shuuten.detect_and_set_context(context)
try:
...
finally:
shuuten.reset_runtime_context(token)
capture()also works for ECS tasks (via ECS metadata v4).
Structured logging with extra¶
Works with both
shuuten.info()andlog = shuuten.get_logger(__name__)— any logger usingShuutenJSONFormatter.
Attach structured context with data¶
Pass a dict under the data key in extra to merge fields top-level into the
JSON log output:
shuuten.info('Incoming event', extra={
'data': {
'method': 'POST',
'path': '/slack/events',
'status': 200,
}
})
# → {"ts": ..., "level": "info", "msg": "Incoming event", "method": "POST", "path": "/slack/events", "status": 200, ...}
Note: Keys in
datamust not conflict with shuuten's built-in output fields (ts,fn,file,lineno,level,msg,logger,stack,kind,shuuten,exc). AValueErroris raised if they do:
Attach internal shuuten context¶
Use the shuuten key to attach structured metadata that is nested under a
shuuten field in the output:
shuuten.info('Processing request', extra={'shuuten': {'caller': 'my_fn', 'request_id': '123'}})
# → {"ts": ..., "msg": "Processing request", "shuuten": {"caller": "my_fn", "request_id": "123"}, ...}
Log a dict or list directly as msg¶
Pass a Python dict or list directly as the message — it will be embedded
as a native JSON object rather than a stringified representation:
shuuten.info({'event': 'app_requested', 'app_id': 'A123', 'scopes': ['incoming-webhook']})
# → {"ts": ..., "msg": {"event": "app_requested", "app_id": "A123", "scopes": [...]}, ...}
This also works with shuuten.get_logger():
Integrations¶
structlog¶
Use Shuuten as a structlog processor. Keep structlog for logging
and rendering; Shuuten forwards alert-worthy events to configured
destinations.
Requirements:
- Install structlog (
pip install shuuten[structlog]) - Configure at least one destination
Then, configure processors for structlog:
import logging
import structlog
import shuuten
from shuuten.integrations.structlog import configure_structlog
shuuten.init(
shuuten.Config(
min_level=logging.INFO,
slack_webhook_url="https://hooks.slack.com/services/...",
)
)
configure_structlog()
log = structlog.get_logger(__name__)
log.debug("logged locally") # not sent to destinations
log.info("processed request")
log.error("failed", order_id=123)
That's it. Shuuten forwards events at or above min_level
to configured destinations while preserving normal structlog
logging and rendering.
min_levelcontrols what Shuuten sends to destinations. It does not filter structlog console output.
Configuration¶
delivery_modecan be configured globally viaConfigorSHUUTEN_DELIVERY_MODE, and overridden percapture()invocation.
You can configure Shuuten via Config in code or environment variables.
| Variable | Description | Default |
|---|---|---|
SHUUTEN_APP |
Application name (used for grouping/metadata) | auto |
SHUUTEN_ENV |
Environment name (prod, dev, staging, etc.) |
auto |
SHUUTEN_MIN_LEVEL |
Minimum level sent to destinations | ERROR |
SHUUTEN_EMIT_LOCAL_LOG |
Emit local structured log when notifying | true |
SHUUTEN_QUIET_LEVEL |
Silence noisy third-party logs (e.g. boto) | WARNING |
SHUUTEN_DEDUPE_WINDOW_S |
Notification dedupe window (seconds); 0 disables |
30 |
SHUUTEN_DELIVERY_MODE |
Alert delivery mode: immediate, deferred, or local_only |
immediate |
Slack¶
| Variable | Description |
|---|---|
SHUUTEN_SLACK_WEBHOOK_URL |
Slack Incoming Webhook URL |
SHUUTEN_SLACK_FORMAT |
blocks or plain |
Microsoft Teams¶
| Variable | Description |
|---|---|
SHUUTEN_TEAMS_WEBHOOK_URL |
Microsoft Teams Incoming Webhook URL |
See Microsoft Teams Webhook Setup
Email (SES)¶
| Variable | Description |
|---|---|
SHUUTEN_SES_FROM |
Verified SES sender |
SHUUTEN_SES_TO |
Comma-separated recipient list |
SHUUTEN_SES_REPLY_TO |
Optional reply-to address |
SHUUTEN_SES_REGION |
Optional SES region |
Supported destinations¶
- Slack (Incoming Webhooks)
- Microsoft Teams (Incoming Webhooks)
- Email (AWS SES)
Note: When running in AWS (e.g. Lambda or ECS), the execution role must be allowed to send email via SES. See AWS docs.
Roadmap¶
- AWS Lambda failure monitoring
- PagerDuty and JSM destinations
- Expanded ECS and EKS support
Credits¶
Created with Cookiecutter using https://github.com/audreyfeldroy/cookiecutter-pypackage