@multiplayer-app/session-recorder-common 2.0.17-alpha.7 → 2.0.17-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
- package/dist/esnext/tsconfig.esnext.tsbuildinfo +0 -1
- package/docs/img/header-js.png +0 -0
- package/eslint.config.js +0 -226
- package/src/SessionRecorderIdGenerator.ts +0 -70
- package/src/SessionRecorderTraceIdRatioBasedSampler.ts +0 -89
- package/src/constants/constants.base.ts +0 -111
- package/src/constants/constants.browser.ts +0 -1
- package/src/constants/constants.node.ts +0 -5
- package/src/exporters/SessionRecorderBrowserTraceExporter.ts +0 -196
- package/src/exporters/SessionRecorderGrpcLogsExporter.ts +0 -52
- package/src/exporters/SessionRecorderGrpcTraceExporter.ts +0 -50
- package/src/exporters/SessionRecorderHttpLogsExporter.ts +0 -58
- package/src/exporters/SessionRecorderHttpTraceExporter.ts +0 -68
- package/src/exporters/SessionRecorderLogsExporterWrapper.ts +0 -36
- package/src/exporters/SessionRecorderTraceExporterWrapper.ts +0 -36
- package/src/exporters/index-browser.ts +0 -1
- package/src/exporters/index-node.ts +0 -6
- package/src/exporters/index.ts +0 -7
- package/src/index-browser.ts +0 -6
- package/src/index-node.ts +0 -7
- package/src/index.ts +0 -7
- package/src/instrumentations/SessionRecorderHttpInstrumentationHooksNode.ts +0 -356
- package/src/instrumentations/index-node.ts +0 -1
- package/src/sdk/capture-exception.ts +0 -102
- package/src/sdk/id-generator.ts +0 -17
- package/src/sdk/index.ts +0 -8
- package/src/sdk/is-gzip.ts +0 -7
- package/src/sdk/mask.ts +0 -161
- package/src/sdk/save-continuous-deb-session.ts +0 -28
- package/src/sdk/schemify.ts +0 -57
- package/src/sdk/set-attribute.ts +0 -210
- package/src/sdk/set-resource-attributes.ts +0 -9
- package/src/type/crash-buffer.ts +0 -64
- package/src/type/index.ts +0 -4
- package/src/type/session-type.enum.ts +0 -20
- package/src/type/session.ts +0 -84
- package/src/type/user-type.enum.ts +0 -5
- package/tsconfig.base.es5.json +0 -8
- package/tsconfig.base.esm.json +0 -7
- package/tsconfig.base.esnext.json +0 -10
- package/tsconfig.base.json +0 -38
- package/tsconfig.esm.json +0 -12
- package/tsconfig.esnext.json +0 -12
- package/tsconfig.json +0 -25
package/eslint.config.js
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js'
|
|
2
|
-
import globals from 'globals'
|
|
3
|
-
import tseslint from '@typescript-eslint/eslint-plugin'
|
|
4
|
-
import tsparser from '@typescript-eslint/parser'
|
|
5
|
-
|
|
6
|
-
export default [
|
|
7
|
-
js.configs.recommended,
|
|
8
|
-
{
|
|
9
|
-
ignores: [
|
|
10
|
-
'node_modules/**',
|
|
11
|
-
'dist/**',
|
|
12
|
-
'build/**',
|
|
13
|
-
'coverage/**',
|
|
14
|
-
'*.min.js',
|
|
15
|
-
'*.bundle.js',
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
files: ['**/*.ts', '**/*.tsx'],
|
|
20
|
-
languageOptions: {
|
|
21
|
-
parser: tsparser,
|
|
22
|
-
parserOptions: {
|
|
23
|
-
ecmaVersion: 'latest',
|
|
24
|
-
sourceType: 'module',
|
|
25
|
-
},
|
|
26
|
-
globals: {
|
|
27
|
-
...globals.browser,
|
|
28
|
-
// Browser globals
|
|
29
|
-
window: 'readonly',
|
|
30
|
-
document: 'readonly',
|
|
31
|
-
navigator: 'readonly',
|
|
32
|
-
localStorage: 'readonly',
|
|
33
|
-
sessionStorage: 'readonly',
|
|
34
|
-
console: 'readonly',
|
|
35
|
-
setTimeout: 'readonly',
|
|
36
|
-
clearTimeout: 'readonly',
|
|
37
|
-
setInterval: 'readonly',
|
|
38
|
-
clearInterval: 'readonly',
|
|
39
|
-
requestAnimationFrame: 'readonly',
|
|
40
|
-
cancelAnimationFrame: 'readonly',
|
|
41
|
-
fetch: 'readonly',
|
|
42
|
-
Headers: 'readonly',
|
|
43
|
-
HeadersInit: 'readonly',
|
|
44
|
-
Request: 'readonly',
|
|
45
|
-
RequestInfo: 'readonly',
|
|
46
|
-
Response: 'readonly',
|
|
47
|
-
FormData: 'readonly',
|
|
48
|
-
File: 'readonly',
|
|
49
|
-
Blob: 'readonly',
|
|
50
|
-
URL: 'readonly',
|
|
51
|
-
URLSearchParams: 'readonly',
|
|
52
|
-
AbortController: 'readonly',
|
|
53
|
-
AbortSignal: 'readonly',
|
|
54
|
-
ReadableStream: 'readonly',
|
|
55
|
-
WritableStream: 'readonly',
|
|
56
|
-
TransformStream: 'readonly',
|
|
57
|
-
TextEncoder: 'readonly',
|
|
58
|
-
TextDecoder: 'readonly',
|
|
59
|
-
HTMLElement: 'readonly',
|
|
60
|
-
HTMLButtonElement: 'readonly',
|
|
61
|
-
HTMLInputElement: 'readonly',
|
|
62
|
-
HTMLTextAreaElement: 'readonly',
|
|
63
|
-
HTMLAnchorElement: 'readonly',
|
|
64
|
-
XMLHttpRequest: 'readonly',
|
|
65
|
-
XMLHttpRequestBodyInit: 'readonly',
|
|
66
|
-
MouseEvent: 'readonly',
|
|
67
|
-
InputEvent: 'readonly',
|
|
68
|
-
MessageEvent: 'readonly',
|
|
69
|
-
EventListener: 'readonly',
|
|
70
|
-
MutationObserver: 'readonly',
|
|
71
|
-
NodeJS: 'readonly',
|
|
72
|
-
Window: 'readonly',
|
|
73
|
-
Document: 'readonly',
|
|
74
|
-
IDBDatabase: 'readonly',
|
|
75
|
-
indexedDB: 'readonly',
|
|
76
|
-
// Node.js globals
|
|
77
|
-
process: 'readonly',
|
|
78
|
-
Buffer: 'readonly',
|
|
79
|
-
__dirname: 'readonly',
|
|
80
|
-
__filename: 'readonly',
|
|
81
|
-
global: 'readonly',
|
|
82
|
-
module: 'readonly',
|
|
83
|
-
require: 'readonly',
|
|
84
|
-
exports: 'readonly',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
plugins: {
|
|
88
|
-
'@typescript-eslint': tseslint,
|
|
89
|
-
},
|
|
90
|
-
rules: {
|
|
91
|
-
// TypeScript specific rules
|
|
92
|
-
'no-namespace': 'off',
|
|
93
|
-
'no-useless-escape': 'off',
|
|
94
|
-
'no-self-assign': 'off',
|
|
95
|
-
'no-useless-catch': 'off',
|
|
96
|
-
'prefer-namespace-keyword': 'off',
|
|
97
|
-
'no-empty-function': 'off',
|
|
98
|
-
'no-unused-vars': 'off',
|
|
99
|
-
'no-explicit-any': 'off',
|
|
100
|
-
'ban-types': 'off',
|
|
101
|
-
'object-curly-spacing': ['error', 'always'],
|
|
102
|
-
'space-before-blocks': 'error',
|
|
103
|
-
'keyword-spacing': 'error',
|
|
104
|
-
|
|
105
|
-
// General rules
|
|
106
|
-
'prefer-const': 'error',
|
|
107
|
-
'block-spacing': ['error', 'always'],
|
|
108
|
-
'keyword-spacing': [
|
|
109
|
-
'error',
|
|
110
|
-
{
|
|
111
|
-
before: true,
|
|
112
|
-
after: true,
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
'indent': ['error', 2, { SwitchCase: 1 }],
|
|
116
|
-
'linebreak-style': ['error', 'unix'],
|
|
117
|
-
'quotes': ['error', 'single'],
|
|
118
|
-
'semi': ['error', 'never'],
|
|
119
|
-
'comma-dangle': ['error', 'always-multiline'],
|
|
120
|
-
'no-console': 'warn',
|
|
121
|
-
'object-curly-spacing': ['error', 'always'],
|
|
122
|
-
'space-in-parens': ['error', 'never'],
|
|
123
|
-
'array-bracket-spacing': ['error', 'never'],
|
|
124
|
-
'no-trailing-spaces': 'error',
|
|
125
|
-
'no-multi-spaces': 'error',
|
|
126
|
-
'no-lonely-if': 'error',
|
|
127
|
-
'key-spacing': 'error',
|
|
128
|
-
'no-useless-escape': 'error',
|
|
129
|
-
'no-self-assign': 'error',
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
files: ['**/*.js', '**/*.mjs'],
|
|
134
|
-
languageOptions: {
|
|
135
|
-
ecmaVersion: 'latest',
|
|
136
|
-
sourceType: 'module',
|
|
137
|
-
globals: {
|
|
138
|
-
// Browser globals
|
|
139
|
-
window: 'readonly',
|
|
140
|
-
document: 'readonly',
|
|
141
|
-
navigator: 'readonly',
|
|
142
|
-
localStorage: 'readonly',
|
|
143
|
-
sessionStorage: 'readonly',
|
|
144
|
-
console: 'readonly',
|
|
145
|
-
setTimeout: 'readonly',
|
|
146
|
-
clearTimeout: 'readonly',
|
|
147
|
-
setInterval: 'readonly',
|
|
148
|
-
clearInterval: 'readonly',
|
|
149
|
-
requestAnimationFrame: 'readonly',
|
|
150
|
-
cancelAnimationFrame: 'readonly',
|
|
151
|
-
fetch: 'readonly',
|
|
152
|
-
Headers: 'readonly',
|
|
153
|
-
HeadersInit: 'readonly',
|
|
154
|
-
Request: 'readonly',
|
|
155
|
-
RequestInfo: 'readonly',
|
|
156
|
-
Response: 'readonly',
|
|
157
|
-
FormData: 'readonly',
|
|
158
|
-
File: 'readonly',
|
|
159
|
-
Blob: 'readonly',
|
|
160
|
-
URL: 'readonly',
|
|
161
|
-
URLSearchParams: 'readonly',
|
|
162
|
-
AbortController: 'readonly',
|
|
163
|
-
AbortSignal: 'readonly',
|
|
164
|
-
ReadableStream: 'readonly',
|
|
165
|
-
WritableStream: 'readonly',
|
|
166
|
-
TransformStream: 'readonly',
|
|
167
|
-
TextEncoder: 'readonly',
|
|
168
|
-
TextDecoder: 'readonly',
|
|
169
|
-
HTMLElement: 'readonly',
|
|
170
|
-
HTMLButtonElement: 'readonly',
|
|
171
|
-
HTMLInputElement: 'readonly',
|
|
172
|
-
HTMLTextAreaElement: 'readonly',
|
|
173
|
-
HTMLAnchorElement: 'readonly',
|
|
174
|
-
XMLHttpRequest: 'readonly',
|
|
175
|
-
XMLHttpRequestBodyInit: 'readonly',
|
|
176
|
-
MouseEvent: 'readonly',
|
|
177
|
-
InputEvent: 'readonly',
|
|
178
|
-
MessageEvent: 'readonly',
|
|
179
|
-
EventListener: 'readonly',
|
|
180
|
-
MutationObserver: 'readonly',
|
|
181
|
-
NodeJS: 'readonly',
|
|
182
|
-
Window: 'readonly',
|
|
183
|
-
Document: 'readonly',
|
|
184
|
-
IDBDatabase: 'readonly',
|
|
185
|
-
indexedDB: 'readonly',
|
|
186
|
-
// Node.js globals
|
|
187
|
-
process: 'readonly',
|
|
188
|
-
Buffer: 'readonly',
|
|
189
|
-
__dirname: 'readonly',
|
|
190
|
-
__filename: 'readonly',
|
|
191
|
-
global: 'readonly',
|
|
192
|
-
module: 'readonly',
|
|
193
|
-
require: 'readonly',
|
|
194
|
-
exports: 'readonly',
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
rules: {
|
|
198
|
-
'no-useless-escape': 'off',
|
|
199
|
-
'no-self-assign': 'off',
|
|
200
|
-
'prefer-const': 'error',
|
|
201
|
-
'block-spacing': ['error', 'always'],
|
|
202
|
-
'keyword-spacing': [
|
|
203
|
-
'error',
|
|
204
|
-
{
|
|
205
|
-
before: true,
|
|
206
|
-
after: true,
|
|
207
|
-
},
|
|
208
|
-
],
|
|
209
|
-
'indent': ['error', 2, { SwitchCase: 1 }],
|
|
210
|
-
'linebreak-style': ['error', 'unix'],
|
|
211
|
-
'quotes': ['error', 'single'],
|
|
212
|
-
'semi': ['error', 'never'],
|
|
213
|
-
'comma-dangle': ['error', 'always-multiline'],
|
|
214
|
-
'no-console': 'warn',
|
|
215
|
-
'object-curly-spacing': ['error', 'always'],
|
|
216
|
-
'space-in-parens': ['error', 'never'],
|
|
217
|
-
'array-bracket-spacing': ['error', 'never'],
|
|
218
|
-
'no-trailing-spaces': 'error',
|
|
219
|
-
'no-multi-spaces': 'error',
|
|
220
|
-
'no-lonely-if': 'error',
|
|
221
|
-
'key-spacing': 'error',
|
|
222
|
-
'no-useless-escape': 'error',
|
|
223
|
-
'no-self-assign': 'error',
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
]
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { IdGenerator } from '@opentelemetry/sdk-trace-base'
|
|
2
|
-
import { SessionType } from './type'
|
|
3
|
-
import { getIdGenerator } from './sdk'
|
|
4
|
-
import {
|
|
5
|
-
MULTIPLAYER_TRACE_PREFIX_MAP,
|
|
6
|
-
} from './constants/constants.base'
|
|
7
|
-
|
|
8
|
-
export class SessionRecorderIdGenerator implements IdGenerator {
|
|
9
|
-
sessionShortId: string
|
|
10
|
-
sessionType?: SessionType
|
|
11
|
-
clientId?: string
|
|
12
|
-
private generateLongId: () => string
|
|
13
|
-
private generateShortId: () => string
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
this.generateLongId = getIdGenerator(16)
|
|
17
|
-
this.generateShortId = getIdGenerator(8)
|
|
18
|
-
this.sessionShortId = ''
|
|
19
|
-
this.clientId = ''
|
|
20
|
-
this.sessionType
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
generateTraceId(): string {
|
|
24
|
-
const traceId = this.generateLongId()
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
(
|
|
28
|
-
!this.sessionShortId
|
|
29
|
-
&& !this.clientId
|
|
30
|
-
)
|
|
31
|
-
|| !this.sessionType
|
|
32
|
-
) {
|
|
33
|
-
return traceId
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const sessionTypePrefix = MULTIPLAYER_TRACE_PREFIX_MAP[this.sessionType]
|
|
37
|
-
const prefix = `${sessionTypePrefix}${[SessionType.SESSION, SessionType.SESSION_CACHE].includes(this.sessionType) ? this.clientId : ''}${this.sessionShortId}`
|
|
38
|
-
const sessionTraceId = `${prefix}${traceId.substring(prefix.length, traceId.length)}`
|
|
39
|
-
|
|
40
|
-
return sessionTraceId
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
generateSpanId(): string {
|
|
44
|
-
return this.generateShortId()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setSessionId(
|
|
48
|
-
sessionShortId: string,
|
|
49
|
-
sessionType?: SessionType,
|
|
50
|
-
clientId?: string,
|
|
51
|
-
) {
|
|
52
|
-
if (
|
|
53
|
-
sessionType
|
|
54
|
-
&& !clientId
|
|
55
|
-
&& [
|
|
56
|
-
SessionType.SESSION,
|
|
57
|
-
SessionType.SESSION_CACHE,
|
|
58
|
-
].includes(sessionType)
|
|
59
|
-
) {
|
|
60
|
-
throw new Error(`Client ID is required for ${[
|
|
61
|
-
SessionType.SESSION,
|
|
62
|
-
SessionType.SESSION_CACHE,
|
|
63
|
-
].join(', ')} session types`)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this.sessionShortId = sessionShortId
|
|
67
|
-
this.sessionType = sessionType
|
|
68
|
-
this.clientId = clientId
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
isValidTraceId,
|
|
4
|
-
Context,
|
|
5
|
-
SpanKind,
|
|
6
|
-
Attributes,
|
|
7
|
-
Link,
|
|
8
|
-
} from '@opentelemetry/api'
|
|
9
|
-
import {
|
|
10
|
-
Sampler,
|
|
11
|
-
SamplingDecision,
|
|
12
|
-
SamplingResult,
|
|
13
|
-
} from '@opentelemetry/sdk-trace-base'
|
|
14
|
-
import {
|
|
15
|
-
ATTR_EXCEPTION_MESSAGE,
|
|
16
|
-
ATTR_EXCEPTION_STACKTRACE,
|
|
17
|
-
ATTR_EXCEPTION_TYPE,
|
|
18
|
-
} from '@opentelemetry/semantic-conventions'
|
|
19
|
-
import {
|
|
20
|
-
MULTIPLAYER_TRACE_DEBUG_PREFIX,
|
|
21
|
-
MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
|
|
22
|
-
MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
|
|
23
|
-
// MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX,
|
|
24
|
-
} from './constants/constants.base'
|
|
25
|
-
|
|
26
|
-
export class SessionRecorderTraceIdRatioBasedSampler implements Sampler {
|
|
27
|
-
private _upperBound: number
|
|
28
|
-
|
|
29
|
-
constructor(private readonly _ratio: number = 0) {
|
|
30
|
-
this._ratio = this._normalize(_ratio)
|
|
31
|
-
this._upperBound = Math.floor(this._ratio * 0xffffffff)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
shouldSample(
|
|
35
|
-
context: Context,
|
|
36
|
-
traceId: string,
|
|
37
|
-
spanName: string,
|
|
38
|
-
spanKind: SpanKind,
|
|
39
|
-
attributes: Attributes,
|
|
40
|
-
links: Link[],
|
|
41
|
-
): SamplingResult {
|
|
42
|
-
if (attributes[ATTR_EXCEPTION_MESSAGE] || attributes[ATTR_EXCEPTION_STACKTRACE] || attributes[ATTR_EXCEPTION_TYPE]) {
|
|
43
|
-
return {
|
|
44
|
-
decision: SamplingDecision.RECORD_AND_SAMPLED,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX)
|
|
50
|
-
|| traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
|
|
51
|
-
|| traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
|
|
52
|
-
// || traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_SESSION_CACHE_PREFIX)
|
|
53
|
-
) {
|
|
54
|
-
return {
|
|
55
|
-
decision: SamplingDecision.RECORD_AND_SAMPLED,
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let decision: SamplingDecision = SamplingDecision.NOT_RECORD
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
isValidTraceId(traceId)
|
|
63
|
-
&& this._accumulate(traceId) < this._upperBound
|
|
64
|
-
) {
|
|
65
|
-
decision = SamplingDecision.RECORD_AND_SAMPLED
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return { decision }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
toString(): string {
|
|
72
|
-
return `SessionRecorderTraceIdRatioBasedSampler{${this._ratio}}`
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private _normalize(ratio: number): number {
|
|
76
|
-
if (typeof ratio !== 'number' || isNaN(ratio)) return 0
|
|
77
|
-
return ratio >= 1 ? 1 : ratio <= 0 ? 0 : ratio
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private _accumulate(traceId: string): number {
|
|
81
|
-
let accumulation = 0
|
|
82
|
-
for (let i = 0; i < traceId.length / 8; i++) {
|
|
83
|
-
const pos = i * 8
|
|
84
|
-
const part = parseInt(traceId.slice(pos, pos + 8), 16)
|
|
85
|
-
accumulation = (accumulation ^ part) >>> 0
|
|
86
|
-
}
|
|
87
|
-
return accumulation
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { SessionType } from '../type'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @deprecated
|
|
5
|
-
*/
|
|
6
|
-
export const MULTIPLAYER_TRACE_DOC_PREFIX = 'd0cd0c'
|
|
7
|
-
|
|
8
|
-
export const MULTIPLAYER_TRACE_DEBUG_PREFIX = 'debdeb'
|
|
9
|
-
|
|
10
|
-
export const MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX = 'cdbcdb'
|
|
11
|
-
|
|
12
|
-
export const MULTIPLAYER_TRACE_SESSION_PREFIX = 'cdbcac'
|
|
13
|
-
|
|
14
|
-
export const MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX = 'debcdb'
|
|
15
|
-
|
|
16
|
-
export const MULTIPLAYER_TRACE_PREFIX_MAP = {
|
|
17
|
-
[SessionType.CONTINUOUS]: MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
|
|
18
|
-
[SessionType.SESSION_CACHE]: MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
|
|
19
|
-
[SessionType.SESSION]: MULTIPLAYER_TRACE_SESSION_PREFIX,
|
|
20
|
-
[SessionType.MANUAL]: MULTIPLAYER_TRACE_DEBUG_PREFIX,
|
|
21
|
-
} as Record<SessionType, string>
|
|
22
|
-
|
|
23
|
-
export const MULTIPLAYER_TRACE_DEBUG_SESSION_SHORT_ID_LENGTH = 8
|
|
24
|
-
|
|
25
|
-
export const MULTIPLAYER_TRACE_CLIENT_ID_LENGTH = 4
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @deprecated Use MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL instead
|
|
29
|
-
*/
|
|
30
|
-
export const MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_URL = 'https://api.multiplayer.app/v1/traces'
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @deprecated Use MULTIPLAYER_OTEL_DEFAULT_LOGS_EXPORTER_HTTP_URL instead
|
|
34
|
-
*/
|
|
35
|
-
export const MULTIPLAYER_OTEL_DEFAULT_LOGS_EXPORTER_URL = 'https://api.multiplayer.app/v1/logs'
|
|
36
|
-
|
|
37
|
-
export const MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL = 'https://otlp.multiplayer.app/v1/traces'
|
|
38
|
-
|
|
39
|
-
export const MULTIPLAYER_OTEL_DEFAULT_LOGS_EXPORTER_HTTP_URL = 'https://otlp.multiplayer.app/v1/logs'
|
|
40
|
-
|
|
41
|
-
export const MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_GRPC_URL = 'https://otlp.multiplayer.app:4317/v1/traces'
|
|
42
|
-
|
|
43
|
-
export const MULTIPLAYER_OTEL_DEFAULT_LOGS_EXPORTER_GRPC_URL = 'https://otlp.multiplayer.app:4317/v1/logs'
|
|
44
|
-
|
|
45
|
-
export const MULTIPLAYER_BASE_API_URL = 'https://api.multiplayer.app'
|
|
46
|
-
|
|
47
|
-
export const MULTIPLAYER_ATTRIBUTE_PREFIX = 'multiplayer.'
|
|
48
|
-
|
|
49
|
-
export const ATTR_MULTIPLAYER_WORKSPACE_ID = 'multiplayer.workspace.id'
|
|
50
|
-
|
|
51
|
-
export const ATTR_MULTIPLAYER_PROJECT_ID = 'multiplayer.project.id'
|
|
52
|
-
|
|
53
|
-
export const ATTR_MULTIPLAYER_PLATFORM_ID = 'multiplayer.platform.id'
|
|
54
|
-
|
|
55
|
-
export const ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE = 'multiplayer.session.auto-save'
|
|
56
|
-
|
|
57
|
-
export const ATTR_MULTIPLAYER_CONTINUOUS_SESSION_AUTO_SAVE_REASON = 'multiplayer.session.auto-save.reason'
|
|
58
|
-
|
|
59
|
-
export const ATTR_MULTIPLAYER_PLATFORM_NAME = 'multiplayer.platform.name'
|
|
60
|
-
|
|
61
|
-
export const ATTR_MULTIPLAYER_CLIENT_ID = 'multiplayer.client.id'
|
|
62
|
-
|
|
63
|
-
export const ATTR_MULTIPLAYER_INTEGRATION_ID = 'multiplayer.integration.id'
|
|
64
|
-
|
|
65
|
-
export const ATTR_MULTIPLAYER_SESSION_ID = 'multiplayer.session.id'
|
|
66
|
-
|
|
67
|
-
export const ATTR_MULTIPLAYER_SESSION_CLIENT_ID = 'multiplayer.session.client.id'
|
|
68
|
-
|
|
69
|
-
export const ATTR_MULTIPLAYER_HTTP_PROXY = 'multiplayer.http.proxy'
|
|
70
|
-
|
|
71
|
-
export const ATTR_MULTIPLAYER_HTTP_PROXY_TYPE = 'multiplayer.http.proxy.type'
|
|
72
|
-
|
|
73
|
-
export const ATTR_MULTIPLAYER_HTTP_REQUEST_BODY = 'multiplayer.http.request.body'
|
|
74
|
-
|
|
75
|
-
export const ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY = 'multiplayer.http.response.body'
|
|
76
|
-
|
|
77
|
-
export const ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS = 'multiplayer.http.request.headers'
|
|
78
|
-
|
|
79
|
-
export const ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS = 'multiplayer.http.response.headers'
|
|
80
|
-
|
|
81
|
-
export const ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY_ENCODING = 'multiplayer.http.response.body.encoding'
|
|
82
|
-
|
|
83
|
-
export const ATTR_MULTIPLAYER_RPC_REQUEST_MESSAGE = 'multiplayer.rpc.request.message'
|
|
84
|
-
|
|
85
|
-
export const ATTR_MULTIPLAYER_RPC_REQUEST_MESSAGE_ENCODING = 'multiplayer.rpc.request.message.encoding'
|
|
86
|
-
|
|
87
|
-
export const ATTR_MULTIPLAYER_RPC_RESPONSE_MESSAGE = 'multiplayer.rpc.response.message'
|
|
88
|
-
|
|
89
|
-
export const ATTR_MULTIPLAYER_GRPC_REQUEST_MESSAGE = 'multiplayer.rpc.grpc.request.message'
|
|
90
|
-
|
|
91
|
-
export const ATTR_MULTIPLAYER_GRPC_REQUEST_MESSAGE_ENCODING = 'multiplayer.rpc.request.message.encoding'
|
|
92
|
-
|
|
93
|
-
export const ATTR_MULTIPLAYER_GRPC_RESPONSE_MESSAGE = 'multiplayer.rpc.grpc.response.message'
|
|
94
|
-
|
|
95
|
-
export const ATTR_MULTIPLAYER_MESSAGING_MESSAGE_BODY = 'multiplayer.messaging.message.body'
|
|
96
|
-
|
|
97
|
-
export const ATTR_MULTIPLAYER_MESSAGING_MESSAGE_BODY_ENCODING = 'multiplayer.messaging.message.body.encoding'
|
|
98
|
-
|
|
99
|
-
export const ATTR_MULTIPLAYER_SESSION_RECORDER_VERSION = 'multiplayer.session-recorder.version'
|
|
100
|
-
|
|
101
|
-
export const ATTR_MULTIPLAYER_ISSUE_CUSTOM_HASH = 'multiplayer.issue.custom-hash'
|
|
102
|
-
|
|
103
|
-
export const ATTR_MULTIPLAYER_ISSUE_HASH = 'multiplayer.issue.hash'
|
|
104
|
-
|
|
105
|
-
export const ATTR_MULTIPLAYER_ISSUE_COMPONENT_HASH = 'multiplayer.issue.component-hash'
|
|
106
|
-
|
|
107
|
-
export const ATTR_MULTIPLAYER_ISSUE_TITLE_HASH = 'multiplayer.issue.title-hash'
|
|
108
|
-
|
|
109
|
-
export const ATTR_MULTIPLAYER_USER_HASH = 'multiplayer.user.hash'
|
|
110
|
-
|
|
111
|
-
export const MASK_PLACEHOLDER = '***MASKED***'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './constants.base'
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'
|
|
2
|
-
|
|
3
|
-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
|
|
4
|
-
import {
|
|
5
|
-
MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,
|
|
6
|
-
MULTIPLAYER_TRACE_DEBUG_PREFIX,
|
|
7
|
-
MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
|
|
8
|
-
MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX,
|
|
9
|
-
} from '../constants/constants.base'
|
|
10
|
-
|
|
11
|
-
export interface SessionRecorderBrowserTraceExporterConfig {
|
|
12
|
-
/** URL for the OTLP endpoint. Defaults to Multiplayer's default traces endpoint. */
|
|
13
|
-
url?: string
|
|
14
|
-
/** API key for authentication. Required. */
|
|
15
|
-
apiKey?: string
|
|
16
|
-
/** Additional headers to include in requests */
|
|
17
|
-
headers?: Record<string, string>
|
|
18
|
-
/** Request timeout in milliseconds */
|
|
19
|
-
timeoutMillis?: number
|
|
20
|
-
/** Whether to use keep-alive connections */
|
|
21
|
-
keepAlive?: boolean
|
|
22
|
-
/** Maximum number of concurrent requests */
|
|
23
|
-
concurrencyLimit?: number
|
|
24
|
-
/** Whether to use postMessage fallback for cross-origin requests */
|
|
25
|
-
usePostMessageFallback?: boolean
|
|
26
|
-
/** PostMessage type identifier */
|
|
27
|
-
postMessageType?: string
|
|
28
|
-
/** PostMessage target origin */
|
|
29
|
-
postMessageTargetOrigin?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Browser-specific trace exporter for Session Recorder
|
|
34
|
-
* Exports traces via HTTP to Multiplayer's OTLP endpoint with browser-specific optimizations
|
|
35
|
-
* Only exports spans with trace IDs starting with Multiplayer prefixes
|
|
36
|
-
*/
|
|
37
|
-
export class SessionRecorderBrowserTraceExporter implements SpanExporter {
|
|
38
|
-
private exporter: OTLPTraceExporter
|
|
39
|
-
private usePostMessage: boolean = false
|
|
40
|
-
private readonly postMessageType: string
|
|
41
|
-
private readonly postMessageTargetOrigin: string
|
|
42
|
-
private readonly config: SessionRecorderBrowserTraceExporterConfig
|
|
43
|
-
|
|
44
|
-
constructor(config: SessionRecorderBrowserTraceExporterConfig = {}) {
|
|
45
|
-
const {
|
|
46
|
-
url = MULTIPLAYER_OTEL_DEFAULT_TRACES_EXPORTER_HTTP_URL,
|
|
47
|
-
apiKey,
|
|
48
|
-
headers = {},
|
|
49
|
-
timeoutMillis = 30000,
|
|
50
|
-
keepAlive = true,
|
|
51
|
-
concurrencyLimit = 20,
|
|
52
|
-
postMessageType = 'MULTIPLAYER_SESSION_DEBUGGER_LIB',
|
|
53
|
-
postMessageTargetOrigin = '*',
|
|
54
|
-
} = config
|
|
55
|
-
|
|
56
|
-
this.config = {
|
|
57
|
-
...config,
|
|
58
|
-
url,
|
|
59
|
-
apiKey,
|
|
60
|
-
headers,
|
|
61
|
-
keepAlive,
|
|
62
|
-
timeoutMillis,
|
|
63
|
-
concurrencyLimit,
|
|
64
|
-
}
|
|
65
|
-
this.postMessageType = postMessageType
|
|
66
|
-
this.postMessageTargetOrigin = postMessageTargetOrigin
|
|
67
|
-
|
|
68
|
-
this.exporter = this._createExporter()
|
|
69
|
-
}
|
|
70
|
-
_export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
|
|
71
|
-
// Only proceed if there are filtered spans
|
|
72
|
-
if (spans.length === 0) {
|
|
73
|
-
resultCallback({ code: 0 })
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (this.usePostMessage) {
|
|
78
|
-
this.exportViaPostMessage(spans, resultCallback)
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this.exporter.export(spans, (result) => {
|
|
83
|
-
if (result.code === 0) {
|
|
84
|
-
resultCallback(result)
|
|
85
|
-
} else if (this.config.usePostMessageFallback) {
|
|
86
|
-
this.usePostMessage = true
|
|
87
|
-
this.exportViaPostMessage(spans, resultCallback)
|
|
88
|
-
} else {
|
|
89
|
-
resultCallback(result)
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
|
|
95
|
-
// Filter spans to only include those with Multiplayer trace prefixes
|
|
96
|
-
const filteredSpans = spans.filter((span) => {
|
|
97
|
-
const traceId = span.spanContext().traceId
|
|
98
|
-
return (
|
|
99
|
-
traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
|
|
100
|
-
traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
|
|
101
|
-
)
|
|
102
|
-
})
|
|
103
|
-
this._export(filteredSpans, resultCallback)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
exportBuffer(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
|
|
107
|
-
const filteredSpans = spans.filter((span) => {
|
|
108
|
-
const traceId = span.spanContext().traceId
|
|
109
|
-
return traceId.startsWith(MULTIPLAYER_TRACE_SESSION_CACHE_PREFIX)
|
|
110
|
-
})
|
|
111
|
-
this._export(filteredSpans, resultCallback)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
shutdown(): Promise<void> {
|
|
115
|
-
return this.exporter.shutdown()
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private exportViaPostMessage(spans: ReadableSpan[], resultCallback: (result: { code: number }) => void): void {
|
|
119
|
-
if (typeof window === 'undefined') {
|
|
120
|
-
resultCallback({ code: 1 })
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
window.postMessage(
|
|
126
|
-
{
|
|
127
|
-
action: 'traces',
|
|
128
|
-
type: this.postMessageType,
|
|
129
|
-
payload: spans.map((span) => this.serializeSpan(span)),
|
|
130
|
-
},
|
|
131
|
-
this.postMessageTargetOrigin,
|
|
132
|
-
)
|
|
133
|
-
resultCallback({ code: 0 })
|
|
134
|
-
} catch (e) {
|
|
135
|
-
resultCallback({ code: 1 })
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
serializeSpan(span: ReadableSpan): any {
|
|
140
|
-
const spanContext = span.spanContext()
|
|
141
|
-
const instrumentationScope: any =
|
|
142
|
-
// OTel SDK (modern)
|
|
143
|
-
(span as any).instrumentationScope ||
|
|
144
|
-
// Older SDKs
|
|
145
|
-
(span as any).instrumentationLibrary || { name: 'unknown', version: undefined, schemaUrl: undefined }
|
|
146
|
-
|
|
147
|
-
const normalizedScope = {
|
|
148
|
-
name: instrumentationScope?.name || 'unknown',
|
|
149
|
-
version: instrumentationScope?.version,
|
|
150
|
-
schemaUrl: instrumentationScope?.schemaUrl,
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
_spanContext: spanContext,
|
|
154
|
-
traceId: spanContext.traceId,
|
|
155
|
-
spanId: spanContext.spanId,
|
|
156
|
-
name: span.name,
|
|
157
|
-
kind: span.kind,
|
|
158
|
-
links: span.links,
|
|
159
|
-
ended: span.ended,
|
|
160
|
-
events: span.events,
|
|
161
|
-
status: span.status,
|
|
162
|
-
endTime: span.endTime,
|
|
163
|
-
startTime: span.startTime,
|
|
164
|
-
duration: span.duration,
|
|
165
|
-
attributes: span.attributes,
|
|
166
|
-
parentSpanId: span.parentSpanContext?.spanId,
|
|
167
|
-
droppedAttributesCount: span.droppedAttributesCount,
|
|
168
|
-
droppedEventsCount: span.droppedEventsCount,
|
|
169
|
-
droppedLinksCount: span.droppedLinksCount,
|
|
170
|
-
instrumentationScope: normalizedScope,
|
|
171
|
-
resource: {
|
|
172
|
-
attributes: span.resource.attributes,
|
|
173
|
-
asyncAttributesPending: span.resource.asyncAttributesPending,
|
|
174
|
-
},
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private _createExporter(): OTLPTraceExporter {
|
|
179
|
-
return new OTLPTraceExporter({
|
|
180
|
-
url: this.config.url,
|
|
181
|
-
headers: {
|
|
182
|
-
'Content-Type': 'application/json',
|
|
183
|
-
...(this.config.apiKey ? { Authorization: this.config.apiKey } : {}),
|
|
184
|
-
...(this.config.headers || {}),
|
|
185
|
-
},
|
|
186
|
-
timeoutMillis: this.config.timeoutMillis,
|
|
187
|
-
keepAlive: this.config.keepAlive,
|
|
188
|
-
concurrencyLimit: this.config.concurrencyLimit,
|
|
189
|
-
})
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
setApiKey(apiKey: string): void {
|
|
193
|
-
this.config.apiKey = apiKey
|
|
194
|
-
this.exporter = this._createExporter()
|
|
195
|
-
}
|
|
196
|
-
}
|