Skip to content

Commit 225dc46

Browse files
committed
Generate RUM resources from resources spans
1 parent 4cc417e commit 225dc46

File tree

3 files changed

+226
-6
lines changed

3 files changed

+226
-6
lines changed

packages/electron/src/domain/rum/convertSpans.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { Observable } from '@datadog/browser-core'
2-
import { generateUUID, ErrorHandling } from '@datadog/browser-core'
3-
import type { RumErrorEvent } from '@datadog/browser-rum-core'
2+
import { ResourceType, generateUUID, ErrorHandling } from '@datadog/browser-core'
3+
import type { RumErrorEvent, RumResourceEvent } from '@datadog/browser-rum-core'
44
import { RumEventType } from '@datadog/browser-rum-core'
55
import type { Trace } from '../trace/trace'
6+
import { createIdentifier } from '../trace/id'
67
import type { CollectedRumEvent } from './events'
78

89
export function startConvertSpanToRumEvent(
@@ -26,6 +27,26 @@ export function startConvertSpanToRumEvent(
2627
}
2728
onRumEventObservable.notify({ event: rumError as RumErrorEvent, source: 'main-process' })
2829
}
30+
if (span.name === 'http.request') {
31+
const rumResource: Partial<RumResourceEvent> = {
32+
type: RumEventType.RESOURCE,
33+
date: span.start / 1e6,
34+
resource: {
35+
id: generateUUID(),
36+
duration: span.duration,
37+
type: ResourceType.NATIVE,
38+
method: span.meta['http.method'],
39+
status_code: span.meta['http.status'],
40+
url: span.meta['http.url'],
41+
},
42+
_dd: {
43+
trace_id: createIdentifier(span.trace_id, 16).toString(10),
44+
span_id: createIdentifier(span.span_id, 16).toString(10),
45+
format_version: 2,
46+
},
47+
}
48+
onRumEventObservable.notify({ event: rumResource as RumResourceEvent, source: 'main-process' })
49+
}
2950
})
3051
})
3152
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Duplicated from https://github.com/DataDog/dd-trace-js/blob/f40cdf96/packages/dd-trace/src/id.js
2+
3+
'use strict'
4+
5+
import crypto from 'node:crypto'
6+
7+
const UINT_MAX = 4_294_967_296
8+
9+
const data = new Uint8Array(8 * 8192)
10+
const zeroId = new Uint8Array(8)
11+
12+
const map = Array.prototype.map
13+
const pad = (byte: number) => `${byte < 16 ? '0' : ''}${byte.toString(16)}`
14+
15+
let batch = 0
16+
17+
// Internal representation of a trace or span ID.
18+
// eslint-disable-next-line no-restricted-syntax
19+
class Identifier {
20+
_buffer: any[] | Uint8Array<ArrayBuffer>
21+
22+
constructor(value: string, radix = 16) {
23+
this._buffer = radix === 16 ? createBuffer(value) : fromString(value, radix)
24+
}
25+
26+
toString(radix = 16) {
27+
return radix === 16 ? toHexString(this._buffer) : toNumberString(this._buffer, radix)
28+
}
29+
30+
toBigInt() {
31+
return Buffer.from(this._buffer).readBigUInt64BE(0)
32+
}
33+
34+
toBuffer() {
35+
return this._buffer
36+
}
37+
38+
toArray() {
39+
if (this._buffer.length === 8) {
40+
return this._buffer
41+
}
42+
return this._buffer.slice(-8)
43+
}
44+
45+
toJSON() {
46+
return this.toString()
47+
}
48+
49+
equals(other: Identifier) {
50+
const length = this._buffer.length
51+
const otherLength = other._buffer.length
52+
53+
// Only compare the bytes available in both IDs.
54+
for (let i = length, j = otherLength; i >= 0 && j >= 0; i--, j--) {
55+
if (this._buffer[i] !== other._buffer[j]) {
56+
return false
57+
}
58+
}
59+
60+
return true
61+
}
62+
}
63+
64+
// Create a buffer, using an optional hexadecimal value if provided.
65+
function createBuffer(value: string) {
66+
if (value === '0') {
67+
return zeroId
68+
}
69+
if (!value) {
70+
return pseudoRandom()
71+
}
72+
73+
const size = Math.ceil(value.length / 16) * 16
74+
const bytes = size / 2
75+
const buffer = []
76+
77+
value = value.padStart(size, '0')
78+
79+
for (let i = 0; i < bytes; i++) {
80+
buffer[i] = Number.parseInt(value.slice(i * 2, i * 2 + 2), 16)
81+
}
82+
83+
return buffer
84+
}
85+
86+
// Convert a numerical string to a buffer using the specified radix.
87+
function fromString(str: string, raddix: number) {
88+
const buffer = new Array(8)
89+
const len = str.length
90+
91+
let pos = 0
92+
let high = 0
93+
let low = 0
94+
95+
if (str[0] === '-') {
96+
pos++
97+
}
98+
99+
const sign = pos
100+
101+
while (pos < len) {
102+
const chr = Number.parseInt(str[pos++], raddix)
103+
104+
if (!(chr >= 0)) {
105+
break
106+
} // NaN
107+
108+
low = low * raddix + chr
109+
high = high * raddix + Math.floor(low / UINT_MAX)
110+
low %= UINT_MAX
111+
}
112+
113+
if (sign) {
114+
// eslint-disable-next-line no-bitwise
115+
high = ~high
116+
117+
if (low) {
118+
low = UINT_MAX - low
119+
} else {
120+
high++
121+
}
122+
}
123+
124+
writeUInt32BE(buffer, high, 0)
125+
writeUInt32BE(buffer, low, 4)
126+
127+
return buffer
128+
}
129+
130+
// Convert a buffer to a numerical string.
131+
function toNumberString(buffer: any, radix: number) {
132+
let high = readInt32(buffer, buffer.length - 8)
133+
let low = readInt32(buffer, buffer.length - 4)
134+
let str = ''
135+
136+
radix = radix || 10
137+
138+
while (1) {
139+
const mod = (high % radix) * UINT_MAX + low
140+
141+
high = Math.floor(high / radix)
142+
low = Math.floor(mod / radix)
143+
str = (mod % radix).toString(radix) + str
144+
145+
if (!high && !low) {
146+
break
147+
}
148+
}
149+
150+
return str
151+
}
152+
153+
// Convert a buffer to a hexadecimal string.
154+
function toHexString(buffer: any) {
155+
return map.call(buffer, pad).join('')
156+
}
157+
158+
// Simple pseudo-random 64-bit ID generator.
159+
function pseudoRandom() {
160+
if (batch === 0) {
161+
crypto.randomFillSync(data)
162+
}
163+
164+
batch = (batch + 1) % 8192
165+
166+
const offset = batch * 8
167+
168+
return [
169+
data[offset] & 0x7f, // only positive int64,
170+
data[offset + 1],
171+
data[offset + 2],
172+
data[offset + 3],
173+
data[offset + 4],
174+
data[offset + 5],
175+
data[offset + 6],
176+
data[offset + 7],
177+
]
178+
}
179+
180+
// Read a buffer to unsigned integer bytes.
181+
function readInt32(buffer: any, offset: any) {
182+
return buffer[offset + 0] * 16_777_216 + (buffer[offset + 1] << 16) + (buffer[offset + 2] << 8) + buffer[offset + 3]
183+
}
184+
185+
// Write unsigned integer bytes to a buffer.
186+
function writeUInt32BE(buffer: any, value: any, offset: any) {
187+
buffer[3 + offset] = value & 255
188+
value >>= 8
189+
buffer[2 + offset] = value & 255
190+
value >>= 8
191+
buffer[1 + offset] = value & 255
192+
value >>= 8
193+
buffer[0 + offset] = value & 255
194+
}
195+
196+
export function createIdentifier(value: any, radix: number) {
197+
return new Identifier(value, radix)
198+
}

