Logs
Note
The Sentry Logs feature is under active development. The information in this document is subject to change.
This document defines the format used by Sentry to ingest logs, as well as the SDK API and behavior that create and send logs to Sentry.
There are two ways to send logs to Sentry: via the log
envelope and Sentry Log protocol (preferred), or the otel_log
envelope and OpenTelemetry Log protocol.
The otel_log
envelope item payload is a JSON object that represents an OpenTelemetry Log. Multiple otel_log
envelope items can be sent in a single 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
}
It consists of the following fields:
severity_text
- String, required. The severity level of the log. One of
trace
,debug
,info
,warn
,error
,fatal
(in order of lowest to highest). severity_number
- Integer, optional. The severity number of the log. See Log Severity Number for more information.
trace_id
- Integer, optional. [HEAVILY RECOMMENDED] The trace id of the log. SDKs should attempt to set this if possible. This is used to link logs to traces and errors in Sentry.
body
- Object, required. The log body/message.
Example:
{
"string_value": "Added item to shopping cart"
}
attributes
- Array<Object>, optional. A dictionary of key-value pairs of arbitrary data attached to the log. Attributes must also declare the type of the value. The following types are supported:
string
,number
,boolean
,integer
,double
.
Example:
{
"attributes": [
{
"key": "string_item",
"value": {
"stringValue": "value"
}
},
{
"key": "integer_item",
"value": {
"intValue": 123
}
},
{
"key": "boolean_value",
"value": {
"boolValue": false
}
},
{
"key": "double_item",
"value": {
"stringValue": "value"
}
}
]
}
time_unix_nano
- String, optional. The time the log was created in Unix nanoseconds. If not set, the SDK should set this to the current time.
The log
envelope item payload is a JSON object that represents a log.
TODO: Document log
Envelope Item Payload once finalized.
The log severity level is a string that represents the severity of the log. The Sentry's default log severity level maps exactly to OpenTelemetry's Severity text field on the OpenTelemetry Logs Spec..
trace
debug
info
warn
error
fatal
The log severity number is an integer that represents the severity of the log. The Sentry's default log severity number maps exactly to OpenTelemetry's Severity number field on the OpenTelemetry Logs Spec.
SeverityNumer 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. |
Default SDK public API should set the lowest severity number for a given severity level. For example, warn
severity level logs collected by the SDK API should use the severity number 13
.
API wise the SDKs are required to expose logging methods which are to be defined in a logger
module or namespace. The SDKs should also include some initialization options to configure the behavior of logs in the SDK.
The SDKs need to expose the following two configuration options:
enableSentryLogs
/enable_sentry_logs
: A boolean flag to control if log envelopes will be generated and sent to Sentry via the Sentry SDK's Logging APIs. If this flag is set tofalse
, the SDK should not send logs to Sentry. Defaults tofalse
.beforeSendLog
/before_send_log
: A function that takes a log object and returns a log object. This function is called before sending the log to Sentry. It can be used to modify the log object or to prevent the log from being sent to Sentry.
Sentry.init({
enableSentryLogs: true,
beforeSendLog(log) {
// Prevent logs from being sent to Sentry if the plan type is enterprise
if (log.attributes["plan.type"] === "enterprise") {
return null;
}
return log;
},
});
At the current moment, logs are not sampled so no sampling configuration needs to be exposed. This may change in the future.
At minimum the SDK needs to implement the following two items to be considered to have a complete logging public API:
Sentry.logger.X
(whereX
is the log level): The log levels aretrace
,debug
,info
,warn
,error
, andfatal
, which is specced out by the protocol below.
Sentry.logger.trace
Sentry.logger.debug
Sentry.logger.info
Sentry.logger.warn
Sentry.logger.error
Sentry.logger.fatal
These methods accepts a string template and the parameters to that string template so the SDK can perform structured logging. Optionally these methods take arbitrary attributes, but not all languages can support both passing a parameterized template and attributes in an easy way.
// Need to use `fmt` helper function in JavaScript for structured logging.
Sentry.logger.info(Sentry.logger.fmt`Adding item ${itemId} for user ${userId}`);
Sentry.logger.warn("User %s performed action %s", [userId, actionName], {
extra: "123",
});
Optionally the SDK can define a log
log severity level, which maps to the info
log severity level in Sentry. If an SDK collects log
severity level, it should map to info
severity level and 9
severity number.
Sentry.logger.log("Added item to shopping cart");
Sentry.logger.emit
: A more verbose version of theSentry.logger.X
methods that accepts a log object/dictionary for maximum customizability. This is only meant to be used by power users, who want full control over what exactly is being set. Note that this API aligns with OTEL's default logging API as well, which gives us flexibility to align on those in the future.
Emit should take the following parameters:
level
: The log severity level of the log. One oftrace
,debug
,info
,warn
,error
,fatal
.message
: The log bodyattributes
: An object/dictionary of key-value pairs of arbitrary data attached to the log. This should not be a nested data structure. If a object or dictionary is passed in, SDKs can choose to either disregard the attribute, or serialize the object into a string.severityNumber
: An optional integer that represents the severity of the log. See Log Severity Number for more information.
Sentry.logger.emit({
level: "info",
message: "Adding item for user",
attributes: {
itemId,
userId,
},
severityNumber: 11,
});
Beyond the two specified sets of methods, SDKs are free to add any extra helpers as they feel is necessary. For example, they could choose to add specialized decorators or helpers for string template creation.
Below are some example SDK implementations to get you started. These are not finalized versions of the API and individual SDK authors should ensure the logging APIs best fit their platforms. When an SDK implements the logging API, this section should be updated with the SDK's specific implementation.
// Browser JavaScript - need to rely on tagged template literals for string templating
Sentry.logger.info(
Sentry.logger.fmt`Adding item ${itemId} for user ${userId}`,
);
// Server-side (Node.js, Bun, Deno) with printf-like syntax
Sentry.logger.info("Adding item %s for user %s", [itemId, userId], {
extra: 123,
});
Sentry.logger.emit({
level: "info",
message: "Adding item for user",
attributes: {
itemId,
userId,
extra: 123,
},
});
# With f-string like syntax
Sentry.logger.info('Adding item {item} for user {user}', item=item_id, user=user_id, attributes={ 'extra': 123 });
Sentry.logger.emit(
level='info',
message='Adding item for user',
attributes={
'item_id': item_id,
'user_id': user_id,
extra: 123
},
)
use function Sentry\logger;
logger()->info('Adding item %s for user %s', [$item_id, $user_id], ['extra' => 123]);
logger()->emit(
level: LogLevel::info(),
message: 'Adding item for user',
attributes: [
'item_id': item_id,
'user_id': user_id,
'extra': 123
],
);
import io.sentry.Sentry;
// example with MessageFormat based string template
Sentry.logger().info("Adding item {0,string} for user {1,string}", item_id, user_id);
Sentry.logger().emit(log -> {
log.setLevel("info");
log.setMessage("Adding item for user");
log.setAttribute("item_id", item_id);
log.setAttribute("user_id", user_id);
log.setAttribute("extra", 123);
});
// Java - Builder Pattern, where setters return builder for method chaining
Sentry.Logger.emit(log -> {
log.setLevel("info")
.setMessage("Adding item for user")
.setAttribute("item_id", item_id)
.setAttribute("user_id", user_id)
.setAttribute("extra", 123)
});
// Kotlin
Sentry.Logger.emit("Adding item for user") { // fun emit(block: LogItem.() -> Unit)
setLevel("info")
setAttribute("item_id", item_id)
setAttribute("user_id", user_id)
setAttribute("extra", 123)
}
// Swift
SentrySDK.logger
.info(message: "Adding item %s for user %s",
params: [itemId, userId],
attributes: ["extra": @"123"]
)
// Objective-C
[SentrySDK.logger
emit :@"Adding item for user"
level: @"info"
attributes: @{ @"extra" : @"123", @"item_id" : item_id, @"user_id" : user_id }
];
Both the Sentry.logger.X
and Sentry.logger.emit
methods are fire-and-forget (have no return value). This means that the SDK can choose to run these methods on a separate thread or queue them up for processing later. This includes evaluating the string template, and running any internal hooks (like beforeSendLog
). The SDK should ensure that the logs are sent in the order they are received.
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 will help debugging logging setups.
Logs should be buffered before being sent. SDKs should keep a buffer of logs on the client (so you can have logs from multiple traces in the buffer) that flushes out based on some kind of condition. A recommended condition is if the buffer length exceeds 25 items, or if 5 seconds have passed. In the future we can use a weight-tracking based approach to determine when to flush logs.
The internal SDK implementation is kept purposefully broad as we are still in early stages. As we figure more out about the logs product and payload characteristics, we'll be able to define a more rigorous spec for logging internals in the SDK. SDKs should take care to not expose logging internals as much as possible to userland so that they can be refactored in the future.
By default the SDK should set the following attributes:
sentry.message.template
: The parameterized template stringsentry.message.parameters.X
: The parameters to the template string, where X is the number that represent the parameters position in the template stringsentry.environment
: The environment set in the SDKsentry.release
: The release set in the SDK
Example:
{
"sentry.message.template": "Adding item %s for user %s",
"sentry.message.parameters.0": "item_id",
"sentry.message.parameters.1": "user_id",
"sentry.environment": "production",
"sentry.release": "1.0.0"
}
Beyond these attributes, we are exploring if the SDK should also send OS, user, and device information automatically (via reading the appropriate contexts from the scope). Given this behaviour can easily be added as a new feature to the SDK, it does not have to be part of the initial SDK implementation until we make a finalized decision.
SDKs should aim to have it so that console/logger integrations create logs as per the appropriate log level if enableSentryLogs
is set to true. Examples of this include JavaScript's console
methods and Pythons logging
standard library.
If SDK authors feel the need, they can also introduce additional options to beyond enableSentryLogs
to gate this functionality. For example an option to control log appenders added via external config like with Log4j
in the Java SDK.
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").