Skip to content

Latest commit

 

History

History
760 lines (577 loc) · 32 KB

File metadata and controls

760 lines (577 loc) · 32 KB
title Logs
description Structured logging protocol with severity levels, trace context, and batched envelope delivery.
spec_id sdk/telemetry/logs
spec_version 2.0.0
spec_status stable
spec_depends_on
id version
sdk/foundations/transport/envelopes
>=1.0.0
id version
sdk/foundations/state-management/scopes/attributes
>=1.0.0
spec_changelog
version date summary
2.0.0
2026-04-01
Default enableLogs to true, add auto-emitted logs opt-in requirements for integrations
version date summary
1.16.0
2026-03-04
Add sentry.timestamp.sequence attribute for deterministic log ordering
version date summary
1.15.0
2026-02-03
Clarified 100 logs per envelope hard limit, SDKs MAY use lower buffer limit
version date summary
1.14.0
2025-12-16
User attributes now optional, guarded by sendDefaultPii
version date summary
1.13.0
2025-11-18
Added client reports requirement (log_item count, log_byte size)
version date summary
1.12.0
2025-11-10
Added buffer hard limit of 1000 queued log events
version date summary
1.11.0
2025-10-30
Added span_id as top-level payload field, replaced sentry.trace.parent_span_id attribute
version date summary
1.10.0
2025-10-22
Hub/Scope MUST offer same logger methods as static API, Client SHOULD offer generic captureLog
version date summary
1.9.0
2025-10-09
Consolidated sentry.origin rules. No origin for manual calls, auto.log.* for libraries, auto.*.* for instrumentation
version date summary
1.8.0
2025-09-10
Added sentry.replay_id as default attribute and replay association behavior
version date summary
1.7.0
2025-08-28
Added message template constraint. MUST NOT send sentry.message.template without parameters
version date summary
1.6.0
2025-06-10
User attributes no longer gated behind sendDefaultPii
version date summary
1.5.0
2025-05-27
Added platform-specific default attributes for browser, backend, mobile/desktop/native
version date summary
1.4.0
2025-05-08
Added Tracing without Performance as prerequisite for logs
version date summary
1.3.0
2025-05-07
Added structured log processing pipeline, removed logsSampleRate init option
version date summary
1.2.0
2025-05-01
Refined buffering requirements (100 items or 5 seconds flush)
version date summary
1.1.0
2025-04-24
Documented log envelope item structure (items array wrapper)
version date summary
1.0.0
2025-04-22
Initial spec. Log protocol, severity model, logger API, init options, otel_log format

Overview

Logs allow Sentry to ingest structured log data from SDKs. SDKs send log envelopes containing structured log payloads with severity levels, trace context, and arbitrary attributes. Sentry uses this data to provide searchable, correlated log views alongside errors and traces.

There are two wire protocols: the log envelope with the Sentry Log protocol (preferred), and the otel_log envelope with the OpenTelemetry Log protocol. All SDKs MUST send logs via the log envelope and Sentry Log protocol. The otel_log format is documented in Appendix: otel_log Format for completeness.

Related specs:


Concepts

Structured Logging

Logs support parameterized message templates for structured logging. Instead of interpolating values into the message body directly, SDKs send the template and parameters separately. This enables Sentry to group similar log messages and perform efficient searching.

Template:    "User %s has logged in!"
Parameter 0: "John"
Body:        "User John has logged in!"

The SDK sends the interpolated body, the sentry.message.template, and sentry.message.parameter.X attributes.

Severity Levels

Logs use a severity model with six levels (lowest to highest): trace, debug, info, warn, error, fatal. These map exactly to OpenTelemetry's Severity text field.

Each level maps to a numeric severity range following the OpenTelemetry Severity Number specification:

SeverityNumber range Range name Meaning
1-4 Trace A fine-grained debugging event. Typically disabled in default configurations.
5-8 Debug A debugging event.
9-12 Info An informational event. Indicates that an event happened.
13-16 Warn A warning event. Not an error but is likely more important than an informational event.
17-20 Error An error event. Something went wrong.
21-24 Fatal A fatal error such as application or system crash.