packages/electron/src/domain/trace/traceAgent.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { decode } from '@msgpack/msgpack'
55
import type { Hooks } from '../../hooks'
66
import type { Trace } from './trace'
77
import tracer from './tracer'
8+
import { createIdentifier } from './id'
89

910
export function createDdTraceAgent(onTraceObservable: Observable<Trace>, hooks: Hooks) {
1011
const server = createServer()
@@ -34,11 +35,11 @@ export function createDdTraceAgent(onTraceObservable: Observable<Trace>, hooks:
3435
const filteredTrace = trace
3536
.filter((span) => !isSdkRequest(span))
3637
.map((span) => ({
37-
// rewrite id
38+
// rewrite ids
3839
...span,
39-
trace_id: Number(span.trace_id)?.toString(16),
40-
span_id: Number(span.span_id)?.toString(16),
41-
parent_id: Number(span.parent_id)?.toString(16),
40+
trace_id: createIdentifier(`${span.trace_id as number}`, 10).toString(16),
41+
span_id: createIdentifier(`${span.span_id as number}`, 10).toString(16),
42+
parent_id: createIdentifier(`${span.parent_id as number}`, 10).toString(16),
4243
meta: {
4344
...span.meta,
4445
'_dd.application.id': defaultRumEventAttributes.application!.id,

0 commit comments

Comments
 (0)