npm package: @multiplayer-app/session-recorder-common
Description: Multiplayer Fullstack Session Recorder - opentelemetry
Author: Multiplayer Software, Inc.
Latest version: 2.0.18

![Description](./docs/img/header-js.png)

<div align="center">
<a href="https://github.com/multiplayer-app/multiplayer-session-recorder-javascript">
  <img src="https://img.shields.io/github/stars/multiplayer-app/multiplayer-session-recorder-javascript?style=social&label=Star&maxAge=2592000" alt="GitHub stars">
</a>
  <a href="https://github.com/multiplayer-app/multiplayer-session-recorder-javascript/blob/main/LICENSE">
    <img src="https://img.shields.io/github/license/multiplayer-app/multiplayer-session-recorder-javascript" alt="License">
  </a>
  <a href="https://multiplayer.app">
    <img src="https://img.shields.io/badge/Visit-multiplayer.app-blue" alt="Visit Multiplayer">
  </a>

</div>
<div>
  <p align="center">
    <a href="https://x.com/trymultiplayer">
      <img src="https://img.shields.io/badge/Follow%20on%20X-000000?style=for-the-badge&logo=x&logoColor=white" alt="Follow on X" />
    </a>
    <a href="https://www.linkedin.com/company/multiplayer-app/">
      <img src="https://img.shields.io/badge/Follow%20on%20LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" alt="Follow on LinkedIn" />
    </a>
    <a href="https://discord.com/invite/q9K3mDzfrx">
      <img src="https://img.shields.io/badge/Join%20our%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join our Discord" />
    </a>
  </p>
</div>

# Multiplayer Full Stack Session Recorder Common

This package provides implementations of the OpenTelemetry API for trace and metrics. It's intended for use both on the server and in the browser.

## Built-in Implementations

