Skip to content

Add trace propagation #631

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

Merged
merged 11 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Add setting that allows switching between the project and user directories for the internal Sentry database location on Windows/Linux ([#616](https://github.com/getsentry/sentry-unreal/pull/616))
- Add non-ASCII characters support for user messages ([#624](https://github.com/getsentry/sentry-unreal/pull/624))
- Add API to allow users to trace their distributed system and connect in-game with backend errors ([#631](https://github.com/getsentry/sentry-unreal/pull/631))
- Allow overriding `UploadSymbolsAutomatically` via environment variable `SENTRY_UPLOAD_SYMBOLS_AUTOMATICALLY` ([#636](https://github.com/getsentry/sentry-unreal/pull/636))

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const FSentryJavaClass SentryJavaClasses::SamplingContext = FSentryJavaClass {
const FSentryJavaClass SentryJavaClasses::CustomSamplingContext = FSentryJavaClass { "io/sentry/CustomSamplingContext", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::TransactionContext = FSentryJavaClass { "io/sentry/TransactionContext", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::TransactionOptions = FSentryJavaClass { "io/sentry/TransactionOptions", ESentryJavaClassType::External };
const FSentryJavaClass SentryJavaClasses::SentryTraceHeader = FSentryJavaClass { "io/sentry/SentryTraceHeader", 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 @@ -25,6 +25,7 @@ struct SentryJavaClasses
const static FSentryJavaClass CustomSamplingContext;
const static FSentryJavaClass TransactionContext;
const static FSentryJavaClass TransactionOptions;
const static FSentryJavaClass SentryTraceHeader;

// System Java classes
const static FSentryJavaClass ArrayList;
Expand Down
10 changes: 10 additions & 0 deletions plugin-dev/Source/Sentry/Private/Android/SentrySpanAndroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ void SentrySpanAndroid::SetupClassMethods()
IsFinishedMethod = GetMethod("isFinished", "()Z");
SetTagMethod = GetMethod("setTag", "(Ljava/lang/String;Ljava/lang/String;)V");
SetDataMethod = GetMethod("setData", "(Ljava/lang/String;Ljava/lang/Object;)V");
ToSentryTraceMethod = GetMethod("toSentryTrace", "()Lio/sentry/SentryTraceHeader;");
}

void SentrySpanAndroid::Finish()
Expand Down Expand Up @@ -48,3 +49,12 @@ void SentrySpanAndroid::RemoveData(const FString& key)
{
SetData(key, TMap<FString, FString>());
}

void SentrySpanAndroid::GetTrace(FString& name, FString& value)
{
FSentryJavaObjectWrapper NativeTraceHeader(SentryJavaClasses::SentryTraceHeader, *CallObjectMethod<jobject>(ToSentryTraceMethod));
FSentryJavaMethod GetValueMethod = NativeTraceHeader.GetMethod("getValue", "()Ljava/lang/String;");

name = TEXT("sentry-trace");
value = NativeTraceHeader.CallMethod<FString>(GetValueMethod);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ class SentrySpanAndroid : public ISentrySpan, public FSentryJavaObjectWrapper
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;

virtual void GetTrace(FString& name, FString& value) override;

private:
FSentryJavaMethod FinishMethod;
FSentryJavaMethod IsFinishedMethod;
FSentryJavaMethod SetTagMethod;
FSentryJavaMethod SetDataMethod;
FSentryJavaMethod ToSentryTraceMethod;
};
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,11 @@ USentryTransaction* SentrySubsystemAndroid::StartTransactionWithContextAndOption

return SentryConvertorsAndroid::SentryTransactionToUnreal(*transaction);
}

USentryTransactionContext* SentrySubsystemAndroid::ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders)
{
auto transactionContext = FSentryJavaObjectWrapper::CallStaticObjectMethod<jobject>(SentryJavaClasses::Sentry, "continueTrace", "(Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext;",
*FSentryJavaObjectWrapper::GetJString(sentryTrace), SentryConvertorsAndroid::StringArrayToNative(baggageHeaders)->GetJObject());

return SentryConvertorsAndroid::SentryTransactionContextToUnreal(*transactionContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ class SentrySubsystemAndroid : public ISentrySubsystem
virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override;
virtual USentryTransaction* StartTransactionWithContext(USentryTransactionContext* context) override;
virtual USentryTransaction* StartTransactionWithContextAndOptions(USentryTransactionContext* context, const TMap<FString, FString>& options) override;
virtual USentryTransactionContext* ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders) override;
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ void SentryTransactionAndroid::SetupClassMethods()
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");
ToSentryTraceMethod = GetMethod("toSentryTrace", "()Lio/sentry/SentryTraceHeader;");
}

USentrySpan* SentryTransactionAndroid::StartChild(const FString& operation, const FString& desctiption)
Expand Down Expand Up @@ -61,3 +62,12 @@ void SentryTransactionAndroid::RemoveData(const FString& key)
{
SetData(key, TMap<FString, FString>());
}

void SentryTransactionAndroid::GetTrace(FString& name, FString& value)
{
FSentryJavaObjectWrapper NativeTraceHeader(SentryJavaClasses::SentryTraceHeader, *CallObjectMethod<jobject>(ToSentryTraceMethod));
FSentryJavaMethod GetValueMethod = NativeTraceHeader.GetMethod("getValue", "()Ljava/lang/String;");

name = TEXT("sentry-trace");
value = NativeTraceHeader.CallMethod<FString>(GetValueMethod);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb
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;
virtual void GetTrace(FString& name, FString& value) override;

private:
FSentryJavaMethod StartChildMethod;
Expand All @@ -29,4 +30,5 @@ class SentryTransactionAndroid : public ISentryTransaction, public FSentryJavaOb
FSentryJavaMethod SetNameMethod;
FSentryJavaMethod SetTagMethod;
FSentryJavaMethod SetDataMethod;
FSentryJavaMethod ToSentryTraceMethod;
};
5 changes: 5 additions & 0 deletions plugin-dev/Source/Sentry/Private/Apple/SentryIdApple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

SentryIdApple::SentryIdApple()
{
// `SentryId` definition was moved to Swift so its name that can be recognized by UE should be taken from "Sentry-Swift.h" to successfully load class on Mac
#if PLATFORM_MAC
IdApple = [[SENTRY_APPLE_CLASS(_TtC6Sentry8SentryId) alloc] init];
#elif PLATFORM_IOS
IdApple = [[SENTRY_APPLE_CLASS(SentryId) alloc] init];
#endif
}

SentryIdApple::SentryIdApple(SentryId* id)
Expand Down
10 changes: 10 additions & 0 deletions plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "SentrySpanApple.h"

#include "SentryDefines.h"

#include "Infrastructure/SentryConvertorsApple.h"

#include "Convenience/SentryInclude.h"
Expand Down Expand Up @@ -51,3 +53,11 @@ void SentrySpanApple::RemoveData(const FString& key)
{
[SpanApple removeDataForKey:key.GetNSString()];
}

void SentrySpanApple::GetTrace(FString& name, FString& value)
{
SentryTraceHeader* traceHeader = [SpanApple toTraceHeader];

name = TEXT("sentry-trace");
value = FString([traceHeader value]);
}
2 changes: 1 addition & 1 deletion plugin-dev/Source/Sentry/Private/Apple/SentrySpanApple.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SentrySpanApple : public ISentrySpan
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;

virtual void GetTrace(FString& name, FString& value) override;

private:
id<SentrySpan> SpanApple;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ void SentrySubsystemApple::InitWithSettings(const USentrySettings* settings, USe
[options addInAppExclude:it->GetNSString()];
}
options.enableAppHangTracking = settings->EnableAppNotRespondingTracking;
options.enableTracing = settings->EnableTracing;
if(settings->EnableTracing && settings->SamplingType == ESentryTracesSamplingType::UniformSampleRate)
{
options.tracesSampleRate = [NSNumber numberWithFloat:settings->TracesSampleRate];
Expand Down Expand Up @@ -303,3 +302,38 @@ USentryTransaction* SentrySubsystemApple::StartTransactionWithContextAndOptions(

return SentryConvertorsApple::SentryTransactionToUnreal(transaction);
}

USentryTransactionContext* SentrySubsystemApple::ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add some tests for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I've added a basic one. The existing sentry-native API is quite limited though so it's somewhat problematic to set up thorough testing for the tracing feature.

{
TArray<FString> traceParts;
sentryTrace.ParseIntoArray(traceParts, TEXT("-"));

if (traceParts.Num() < 2)
{
return nullptr;
}

SentrySampleDecision sampleDecision = kSentrySampleDecisionUndecided;
if (traceParts.Num() == 3)
{
sampleDecision = traceParts[2].Equals(TEXT("1")) ? kSentrySampleDecisionYes : kSentrySampleDecisionNo;
}

// `SentryId` definition was moved to Swift so its name that can be recognized by UE should be taken from "Sentry-Swift.h" to successfully load class on Mac

#if PLATFORM_MAC
SentryId* traceId = [[SENTRY_APPLE_CLASS(_TtC6Sentry8SentryId) alloc] initWithUUIDString:traceParts[0].GetNSString()];
#elif PLATFORM_IOS
SentryId* traceId = [[SENTRY_APPLE_CLASS(SentryId) alloc] initWithUUIDString:traceParts[0].GetNSString()];
#endif

SentryTransactionContext* transactionContext = [[SENTRY_APPLE_CLASS(SentryTransactionContext) alloc] initWithName:@"<unlabeled transaction>" operation:@"default"
traceId:traceId
spanId:[[SENTRY_APPLE_CLASS(SentrySpanId) alloc] init]
parentSpanId:[[SENTRY_APPLE_CLASS(SentrySpanId) alloc] initWithValue:traceParts[1].GetNSString()]
parentSampled:sampleDecision];

// currently `sentry-cocoa` doesn't have API for `SentryTransactionContext` to set `baggageHeaders`

return SentryConvertorsApple::SentryTransactionContextToUnreal(transactionContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ class SentrySubsystemApple : public ISentrySubsystem
virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override;
virtual USentryTransaction* StartTransactionWithContext(USentryTransactionContext* context) override;
virtual USentryTransaction* StartTransactionWithContextAndOptions(USentryTransactionContext* context, const TMap<FString, FString>& options) override;
virtual USentryTransactionContext* ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders) override;
};
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ void SentryTransactionApple::RemoveData(const FString& key)
{
[TransactionApple removeDataForKey:key.GetNSString()];
}

void SentryTransactionApple::GetTrace(FString& name, FString& value)
{
SentryTraceHeader* traceHeader = [TransactionApple toTraceHeader];

name = TEXT("sentry-trace");
value = FString([traceHeader value]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SentryTransactionApple : public ISentryTransaction
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;
virtual void GetTrace(FString& name, FString& value) override;

private:
id<SentrySpan> TransactionApple;
Expand Down
18 changes: 18 additions & 0 deletions plugin-dev/Source/Sentry/Private/Desktop/SentrySpanDesktop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

#if USE_SENTRY_NATIVE

void CopySpanTracingHeader(const char *key, const char *value, void *userdata)
{
sentry_value_t *header = static_cast<sentry_value_t*>(userdata);
sentry_value_set_by_key(*header, key, sentry_value_new_string(value));
}

SentrySpanDesktop::SentrySpanDesktop(sentry_span_t* span)
: SpanDesktop(span)
, isFinished(false)
Expand Down Expand Up @@ -62,4 +68,16 @@ void SentrySpanDesktop::RemoveData(const FString& key)
sentry_span_remove_data(SpanDesktop, TCHAR_TO_ANSI(*key));
}

void SentrySpanDesktop::GetTrace(FString& name, FString& value)
{
sentry_value_t tracingHeader = sentry_value_new_object();

sentry_span_iter_headers(SpanDesktop, CopySpanTracingHeader, &tracingHeader);

name = TEXT("sentry-trace");
value = FString(sentry_value_as_string(sentry_value_get_by_key(tracingHeader, "sentry-trace")));

sentry_value_decref(tracingHeader);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class SentrySpanDesktop : public ISentrySpan
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;
virtual void GetTrace(FString& name, FString& value) override;

private:
sentry_span_t* SpanDesktop;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,21 @@ USentryTransaction* SentrySubsystemDesktop::StartTransactionWithContextAndOption
return StartTransactionWithContext(context);
}

USentryTransactionContext* SentrySubsystemDesktop::ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders)
{
sentry_transaction_context_t* nativeTransactionContext = sentry_transaction_context_new("<unlabeled transaction>", "default");
sentry_transaction_context_update_from_header(nativeTransactionContext, "sentry-trace", TCHAR_TO_ANSI(*sentryTrace));

// currently `sentry-native` doesn't have API for `sentry_transaction_context_t` to set `baggageHeaders`

TSharedPtr<SentryTransactionContextDesktop> transactionContextDesktop = MakeShareable(new SentryTransactionContextDesktop(nativeTransactionContext));

USentryTransactionContext* TransactionContext = NewObject<USentryTransactionContext>();
TransactionContext->InitWithNativeImpl(transactionContextDesktop);

return TransactionContext;
}

USentryBeforeSendHandler* SentrySubsystemDesktop::GetBeforeSendHandler()
{
return beforeSend;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class SentrySubsystemDesktop : public ISentrySubsystem
virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) override;
virtual USentryTransaction* StartTransactionWithContext(USentryTransactionContext* context) override;
virtual USentryTransaction* StartTransactionWithContextAndOptions(USentryTransactionContext* context, const TMap<FString, FString>& options) override;
virtual USentryTransactionContext* ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders) override;

USentryBeforeSendHandler* GetBeforeSendHandler();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

#if USE_SENTRY_NATIVE

void CopyTransactionTracingHeader(const char *key, const char *value, void *userdata)
{
sentry_value_t *header = static_cast<sentry_value_t*>(userdata);
sentry_value_set_by_key(*header, key, sentry_value_new_string(value));
}

SentryTransactionDesktop::SentryTransactionDesktop(sentry_transaction_t* transaction)
: TransactionDesktop(transaction)
, isFinished(false)
Expand Down Expand Up @@ -77,4 +83,16 @@ void SentryTransactionDesktop::RemoveData(const FString& key)
sentry_transaction_remove_data(TransactionDesktop, TCHAR_TO_ANSI(*key));
}

void SentryTransactionDesktop::GetTrace(FString& name, FString& value)
{
sentry_value_t tracingHeader = sentry_value_new_object();

sentry_transaction_iter_headers(TransactionDesktop, CopyTransactionTracingHeader, &tracingHeader);

name = TEXT("sentry-trace");
value = FString(sentry_value_as_string(sentry_value_get_by_key(tracingHeader, "sentry-trace")));

sentry_value_decref(tracingHeader);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SentryTransactionDesktop : public ISentryTransaction
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;
virtual void GetTrace(FString& name, FString& value) override;

private:
sentry_transaction_t* TransactionDesktop;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ class ISentrySpan
virtual void RemoveTag(const FString& key) = 0;
virtual void SetData(const FString& key, const TMap<FString, FString>& values) = 0;
virtual void RemoveData(const FString& key) = 0;
virtual void GetTrace(FString& name, FString& value) = 0;
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ class ISentrySubsystem
virtual USentryTransaction* StartTransaction(const FString& name, const FString& operation) = 0;
virtual USentryTransaction* StartTransactionWithContext(USentryTransactionContext* context) = 0;
virtual USentryTransaction* StartTransactionWithContextAndOptions(USentryTransactionContext* context, const TMap<FString, FString>& options) = 0;
virtual USentryTransactionContext* ContinueTrace(const FString& sentryTrace, const TArray<FString>& baggageHeaders) = 0;
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ class ISentryTransaction
virtual void RemoveTag(const FString& key) = 0;
virtual void SetData(const FString& key, const TMap<FString, FString>& values) = 0;
virtual void RemoveData(const FString& key) = 0;
virtual void GetTrace(FString& name, FString& value) = 0;
};
8 changes: 8 additions & 0 deletions plugin-dev/Source/Sentry/Private/SentrySpan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ void USentrySpan::RemoveData(const FString& key)
SentrySpanNativeImpl->RemoveData(key);
}

void USentrySpan::GetTrace(FString& name, FString& value)
{
if (!SentrySpanNativeImpl || SentrySpanNativeImpl->IsFinished())
return;

SentrySpanNativeImpl->GetTrace(name, value);
}

void USentrySpan::InitWithNativeImpl(TSharedPtr<ISentrySpan> spanImpl)
{
SentrySpanNativeImpl = spanImpl;
Expand Down
8 changes: 8 additions & 0 deletions plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,14 @@ USentryTransaction* USentrySubsystem::StartTransactionWithContextAndOptions(USen
return SubsystemNativeImpl->StartTransactionWithContextAndOptions(Context, Options);
}

USentryTransactionContext* USentrySubsystem::ContinueTrace(const FString& SentryTrace, const TArray<FString>& BaggageHeaders)
{
if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled())
return nullptr;

return SubsystemNativeImpl->ContinueTrace(SentryTrace, BaggageHeaders);
}

bool USentrySubsystem::IsSupportedForCurrentSettings()
{
if(!IsCurrentBuildConfigurationEnabled() || !IsCurrentBuildTargetEnabled() || !IsCurrentPlatformEnabled())
Expand Down
8 changes: 8 additions & 0 deletions plugin-dev/Source/Sentry/Private/SentryTransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ void USentryTransaction::RemoveData(const FString& key)
SentryTransactionNativeImpl->RemoveData(key);
}

void USentryTransaction::GetTrace(FString& name, FString& value)
{
if (!SentryTransactionNativeImpl || SentryTransactionNativeImpl->IsFinished())
return;

SentryTransactionNativeImpl->GetTrace(name, value);
}

void USentryTransaction::InitWithNativeImpl(TSharedPtr<ISentryTransaction> transactionImpl)
{
SentryTransactionNativeImpl = transactionImpl;
Expand Down
Loading
Loading