mirror of
https://github.com/getsentry/sentry-for-ai.git
synced 2026-09-14 14:28:22 +08:00
64dfeaa1f1
These per-SDK skills, plus a handful of setup/workflow skills, are superseded by the sentry-instrument skill and the references/sdks/ trees. Move them under skills-legacy/ so they are excluded from the plugin build and skipped by the drift automation, while skills.sentry.dev can still serve them as a fallback for existing URL consumers. Moves 26 skills: the 19 per-SDK skills plus sentry-sdk-skill-creator, sentry-fix-issues, the three sentry-span-streaming-* skills, sentry-instrument-logging, and sentry-instrumentation-guide. Regenerates SKILL_TREE.md and repaths the moved entries in CODEOWNERS. sentry-sdk is intentionally left in place; its removal is handled in a separate PR.
230 lines
8.1 KiB
Markdown
230 lines
8.1 KiB
Markdown
# Logging — Sentry Python SDK
|
||
|
||
> Minimum SDK: `sentry-sdk` 2.35.0+ for structured Sentry Logs (`enable_logs=True`)
|
||
|
||
## Two Logging Systems
|
||
|
||
| System | Produces | Requires |
|
||
|--------|----------|---------|
|
||
| **Sentry Structured Logs** | Searchable log records in Sentry Logs UI | `enable_logs=True` + `sentry_sdk.logger.*` |
|
||
| **LoggingIntegration (classic)** | Breadcrumbs and/or error events from stdlib `logging` | Auto-enabled by default |
|
||
|
||
These run simultaneously. They are **not** mutually exclusive.
|
||
|
||
## Configuration
|
||
|
||
```python
|
||
import sentry_sdk
|
||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||
import logging
|
||
|
||
sentry_sdk.init(
|
||
dsn="https://<key>@<org>.ingest.sentry.io/<project>",
|
||
enable_logs=True, # required for structured Sentry Logs
|
||
integrations=[
|
||
LoggingIntegration(
|
||
sentry_logs_level=logging.INFO, # stdlib records → Sentry Logs (requires enable_logs=True)
|
||
level=logging.INFO, # stdlib records → breadcrumbs
|
||
event_level=logging.ERROR, # stdlib records → Sentry error events
|
||
),
|
||
],
|
||
)
|
||
```
|
||
|
||
### `LoggingIntegration` parameters
|
||
|
||
| Parameter | Default | Effect |
|
||
|-----------|---------|--------|
|
||
| `sentry_logs_level` | `INFO` | Records ≥ level → **structured Sentry Logs** (needs `enable_logs=True`) |
|
||
| `level` | `INFO` | Records ≥ level → **breadcrumbs** (attached to next error event) |
|
||
| `event_level` | `ERROR` | Records ≥ level → standalone **Sentry error events** |
|
||
|
||
## Code Examples
|
||
|
||
### Sentry Structured Logs — direct API
|
||
|
||
```python
|
||
import sentry_sdk
|
||
from sentry_sdk import logger as sentry_logger
|
||
|
||
sentry_sdk.init(dsn="...", enable_logs=True)
|
||
|
||
sentry_logger.trace("Starting database connection {database}", database="users")
|
||
sentry_logger.debug("Cache miss for user {user_id}", user_id=123)
|
||
sentry_logger.info("Updated global cache")
|
||
sentry_logger.warning("Rate limit reached for endpoint {endpoint}", endpoint="/api/results/")
|
||
sentry_logger.error(
|
||
"Failed to process payment. Order: {order_id}. Amount: {amount}",
|
||
order_id="or_2342",
|
||
amount=99.99,
|
||
)
|
||
sentry_logger.fatal("Database {database} connection pool exhausted", database="users")
|
||
```
|
||
|
||
Template `{attribute_name}` placeholders become **individually searchable attributes** in the Sentry Logs UI.
|
||
|
||
### Extra structured attributes
|
||
|
||
```python
|
||
from sentry_sdk import logger as sentry_logger
|
||
|
||
sentry_logger.error(
|
||
"Payment processing failed",
|
||
attributes={
|
||
"payment.provider": "stripe",
|
||
"payment.method": "credit_card",
|
||
"payment.currency": "USD",
|
||
"user.subscription_tier": "premium",
|
||
},
|
||
)
|
||
```
|
||
|
||
### stdlib `logging` → Sentry Logs
|
||
|
||
```python
|
||
import logging
|
||
import sentry_sdk
|
||
from sentry_sdk.integrations.logging import LoggingIntegration
|
||
|
||
sentry_sdk.init(
|
||
dsn="...",
|
||
enable_logs=True,
|
||
integrations=[LoggingIntegration(sentry_logs_level=logging.INFO)],
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
logger.info("User signed in", extra={"user_id": 42, "region": "eu-west-1"})
|
||
# extra fields become searchable attributes on the log entry
|
||
```
|
||
|
||
### Loguru integration
|
||
|
||
```python
|
||
import sentry_sdk
|
||
from loguru import logger
|
||
|
||
# LoguruIntegration auto-activates when loguru is installed
|
||
sentry_sdk.init(dsn="...", enable_logs=True)
|
||
|
||
logger.info("Application started")
|
||
logger.error("Critical failure in payment module")
|
||
```
|
||
|
||
Manual configuration for full control:
|
||
|
||
```python
|
||
import sentry_sdk
|
||
from sentry_sdk.integrations.loguru import LoguruIntegration, LoggingLevels
|
||
|
||
sentry_sdk.init(
|
||
dsn="...",
|
||
enable_logs=True,
|
||
integrations=[
|
||
LoguruIntegration(
|
||
sentry_logs_level=LoggingLevels.INFO.value, # → structured logs
|
||
level=LoggingLevels.INFO.value, # → breadcrumbs
|
||
event_level=LoggingLevels.ERROR.value, # → error events
|
||
)
|
||
],
|
||
)
|
||
```
|
||
|
||
### `before_send_log` hook — filter and modify
|
||
|
||
```python
|
||
import sentry_sdk
|
||
from typing import Optional
|
||
|
||
def before_log(log, hint) -> Optional[dict]:
|
||
# Drop all INFO logs
|
||
if log["severity_text"] == "info":
|
||
return None
|
||
# Add custom attribute
|
||
log["attributes"]["processed_by"] = "before_log"
|
||
return log
|
||
|
||
sentry_sdk.init(dsn="...", enable_logs=True, before_send_log=before_log)
|
||
```
|
||
|
||
### `Log` object schema
|
||
|
||
| Key | Type | Description |
|
||
|-----|------|-------------|
|
||
| `severity_text` | `str` | `"trace"` / `"debug"` / `"info"` / `"warning"` / `"error"` / `"fatal"` |
|
||
| `severity_number` | `int` | 1–24 (OpenTelemetry spec) |
|
||
| `body` | `str` | Rendered message string |
|
||
| `attributes` | `dict` | Custom key-value pairs |
|
||
| `time_unix_nano` | `int` | Unix epoch in nanoseconds |
|
||
| `trace_id` | `str \| None` | Associated trace ID |
|
||
|
||
### Suppress noisy loggers
|
||
|
||
Events/breadcrumbs and Sentry Logs have **separate** ignore lists. `ignore_logger()` only suppresses breadcrumbs and error events — it does **not** affect Sentry Logs. This means framework loggers silenced for events (e.g. `django.server`) can still be captured as Sentry Logs.
|
||
|
||
```python
|
||
from sentry_sdk.integrations.logging import (
|
||
ignore_logger,
|
||
ignore_logger_for_sentry_logs,
|
||
unignore_logger,
|
||
unignore_logger_for_sentry_logs,
|
||
)
|
||
|
||
# Suppress breadcrumbs and error events only (Sentry Logs still captured)
|
||
ignore_logger("a.spammy.logger")
|
||
ignore_logger("boto3.*") # glob patterns supported
|
||
|
||
# Suppress Sentry Logs only (breadcrumbs and error events still captured)
|
||
ignore_logger_for_sentry_logs("a.noisy.logger")
|
||
|
||
# Reverse a previous ignore
|
||
unignore_logger("a.spammy.logger")
|
||
unignore_logger_for_sentry_logs("a.noisy.logger")
|
||
```
|
||
|
||
| Helper | Affects |
|
||
|--------|---------|
|
||
| `ignore_logger(name)` | Breadcrumbs + error events only |
|
||
| `ignore_logger_for_sentry_logs(name)` | Sentry Logs only |
|
||
| `unignore_logger(name)` | Reverses `ignore_logger()` |
|
||
| `unignore_logger_for_sentry_logs(name)` | Reverses `ignore_logger_for_sentry_logs()` |
|
||
|
||
## Decision Table
|
||
|
||
| Goal | Tool |
|
||
|------|------|
|
||
| Searchable structured records in Sentry Logs UI | `sentry_sdk.logger.*` + `enable_logs=True` |
|
||
| Bridge existing stdlib `logging` to Sentry Logs | `LoggingIntegration(sentry_logs_level=...)` + `enable_logs=True` |
|
||
| Bridge loguru to Sentry Logs | `LoguruIntegration(sentry_logs_level=...)` + `enable_logs=True` |
|
||
| Breadcrumbs for error context | `LoggingIntegration(level=logging.INFO)` (default) |
|
||
| Auto-create error events from log calls | `LoggingIntegration(event_level=logging.ERROR)` (default) |
|
||
| Drop/modify a log before sending | `before_send_log` callback |
|
||
| Suppress a logger from breadcrumbs/events only | `ignore_logger(name)` |
|
||
| Suppress a logger from Sentry Logs only | `ignore_logger_for_sentry_logs(name)` |
|
||
| Re-enable a previously ignored logger | `unignore_logger(name)` / `unignore_logger_for_sentry_logs(name)` |
|
||
|
||
## Automatically Added Attributes
|
||
|
||
Every log record receives these automatically:
|
||
|
||
| Attribute | Source |
|
||
|-----------|--------|
|
||
| `sentry.environment` | `init(environment=...)` |
|
||
| `sentry.release` | `init(release=...)` |
|
||
| `sentry.sdk.name` / `sentry.sdk.version` | SDK metadata |
|
||
| `server.address` | hostname |
|
||
| `sentry.message.template` | Original template string |
|
||
| `sentry.message.parameter.*` | Template placeholder values |
|
||
| `user.id`, `user.name`, `user.email` | Active scope user (if set) |
|
||
|
||
## Troubleshooting
|
||
|
||
| Issue | Solution |
|
||
|-------|----------|
|
||
| `sentry_sdk.logger.*` calls have no effect | Ensure `enable_logs=True` is set in `sentry_sdk.init()` |
|
||
| stdlib logs not appearing as Sentry Logs | Set `sentry_logs_level` in `LoggingIntegration` and `enable_logs=True` |
|
||
| Logger's own level filters out records | SDK respects the logger's configured level — set it to `logging.INFO` or lower |
|
||
| Too many log records hitting quota | Use `before_send_log` to filter by severity or attribute |
|
||
| Loguru not auto-activating | Verify `loguru` is installed; or add `LoguruIntegration()` explicitly |
|
||
| `ignore_logger()` still sending Sentry Logs | Expected — `ignore_logger()` only suppresses breadcrumbs/events; use `ignore_logger_for_sentry_logs()` to suppress Sentry Logs |
|
||
| Framework logger (e.g. `django.server`) not appearing in Sentry Logs | It may be in the default Sentry Logs ignore list; call `unignore_logger_for_sentry_logs("django.server")` |
|