Skip to content

Commit

Permalink
add SessionIdEventSender and wire up into lifecycle
Browse files Browse the repository at this point in the history
  • Loading branch information
breedx-splk committed Sep 3, 2024
1 parent 5a230fc commit 0eee201
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.opentelemetry.android.instrumentation.AndroidInstrumentationLoader
import io.opentelemetry.android.internal.services.ServiceManager
import io.opentelemetry.android.session.SessionManager
import io.opentelemetry.sdk.OpenTelemetrySdk
import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider

class SdkPreconfiguredRumBuilder
@JvmOverloads
Expand Down Expand Up @@ -50,6 +51,14 @@ class SdkPreconfiguredRumBuilder
// might turn off/on additional telemetry depending on whether the app is active or not
appLifecycleService.registerListener(timeoutHandler)

val eventLogger =
SdkEventLoggerProvider.create(sdk.logsBridge)
.get(OpenTelemetryRum::class.java.simpleName)

sessionManager.addObserver(SessionIdEventSender(eventLogger))
// After addObserver(), we call getSessionId() to trigger a session.start event
sessionManager.getSessionId()

val openTelemetryRum = OpenTelemetryRumImpl(sdk, sessionManager)

// Install instrumentations
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android

import io.opentelemetry.android.common.RumConstants.Events.EVENT_SESSION_END
import io.opentelemetry.android.common.RumConstants.Events.EVENT_SESSION_START
import io.opentelemetry.android.session.Session
import io.opentelemetry.android.session.SessionObserver
import io.opentelemetry.api.incubator.events.EventLogger
import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes.SESSION_ID
import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes.SESSION_PREVIOUS_ID

