Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
92b2fbe
Define option to enable logs
limbonaut Oct 6, 2025
55953f1
Add intermediary logging API (as log() method for now) on Apple
limbonaut Oct 7, 2025
f484ea5
Enable logs in the project
limbonaut Oct 7, 2025
437ca29
Add structured log call to project (a test)
limbonaut Oct 7, 2025
2df8297
Change arg order in temporary API method
limbonaut Oct 7, 2025
bbf9c52
Add incomplete native implementation
limbonaut Oct 7, 2025
0b3be7a
Add initial Android implementation
limbonaut Oct 7, 2025
c576edb
Add SentrySDK.logger API
limbonaut Oct 7, 2025
e62c253
Add default values
limbonaut Oct 8, 2025
0a5f97d
Update enrich_events.gd
limbonaut Oct 8, 2025
a911727
Add optimized path for log() on Android
limbonaut Oct 8, 2025
678ac95
Add class descriptions for similar-named SentryLogger and
limbonaut Oct 8, 2025
7ea4b60
Introduce SentryLogLevel enum
limbonaut Oct 8, 2025
792402a
Fix attributes on Android
limbonaut Oct 8, 2025
605fa60
Fix logger test
limbonaut Oct 8, 2025
4dc480f
Add logger.trace()
limbonaut Oct 8, 2025
e58ed52
Introduce experimental options
limbonaut Oct 8, 2025
b3eb911
Fix using experimental option
limbonaut Oct 8, 2025
34db858
Processing structure
limbonaut Oct 8, 2025
77fb5e8
Expland SentryLog, add CocoaLog
limbonaut Oct 8, 2025
d109deb
Export SentryLog API
limbonaut Oct 9, 2025
539dbdf
Add Log.add_attributes() and log.remove_attribute() with Cocoa
limbonaut Oct 9, 2025
b778863
Add NativeLog implementation
limbonaut Oct 9, 2025
3d538c4
Move enum to SentryLog
limbonaut Oct 9, 2025
00a202d
Fix cocoa util variant conversion
limbonaut Oct 9, 2025
2a1dbea
Add tests
limbonaut Oct 9, 2025
a49dba5
Register NativeLog class
limbonaut Oct 9, 2025
b6eb22a
Add mostly implemented AndroidLog
limbonaut Oct 10, 2025
5f7957f
add_attributes and remove_attribute on Android
limbonaut Oct 10, 2025
deba6ae
Fix cutting UTF-8 strings
limbonaut Oct 10, 2025
db26f47
Fix get_attribute() on Android
limbonaut Oct 10, 2025
50c6729
Add mobile tests for structured logs
limbonaut Oct 10, 2025
194377b
Forgot mobile test support in main loop
limbonaut Oct 10, 2025
53c5573
Add UTF-8 test
limbonaut Oct 10, 2025
c810112
Undo changes in enrich_events.gd
limbonaut Oct 10, 2025
06e8005
Fixes for NativeLog
limbonaut Oct 10, 2025
61eba8a
Fix memleak with SentrySDK.logger
limbonaut Oct 10, 2025
16f5f74
Improve native log type error message
limbonaut Oct 10, 2025
b887455
Don't print to engine logger within another log operation
limbonaut Oct 10, 2025
0848118
Fixes for NativeLog
limbonaut Oct 10, 2025
92c69d0
Don't discard event, so not to trigger debug printing
limbonaut Oct 10, 2025
4d0452a
logging: Move print.h to logging
limbonaut Oct 11, 2025
cd61aa6
Move print & logger into logging, extract logging state
limbonaut Oct 11, 2025
fd4290d
Log runtime errors
limbonaut Oct 13, 2025
25f7d14
Log more error attributes
limbonaut Oct 13, 2025
d63a1be
Merge branch 'main' into feat/structured-logging
limbonaut Oct 13, 2025
04a3ee7
Merge branch 'main' into feat/structured-logging
limbonaut Oct 21, 2025
fc9790d
Remove string interpolation and attributes from API
limbonaut Oct 21, 2025
7638bb6
Adjust tests
limbonaut Oct 21, 2025
6ca173f
Update docs
limbonaut Oct 21, 2025
3a1222c
Doc corrections
limbonaut Oct 21, 2025
376d119
Use proper sentry.origin
limbonaut Oct 21, 2025
a52b120
Update mobile_tests.gd
limbonaut Oct 22, 2025
b111391
Fix boolean conv on native
limbonaut Oct 22, 2025
d5a696b
Test type with boolean
limbonaut Oct 22, 2025
01c2b73
Revert type check
limbonaut Oct 22, 2025
b4adbc9
Update test_structured_logs.gd
limbonaut Oct 22, 2025
c8d6e6e
Fix XML formatting
limbonaut Oct 22, 2025
441f7e9
Test trace shortcut
limbonaut Oct 22, 2025
36ff595
Update SentryLogger.xml
limbonaut Oct 22, 2025
3c3b31a
Comment in mobile tests
limbonaut Oct 22, 2025
688d03b
Clarify comment
limbonaut Oct 22, 2025
edfa79d
Fix "warning" => "warn" on native
limbonaut Oct 22, 2025
f25971c
Fix String => Variant on Android
limbonaut Oct 22, 2025
ffddc4d
Update CHANGELOG.md
limbonaut Oct 22, 2025
42b007a
Populate sentry.event_id onlly if not empty
limbonaut Oct 22, 2025
84e0fa9
Merge remote-tracking branch 'origin/main' into feat/structured-logging
limbonaut Oct 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import io.sentry.Breadcrumb
import io.sentry.Hint
import io.sentry.ISerializer
import io.sentry.Sentry
import io.sentry.SentryAttributes
import io.sentry.SentryEvent
import io.sentry.SentryLevel
import io.sentry.SentryOptions
import io.sentry.android.core.SentryAndroid
import io.sentry.logger.SentryLogParameters
import io.sentry.protocol.Message
import io.sentry.protocol.SentryException
import io.sentry.protocol.SentryStackFrame
Expand Down Expand Up @@ -132,6 +134,7 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
environment: String,
sampleRate: Float,
maxBreadcrumbs: Int,
enableLogs: Boolean
) {
Log.v(TAG, "Initializing Sentry Android")
SentryAndroid.init(godot.getActivity()!!.applicationContext) { options ->
Expand All @@ -144,6 +147,7 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
options.maxBreadcrumbs = maxBreadcrumbs
options.sdkVersion?.name = "sentry.java.android.godot"
options.nativeSdkName = "sentry.native.android.godot"
options.logs.isEnabled = enableLogs
options.beforeSend =
SentryOptions.BeforeSendCallback { event: SentryEvent, hint: Hint ->
Log.v(TAG, "beforeSend: ${event.eventId} isCrashed: ${event.isCrashed}")
Expand Down Expand Up @@ -237,6 +241,20 @@ class SentryAndroidGodotPlugin(godot: Godot) : GodotPlugin(godot) {
Sentry.addBreadcrumb(crumb)
}

@UsedByGodot
fun log(level: Int, body: String, attributes: Dictionary) {
if (attributes.isEmpty()) {
Sentry.logger().log(level.toSentryLogLevel(), body)
} else {
val sentryAttributes = SentryAttributes.fromMap(attributes)
Sentry.logger().log(
level.toSentryLogLevel(),
SentryLogParameters.create(sentryAttributes),
body
)
}
}

@UsedByGodot
fun captureMessage(message: String, level: Int): String {
val id = Sentry.captureMessage(message, level.toSentryLevel())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.sentry.godotplugin

import io.sentry.SentryLevel
import io.sentry.SentryLogLevel
import java.util.Date
import java.time.Instant

Expand All @@ -14,6 +15,17 @@ fun Int.toSentryLevel(): SentryLevel =
else -> SentryLevel.ERROR
}

fun Int.toSentryLogLevel(): SentryLogLevel =
when (this) {
0 -> SentryLogLevel.TRACE
1 -> SentryLogLevel.DEBUG
2 -> SentryLogLevel.INFO
3 -> SentryLogLevel.WARN
4 -> SentryLogLevel.ERROR
5 -> SentryLogLevel.FATAL
else -> SentryLogLevel.INFO
}

fun SentryLevel.toInt(): Int =
when (this) {
SentryLevel.DEBUG -> 0
Expand Down
1 change: 1 addition & 0 deletions project/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ options/dsn="https://[email protected]
options/attach_scene_tree=true
logger/include_variables=true
experimental/attach_screenshot=true
experimental/enable_logs=true
2 changes: 1 addition & 1 deletion project/test/suites/test_logger_integration.gd
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func test_gdscript_error_event_structure() -> void:

assert_json(json).describe("Logger event has logger attribute") \
.at("/") \
.must_contain("logger", "SentryLogger") \
.must_contain("logger", "SentryGodotLogger") \
.verify()

assert_json(json).describe("Logger error event contains exception data") \
Expand Down
7 changes: 6 additions & 1 deletion project/views/enrich_events.gd
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ extends VBoxContainer


func _ready() -> void:
pass
SentrySDK.logger.debug("Starting %s at %d usec", ["SentrySDK", Time.get_ticks_usec()], {
"hello": "world!",
"meaning.of.life": 42
})

SentrySDK.logger.info("Hello")


func _on_add_breadcrumb_button_pressed() -> void:
Expand Down
4 changes: 3 additions & 1 deletion src/register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "sentry/sentry_attachment.h"
#include "sentry/sentry_breadcrumb.h"
#include "sentry/sentry_event.h"
#include "sentry/sentry_godot_logger.h"
#include "sentry/sentry_logger.h"
#include "sentry/sentry_options.h"
#include "sentry/sentry_sdk.h"
Expand Down Expand Up @@ -48,6 +49,7 @@ void register_runtime_classes() {
GDREGISTER_INTERNAL_CLASS(RuntimeConfig);
GDREGISTER_CLASS(SentryUser);
GDREGISTER_CLASS(SentryTimestamp);
GDREGISTER_CLASS(SentryLogger);
GDREGISTER_CLASS(SentrySDK);
GDREGISTER_ABSTRACT_CLASS(SentryAttachment);
GDREGISTER_ABSTRACT_CLASS(SentryEvent);
Expand All @@ -56,7 +58,7 @@ void register_runtime_classes() {
GDREGISTER_INTERNAL_CLASS(SentryEventProcessor);
GDREGISTER_INTERNAL_CLASS(ScreenshotProcessor);
GDREGISTER_INTERNAL_CLASS(ViewHierarchyProcessor);
GDREGISTER_INTERNAL_CLASS(SentryLogger);
GDREGISTER_INTERNAL_CLASS(SentryGodotLogger);

#ifdef SDK_NATIVE
GDREGISTER_INTERNAL_CLASS(native::NativeEvent);
Expand Down
46 changes: 45 additions & 1 deletion src/sentry/android/android_sdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@

using namespace godot;

namespace {

inline Variant _as_attribute(const Variant &p_value) {
Variant::Type type = p_value.get_type();
return (type < Variant::BOOL || type > Variant::STRING) ? (Variant)p_value.stringify() : p_value;
}

} // unnamed namespace

namespace sentry::android {

void SentryAndroidBeforeSendHandler::_initialize(Object *p_android_plugin) {
Expand Down Expand Up @@ -92,6 +101,40 @@ void AndroidSDK::add_breadcrumb(const Ref<SentryBreadcrumb> &p_breadcrumb) {
android_plugin->call(ANDROID_SN(addBreadcrumb), crumb->get_handle());
}

void AndroidSDK::log(LogLevel p_level, const String &p_body, const Array &p_params, const Dictionary &p_attributes) {
ERR_FAIL_NULL(android_plugin);

if (p_body.is_empty()) {
return;
}

String body = p_body;

bool has_params = !p_params.is_empty();
bool has_attributes = !p_attributes.is_empty();

Dictionary attributes;

if (has_params) {
attributes["sentry.message.template"] = body;
for (int i = 0; i < p_params.size(); i++) {
String key = "sentry.message.parameter." + itos(i);
attributes[key] = _as_attribute(p_params[i]);
}
body = body % p_params;
}

if (has_attributes) {
const Array &keys = p_attributes.keys();
for (int i = 0; i < keys.size(); i++) {
const String &key = keys[i];
attributes[key] = _as_attribute(p_attributes[key]);
}
}

android_plugin->call(ANDROID_SN(log), p_level, body, attributes);
}

String AndroidSDK::capture_message(const String &p_message, Level p_level) {
ERR_FAIL_NULL_V(android_plugin, String());
return android_plugin->call(ANDROID_SN(captureMessage), p_message, p_level);
Expand Down Expand Up @@ -164,7 +207,8 @@ void AndroidSDK::init(const PackedStringArray &p_global_attachments, const Calla
SentryOptions::get_singleton()->get_dist(),
SentryOptions::get_singleton()->get_environment(),
SentryOptions::get_singleton()->get_sample_rate(),
SentryOptions::get_singleton()->get_max_breadcrumbs());
SentryOptions::get_singleton()->get_max_breadcrumbs(),
SentryOptions::get_singleton()->get_enable_logs());

if (is_enabled()) {
set_user(SentryUser::create_default());
Expand Down
2 changes: 2 additions & 0 deletions src/sentry/android/android_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class AndroidSDK : public InternalSDK {
virtual Ref<SentryBreadcrumb> create_breadcrumb() override;
virtual void add_breadcrumb(const Ref<SentryBreadcrumb> &p_breadcrumb) override;

virtual void log(LogLevel p_level, const String &p_body, const Array &p_params = Array(), const Dictionary &p_attributes = Dictionary()) override;

virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override;
virtual String get_last_event_id() override;

Expand Down
1 change: 1 addition & 0 deletions src/sentry/android/android_string_names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ AndroidStringNames::AndroidStringNames() {
setUser = StringName("setUser");
removeUser = StringName("removeUser");
addBreadcrumb = StringName("addBreadcrumb");
log = StringName("log");
captureMessage = StringName("captureMessage");
getLastEventId = StringName("getLastEventId");
captureError = StringName("captureError");
Expand Down
1 change: 1 addition & 0 deletions src/sentry/android/android_string_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class AndroidStringNames {
StringName setUser;
StringName removeUser;
StringName addBreadcrumb;
StringName log;
StringName captureMessage;
StringName getLastEventId;
StringName captureError;
Expand Down
2 changes: 2 additions & 0 deletions src/sentry/cocoa/cocoa_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class CocoaSDK : public InternalSDK {
virtual Ref<SentryBreadcrumb> create_breadcrumb() override;
virtual void add_breadcrumb(const Ref<SentryBreadcrumb> &p_breadcrumb) override;

virtual void log(LogLevel p_level, const String &p_body, const Array &p_params = Array(), const Dictionary &p_attributes = Dictionary()) override;

virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override;
virtual String get_last_event_id() override;

Expand Down
90 changes: 90 additions & 0 deletions src/sentry/cocoa/cocoa_sdk.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@

using namespace godot;

namespace {

NSObject *_as_attribute(const Variant &p_value) {
switch (p_value.get_type()) {
case Variant::BOOL: {
return [NSNumber numberWithBool:(bool)p_value];
} break;
case Variant::INT: {
return [NSNumber numberWithLongLong:(int64_t)p_value];
} break;
case Variant::FLOAT: {
return [NSNumber numberWithDouble:(double)p_value];
} break;
default: {
return [NSString stringWithUTF8String:p_value.stringify().utf8()];
} break;
}
}

} // unnamed namespace

namespace sentry::cocoa {

void CocoaSDK::set_context(const String &p_key, const Dictionary &p_value) {
Expand Down Expand Up @@ -79,6 +100,73 @@
[objc::SentrySDK addBreadcrumb:crumb->get_cocoa_breadcrumb()];
}

void CocoaSDK::log(LogLevel p_level, const String &p_body, const Array &p_params, const Dictionary &p_attributes) {
if (p_body.is_empty()) {
return;
}

String body = p_body;

NSMutableDictionary *attributes = nil;
bool has_params = !p_params.is_empty();
bool has_attributes = !p_attributes.is_empty();

if (has_params || has_attributes) {
attributes = [[NSMutableDictionary alloc] initWithCapacity:p_params.size() + p_attributes.size() + 1];

if (has_params) {
[attributes setObject:string_to_objc(body) forKey:@"sentry.message.template"];
for (int i = 0; i < p_params.size(); i++) {
NSString *objc_key = [NSString stringWithFormat:@"sentry.message.parameter.%d", i];
NSObject *objc_value = _as_attribute(p_params[i]);
[attributes setObject:objc_value forKey:objc_key];
}
body = body % p_params;
}

if (has_attributes) {
const Array &keys = p_attributes.keys();
for (int i = 0; i < keys.size(); i++) {
const String &key = keys[i];
const NSString *objc_key = [NSString stringWithUTF8String:key.utf8()];
const NSObject *objc_value = _as_attribute(p_attributes[key]);
[attributes setObject:objc_value forKey:objc_key];
}
}
}

switch (p_level) {
case LOG_LEVEL_TRACE: {
[[objc::SentrySDK logger] trace:string_to_objc(body)
attributes:attributes];
} break;
case LOG_LEVEL_DEBUG: {
[[objc::SentrySDK logger] debug:string_to_objc(body)
attributes:attributes];
} break;
case LOG_LEVEL_INFO: {
[[objc::SentrySDK logger] info:string_to_objc(body)
attributes:attributes];
} break;
case LOG_LEVEL_WARN: {
[[objc::SentrySDK logger] warn:string_to_objc(body)
attributes:attributes];
} break;
case LOG_LEVEL_ERROR: {
[[objc::SentrySDK logger] error:string_to_objc(body)
attributes:attributes];
} break;
case LOG_LEVEL_FATAL: {
[[objc::SentrySDK logger] fatal:string_to_objc(body)
attributes:attributes];
} break;
default: {
[[objc::SentrySDK logger] debug:string_to_objc(body)
attributes:attributes];
} break;
}
}

String CocoaSDK::capture_message(const String &p_message, Level p_level) {
objc::SentryId *event_id = [objc::SentrySDK captureMessage:string_to_objc(p_message)
withScopeBlock:^(objc::SentryScope *scope) {
Expand Down Expand Up @@ -166,6 +254,8 @@
// NOTE: This only works for captureMessage(), unfortunately.
options.attachStacktrace = false;

options.experimental.enableLogs = SentryOptions::get_singleton()->get_enable_logs();

options.initialScope = ^(objc::SentryScope *scope) {
// Add global attachments
for (const String &path : p_global_attachments) {
Expand Down
2 changes: 2 additions & 0 deletions src/sentry/disabled/disabled_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class DisabledSDK : public InternalSDK {
virtual Ref<SentryBreadcrumb> create_breadcrumb() override { return memnew(DisabledBreadcrumb); }
virtual void add_breadcrumb(const Ref<SentryBreadcrumb> &p_breadcrumb) override {}

virtual void log(LogLevel p_level, const String &p_body, const Array &p_params = Array(), const Dictionary &p_attributes = Dictionary()) override {}

virtual String capture_message(const String &p_message, Level p_level = sentry::LEVEL_INFO) override { return ""; }
virtual String get_last_event_id() override { return ""; }

Expand Down
3 changes: 3 additions & 0 deletions src/sentry/internal_sdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define INTERNAL_SDK_H

#include "sentry/level.h"
#include "sentry/log_level.h"
#include "sentry/sentry_attachment.h"
#include "sentry/sentry_breadcrumb.h"
#include "sentry/sentry_event.h"
Expand Down Expand Up @@ -31,6 +32,8 @@ class InternalSDK {
// TODO: Consider adding the following function.
// virtual void clear_breadcrumbs() = 0;

virtual void log(LogLevel p_level, const String &p_body, const Array &p_params = Array(), const Dictionary &p_attributes = Dictionary()) = 0;

virtual String capture_message(const String &p_message, Level p_level) = 0;
virtual String get_last_event_id() = 0;

Expand Down
14 changes: 14 additions & 0 deletions src/sentry/log_level.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

namespace sentry {

enum LogLevel {
LOG_LEVEL_TRACE,
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARN,
LOG_LEVEL_ERROR,
LOG_LEVEL_FATAL
};

}
Loading
Loading