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:ValueError: shuuten: extra 'data' keys ['msg'] conflict with built-in log output fields.
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():
log = shuuten.get_logger(__name__)
log.info({'event': 'app_requested', 'app_id': 'A123'})
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
Metadata
Release files for shuuten 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| shuuten-0.6.0.tar.gz | 39.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shuuten-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.5 kB
Release files / shuuten-0.6.0.tar.gz
| Download URL | shuuten-0.6.0.tar.gz |
|---|---|
| Size | 39.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
624229b274edd6662b3e0960d702351eaa1074df6c8d1144de7f19caff3d7552
|
|
BLAKE2b-256 checksum How to use checksums |
01e8bc646788fc057d6359344a0609b81976892f235991a656d12c987cc103c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 28, 2026.
Transparency logRelease files / shuuten-0.6.0-py3-none-any.whl
| Download URL | shuuten-0.6.0-py3-none-any.whl |
|---|---|
| Size | 37.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3db0df3e27e48f01167bd1059049036895b99e78c7ef9ed7c8b0b9fb29f78976
|
|
BLAKE2b-256 checksum How to use checksums |
12f04307877b2b65572a11cbd79f34bd5deb34fdfec5a6e80c2e697d32512542
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 28, 2026.
Transparency log