| title | What to Log |
|---|---|
| sidebar_order | 10 |
| description | Practical guidance on what to log, how to search logs, and when to set alerts. |
You've set up Sentry Logs. Now what?
This guide covers the high-value logging patterns that help you debug faster and catch problems before users report them.
Every structured log follows the same format:
Sentry.logger.<level>(message, { attributes });from sentry_sdk import logger
logger.<level>(message, attribute=value)\Sentry\logger()-><level>(message, attributes: [...]);using Sentry;
SentrySdk.Logger.<Level>(message);Logs in Sentry are automatically trace-connected. Each log shows a trace ID that links to the full trace view.
Sentry.logger.<level>(message)import 'package:sentry/sentry.dart';
Sentry.logger.<level>(message);import Sentry
SentrySDK.logger.<level>(message, attributes: [...])import io.sentry.Sentry
Sentry.logger().<level>(message)Levels: trace, debug, info, warn (or warning in Python), error, fatal
Attributes: Key-value pairs you can search and filter on. Consistency matters, so use whatever naming convention fits your codebase.
Sentry.logger.info("Order completed", {
orderId: "order_123",
userId: user.id,
amount: 149.99,
paymentMethod: "stripe",
});from sentry_sdk import logger as sentry_logger
sentry_logger.info("Order completed",
order_id="order_123",
user_id=user.id,
amount=149.99,
payment_method="stripe"
)\Sentry\logger()->info('Order completed', attributes: [
'order_id' => 'order_123',
'user_id' => $user->id,
'amount' => 149.99,
'payment_method' => 'stripe',
]);using Sentry;
SentrySdk.Logger.Info("Order completed", logger => logger
.SetAttribute("orderId", "order_123")
.SetAttribute("userId", user.Id)
.SetAttribute("amount", 149.99)
.SetAttribute("paymentMethod", "stripe"));Sentry.logger.info('Order completed',
order_id: 'order_123',
user_id: user.id,
amount: 149.99,
payment_method: 'stripe'
)Sentry.logger.info('Order completed', attributes: {
'orderId': 'order_123',
'userId': user.id,
'amount': 149.99,
'paymentMethod': 'stripe',
});import Sentry
SentrySDK.logger.info("Order completed", attributes: [
"orderId": "order_123",
"userId": user.id,
"amount": 149.99,
"paymentMethod": "stripe"
])import io.sentry.Sentry
Sentry.logger().info("Order completed orderId=%s userId=%s amount=%.2f",
"order_123", user.id, 149.99)Logs in Sentry are automatically trace-connected. Each log shows a trace ID that links to the full trace view.
Start with these five areas and you'll catch most issues before users do.
Login flows are invisible until something breaks. Log successes and failures to spot patterns like brute force attempts, OAuth misconfigurations, or MFA issues.
// After successful authentication
Sentry.logger.info("User logged in", {
userId: user.id,
authMethod: "oauth",
provider: "google",
});
// After authentication fails
Sentry.logger.warn("Login failed", {
email: maskedEmail,
reason: "invalid_password",
attemptCount: 3,
});from sentry_sdk import logger as sentry_logger
# After successful authentication
sentry_logger.info("User logged in",
user_id=user.id,
auth_method="oauth",
provider="google"
)
# After authentication fails
sentry_logger.warning("Login failed",
email=masked_email,
reason="invalid_password",
attempt_count=3
)// After successful authentication
\Sentry\logger()->info('User logged in', attributes: [
'user_id' => $user->id,
'auth_method' => 'oauth',
'provider' => 'google',
]);
// After authentication fails
\Sentry\logger()->warn('Login failed', attributes: [
'email' => $maskedEmail,
'reason' => 'invalid_password',
'attempt_count' => 3,
]);// After successful authentication
SentrySdk.Logger.Info("User logged in", logger => logger
.SetAttribute("userId", user.Id)
.SetAttribute("authMethod", "oauth")
.SetAttribute("provider", "google"));
// After authentication fails
SentrySdk.Logger.Warning("Login failed", logger => logger
.SetAttribute("email", maskedEmail)
.SetAttribute("reason", "invalid_password")
.SetAttribute("attemptCount", 3));# After successful authentication
Sentry.logger.info('User logged in',
user_id: user.id,
auth_method: 'oauth',
provider: 'google'
)
# After authentication fails
Sentry.logger.warn('Login failed',
email: masked_email,
reason: 'invalid_password',
attempt_count: 3
)// After successful authentication
Sentry.logger.info('User logged in', attributes: {
'userId': user.id,
'authMethod': 'oauth',
'provider': 'google',
});
// After authentication fails
Sentry.logger.warn('Login failed', attributes: {
'email': maskedEmail,
'reason': 'invalid_password',
'attemptCount': 3,
});import Sentry
// After successful authentication
SentrySDK.logger.info("User logged in", attributes: [
"userId": user.id,
"authMethod": "oauth",
"provider": "google"
])
// After authentication fails
SentrySDK.logger.warn("Login failed", attributes: [
"email": maskedEmail,
"reason": "invalid_password",
"attemptCount": 3
])import io.sentry.Sentry
// After successful authentication
Sentry.logger().info("User logged in userId=%s authMethod=%s", user.id, "oauth")
// After authentication fails
Sentry.logger().warn("Login failed email=%s reason=%s", maskedEmail, "invalid_password")Query in Explore > Logs: userId:123 "logged in" or severity:warn authMethod:*
Alert idea: Alert when severity:warn "Login failed" spikes in a 5-minute window—this can indicate brute force attempts or auth provider issues.
Money paths need visibility even when they succeed. When payments fail, you need context fast.
// After payment gateway returns an error
Sentry.logger.error("Payment failed", {
orderId: "order_123",
amount: 99.99,
gateway: "stripe",
errorCode: "card_declined",
cartItems: 3,
});from sentry_sdk import logger as sentry_logger
# After payment gateway returns an error
sentry_logger.error("Payment failed",
order_id="order_123",
amount=99.99,
gateway="stripe",
error_code="card_declined",
cart_items=3
)// After payment gateway returns an error
\Sentry\logger()->error('Payment failed', attributes: [
'order_id' => 'order_123',
'amount' => 99.99,
'gateway' => 'stripe',
'error_code' => 'card_declined',
'cart_items' => 3,
]);// After payment gateway returns an error
SentrySdk.Logger.Error("Payment failed", logger => logger
.SetAttribute("orderId", "order_123")
.SetAttribute("amount", 99.99)
.SetAttribute("gateway", "stripe")
.SetAttribute("errorCode", "card_declined")
.SetAttribute("cartItems", 3));# After payment gateway returns an error
Sentry.logger.error('Payment failed',
order_id: 'order_123',
amount: 99.99,
gateway: 'stripe',
error_code: 'card_declined',
cart_items: 3
)// After payment gateway returns an error
Sentry.logger.error('Payment failed', attributes: {
'orderId': 'order_123',
'amount': 99.99,
'gateway': 'stripe',
'errorCode': 'card_declined',
'cartItems': 3,
});import Sentry
// After payment gateway returns an error
SentrySDK.logger.error("Payment failed", attributes: [
"orderId": "order_123",
"amount": 99.99,
"gateway": "stripe",
"errorCode": "card_declined",
"cartItems": 3
])import io.sentry.Sentry
// After payment gateway returns an error
Sentry.logger().error("Payment failed orderId=%s gateway=%s errorCode=%s",
"order_123", "stripe", "card_declined")Query in Explore > Logs: orderId:order_123 or severity:error gateway:stripe
Alert idea: Alert when severity:error gateway:* spikes—this can indicate payment provider outages.
Traces capture what your code does. Logs capture context about external triggers and async boundaries. These are things like webhooks, scheduled tasks, and third-party API responses that traces can't automatically instrument.
// Third-party API call
const start = Date.now();
const response = await shippingApi.getRates(items);
Sentry.logger.info("Shipping rates fetched", {
service: "shipping-provider",
endpoint: "/rates",
durationMs: Date.now() - start,
rateCount: response.rates.length,
});
// Webhook received
Sentry.logger.info("Webhook received", {
source: "stripe",
eventType: "payment_intent.succeeded",
paymentId: event.data.object.id,
});import time
from sentry_sdk import logger as sentry_logger
# Third-party API call
start = time.time()
response = shipping_api.get_rates(items)
sentry_logger.info("Shipping rates fetched",
service="shipping-provider",
endpoint="/rates",
duration_ms=int((time.time() - start) * 1000),
rate_count=len(response.rates)
)
# Webhook received
sentry_logger.info("Webhook received",
source="stripe",
event_type="payment_intent.succeeded",
payment_id=event["data"]["object"]["id"]
)// Third-party API call
$start = microtime(true);
$response = $shippingApi->getRates($items);
\Sentry\logger()->info('Shipping rates fetched', attributes: [
'service' => 'shipping-provider',
'endpoint' => '/rates',
'duration_ms' => (int)((microtime(true) - $start) * 1000),
'rate_count' => count($response->rates),
]);
// Webhook received
\Sentry\logger()->info('Webhook received', attributes: [
'source' => 'stripe',
'event_type' => 'payment_intent.succeeded',
'payment_id' => $event['data']['object']['id'],
]);// Third-party API call
var stopwatch = Stopwatch.StartNew();
var response = await shippingApi.GetRatesAsync(items);
SentrySdk.Logger.Info("Shipping rates fetched", logger => logger
.SetAttribute("service", "shipping-provider")
.SetAttribute("endpoint", "/rates")
.SetAttribute("durationMs", stopwatch.ElapsedMilliseconds)
.SetAttribute("rateCount", response.Rates.Count));
// Webhook received
SentrySdk.Logger.Info("Webhook received", logger => logger
.SetAttribute("source", "stripe")
.SetAttribute("eventType", "payment_intent.succeeded")
.SetAttribute("paymentId", eventData.Object.Id));# Third-party API call
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
response = shipping_api.get_rates(items)
Sentry.logger.info('Shipping rates fetched',
service: 'shipping-provider',
endpoint: '/rates',
duration_ms: ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).to_i,
rate_count: response.rates.length
)
# Webhook received
Sentry.logger.info('Webhook received',
source: 'stripe',
event_type: 'payment_intent.succeeded',
payment_id: event['data']['object']['id']
)// Third-party API call
final stopwatch = Stopwatch()..start();
final response = await shippingApi.getRates(items);
Sentry.logger.info('Shipping rates fetched', attributes: {
'service': 'shipping-provider',
'endpoint': '/rates',
'durationMs': stopwatch.elapsedMilliseconds,
'rateCount': response.rates.length,
});
// Webhook received
Sentry.logger.info('Webhook received', attributes: {
'source': 'stripe',
'eventType': 'payment_intent.succeeded',
'paymentId': event.data.object.id,
});import Sentry
// Third-party API call
let start = Date()
let response = try await shippingApi.getRates(items)
SentrySDK.logger.info("Shipping rates fetched", attributes: [
"service": "shipping-provider",
"endpoint": "/rates",
"durationMs": Int(Date().timeIntervalSince(start) * 1000),
"rateCount": response.rates.count
])
// Webhook received
SentrySDK.logger.info("Webhook received", attributes: [
"source": "stripe",
"eventType": "payment_intent.succeeded",
"paymentId": event.data.object.id
])import io.sentry.Sentry
// Third-party API call
val start = System.currentTimeMillis()
val response = shippingApi.getRates(items)
Sentry.logger().info("Shipping rates fetched service=%s durationMs=%d",
"shipping-provider", System.currentTimeMillis() - start)
// Webhook received
Sentry.logger().info("Webhook received source=%s eventType=%s",
"stripe", "payment_intent.succeeded")Query in Explore > Logs: service:shipping-provider durationMs:>2000 or source:stripe
Alert idea: Alert when service:* durationMs:>3000 to catch third-party slowdowns before they cascade.
Jobs run outside the request context. Without logs, failed jobs are invisible until someone notices missing data.
// Inside background job handler
Sentry.logger.info("Job started", {
jobType: "email-digest",
jobId: "job_456",
queue: "notifications",
});
Sentry.logger.error("Job failed", {
jobType: "email-digest",
jobId: "job_456",
retryCount: 3,
lastError: "SMTP timeout",
});from sentry_sdk import logger as sentry_logger
# Inside background job handler
sentry_logger.info("Job started",
job_type="email-digest",
job_id="job_456",
queue="notifications"
)
sentry_logger.error("Job failed",
job_type="email-digest",
job_id="job_456",
retry_count=3,
last_error="SMTP timeout"
)// Inside background job handler
\Sentry\logger()->info('Job started', attributes: [
'job_type' => 'email-digest',
'job_id' => 'job_456',
'queue' => 'notifications',
]);
\Sentry\logger()->error('Job failed', attributes: [
'job_type' => 'email-digest',
'job_id' => 'job_456',
'retry_count' => 3,
'last_error' => 'SMTP timeout',
]);// Inside background job handler
SentrySdk.Logger.Info("Job started", logger => logger
.SetAttribute("jobType", "email-digest")
.SetAttribute("jobId", "job_456")
.SetAttribute("queue", "notifications"));
SentrySdk.Logger.Error("Job failed", logger => logger
.SetAttribute("jobType", "email-digest")
.SetAttribute("jobId", "job_456")
.SetAttribute("retryCount", 3)
.SetAttribute("lastError", "SMTP timeout"));# Inside background job handler
Sentry.logger.info('Job started',
job_type: 'email-digest',
job_id: 'job_456',
queue: 'notifications'
)
Sentry.logger.error('Job failed',
job_type: 'email-digest',
job_id: 'job_456',
retry_count: 3,
last_error: 'SMTP timeout'
)// Inside background job handler
Sentry.logger.info('Job started', attributes: {
'jobType': 'email-digest',
'jobId': 'job_456',
'queue': 'notifications',
});
Sentry.logger.error('Job failed', attributes: {
'jobType': 'email-digest',
'jobId': 'job_456',
'retryCount': 3,
'lastError': 'SMTP timeout',
});import Sentry
// Inside background job handler
SentrySDK.logger.info("Job started", attributes: [
"jobType": "email-digest",
"jobId": "job_456",
"queue": "notifications"
])
SentrySDK.logger.error("Job failed", attributes: [
"jobType": "email-digest",
"jobId": "job_456",
"retryCount": 3,
"lastError": "SMTP timeout"
])import io.sentry.Sentry
// Inside background job handler
Sentry.logger().info("Job started jobType=%s jobId=%s queue=%s",
"email-digest", "job_456", "notifications")
Sentry.logger().error("Job failed jobType=%s jobId=%s retryCount=%d",
"email-digest", "job_456", 3)Query in Explore > Logs: jobType:email-digest severity:error
Alert idea: Alert when severity:error jobType:* spikes—this can indicate queue processing issues or downstream failures.
When something breaks after a deploy, the first question is "what changed?" Logging flag evaluations and config reloads gives you that answer instantly.
// When feature flag is checked or config changes
Sentry.logger.info("Feature flag evaluated", {
flag: "new-checkout-flow",
enabled: true,
userId: user.id,
});
Sentry.logger.warn("Config reloaded", {
reason: "env-change",
changedKeys: ["API_TIMEOUT", "MAX_CONNECTIONS"],
});from sentry_sdk import logger as sentry_logger
# When feature flag is checked or config changes
sentry_logger.info("Feature flag evaluated",
flag="new-checkout-flow",
enabled=True,
user_id=user.id
)
sentry_logger.warning("Config reloaded",
reason="env-change",
changed_keys=["API_TIMEOUT", "MAX_CONNECTIONS"]
)// When feature flag is checked or config changes
\Sentry\logger()->info('Feature flag evaluated', attributes: [
'flag' => 'new-checkout-flow',
'enabled' => true,
'user_id' => $user->id,
]);
\Sentry\logger()->warn('Config reloaded', attributes: [
'reason' => 'env-change',
'changed_keys' => ['API_TIMEOUT', 'MAX_CONNECTIONS'],
]);// When feature flag is checked or config changes
SentrySdk.Logger.Info("Feature flag evaluated", logger => logger
.SetAttribute("flag", "new-checkout-flow")
.SetAttribute("enabled", true)
.SetAttribute("userId", user.Id));
SentrySdk.Logger.Warning("Config reloaded", logger => logger
.SetAttribute("reason", "env-change")
.SetAttribute("changedKeys", new[] { "API_TIMEOUT", "MAX_CONNECTIONS" }));# When feature flag is checked or config changes
Sentry.logger.info('Feature flag evaluated',
flag: 'new-checkout-flow',
enabled: true,
user_id: user.id
)
Sentry.logger.warn('Config reloaded',
reason: 'env-change',
changed_keys: ['API_TIMEOUT', 'MAX_CONNECTIONS']
)// When feature flag is checked or config changes
Sentry.logger.info('Feature flag evaluated', attributes: {
'flag': 'new-checkout-flow',
'enabled': true,
'userId': user.id,
});
Sentry.logger.warn('Config reloaded', attributes: {
'reason': 'env-change',
'changedKeys': ['API_TIMEOUT', 'MAX_CONNECTIONS'],
});import Sentry
// When feature flag is checked or config changes
SentrySDK.logger.info("Feature flag evaluated", attributes: [
"flag": "new-checkout-flow",
"enabled": true,
"userId": user.id
])
SentrySDK.logger.warn("Config reloaded", attributes: [
"reason": "env-change",
"changedKeys": ["API_TIMEOUT", "MAX_CONNECTIONS"]
])import io.sentry.Sentry
// When feature flag is checked or config changes
Sentry.logger().info("Feature flag evaluated flag=%s enabled=%b userId=%s",
"new-checkout-flow", true, user.id)
Sentry.logger().warn("Config reloaded reason=%s", "env-change")Query in Explore > Logs: flag:new-checkout-flow or "Config reloaded"
- Go to Explore > Logs
- Enter your search query (e.g.,
severity:error gateway:*) - Click Save As - Alert
- Choose a threshold type:
- Static: Alert when count exceeds a value
- Percent Change: Alert when count changes relative to a previous period
- Anomaly: Let Sentry detect unusual patterns
- Configure notification channels and save
Learn about creating alerts and best practices for reducing noise and routing notifications.
In development, set sample rates to 100% to catch everything. This helps you understand what logs are being generated and tune your instrumentation before it hits production.
Development configuration:
Sentry.init({
dsn: "...",
environment: "development",
tracesSampleRate: 1.0, // 100% of traces
// Capture all logs in development
integrations: [
Sentry.captureConsoleIntegration({
levels: ["log", "info", "warn", "error", "debug"],
}),
],
});import sentry_sdk
sentry_sdk.init(
dsn="...",
environment="development",
traces_sample_rate=1.0, # 100% of traces
enable_logs=True,
)\Sentry\init([
'dsn' => '...',
'environment' => 'development',
'traces_sample_rate' => 1.0, // 100% of traces
'enable_logs' => true,
]);SentrySdk.Init(options =>
{
options.Dsn = "...";
options.Environment = "development";
options.TracesSampleRate = 1.0; // 100% of traces
options.EnableLogs = true;
});Sentry.init do |config|
config.dsn = '...'
config.environment = 'development'
config.traces_sample_rate = 1.0 # 100% of traces
config.enable_logs = true
endawait SentryFlutter.init(
(options) {
options.dsn = '...';
options.environment = 'development';
options.tracesSampleRate = 1.0; // 100% of traces
options.enableLogs = true;
},
appRunner: () => runApp(MyApp()),
);import Sentry
SentrySDK.start { options in
options.dsn = "..."
options.environment = "development"
options.tracesSampleRate = 1.0 // 100% of traces
options.enableLogs = true
}import io.sentry.android.core.SentryAndroid
SentryAndroid.init(context) { options ->
options.dsn = "..."
options.environment = "development"
options.tracesSampleRate = 1.0 // 100% of traces
options.logs.enabled = true
}Use verbose logging levels like debug (development diagnostics) and trace (fine-grained execution details) freely in development. You can filter these out in production using beforeSendLog to only capture info and above.
Local debugging often means many small logs tracing execution flow. In production, this creates noise that's hard to query.
Instead, log fewer messages with higher cardinality. Store events during execution and emit them as a single structured log.
Don't do this:
Sentry.logger.info("Checkout started", { userId: "882" });
Sentry.logger.info("Discount applied", { code: "WINTER20" });
Sentry.logger.error("Payment failed", { reason: "Insufficient Funds" });sentry_logger.info("Checkout started", user_id="882")
sentry_logger.info("Discount applied", code="WINTER20")
sentry_logger.error("Payment failed", reason="Insufficient Funds")\Sentry\logger()->info('Checkout started', attributes: ['user_id' => '882']);
\Sentry\logger()->info('Discount applied', attributes: ['code' => 'WINTER20']);
\Sentry\logger()->error('Payment failed', attributes: ['reason' => 'Insufficient Funds']);SentrySdk.Logger.Info("Checkout started", l => l.SetAttribute("userId", "882"));
SentrySdk.Logger.Info("Discount applied", l => l.SetAttribute("code", "WINTER20"));
SentrySdk.Logger.Error("Payment failed", l => l.SetAttribute("reason", "Insufficient Funds"));Sentry.logger.info('Checkout started', user_id: '882')
Sentry.logger.info('Discount applied', code: 'WINTER20')
Sentry.logger.error('Payment failed', reason: 'Insufficient Funds')Sentry.logger.info('Checkout started', attributes: {'userId': '882'});
Sentry.logger.info('Discount applied', attributes: {'code': 'WINTER20'});
Sentry.logger.error('Payment failed', attributes: {'reason': 'Insufficient Funds'});SentrySDK.logger.info("Checkout started", attributes: ["userId": "882"])
SentrySDK.logger.info("Discount applied", attributes: ["code": "WINTER20"])
SentrySDK.logger.error("Payment failed", attributes: ["reason": "Insufficient Funds"])Sentry.logger().info("Checkout started userId=%s", "882")
Sentry.logger().info("Discount applied code=%s", "WINTER20")
Sentry.logger().error("Payment failed reason=%s", "Insufficient Funds")These logs are trace-connected, but searching for the error won't return the userId or discount code from the same transaction.
Do this instead:
Sentry.logger.error("Checkout failed", {
userId: "882",
orderId: "order_pc_991",
cartTotal: 142.5,
discountCode: "WINTER20",
paymentMethod: "stripe",
errorReason: "Insufficient Funds",
itemCount: 4,
});sentry_logger.error("Checkout failed",
user_id="882",
order_id="order_pc_991",
cart_total=142.50,
discount_code="WINTER20",
payment_method="stripe",
error_reason="Insufficient Funds",
item_count=4
)\Sentry\logger()->error('Checkout failed', attributes: [
'user_id' => '882',
'order_id' => 'order_pc_991',
'cart_total' => 142.50,
'discount_code' => 'WINTER20',
'payment_method' => 'stripe',
'error_reason' => 'Insufficient Funds',
'item_count' => 4,
]);SentrySdk.Logger.Error("Checkout failed", logger => logger
.SetAttribute("userId", "882")
.SetAttribute("orderId", "order_pc_991")
.SetAttribute("cartTotal", 142.50)
.SetAttribute("discountCode", "WINTER20")
.SetAttribute("paymentMethod", "stripe")
.SetAttribute("errorReason", "Insufficient Funds")
.SetAttribute("itemCount", 4));Sentry.logger.error('Checkout failed',
user_id: '882',
order_id: 'order_pc_991',
cart_total: 142.50,
discount_code: 'WINTER20',
payment_method: 'stripe',
error_reason: 'Insufficient Funds',
item_count: 4
)Sentry.logger.error('Checkout failed', attributes: {
'userId': '882',
'orderId': 'order_pc_991',
'cartTotal': 142.50,
'discountCode': 'WINTER20',
'paymentMethod': 'stripe',
'errorReason': 'Insufficient Funds',
'itemCount': 4,
});import Sentry
SentrySDK.logger.error("Checkout failed", attributes: [
"userId": "882",
"orderId": "order_pc_991",
"cartTotal": 142.50,
"discountCode": "WINTER20",
"paymentMethod": "stripe",
"errorReason": "Insufficient Funds",
"itemCount": 4
])import io.sentry.Sentry
import io.sentry.SentryAttribute
import io.sentry.SentryAttributes
import io.sentry.SentryLogLevel
import io.sentry.logger.SentryLogParameters
Sentry.logger().log(
SentryLogLevel.ERROR,
SentryLogParameters.create(
SentryAttributes.of(
SentryAttribute.stringAttribute("userId", "882"),
SentryAttribute.stringAttribute("orderId", "order_pc_991"),
SentryAttribute.doubleAttribute("cartTotal", 142.50),
SentryAttribute.stringAttribute("discountCode", "WINTER20"),
SentryAttribute.stringAttribute("paymentMethod", "stripe"),
SentryAttribute.stringAttribute("errorReason", "Insufficient Funds"),
SentryAttribute.integerAttribute("itemCount", 4)
)
),
"Checkout failed"
)One log tells the whole story. Search for the error and get full context.
If you can't install the Sentry SDK or need platform-level logs (CDN, database, load balancer), use Log Drains.
Platform drains: Vercel, Cloudflare Workers, Heroku, Supabase
Forwarders: OpenTelemetry Collector, Vector, Fluent Bit, AWS CloudWatch, Kafka
| Category | Level | Example Attributes |
|---|---|---|
| Auth events | info/warn |
userId, authMethod, reason |
| Payments | info/error |
orderId, amount, gateway, errorCode |
| External APIs | info |
service, endpoint, durationMs |
| Background jobs | info/error |
jobType, jobId, retryCount |
| Feature flags | info |
flag, enabled, changedKeys |
Explore the Logs product walkthrough guides to learn more about the Sentry interface and discover additional tips.