- [Session Recorder OpenTelemetry Core](#session-recorder-opentelemetry-core)
  - [Built-in Implementations](#built-in-implementations)
    - [Constants](#constants)
    - [Setup opentelemetry for capturing http request/response body](#session-recorder-http-instrumentation-hooks-node)
    - [Session Recorder Http Trace exporter web](#session-recorder-http-trace-exporter-web)
    - [Session Recorder id generator](#session-recorder-id-generator)
    - [Trace id ratio based sampler](#trace-id-ratio-based-sampler)
    - [Helper for capturing exception in session recording](#helper-for-capturing-exceptions)
    - [Helpers for adding content to session recording](#helper-for-setting-attributes-to-span)
  - [License](#license)

### Constants

```javascript
import {
  MULTIPLAYER_TRACE_DOC_PREFIX,
  MULTIPLAYER_TRACE_DEBUG_PREFIX,
  MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_URL,
  MULTIPLAYER_OTEL_DEFAULT_LOGS_EXPORTER_URL,
  MULTIPLAYER_ATTRIBUTE_PREFIX,
  MULTIPLAYER_MAX_HTTP_REQUEST_RESPONSE_SIZE,
  ATTR_MULTIPLAYER_DEBUG_SESSION,
  ATTR_MULTIPLAYER_HTTP_REQUEST_BODY,
  ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY,
  ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS,
  ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS,
  ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY_ENCODING,
  ATTR_MULTIPLAYER_RPC_REQUEST_MESSAGE,
  ATTR_MULTIPLAYER_RPC_RESPONSE_MESSAGE,
  ATTR_MULTIPLAYER_GRPC_REQUEST_MESSAGE,
  ATTR_MULTIPLAYER_GRPC_RESPONSE_MESSAGE,
  ATTR_MULTIPLAYER_MESSAGING_MESSAGE_BODY
} from '@multiplayer-app/session-recorder-common'
```

### Setup opentelemetry for capturing http request/response body

Session Recorder hooks for nodejs http instrumentation for injecting http request/response headers and payload to span.

```javascript
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { type Instrumentation } from '@opentelemetry/instrumentation'
import { SessionRecorderHttpInstrumentationHooks } from '@multiplayer-app/session-recorder-common'

export const instrumentations: Instrumentation[] = getNodeAutoInstrumentations({
  '@opentelemetry/instrumentation-http': {
    enabled: true,
    responseHook: SessionRecorderHttpInstrumentationHooks.responseHook({
      maxPayloadSizeBytes: 1000,
      uncompressPayload: true,
      captureHeaders: true,
      captureBody: true,
      isMaskBodyEnabled: true,
      isMaskHeadersEnabled: true,
      maskBody: (data, span) => {
        // mask logic here
        return data
      },
      maskHeaders: (data, span) => {
        // mask logic here
        return data
      },
      maskBodyFieldsList: ['password', 'card'],
      maskHeadersList: ['x-trace-id'],
      headersToInclude: ['Set-Cookie', 'Authorization'],
      headersToExclude: ['Cookie'],
    }),
    requestHook: SessionRecorderHttpInstrumentationHooks.requestHook({
      maxPayloadSizeBytes: 1000,
      captureHeaders: true,
      captureBody: true,
      isMaskBodyEnabled: true,
      isMaskHeadersEnabled: true,
      maskBody: (data, span) => {
        // mask logic here
        return data
      },
      maskHeaders: (data, span) => {
        // mask logic here
        return data
      },
      maskBodyFieldsList: ['password', 'card'],
      maskHeadersList: ['x-trace-id'],
      headersToInclude: ['Set-Cookie', 'Authorization'],
      headersToExclude: ['Cookie'],
    }),
  },
)
```

### Session Recorder Http Trace exporter web

```javascript
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'
import { SessionRecorderBrowserTraceExporter } from '@multiplayer-app/session-recorder-common'

const collectorOptions = {
  url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is https://api.multiplayer.app/v1/traces
  apiKey: '<multiplayer-otlp-key>' // api key from multiplayer integration
}

const exporter = new SessionRecorderBrowserTraceExporter(collectorOptions)
const provider = new WebTracerProvider({
  spanProcessors: [
    new BatchSpanProcessor(exporter, {
      // The maximum queue size. After the size is reached spans are dropped.
      maxQueueSize: 100,
      // The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
      maxExportBatchSize: 10,
      // The interval between two consecutive exports
      scheduledDelayMillis: 500,
      // How long the export can run before it is cancelled
      exportTimeoutMillis: 30000
    })
  ]
})

provider.register()
```

### Session Recorder id generator

```javascript
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'
import { SessionRecorderIdGenerator, SessionRecorderBrowserTraceExporter } from '@multiplayer-app/session-recorder-common'

const idGenerator = new SessionRecorderIdGenerator({ autoDocTracesRatio: 0.05 })

const collectorOptions = {
  url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is https://api.multiplayer.app/v1/traces
  apiKey: '<multiplayer-otlp-key>' // api key from multiplayer integration
}

const exporter = new SessionRecorderBrowserTraceExporter(collectorOptions)
const provider = new WebTracerProvider({
  spanProcessors: [
    new BatchSpanProcessor(exporter, {
      // The maximum queue size. After the size is reached spans are dropped.
      maxQueueSize: 100,
      // The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
      maxExportBatchSize: 10,
      // The interval between two consecutive exports
      scheduledDelayMillis: 500,
      // How long the export can run before it is cancelled
      exportTimeoutMillis: 30000
    })
  ],
  idGenerator
})

idGenerator.setSessionId('<multiplayer-debug-session-short-id>')
```

### Trace id ratio based sampler

Session Recorder sampler will always sample traces with appropriate prefixes, other traces will be sampled using ration provided to constructor.

```javascript
import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web'
import {
  SessionRecorderTraceIdRatioBasedSampler,
  SessionRecorderBrowserTraceExporter
} from '@multiplayer-app/session-recorder-common'

const collectorOptions = {
  url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is https://api.multiplayer.app/v1/traces
  apiKey: '<multiplayer-otlp-key>' // api key from multiplayer integration
}

const exporter = new SessionRecorderBrowserTraceExporter(collectorOptions)
const provider = new WebTracerProvider({
  spanProcessors: [
    new BatchSpanProcessor(exporter, {
      // The maximum queue size. After the size is reached spans are dropped.
      maxQueueSize: 100,
      // The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
      maxExportBatchSize: 10,
      // The interval between two consecutive exports
      scheduledDelayMillis: 500,
      // How long the export can run before it is cancelled
      exportTimeoutMillis: 30000
    })
  ],
  sampler: new SessionRecorderTraceIdRatioBasedSampler(0.05)
})
```

### Helper for capturing exception in session recording

```javascript
import { SessionRecorderSdk } from '@multiplayer-app/session-recorder-common'

const error = new Error('Some text here')

SessionRecorderSdk.captureException(error)
```

### Helpers for adding content to session recording

```javascript
import { SessionRecorderSdk } from '@multiplayer-app/session-recorder-common'

SessionRecorderSdk.setAttribute('{{SOME_KEY}}', '{{SOME_VALUE}}')

// following helpers do masking of sensitive fields
SessionRecorderSdk.setHttpRequestBody('{{ANY_REQUEST_PAYLOAD_HERE}}')

SessionRecorderSdk.setHttpRequestHeaders({ Cookie: '...', Authorization: '...' })

SessionRecorderSdk.setHttpResponseBody({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })

SessionRecorderSdk.setHttpResponseHeaders({ 'Set-Cookie': '...' })

SessionRecorderSdk.setMessageBody({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })

SessionRecorderSdk.setRpcRequestMessage({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })

SessionRecorderSdk.setRpcResponseMessage({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })

SessionRecorderSdk.setGrpcRequestMessage({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })

SessionRecorderSdk.setGrpcResponseMessage({ some_payload: '{{ANY_REQUEST_PAYLOAD_HERE}}' })
```

## License

MIT - See [LICENSE](./LICENSE) for more information.