diff --git a/CHANGELOG.md b/CHANGELOG.md index b700b9cc2..23e29bcde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add app hang tracking for desktop platforms ([#1270](https://github.com/getsentry/sentry-unreal/pull/1270)) +- Add global attributes support for Android ([#1274](https://github.com/getsentry/sentry-unreal/pull/1274)) ### Fixes diff --git a/integration-test/Integration.Android.Tests.ps1 b/integration-test/Integration.Android.Tests.ps1 index e81e21cc6..d9009e58a 100644 --- a/integration-test/Integration.Android.Tests.ps1 +++ b/integration-test/Integration.Android.Tests.ps1 @@ -432,7 +432,7 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa # Fetch logs from Sentry with automatic polling try { - $script:CapturedLogs = Get-SentryTestLog -AttributeName 'test_id' -AttributeValue $script:TestId -Fields @('handler_added', 'to_be_removed') + $script:CapturedLogs = Get-SentryTestLog -AttributeName 'test_id' -AttributeValue $script:TestId -Fields @('handler_added', 'to_be_removed', 'global_attr', 'global_removed') } catch { Write-Host "Warning: $_" -ForegroundColor Red @@ -483,8 +483,15 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $log.'to_be_removed' | Should -BeNullOrEmpty } - # Note: Global log attributes (SetAttribute/RemoveAttribute on subsystem) are not supported - # on Android (sentry-java) - the implementation is a no-op. These are tested in desktop tests only. + It "Should have global attribute set on subsystem" { + $log = $script:CapturedLogs[0] + $log.global_attr | Should -Be 'global_value' + } + + It "Should not have global attribute that was removed from subsystem" { + $log = $script:CapturedLogs[0] + $log.global_removed | Should -BeNullOrEmpty + } } Context "Metrics Capture Tests" { @@ -502,7 +509,7 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan # Fetch all three metric types from Sentry with automatic polling - $metricFields = @('handler_added', 'to_be_removed') + $metricFields = @('handler_added', 'to_be_removed', 'global_attr', 'global_removed') try { $script:CapturedCounterMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields @@ -603,6 +610,16 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $metric = $script:CapturedCounterMetrics[0] $metric.test_id | Should -Be $script:TestId } + + It "Should have global attribute set on subsystem" { + $metric = $script:CapturedCounterMetrics[0] + $metric.global_attr | Should -Be 'global_value' + } + + It "Should not have global attribute that was removed from subsystem" { + $metric = $script:CapturedCounterMetrics[0] + $metric.global_removed | Should -BeNullOrEmpty + } } Context "Tracing Capture Tests" { diff --git a/integration-test/Integration.Desktop.Tests.ps1 b/integration-test/Integration.Desktop.Tests.ps1 index 1f1e83f7d..a627ad9bd 100644 --- a/integration-test/Integration.Desktop.Tests.ps1 +++ b/integration-test/Integration.Desktop.Tests.ps1 @@ -530,7 +530,7 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan # Fetch all three metric types from Sentry with automatic polling - $metricFields = @('handler_added', 'to_be_removed') + $metricFields = @('handler_added', 'to_be_removed', 'global_attr', 'global_removed') try { $script:CapturedCounterMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields @@ -635,6 +635,16 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa $metric = $script:CapturedCounterMetrics[0] $metric.test_id | Should -Be $script:TestId } + + It "Should have global attribute set on subsystem" { + $metric = $script:CapturedCounterMetrics[0] + $metric.global_attr | Should -Be 'global_value' + } + + It "Should not have global attribute that was removed from subsystem" { + $metric = $script:CapturedCounterMetrics[0] + $metric.global_removed | Should -BeNullOrEmpty + } } Context "Tracing Capture Tests" { diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp index 701bab3c6..3b8bab544 100644 --- a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp @@ -382,12 +382,18 @@ void FAndroidSentrySubsystem::RemoveTag(const FString& key) void FAndroidSentrySubsystem::SetAttribute(const FString& key, const FSentryVariant& value) { - // No-op: Android SDK doesn't support global log attributes + TSharedPtr nativeValue = FAndroidSentryConverters::VariantToNative(value); + if (nativeValue.IsValid()) + { + FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "setAttribute", "(Ljava/lang/String;Ljava/lang/Object;)V", + *FSentryJavaObjectWrapper::GetJString(key), nativeValue->GetJObject()); + } } void FAndroidSentrySubsystem::RemoveAttribute(const FString& key) { - // No-op: Android SDK doesn't support global log attributes + FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::SentryBridgeJava, "removeAttribute", "(Ljava/lang/String;)V", + *FSentryJavaObjectWrapper::GetJString(key)); } void FAndroidSentrySubsystem::SetLevel(ESentryLevel level) diff --git a/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java b/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java index 7db54cc2d..1837e7361 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java +++ b/plugin-dev/Source/Sentry/Private/Android/Java/SentryBridgeJava.java @@ -210,6 +210,14 @@ public void run(@NonNull IScope scope) { }); } + public static void setAttribute(final String key, final Object value) { + Sentry.setAttribute(key, value); + } + + public static void removeAttribute(final String key) { + Sentry.removeAttribute(key); + } + public static void setLevel(final SentryLevel level) { Sentry.configureScope(new ScopeCallback() { @Override diff --git a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp index 50ecb11a4..c5635f315 100644 --- a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp +++ b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp @@ -220,6 +220,11 @@ void USentryPlaygroundGameInstance::RunMetricTest() FString TestId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); + SentrySubsystem->SetAttribute(TEXT("global_attr"), FSentryVariant(TEXT("global_value"))); + + SentrySubsystem->SetAttribute(TEXT("global_removed"), FSentryVariant(TEXT("should_not_appear"))); + SentrySubsystem->RemoveAttribute(TEXT("global_removed")); + TMap CounterAttributes; CounterAttributes.Add(TEXT("test_id"), FSentryVariant(TestId)); CounterAttributes.Add(TEXT("to_be_removed"), FSentryVariant(TEXT("original_value")));