SDKs SHOULD set the lowest severity number for a given severity level (e.g., warn uses severity number 13).

Log Buffering

Logs are buffered before sending. SDKs collect logs into a buffer and flush them as a batch in a single log envelope item, rather than sending each log individually. This reduces network overhead and aligns with Relay, which is optimized for up to 100 logs per envelope.


Behavior

Log Processing Pipeline

Log processing MUST follow this order:

  1. Capture log via Public APIs (e.g. Sentry.logger.info) or via SDK integrations.
  2. Check if logging is enabled via enableLogs/enable_logs. If not, skip remaining steps.
  3. Pass the log to Hub/Scope via a generic method (e.g., captureLog).
  4. Pass the log to the Client via a generic method (e.g., captureLog).
  5. Process captured log (attach attributes per Default Attributes).
  6. Run beforeSendLog/before_send_log to filter or modify the log.
  7. Add log to buffer/batch processor per Buffering.
  8. At flush time, send array of logs to Sentry via log envelope, applying rate limiting per Data Category and Rate Limiting.

An SDK SHOULD implement Tracing without Performance before adding support for logs. This is required to ensure logs are associated with traces and that the correct trace context is sent to Sentry.

Default Attributes

SDKs MUST attach the following attributes to every log:

Attribute Since Description
sentry.environment 1.0.0 The environment set in the SDK, if defined.
sentry.release 1.0.0 The release set in the SDK, if defined.
sentry.sdk.name 1.0.0 The name of the SDK that sent the log.
sentry.sdk.version 1.0.0 The version of the SDK that sent the log.
sentry.replay_id 1.8.0 The replay ID of the active replay when the log was collected. MUST NOT be set if no replay is active.

For parameterized logs, SDKs MUST also attach:

Attribute Since Description
sentry.message.template 1.0.0 The parameterized template string.
sentry.message.parameter.X 1.0.0 Parameters to the template. X is either the positional index (0, 1, etc.) or the parameter name (item_id, user_id, etc.).

If there are no sentry.message.parameter.X attributes, SDKs MUST NOT attach sentry.message.template (since 1.7.0). This reduces duplicate content and simplifies PII processing.

SDKs SHOULD minimize default attributes. Logs are charged per byte size. New default attributes SHOULD only be added after significant user feedback and discussion with the SDK and ingest teams.

SDK Integration Origin

If a log is generated by an SDK integration, the SDK SHOULD set the sentry.origin attribute per the Trace Origin documentation.

Since 1.9.0, logs follow these rules:

  1. User calls Sentry's Logging API directly: SDKs MUST NOT send sentry.origin. This intentionally deviates from the Trace Origin spec to minimize log size.
  2. Captured from a logging library: Use auto.log.<name> format (e.g., "auto.log.serilog").
  3. Auto-emitted from other instrumented systems: Use auto.<category>.<integration-name> format (e.g., "auto.db.prisma").

User Attributes

SDKs MAY attach user information as attributes, guarded by sendDefaultPii (since 1.14.0):

Attribute Description
user.id The user ID. Maps to id in the User payload.
user.name The username. Maps to username in the User payload.
user.email The email address. Maps to email in the User payload.

User Agent Parsing

By default, Relay parses the user agent attached to an incoming log envelope to extract browser and os information. SDKs MAY attach these attributes if they do not forward a user agent:

Attribute Description
browser.name Display name of the browser. Maps to name in Browser Context.
browser.version Version string of the browser. Maps to version in Browser Context.

Backend SDK Attributes

Backend SDKs (Node.js, Python, PHP, etc.) SHOULD attach:

Attribute Description
server.address The server address. Equivalent to server_name on errors and transactions.

Mobile, Desktop, and Native SDK Attributes

Mobile, desktop, and native SDKs (Android, Apple, Electron, etc.) SHOULD attach:

