The Multiplayer Full Stack Session Recorder is a powerful tool that offers deep session replays with insights spanning frontend screens, platform traces, metrics, and logs. It helps your team pinpoint and resolve bugs faster by providing a complete picture of your backend system architecture. No more wasted hours combing through APM data; the Multiplayer Full Stack Session Recorder does it all in one place.
Add to your gradle file:
implementation 'app.multiplayer:session_recorder:1.0.0'
Multiplayer Full Stack Session Recorder is built on top of OpenTelemetry.
No problem. You can set it up in a few minutes. If your services don't already use OpenTelemetry, you'll first need to install the OpenTelemetry libraries. Detailed instructions for this can be found in the OpenTelemetry documentation.
You have two primary options for routing your data to Multiplayer:
Direct Exporter: This option involves using the Multiplayer Exporter directly within your services. It's a great choice for new applications or startups because it's simple to set up and doesn't require any additional infrastructure. You can configure it to send all session recording data to Multiplayer while optionally sending a sampled subset of data to your existing observability platform.
OpenTelemetry Collector: For large, scaled platforms, we recommend using an OpenTelemetry Collector. This approach provides more flexibility by having your services send all telemetry to the collector, which then routes specific session recording data to Multiplayer and other data to your existing observability tools.
Send OpenTelemetry data from your services to Multiplayer and optionally other destinations (e.g., OpenTelemetry Collectors, observability platforms, etc.).
This is the quickest way to get started, but consider using an OpenTelemetry Collector (see Option 2 below) if you're scalling or a have a large platform.
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import app.multiplayer.session_recorder.exporter.SessionRecorderLogsExporterWrapper;
import app.multiplayer.session_recorder.exporter.SessionRecorderTraceExporterWrapper;
// # set up Multiplayer exporters. Note: GRPC exporters are also available.
// # see: `SessionRecorderOtlpHttpSpanExporter` and `SessionRecorderOtlpGrpcLogExporter`
// Multiplayer exporter wrappers filter out session recording atrtributes before passing to provided exporter
SpanExporter spanExporter = SessionRecorderTraceExporterWrapper(
// add any OTLP trace exporter
OtlpHttpSpanExporter.builder()
.setEndpoint("...")
.build()
);
LogRecordExporter logsExporter = SessionRecorderLogsExporterWrapper(
// add any OTLP log exporter
OtlpHttpLogRecordExporter.builder()
.setEndpoint("...")
.build()
);If you're scalling or a have a large platform, consider running a dedicated collector. See the Multiplayer OpenTelemetry collector repository which shows how to configure the standard OpenTelemetry Collector to send data to Multiplayer and optional other destinations.
Add standard OpenTelemetry code to export OTLP data to your collector.
See a basic example below:
import io.opentelemetry.sdk.trace.export.SpanExporter;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
SpanExporter spanExporter = OtlpHttpSpanExporter.builder()
.setEndpoint("http://<OTLP_COLLECTOR_URL>/v1/traces")
.build();
LogRecordExporter logsExporter = OtlpHttpLogRecordExporter.builder()
.setEndpoint("http://<OTLP_COLLECTOR_URL>/v1/logs")
.build();In addition to sending traces and logs, you need to capture request and response content. We offer solution for this:
Multiplayer Proxy: Alternatively, you can run a Multiplayer Proxy to handle this outside of your services. This is ideal for large-scale applications and supports all languages, including those like Java that don't allow for in-service request/response hooks. The proxy can be deployed in various ways, such as an Ingress Proxy, a Sidecar Proxy, or an Embedded Proxy, to best fit your architecture.
The Multiplayer Proxy enables capturing request/response and header content without changing service code. See instructions at the Multiplayer Proxy repository.
The Multiplayer Full Stack Session Recorder can be used inside the CLI apps.
The Multiplayer Time Travel Demo includes an example java CLI app.
See an additional example below.
Use the following code below to initialize and run the session recorder.
Example for Session Recorder initialization relies on OpenTelemetry.java file. Copy that file and put next to quick start code.
// IMPORTANT: set up OpenTelemetry
// for an example see ./examples/src/main/java/app/multiplayer/session_recorder/OpenTelemetry.java
// NOTE: for the code below to work, copy ./examples/src/main/java/app/multiplayer/session_recorder/OpenTelemetry.java to ./OpenTelemetry.java
import app.multiplayer.session_recorder.SessionRecorder;
import app.multiplayer.session_recorder.type.SessionRecorderConfig;
import app.multiplayer.session_recorder.type.Session;
import app.multiplayer.session_recorder.type.SessionType;
import app.multiplayer.session_recorder.trace.SessionRecorderRandomIdGenerator;
// Configure and initialize
SessionRecorderConfig config = new SessionRecorderConfig();
config.setApiKey("MULTIPLAYER_API_KEY"); // note: replace with your Multiplayer API key
config.setTraceIdGenerator(OpenTelemetry.getIdGenerator());
// Initialize the SessionRecorder
SessionRecorder.init(config);Below is an example showing how to create a session recording in MANUAL mode. Manual session recordings stream and save all the data between calling start and stop.
// create session
Session session = new Session();
session.setName("This is test session");
session.addTag("environment", "production");
session.addTag("version", "v1.0.0");
session.addTag("feature", "session-recording");
session.addSessionAttribute("userId", "12345");
session.addSessionAttribute("environment", "production");
session.addSessionAttribute("version", "1.0.0");
session.addResourceAttribute("service.name", "my-service");
session.addResourceAttribute("service.version", "1.0.0");
session.addResourceAttribute("deployment.environment", "production");
SessionRecorder.start(SessionType.MANUAL, session);
// do something here
Session stopSession = new Session();
stopSession.addSessionAttribute("status", "completed");
SessionRecorder.stop(stopSession);Below is an example showing how to create a session in CONTINUOUS mode. Continuous session recordings stream all the data received between calling start and stop -
but only save a rolling window data (90 seconds by default) when:
- an exception or error occurs;
- when
saveis called; or - programmatically, when the auto-save attribute is attached to a span.
// create session
Session session = new Session();
session.setName("This is test session");
session.addTag("environment", "production");
session.addTag("version", "v1.0.0");
session.addTag("feature", "session-recording");
session.addSessionAttribute("userId", "12345");
session.addSessionAttribute("environment", "production");
session.addSessionAttribute("version", "1.0.0");
session.addResourceAttribute("service.name", "my-service");
session.addResourceAttribute("service.version", "1.0.0");
session.addResourceAttribute("deployment.environment", "production");
SessionRecorder.start(SessionType.CONTINUOUS, session);
// do something here
SessionRecorder.save()
// do something here
SessionRecorder.save()
// do something here
Session stopSession = new Session();
stopSession.addSessionAttribute("status", "completed");
SessionRecorder.stop(stopSession);Continuous session recordings may also be saved from within any service or component involved in a trace by adding the attributes below to a span:
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import static app.multiplayer.session_recorder.constants.SessionRecorderConstants.ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE;
import static app.multiplayer.session_recorder.constants.SessionRecorderConstants.ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE_REASON;
Span activeSpan = Span.current();
if (activeSpan != null && activeSpan.getSpanContext().isValid()) {
activeSpan.setAttribute(
ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE,
true
);
activeSpan.setAttribute(
ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE_REASON,
"Some reason"
);
}MIT — see LICENSE.