internal class SessionIdEventSender(private val eventLogger: EventLogger) : SessionObserver {
override fun onSessionStarted(
newSession: Session,
previousSession: Session,
) {
val eventBuilder =
eventLogger
.builder(EVENT_SESSION_START)
.put(SESSION_ID, newSession.getId())
val previousSessionId = previousSession.getId()
if (previousSessionId.isNotEmpty()) {
eventBuilder.put(SESSION_PREVIOUS_ID, previousSessionId)
}
eventBuilder.emit()
}

override fun onSessionEnded(session: Session) {
if (session.getId().isEmpty()) {
return
}
eventLogger.builder(EVENT_SESSION_END)
.put(SESSION_ID, session.getId())
.emit()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.awaitility.Awaitility.await;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -48,8 +47,6 @@
import io.opentelemetry.api.incubator.logs.AnyValue;
import io.opentelemetry.api.incubator.logs.KeyAnyValue;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerBuilder;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
Expand All @@ -58,13 +55,13 @@
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.contrib.disk.buffering.SpanToDiskExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
import io.opentelemetry.sdk.trace.data.SpanData;
Expand All @@ -75,6 +72,7 @@
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
Expand Down Expand Up @@ -178,14 +176,16 @@ public void shouldBuildLogRecordProvider() {
eventLogger.builder("test.event").put("body.field", "foo").setAttributes(attrs).emit();

List<LogRecordData> logs = logsExporter.getFinishedLogRecordItems();
assertThat(logs).hasSize(1);
assertThat(logs).hasSize(2);
assertThat(logs.get(0))
.hasAttributesSatisfyingExactly(equalTo(stringKey("event.name"), "session.start"));
assertThat(logs.get(1))
.hasAttributesSatisfyingExactly(
equalTo(stringKey("event.name"), "test.event"),
equalTo(stringKey("mega"), "hit"))
.hasResource(resource);

AnyValue<?> bodyValue = ((AnyValueBody) logs.get(0).getBody()).asAnyValue();
AnyValue<?> bodyValue = ((AnyValueBody) logs.get(1).getBody()).asAnyValue();
List<KeyAnyValue> payload = (List<KeyAnyValue>) bodyValue.getValue();
assertThat(payload).hasSize(1);
KeyAnyValue expected = KeyAnyValue.of("body.field", AnyValue.of("foo"));
Expand Down Expand Up @@ -308,8 +308,11 @@ public void setLogRecordExporterCustomizer() {
() -> assertThat(logsExporter.getFinishedLogRecordItems()).isNotEmpty());
assertThat(wasCalled.get()).isTrue();
Collection<LogRecordData> logs = logsExporter.getFinishedLogRecordItems();
assertThat(logs).hasSize(1);
assertThat(logs.iterator().next())
assertThat(logs).hasSize(2);
Iterator<LogRecordData> iter = logs.iterator();
assertThat(iter.next())
.hasAttributesSatisfyingExactly(equalTo(stringKey("event.name"), "session.start"));
assertThat(iter.next())
.hasBody("foo")
.hasAttributesSatisfyingExactly(equalTo(stringKey("bing"), "bang"))
.hasSeverity(Severity.FATAL3);
Expand Down Expand Up @@ -416,14 +419,13 @@ public void verifyGlobalAttrsForLogs() {
logger.logRecordBuilder().setAttribute(stringKey("localAttrKey"), "localAttrValue").emit();

List<LogRecordData> recordedLogs = logRecordExporter.getFinishedLogRecordItems();
assertThat(recordedLogs).hasSize(1);
LogRecordData logRecordData = recordedLogs.get(0);
OpenTelemetryAssertions.assertThat(logRecordData)
.hasAttributes(
Attributes.builder()
.put("someGlobalKey", "someGlobalValue")
.put("localAttrKey", "localAttrValue")
.build());
assertThat(recordedLogs).hasSize(2);
assertThat(recordedLogs.get(0))
.hasAttributesSatisfying(equalTo(stringKey("event.name"), "session.start"));
assertThat(recordedLogs.get(1))
.hasAttributesSatisfyingExactly(
equalTo(stringKey("someGlobalKey"), "someGlobalValue"),
equalTo(stringKey("localAttrKey"), "localAttrValue"));
}

@Test
Expand All @@ -446,11 +448,11 @@ public void verifyServicesAreStarted() {
@Test
public void verifyPreconfiguredServicesInitialization() {
OpenTelemetrySdk openTelemetrySdk = mock();
// Work around sdk EventLogger api limitations
LoggerProvider logsBridge = mock(LoggerProvider.class);
LoggerBuilder loggerBuilder = mock();
when(openTelemetrySdk.getLogsBridge()).thenReturn(logsBridge);
when(logsBridge.loggerBuilder(any())).thenReturn(loggerBuilder);
SdkLoggerProvider loggerProvider =
SdkLoggerProvider.builder()
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logsExporter))
.build();
when(openTelemetrySdk.getLogsBridge()).thenReturn(loggerProvider);

OpenTelemetryRum.builder(application, openTelemetrySdk, true).build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.android;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;

import io.opentelemetry.android.session.Session;
import io.opentelemetry.api.incubator.events.EventLogger;
import io.opentelemetry.api.incubator.logs.KeyAnyValue;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider;
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class SessionIdEventSenderTest {
@RegisterExtension
static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create();

private SessionIdEventSender underTest;

@BeforeEach
void setup() {
LoggerProvider loggerProvider = otelTesting.getOpenTelemetry().getLogsBridge();
EventLogger eventLogger = SdkEventLoggerProvider.create(loggerProvider).get("testLogging");
underTest = new SessionIdEventSender(eventLogger);
}

@Test
void shouldEmitSessionStartEvent() {
Session newSession = new Session.DefaultSession("123", 0);
Session oldSession = new Session.DefaultSession("666", 0);
underTest.onSessionStarted(newSession, oldSession);

List<LogRecordData> logs = otelTesting.getLogRecords();
assertEquals(1, logs.size());
LogRecordData log = logs.get(0);
// TODO: Use new event body assertions when available.
assertThat(log)
.hasAttributesSatisfyingExactly(equalTo(stringKey("event.name"), "session.start"));

AnyValueBody body = (AnyValueBody) log.getBody();
List<KeyAnyValue> kvBody = (List<KeyAnyValue>) body.asAnyValue().getValue();
assertThat(kvBody.get(0).getKey()).isEqualTo("session.previous_id");
assertThat(kvBody.get(0).getAnyValue().asString()).isEqualTo("666");
assertThat(kvBody.get(1).getKey()).isEqualTo("session.id");
assertThat(kvBody.get(1).getAnyValue().asString()).isEqualTo("123");
}

@Test
void shouldEmitSessionEndEvent() {
Session session = new Session.DefaultSession("123", 0);
underTest.onSessionEnded(session);

List<LogRecordData> logs = otelTesting.getLogRecords();
assertEquals(1, logs.size());
LogRecordData log = logs.get(0);
// TODO: Use new event body assertions when available.
assertThat(log)
.hasAttributesSatisfyingExactly(equalTo(stringKey("event.name"), "session.end"));

AnyValueBody body = (AnyValueBody) log.getBody();
List<KeyAnyValue> kvBody = (List<KeyAnyValue>) body.asAnyValue().getValue();
assertThat(kvBody.get(0).getKey()).isEqualTo("session.id");
assertThat(kvBody.get(0).getAnyValue().asString()).isEqualTo("123");
}
}

0 comments on commit 0eee201

Please sign in to comment.