Attribute Description
os.name OS name. Maps to name in OS Context.
os.version OS version. Maps to version in OS Context.
device.brand Device brand. Maps to brand in Device Context.
device.model Device model. Maps to model in Device Context.
device.family Device family. Maps to family in Device Context.

Buffering

SDKs MUST buffer logs before sending. A simple initial strategy is flushing when the buffer exceeds 100 items or 5 seconds have passed. The buffer SHOULD forward logs to the transport in the scenarios outlined in the telemetry buffer data forwarding scenarios.

SDKs MUST NOT send more than 100 logs per envelope (since 1.15.0). Relay is optimized for this limit.

SDKs MUST enforce a hard limit of 1000 queued log events to prevent out-of-memory issues (since 1.12.0). Logs added beyond this limit are dropped. SDKs MAY use a lower limit but MUST NOT exceed 1000.

SDKs MUST NOT release logging capabilities to users without a buffering implementation.

Data Category and Rate Limiting

Logs use the log_item data category for rate limiting in Relay. Both log and otel_log envelopes are covered by this data category. SDKs MUST implement this data category. Rate limiting applies as usual with no special behavior for logs.

SDKs MUST track client outcomes for this data category to report how often logs are dropped.

SDK Integrations

SDKs SHOULD provide integrations that capture logs from platform logging libraries (e.g., JavaScript's console, Python's logging, Java's Log4j) when enableLogs/enable_logs is set to true.

SDKs MAY introduce additional options beyond enableLogs/enable_logs to gate integration-specific functionality (e.g., controlling log appenders added via external config).

Auto-Emitted Logs

Logs emitted via the public API are opt-out. The enableLogs/enable_logs option MUST act as a general kill switch for all logs sent to Sentry. When set to false, no logs MUST be sent, regardless of whether they originate from the public API, auto-emitting integrations, or third-party bindings.

However, logs that are automatically emitted by integrations or libraries without an explicit user call follow different rules because they can generate unexpected volume and cost.

Integrations that auto-emit logs - An SDK integration that automatically captures logs (e.g., forwarding console.log calls, capturing framework-level diagnostic output) MUST be opt-in. The integration MUST NOT emit any logs unless the user explicitly enables it. The opt-in mechanism MAY be a dedicated integration-level option or requiring the user to explicitly add the integration.

Third-party log bindings - An SDK feature that binds to an external library's logging API and mirrors those logs as Sentry logs in the background (e.g., syncing from Python's logging module or a framework's built-in logger) MUST also be opt-in. Users MUST explicitly enable the binding before any logs are forwarded to Sentry.

These rules exist because auto-emitted logs can cause a high volume of telemetry that directly impacts a user's quota. Unlike errors, where users have prior intuition about volume, silently emitting logs on their behalf could lead to unexpected costs. Requiring opt-in ensures users remain in control of their log volume.

Tracing Association

Logs SHOULD be associated with traces. If a log is recorded during an active span, SDKs SHOULD set the span_id property to the active span's ID.

Replay Association

Logs SHOULD be associated with replays. If a log is recorded during an active replay, SDKs SHOULD set the sentry.replay_id attribute to the active replay's ID.

Log Ordering

Some runtimes (notably Cloudflare Workers) freeze timer APIs (Date.now(), performance.now()) during synchronous execution, causing multiple logs to share identical timestamps. SDKs that target runtimes where timestamps may be frozen or lack sub-millisecond precision MUST attach a sentry.timestamp.sequence integer attribute to every log. SDKs that only target runtimes with reliable sub-millisecond timestamps MAY omit it.

When sent, the sequence integer MUST:

  • Start at 0 when the SDK initializes.
  • Increment by 1 for each log that is captured.
  • Reset to 0 when:
    • The SDK is re-initialized.
    • The current log's integer millisecond differs from the previous log's integer millisecond (i.e., floor(timestamp_seconds * 1000) changes).

The sequence provides deterministic ordering within a single SDK instance. It does not guarantee ordering across independent processes or workers, which have separate counters. The reset behavior ensures the sequence only increments for consecutive logs that share the same timestamp.

Debug Mode

Debug Mode

If debug is set to true in SDK init, calls to the Sentry logger API SHOULD also print to the console with the appropriate log level. This aids debugging of logging setups.

Client Reports

SDKs MUST report count (log_item) and size in bytes (log_byte) of discarded log messages. An approximation of log size is sufficient (e.g., by counting as if serialized).


Wire Format

log Envelope Item

The log envelope item contains an array of log payloads encoded as JSON, allowing multiple logs per envelope item.

An envelope MUST contain at most one log envelope item. All log entries for a flush are batched into a single item's items array. Logs from different traces MAY be mixed into the same log item, but if they are, the envelope MUST NOT include a DSC (dynamic sampling context) header.

Item Headers:

Field Type Required Description
type String REQUIRED MUST be "log".
item_count Integer REQUIRED The number of log entries in the items array. MUST match the actual count.
content_type String REQUIRED MUST be "application/vnd.sentry.items.log+json".
{
  "type": "log",
  "item_count": 5,
  "content_type": "application/vnd.sentry.items.log+json"
}
{
  "items": [{..log..}, {..log..}, {..log..}, {..log..}, {..log..}]
}

log Envelope Item Payload

Each log in the items array is a JSON object:

{
  "timestamp": 1544719860.0,
  "trace_id": "5b8efff798038103d269b633813fc60c",
  "span_id": "b0e6f15b45c36b12",
  "level": "info",
  "body": "User John has logged in!",
  "severity_number": 9,
  "attributes": {
    "sentry.message.template": {
      "value": "User %s has logged in!",
      "type": "string"
    },
    "sentry.message.parameter.0": {
      "value": "John",
      "type": "string"
    }
  }
}
Field Type Required Since Description
timestamp Number REQUIRED 1.0.0 Timestamp of the log in seconds since the Unix epoch.
trace_id String REQUIRED 1.0.0 Trace ID as 16 random bytes encoded as a hex string (32 characters). MUST be grabbed from the current propagation context.
level String REQUIRED 1.0.0 Severity level. One of trace, debug, info, warn, error, fatal.
body String REQUIRED 1.0.0 The log body/message.
span_id String OPTIONAL 1.11.0 Span ID of the active span when the log was collected, as 8 random bytes encoded as a hex string (16 characters).
severity_number Integer OPTIONAL 1.0.0 Severity number per Severity Levels. Inferred from level unless explicitly set.
attributes Object OPTIONAL 1.0.0 Key-value pairs of arbitrary data. Values MUST declare their type. See Attributes.

otel_log Envelope Item Payload

The otel_log envelope item payload is a JSON object implementing the OpenTelemetry Log Data Model. Multiple otel_log items MAY be sent per envelope.

{
  "severity_text": "info",
  "body": {
    "string_value": "User John has logged in!"
  },
  "attributes": [
    {
      "key": "sentry.message.template",
      "value": { "string_value": "User %s has logged in!" }
    },
    {
      "key": "sentry.message.parameters.0",
      "value": { "string_value": "John" }
    }
  ],
  "time_unix_nano": "1741191250694000000",
  "trace_id": "edec519707974fc8bfccb5a017e17394",
  "severity_number": 9
}
Field Type Required Description
severity_text String REQUIRED Severity level. One of trace, debug, info, warn, error, fatal.
body Object REQUIRED The log body. Contains string_value with the message text.
trace_id String OPTIONAL Trace ID for linking logs to traces and errors. Heavily recommended.
severity_number Integer OPTIONAL Severity number per Severity Levels.
attributes Array<Object> OPTIONAL Key-value pairs. Each entry has key (string) and value (object with typed value). Supported value fields: stringValue, intValue (string-encoded), boolValue, doubleValue.
time_unix_nano String OPTIONAL Creation time in Unix nanoseconds. SDKs SHOULD set this to the current time if not provided.

Public API

Initialization Options

SDKs MUST expose the following configuration options:

Option Type Default Since Description
enableLogs / enable_logs Boolean true 1.0.0 Controls whether log envelopes are generated and sent. If false, the SDK MUST NOT send logs.
beforeSendLog / before_send_log Function - 1.0.0 OPTIONAL callback receiving a log object. Returns a modified log or null to drop it.

The default for enableLogs/enable_logs was changed from false to true in version 2.0.0. The previous default required users to explicitly opt in before they could use the logging primitives. By defaulting to true, adding a Sentry.logger.* statement is itself the opt-in. Users can start logging without additional configuration. This aligns the behavior with enableMetrics/enable_metrics, which also defaults to true.

While logs functionality is in an experimental state, SDKs SHOULD put these options in an experimental namespace to avoid breaking changes:

Sentry.init({
  // stable
  enableLogs: true,

  // experimental
  _experiments: { enableLogs: true },
});

Logger Module

SDKs MUST expose logging methods in a logger module or namespace, with one method per severity level:

  • Sentry.logger.trace
  • Sentry.logger.debug
  • Sentry.logger.info
  • Sentry.logger.warn
  • Sentry.logger.error
  • Sentry.logger.fatal

These methods accept a string template and parameters for structured logging. They MAY also accept arbitrary attributes.

The Hub/Scope and Client MUST exclusively provide a generic logging method (e.g., captureLog). They MUST NOT replicate the static Sentry.logger.X API methods to avoid increasing our API surface. Users utilizing multiple Hub/Scope or Clients will need to rely on the generic method for logging.

Threading and Concurrency

The Sentry.logger.X methods are fire-and-forget (no return value). SDKs MAY run these methods on a separate thread or queue them for later processing, including evaluating the string template and running internal hooks like beforeSendLog.

SDKs MUST ensure logs are sent in the order they are received.

Putting everything on a background thread risks losing logs that occur directly before a crash. Mobile SDKs will need a more robust mechanism. The individual SDK will need to determine the best approach for their platform.


Examples

SDK API Usage

These examples are illustrative, not normative. SDK authors SHOULD ensure the API best fits their platform.

// Browser - tagged template literals
Sentry.logger.info(Sentry.logger.fmt`Adding item ${itemId} for user ${userId}`);

// Server-side - printf-like syntax
Sentry.logger.info("Adding item %s for user %s", [itemId, userId], {
  extra: 123,
});
Sentry.logger.info('Adding item {item} for user {user}', item=item_id, user=user_id, attributes={ 'extra': 123 });
use function Sentry\logger;

logger()->info('Adding item %s for user %s', [$item_id, $user_id], ['extra' => 123]);
Sentry.logger().info("Adding item %s for user %s", itemId, userId);
SentrySDK.logger
  .info(message: "Adding item %s for user %s",
    params: [itemId, userId],
    attributes: ["extra": @"123"]
  )

Full log Envelope

A complete log envelope with six log entries at different severity levels:

{ "sdk": { "name": "sentry.javascript.browser", "version": "9.15.0" } }
{
  "type": "log",
  "item_count": 6,
  "content_type": "application/vnd.sentry.items.log+json"
}
{
  "items": [
    {
      "timestamp": 1746456149.0191,
      "level": "trace",
      "body": "Modal animation performance: openTime=120ms, closeTime=85ms, fps=60",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 1,
      "attributes": {
        "modalId": { "value": "product_details", "type": "string" },
        "animationType": { "value": "slide-up", "type": "string" },
        "devicePixelRatio": { "value": 2, "type": "integer" },
        "browser": { "value": "Chrome", "type": "string" },
        "memoryUsage": { "value": "45MB", "type": "string" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "Modal animation performance: openTime=%sms, closeTime=%sms, fps=%s",
          "type": "string"
        },
        "sentry.message.parameter.0": { "value": 120, "type": "integer" },
        "sentry.message.parameter.1": { "value": 85, "type": "integer" },
        "sentry.message.parameter.2": { "value": 60, "type": "integer" },
        "sentry.timestamp.sequence": { "value": 0, "type": "integer" }
      }
    },
    {
      "timestamp": 1746456149.0191998,
      "level": "debug",
      "body": "Component ProductCard rendered 3 times",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 5,
      "attributes": {
        "props": { "value": "{\"productId\":\"prod_123\"}", "type": "string" },
        "renderTime": { "value": 45, "type": "integer" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "Component %s rendered %s times",
          "type": "string"
        },
        "sentry.message.parameter.0": {
          "value": "ProductCard",
          "type": "string"
        },
        "sentry.message.parameter.1": { "value": 3, "type": "integer" },
        "sentry.timestamp.sequence": { "value": 1, "type": "integer" }
      }
    },
    {
      "timestamp": 1746456149.0191998,
      "level": "info",
      "body": "Form checkout_form submitted successfully with 8 fields",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 9,
      "attributes": {
        "step": { "value": "payment_details", "type": "string" },
        "timeSpent": { "value": 120, "type": "integer" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "Form %s submitted successfully with %s fields",
          "type": "string"
        },
        "sentry.message.parameter.0": {
          "value": "checkout_form",
          "type": "string"
        },
        "sentry.message.parameter.1": { "value": 8, "type": "integer" },
        "sentry.timestamp.sequence": { "value": 2, "type": "integer" }
      }
    },
    {
      "timestamp": 1746456149.0192997,
      "level": "warn",
      "body": "API endpoint /api/products response time 2500ms exceeds threshold of 1000ms",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 13,
      "attributes": {
        "userAgent": { "value": "Mozilla/5.0...", "type": "string" },
        "connectionType": { "value": "4g", "type": "string" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "API endpoint %s response time %sms exceeds threshold of %sms",
          "type": "string"
        },
        "sentry.message.parameter.0": {
          "value": "/api/products",
          "type": "string"
        },
        "sentry.message.parameter.1": { "value": 2500, "type": "integer" },
        "sentry.message.parameter.2": { "value": 1000, "type": "integer" },
        "sentry.timestamp.sequence": { "value": 3, "type": "integer" }
      }
    },
    {
      "timestamp": 1746456149.0192997,
      "level": "error",
      "body": "Failed to load images for products prod_123, prod_456",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 17,
      "attributes": {
        "error": { "value": "NetworkError", "type": "string" },
        "statusCode": { "value": 404, "type": "integer" },
        "retryAttempts": { "value": 2, "type": "integer" },
        "browser": { "value": "Chrome", "type": "string" },
        "version": { "value": "120.0", "type": "string" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "Failed to load images for products %s",
          "type": "string"
        },
        "sentry.message.parameter.0": {
          "value": "prod_123, prod_456",
          "type": "string"
        },
        "sentry.timestamp.sequence": { "value": 4, "type": "integer" }
      }
    },
    {
      "timestamp": 1746456149.0192997,
      "level": "fatal",
      "body": "State corruption detected in ShoppingCart component during UPDATE_CART action",
      "trace_id": "624f66e93a04469f9992c7e9f1485056",
      "severity_number": 21,
      "attributes": {
        "error": { "value": "ReduxStateError", "type": "string" },
        "previousState": { "value": "valid", "type": "string" },
        "currentState": { "value": "invalid", "type": "string" },
        "sentry.sdk.name": {
          "value": "sentry.javascript.browser",
          "type": "string"
        },
        "sentry.sdk.version": { "value": "9.15.0", "type": "string" },
        "sentry.message.template": {
          "value": "State corruption detected in %s component during %s action",
          "type": "string"
        },
        "sentry.message.parameter.0": {
          "value": "ShoppingCart",
          "type": "string"
        },
        "sentry.message.parameter.1": {
          "value": "UPDATE_CART",
          "type": "string"
        },
        "sentry.timestamp.sequence": { "value": 5, "type": "integer" }
      }
    }
  ]
}

Changelog