Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add performance monitoring API #470

Merged
merged 33 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
41588a6
Add tracing settings
tustanivsky Dec 7, 2023
390dd0e
Update tracing init for Apple
tustanivsky Dec 7, 2023
ad583af
Merge branch 'main' into feat/performance-monitoring
tustanivsky Dec 8, 2023
ec0c501
Add tracing init for Android
tustanivsky Dec 8, 2023
79bf971
Replace switch in Apple tracing init
tustanivsky Dec 8, 2023
5424cbc
Add tracing init for desktop
tustanivsky Dec 8, 2023
d6e8f38
Add placeholder classes for performance monitoring API
tustanivsky Dec 8, 2023
17384d9
Update plugin snapshot
tustanivsky Dec 8, 2023
cf91b7f
Add get/set methods for Span and Trancaction native impl
tustanivsky Dec 11, 2023
0241501
Merge branch 'main' into feat/performance-monitoring
tustanivsky Dec 11, 2023
b1cd1ab
Add minimap API for starting/finishing transactions
tustanivsky Dec 11, 2023
36c1525
Add basic spans implementation
tustanivsky Dec 12, 2023
be52bb7
Add missing implementation parts
tustanivsky Dec 13, 2023
0b4c328
Add performance test workflow to demo
tustanivsky Dec 14, 2023
95498d3
Fix Android crash
tustanivsky Dec 14, 2023
6c7664f
Add transaction and span convertors for desktop
tustanivsky Dec 15, 2023
a6172b4
Merge branch 'main' into feat/performance-monitoring
tustanivsky Dec 19, 2023
1f06047
Add more performance monitoring API methods
tustanivsky Dec 19, 2023
a6b8710
Fix comment
tustanivsky Dec 20, 2023
54c4922
Fix method signature
tustanivsky Dec 20, 2023
b040fca
Add missing Android implmentations
tustanivsky Dec 20, 2023
fb8b5ce
Update sample
tustanivsky Dec 20, 2023
4151f3c
Remove transaction SetName implementation
tustanivsky Dec 20, 2023
6f2d793
Update settings order
tustanivsky Dec 20, 2023
69bd7cb
Update changelog
tustanivsky Dec 20, 2023
2d9df06
Fix members init order
tustanivsky Dec 20, 2023
e9b8fd8
Add const specifier to IsFinished method
tustanivsky Dec 21, 2023
c523eef
Add automation test for transaction and span
tustanivsky Dec 21, 2023
5a99934
Update plugin-dev/Source/Sentry/Private/SentrySettings.cpp
tustanivsky Jan 12, 2024
c4eeadb
Add missing implementation for Android span
tustanivsky Jan 16, 2024
337b2a1
Add log messages about sampling functions not being implemented
tustanivsky Jan 16, 2024
60af3e3
Add log message about missing transaction SetName implementation for …
tustanivsky Jan 16, 2024
418d981
Merge branch 'main' into feat/performance-monitoring
tustanivsky Jan 16, 2024
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Add performance monitoring API ([#470](https://github.com/getsentry/sentry-unreal/pull/470))

### Fixes

- Fix Linux intermediates paths in `FilterPlugin.ini` ([#468](https://github.com/getsentry/sentry-unreal/pull/468))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@

#include "SentryScope.h"
#include "SentryId.h"
#include "SentryTransaction.h"
#include "SentrySpan.h"
#include "SentryDefines.h"

#include "Android/SentryScopeAndroid.h"
#include "Android/SentryIdAndroid.h"
#include "Android/SentryTransactionAndroid.h"
#include "Android/SentrySpanAndroid.h"

#include "Android/AndroidApplication.h"
#include "Android/AndroidJavaEnv.h"
Expand Down Expand Up @@ -149,6 +153,22 @@ USentryId* SentryConvertorsAndroid::SentryIdToUnreal(jobject id)
return unrealId;
}

USentryTransaction* SentryConvertorsAndroid::SentryTransactionToUnreal(jobject transaction)
{
TSharedPtr<SentryTransactionAndroid> transactionNativeImpl = MakeShareable(new SentryTransactionAndroid(transaction));
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
return unrealTransaction;
}

USentrySpan* SentryConvertorsAndroid::SentrySpanToUnreal(jobject span)
{
TSharedPtr<SentrySpanAndroid> spanNativeImpl = MakeShareable(new SentrySpanAndroid(span));
USentrySpan* unrealSpan = NewObject<USentrySpan>();
unrealSpan->InitWithNativeImpl(spanNativeImpl);
return unrealSpan;
}

TMap<FString, FString> SentryConvertorsAndroid::StringMapToUnreal(jobject map)
{
TMap<FString, FString> result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class USentryScope;
class USentryId;
class USentryTransaction;
class USentrySpan;
class FSentryJavaObjectWrapper;
class FJsonValue;

Expand All @@ -24,6 +26,8 @@ class SentryConvertorsAndroid
static ESentryLevel SentryLevelToUnreal(jobject level);
static USentryScope* SentryScopeToUnreal(jobject scope);
static USentryId* SentryIdToUnreal(jobject id);
static USentryTransaction* SentryTransactionToUnreal(jobject transaction);
static USentrySpan* SentrySpanToUnreal(jobject span);
static TMap<FString, FString> StringMapToUnreal(jobject stringMap);
static TArray<FString> StringListToUnreal(jobject stringList);
static TArray<uint8> ByteArrayToUnreal(jbyteArray byteArray);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const FSentryJavaClass SentryJavaClasses::UserFeedback = FSentryJavaClass { "io
const FSentryJavaClass SentryJavaClasses::Message = FSentryJavaClass { "io/sentry/protocol/Message", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::SentryLevel = FSentryJavaClass { "io/sentry/SentryLevel", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::SentryHint = FSentryJavaClass { "io/sentry/Hint", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::Transaction = FSentryJavaClass { "io/sentry/ITransaction", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::Span = FSentryJavaClass { "io/sentry/ISpan", ESentryJavaClassType::External };

// System Java classes definitions
const FSentryJavaClass SentryJavaClasses::ArrayList = FSentryJavaClass { "java/util/ArrayList", ESentryJavaClassType::System };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct SentryJavaClasses
const static FSentryJavaClass Message;
const static FSentryJavaClass SentryLevel;
const static FSentryJavaClass SentryHint;
const static FSentryJavaClass Transaction;
const static FSentryJavaClass Span;

// System Java classes
const static FSentryJavaClass ArrayList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.sentry.Breadcrumb;
import io.sentry.Hint;
import io.sentry.IHub;
import io.sentry.SamplingContext;
import io.sentry.IScope;
import io.sentry.ScopeCallback;
import io.sentry.Sentry;
Expand All @@ -30,6 +31,7 @@
public class SentryBridgeJava {
public static native void onConfigureScope(long callbackAddr, IScope scope);
public static native SentryEvent onBeforeSend(long handlerAddr, SentryEvent event, Hint hint);
public static native Double onTracesSampler(long samplerAddr, SamplingContext samplingContext);

public static void init(Activity activity, final String settingsJsonStr, final long beforeSendHandler) {
SentryAndroid.init(activity, new Sentry.OptionsConfiguration<SentryAndroidOptions>() {
Expand Down Expand Up @@ -62,6 +64,19 @@ public SentryEvent execute(SentryEvent event, Hint hint) {
for (int i = 0; i < Excludes.length(); i++) {
options.addInAppExclude(Excludes.getString(i));
}
options.setEnableTracing(settingJson.getBoolean("enableTracing"));
if(settingJson.has("tracesSampleRate")) {
options.setTracesSampleRate(settingJson.getDouble("tracesSampleRate"));
}
if(settingJson.has("tracesSampler")) {
final long samplerAddr = settingJson.getLong("tracesSampler");
options.setTracesSampler(new SentryOptions.TracesSamplerCallback() {
@Override
public Double sample(SamplingContext samplingContext) {
return onTracesSampler(samplerAddr, samplingContext);
}
});
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onBeforeSend(JNIEnv* e
HintToProcess->InitWithNativeImpl(MakeShareable(new SentryHintAndroid(hint)));

return handler->HandleBeforeSend(EventToProcess, HintToProcess) ? event : nullptr;
}

JNI_METHOD jobject Java_io_sentry_unreal_SentryBridgeJava_onTracesSampler(JNIEnv* env, jclass clazz, jlong objAddr, jobject samplingContext)
{
return nullptr;
}
50 changes: 50 additions & 0 deletions plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2023 Sentry. All Rights Reserved.

#include "SentrySpanAndroid.h"

#include "Infrastructure/SentryConvertorsAndroid.h"
#include "Infrastructure/SentryJavaClasses.h"

SentrySpanAndroid::SentrySpanAndroid(jobject span)
: FSentryJavaObjectWrapper(SentryJavaClasses::Span, span)
{
SetupClassMethods();
}

void SentrySpanAndroid::SetupClassMethods()
{
FinishMethod = GetMethod("finish", "()V");
IsFinishedMethod = GetMethod("isFinished", "()Z");
SetTagMethod = GetMethod("setTag", "(Ljava/lang/String;Ljava/lang/String;)V");
SetDataMethod = GetMethod("setData", "(Ljava/lang/String;Ljava/lang/Object;)V");
}

void SentrySpanAndroid::Finish()
{
CallMethod<void>(FinishMethod);
}

bool SentrySpanAndroid::IsFinished() const
{
return false;
tustanivsky marked this conversation as resolved.
Show resolved Hide resolved
}

void SentrySpanAndroid::SetTag(const FString& key, const FString& value)
{
CallMethod<void>(SetTagMethod, *GetJString(key), *GetJString(value));
}

void SentrySpanAndroid::RemoveTag(const FString& key)
{
SetTag(key, TEXT(""));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL.
Do you happen to know where this gets filtered? Is that tag still part of the event or does it get removed by the SDK?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on a previous experience with setting extra context for captured events I assume that the "removed" tag is still sent to Sentry however it's not displayed due to its empty value. I wasn't able to find any code in sentry-java that removes it explicitly on the client side but it's hardly confirms my theory

}

void SentrySpanAndroid::SetData(const FString& key, const TMap<FString, FString>& values)
{
CallMethod<void>(SetDataMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject());
}

void SentrySpanAndroid::RemoveData(const FString& key)
{
SetData(key, TMap<FString, FString>());
}
29 changes: 29 additions & 0 deletions plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2023 Sentry. All Rights Reserved.

#pragma once

#include "Interface/SentrySpanInterface.h"

#include "Infrastructure/SentryJavaObjectWrapper.h"

class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper
{
public:
SentrySpanAndroid(jobject span);

void SetupClassMethods();

virtual void Finish() override;
virtual bool IsFinished() const override;
virtual void SetTag(const FString& key, const FString& value) override;
virtual void RemoveTag(const FString& key) override;
virtual void SetData(const FString& key, const TMap<FString, FString>& values) override;
virtual void RemoveData(const FString& key) override;


private:
FSentryJavaMethod FinishMethod;
FSentryJavaMethod IsFinishedMethod;
FSentryJavaMethod SetTagMethod;
FSentryJavaMethod SetDataMethod;
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ void SentrySubsystemAndroid::InitWithSettings(const USentrySettings* settings, U
SettingsJson->SetArrayField(TEXT("inAppInclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppInclude));
SettingsJson->SetArrayField(TEXT("inAppExclude"), SentryConvertorsAndroid::StrinArrayToJsonArray(settings->InAppExclude));
SettingsJson->SetBoolField(TEXT("sendDefaultPii"), settings->SendDefaultPii);
SettingsJson->SetBoolField(TEXT("enableTracing"), settings->EnableTracing);
if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate)
{
SettingsJson->SetNumberField(TEXT("tracesSampleRate"), settings->TracesSampleRate);
}
if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::TracesSampler)
{
SettingsJson->SetNumberField(TEXT("tracesSampler"), (jlong)0);
}

FString SettingsJsonStr;
TSharedRef<TJsonWriter<>> JsonWriter = TJsonWriterFactory<>::Create(&SettingsJsonStr);
Expand Down Expand Up @@ -184,3 +193,11 @@ void SentrySubsystemAndroid::EndSession()
{
FSentryJavaObjectWrapper::CallStaticMethod<void>(SentryJavaClasses::SentryBridgeJava, "endSession", "()V", nullptr);
}

USentryTransaction* SentrySubsystemAndroid::StartTransaction(const FString& name, const FString& operation)
{
auto transaction = FSentryJavaObjectWrapper::CallStaticObjectMethod<jobject>(SentryJavaClasses::Sentry, "startTransaction", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction;",
*FSentryJavaObjectWrapper::GetJString(name), *FSentryJavaObjectWrapper::GetJString(operation));

return SentryConvertorsAndroid::SentryTransactionToUnreal(*transaction);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ class SentrySubsystemAndroid : public ISentrySubsystem
virtual void SetLevel(ESentryLevel level) override;
virtual void StartSession() override;
virtual void EndSession() override;
virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2023 Sentry. All Rights Reserved.

#include "SentryTransactionAndroid.h"

#include "Infrastructure/SentryConvertorsAndroid.h"
#include "Infrastructure/SentryJavaClasses.h"

SentryTransactionAndroid::SentryTransactionAndroid(jobject transaction)
: FSentryJavaObjectWrapper(SentryJavaClasses::Transaction, transaction)
{
SetupClassMethods();
}

void SentryTransactionAndroid::SetupClassMethods()
{
StartChildMethod = GetMethod("startChild", "(Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan;");
FinishMethod = GetMethod("finish", "()V");
IsFinishedMethod = GetMethod("isFinished", "()Z");
SetNameMethod = GetMethod("setName", "(Ljava/lang/String;)V");
SetTagMethod = GetMethod("setTag", "(Ljava/lang/String;Ljava/lang/String;)V");
SetDataMethod = GetMethod("setData", "(Ljava/lang/String;Ljava/lang/Object;)V");
}

USentrySpan* SentryTransactionAndroid::StartChild(const FString& operation, const FString& desctiption)
{
auto span = CallObjectMethod<jobject>(StartChildMethod, *GetJString(operation), *GetJString(desctiption));
return SentryConvertorsAndroid::SentrySpanToUnreal(*span);
}

void SentryTransactionAndroid::Finish()
{
CallMethod<void>(FinishMethod);
}

bool SentryTransactionAndroid::IsFinished() const
{
return CallMethod<bool>(IsFinishedMethod);;
}

void SentryTransactionAndroid::SetName(const FString& name)
{
CallMethod<void>(SetNameMethod, *GetJString(name));
}

void SentryTransactionAndroid::SetTag(const FString& key, const FString& value)
{
CallMethod<void>(SetTagMethod, *GetJString(key), *GetJString(value));
}

void SentryTransactionAndroid::RemoveTag(const FString& key)
{
SetTag(key, TEXT(""));
}

void SentryTransactionAndroid::SetData(const FString& key, const TMap<FString, FString>& values)
{
CallMethod<void>(SetDataMethod, *GetJString(key), SentryConvertorsAndroid::StringMapToNative(values)->GetJObject());
}

void SentryTransactionAndroid::RemoveData(const FString& key)
{
SetData(key, TMap<FString, FString>());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2023 Sentry. All Rights Reserved.

#pragma once

#include "Interface/SentryTransactionInterface.h"

#include "Infrastructure/SentryJavaObjectWrapper.h"

class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaObjectWrapper
{
public:
SentryTransactionAndroid(jobject transaction);

void SetupClassMethods();

virtual USentrySpan* StartChild(const FString& operation, const FString& desctiption) override;
virtual void Finish() override;
virtual bool IsFinished() const override;
virtual void SetName(const FString& name) override;
virtual void SetTag(const FString& key, const FString& value) override;
virtual void RemoveTag(const FString& key) override;
virtual void SetData(const FString& key, const TMap<FString, FString>& values) override;
virtual void RemoveData(const FString& key) override;

private:
FSentryJavaMethod StartChildMethod;
FSentryJavaMethod FinishMethod;
FSentryJavaMethod IsFinishedMethod;
FSentryJavaMethod SetNameMethod;
FSentryJavaMethod SetTagMethod;
FSentryJavaMethod SetDataMethod;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
#include "SentryConvertorsApple.h"
#include "SentryScope.h"
#include "SentryId.h"
#include "SentryTransaction.h"
#include "SentrySpan.h"
#include "SentryDefines.h"

#include "Apple/SentryScopeApple.h"
#include "Apple/SentryIdApple.h"
#include "Apple/SentryTransactionApple.h"
#include "Apple/SentrySpanApple.h"

SentryLevel SentryConvertorsApple::SentryLevelToNative(ESentryLevel level)
{
Expand Down Expand Up @@ -147,6 +151,22 @@ USentryId* SentryConvertorsApple::SentryIdToUnreal(SentryId* id)
return unrealId;
}

USentryTransaction* SentryConvertorsApple::SentryTransactionToUnreal(id<SentrySpan> transaction)
{
TSharedPtr<SentryTransactionApple> transactionNativeImpl = MakeShareable(new SentryTransactionApple(transaction));
USentryTransaction* unrealTransaction = NewObject<USentryTransaction>();
unrealTransaction->InitWithNativeImpl(transactionNativeImpl);
return unrealTransaction;
}

USentrySpan* SentryConvertorsApple::SentrySpanToUnreal(id<SentrySpan> span)
{
TSharedPtr<SentrySpanApple> spanNativeImpl = MakeShareable(new SentrySpanApple(span));
USentrySpan* unrealSpan = NewObject<USentrySpan>();
unrealSpan->InitWithNativeImpl(spanNativeImpl);
return unrealSpan;
}

SentryLevel SentryConvertorsApple::StringToSentryLevel(NSString* string)
{
SentryLevel nativeLevel = kSentryLevelDebug;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

class USentryScope;
class USentryId;
class USentryTransaction;

class SentryConvertorsApple
{
Expand All @@ -25,6 +26,8 @@ class SentryConvertorsApple
static TArray<uint8> ByteDataToUnreal(NSData* data);
static USentryScope* SentryScopeToUnreal(SentryScope* scope);
static USentryId* SentryIdToUnreal(SentryId* id);
static USentryTransaction* SentryTransactionToUnreal(id<SentrySpan> transaction);
static USentrySpan* SentrySpanToUnreal(id<SentrySpan> span);

/** Other conversions */
static SentryLevel StringToSentryLevel(NSString* string);
Expand Down
Loading