Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import com.launchdarkly.sdk.android.LDClient
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.logs.Severity
import io.opentelemetry.api.trace.Span
import io.opentelemetry.context.Context
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
Expand All @@ -22,6 +24,7 @@ import java.io.BufferedInputStream
import java.net.HttpURLConnection
import java.net.URL


class ViewModel(application: Application) : AndroidViewModel(application) {

fun triggerMetric() {
Expand Down Expand Up @@ -83,6 +86,23 @@ class ViewModel(application: Application) : AndroidViewModel(application) {

fun triggerLogWithContext(message: String) {
val text = message.ifEmpty { "Log with span context" }

val parentSpan = LDObserve.startSpan("parentSpan")
parentSpan.makeCurrent().use {
val context = Context.current()

Thread {
context.makeCurrent().use {
val childSpan = LDObserve.startSpan("childSpan")
childSpan.makeCurrent().use {
// do work
childSpan.end()
}
}
}.start()
}
parentSpan.end()

viewModelScope.launch(Dispatchers.IO) {
val span = LDObserve.startSpan(
name = "log-context-demo",
Expand All @@ -97,6 +117,10 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
// Simulate a detached thread where OTel context is lost automatically.
// Span.current() here returns INVALID, so we pass the captured context explicitly.
Thread {
Span.wrap(capturedContext).makeCurrent().use {
val childSpan = LDObserve.startSpan("child of log-context-demo", Attributes.empty())
childSpan.end()
}
LDObserve.recordLog(
message = text,
severity = Severity.WARN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public class ObservabilityBridge(
tracesApi = com.launchdarkly.observability.api.ObservabilityOptions.TracesApi(includeErrors = true, includeSpans = true),
metricsApi = com.launchdarkly.observability.api.ObservabilityOptions.MetricsApi.enabled(),
instrumentations = com.launchdarkly.observability.api.ObservabilityOptions.Instrumentations(
crashReporting = false, launchTime = true, activityLifecycle = true
crashReporting = false, launchTime = observability.launchTime, activityLifecycle = true
),
logAdapter = LDAndroidLogging.adapter(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class LDObservabilityOptions {
@JvmField var backendUrl: String = ""
@JvmField var contextFriendlyName: String? = null
@JvmField var attributes: HashMap<String, Any?>? = null
@JvmField var launchTime: Boolean = true

constructor()

Expand All @@ -18,7 +19,8 @@ public class LDObservabilityOptions {
otlpEndpoint: String,
backendUrl: String,
contextFriendlyName: String?,
attributes: HashMap<String, Any?>? = null
attributes: HashMap<String, Any?>? = null,
launchTime: Boolean = true
) {
this.isEnabled = isEnabled
this.serviceName = serviceName
Expand All @@ -27,6 +29,7 @@ public class LDObservabilityOptions {
this.backendUrl = backendUrl
this.contextFriendlyName = contextFriendlyName
this.attributes = attributes
this.launchTime = launchTime
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ interface ObjcObservabilityOptions

[NullAllowed, Export("attributes")]
NSDictionary Attributes { get; set; }

[Export("networkRequests")]
bool NetworkRequests { get; set; }

[Export("launchTimes")]
bool LaunchTimes { get; set; }
}

[BaseType(typeof(NSObject))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ public final class ObservabilityBridge: NSObject {
resourceAttributes: buildResourceAttributes(observability.attributes),
crashReporting: .init(source: .none),
instrumentation: .init(
urlSession: .enabled,
urlSession: .disabled, // Network tracing happens on the .NET side via System.Net.Http activities.
userTaps: .enabled,
memory: .disabled,
memoryWarnings: .disabled,
cpu: .disabled,
launchTimes: .enabled
launchTimes: observability.launchTimes ? .enabled : .disabled
)
))
observabilityPlugin.distroAttributes = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public final class ObjcObservabilityOptions: NSObject {
@objc public var otlpEndpoint: String = ""
@objc public var backendUrl: String = ""
@objc public var attributes: NSDictionary?
@objc public var networkRequests: Bool = true
@objc public var launchTimes: Bool = true

@objc public override init() {
super.init()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<PackageId>LaunchDarkly.SessionReplay</PackageId>
<Version>0.6.0</Version>
<Version>0.8.0</Version>
<UseLocalClientSdk>false</UseLocalClientSdk>
<Authors>LaunchDarkly</Authors>
<Owners>LaunchDarkly</Owners>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ internal static class LDNativeAndroidMapping
options.OtlpEndpoint,
options.BackendUrl,
options.ContextFriendlyName,
DictionaryTypeConverters.ToJavaDictionary(options.Attributes)
DictionaryTypeConverters.ToJavaDictionary(options.Attributes),
options.Instrumentation.LaunchTimes
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public void Start(string mobileKey, ObservabilityOptions observability, SessionR
ServiceVersion = observability.ServiceVersion ?? "0.1.0",
OtlpEndpoint = observability.OtlpEndpoint ?? "https://otel.observability.app.launchdarkly.com:4318",
BackendUrl = observability.BackendUrl ?? "https://pub.observability.app.launchdarkly.com",
Attributes = DictionaryTypeConverters.ToNSDictionary(observability.Attributes)
Attributes = DictionaryTypeConverters.ToNSDictionary(observability.Attributes),
NetworkRequests = observability.Instrumentation.NetworkRequests,
LaunchTimes = observability.Instrumentation.LaunchTimes
};

var objcReplay = new ObjcSessionReplayOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal sealed class PluginOrchestrator

private int _createdCount;
private int _registeredCount;
private bool _initialized;

internal ObservabilityService? ObservabilityService { get; private set; }
internal SessionReplayService? SessionReplayService { get; private set; }
Expand All @@ -24,7 +25,8 @@ private PluginOrchestrator() { }

private void InitializeAll()
{
if (_registeredCount < _createdCount) return;
if (_initialized || _registeredCount < _createdCount) return;
_initialized = true;
Comment thread
cursor[bot] marked this conversation as resolved.

var metadata = ObservabilityService?.Metadata ?? SessionReplayService?.Metadata;
if (metadata == null) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ public sealed class LDTracer : IDisposable
{
private readonly TracerProvider _tracerProvider;

internal LDTracer(string serviceName)
internal LDTracer(string serviceName, bool networkRequests = true)
{
_tracerProvider = OTelSdk.CreateTracerProviderBuilder()
.AddSource(serviceName)
var builder = OTelSdk.CreateTracerProviderBuilder()
.AddSource(serviceName);

if (networkRequests)
builder.AddSource("System.Net.Http");

_tracerProvider = builder
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ObservabilityOptions
public string BackendUrl { get; set; } = DefaultBackendUrl;
public string? ContextFriendlyName { get; set; }
public IDictionary<string, object?>? Attributes { get; set; }
public InstrumentationOptions Instrumentation { get; set; } = new();

public ObservabilityOptions() { }

Expand All @@ -27,7 +28,8 @@ public ObservabilityOptions(
string? otlpEndpoint = null,
string? backendUrl = null,
string? contextFriendlyName = null,
IDictionary<string, object?>? attributes = null)
IDictionary<string, object?>? attributes = null,
InstrumentationOptions? instrumentation = null)
{
IsEnabled = isEnabled;
ServiceName = serviceName;
Expand All @@ -36,5 +38,22 @@ public ObservabilityOptions(
BackendUrl = backendUrl ?? DefaultBackendUrl;
ContextFriendlyName = contextFriendlyName;
Attributes = attributes;
Instrumentation = instrumentation ?? new InstrumentationOptions();
}
}

public class InstrumentationOptions
{
public bool NetworkRequests { get; set; } = true;
public bool LaunchTimes { get; set; } = true;

public InstrumentationOptions() { }

public InstrumentationOptions(
bool networkRequests = true,
bool launchTimes = true)
{
NetworkRequests = networkRequests;
LaunchTimes = launchTimes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class ObservabilityPlugin : Plugin

public ObservabilityPlugin(ObservabilityOptions options) : base("LaunchDarkly.Observability")
{
if (options.IsEnabled && options.Instrumentation.NetworkRequests)
AppContext.SetSwitch("System.Net.Http.EnableActivityPropagation", true);
Comment thread
cursor[bot] marked this conversation as resolved.
ObservabilityService = new ObservabilityService(options);
PluginOrchestrator.Instance.AddObservabilityService(ObservabilityService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal ObservabilityService(ObservabilityOptions options)
internal void Initialize()
{
_tracer?.Dispose();
_tracer = new LDTracer(Options.ServiceName);
_tracer = new LDTracer(Options.ServiceName, Options.Instrumentation.NetworkRequests);

#if IOS
_nativeLogger = LDObserveBridge.GetObjcLogger();
Expand Down
6 changes: 5 additions & 1 deletion sdk/@launchdarkly/mobile-dotnet/sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ public static MauiApp CreateMauiApp()
#endif
otlpEndpoint: otlpEndpoint,
backendUrl: backendUrl,
attributes: new Dictionary<string, object?> { { "test-options-attribute", "maui-sample-value" } }
attributes: new Dictionary<string, object?> { { "test-options-attribute", "maui-sample-value" } },
instrumentation: new InstrumentationOptions(
networkRequests: true,
launchTimes: true
)
)))
.Add(new SessionReplayPlugin(new SessionReplayOptions(
isEnabled: true,
Expand Down
3 changes: 3 additions & 0 deletions sdk/@launchdarkly/mobile-dotnet/sample/MauiSample9.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@

<PropertyGroup Condition="$(TargetFramework.Contains('android'))">
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<!-- Use SocketsHttpHandler so System.Net.Http emits Activity diagnostics
for automatic HTTP tracing via LDObserve. -->
<UseNativeHttpHandler>false</UseNativeHttpHandler>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ interface TracesApi {
* @param name The name of the span
* @param attributes The attributes to record with the span
*/
fun startSpan(name: String, attributes: Attributes): Span
fun startSpan(name: String, attributes: Attributes = Attributes.empty()): Span
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class LDObserve(private val client: Observe) : Observe {
override fun recordError(error: Error, attributes: Attributes) {}
override fun recordLog(message: String, severity: Severity, attributes: Attributes, spanContext: SpanContext?) {}
override fun startSpan(name: String, attributes: Attributes): Span {
return Span.getInvalid() // Observability plugin was not initialized before being used.
return Span.getInvalid()
}
override fun flush(): Boolean {
return false // No-op, return false to indicate flush was not successful
Expand Down
Loading