dd-trace 5.103.0 → 5.105.0
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/LICENSE-3rdparty.csv +90 -102
- package/index.d.ts +107 -6
- package/package.json +18 -17
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +15 -2
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/cucumber.js +181 -35
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -67
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +31 -229
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/ioredis.js +3 -3
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +390 -183
- package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/memcached.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +399 -107
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
- package/packages/datadog-instrumentations/src/mongoose.js +10 -12
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pg.js +1 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +537 -297
- package/packages/datadog-instrumentations/src/router.js +80 -34
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +223 -45
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-graphql/src/utils.js +4 -1
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +35 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +311 -35
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-redis/src/index.js +37 -2
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +24 -20
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +49 -21
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/reporter.js +24 -11
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
- package/packages/dd-trace/src/config/supported-configurations.json +36 -8
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +57 -42
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/request.js +3 -1
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +10 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/log/writer.js +3 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/noop/span.js +3 -1
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -46
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +48 -8
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/test.js +318 -13
- package/packages/dd-trace/src/plugins/util/web.js +89 -64
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
- package/vendor/dist/opentracing/LICENSE +0 -201
- package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
- package/vendor/dist/opentracing/constants.d.ts +0 -61
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
- package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
- package/vendor/dist/opentracing/functions.d.ts +0 -20
- package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
- package/vendor/dist/opentracing/index.d.ts +0 -12
- package/vendor/dist/opentracing/index.js +0 -1
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
- package/vendor/dist/opentracing/noop.d.ts +0 -8
- package/vendor/dist/opentracing/reference.d.ts +0 -33
- package/vendor/dist/opentracing/span.d.ts +0 -147
- package/vendor/dist/opentracing/span_context.d.ts +0 -26
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
- package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
- package/vendor/dist/opentracing/tracer.d.ts +0 -127
|
@@ -4,11 +4,9 @@ const zlib = require('zlib')
|
|
|
4
4
|
const pkg = require('../../../../package.json')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const request = require('../exporters/common/request')
|
|
7
|
-
const {
|
|
7
|
+
const { encode: encodeMsgpack } = require('../msgpack')
|
|
8
8
|
const { getAgentUrl } = require('../agent/url')
|
|
9
9
|
|
|
10
|
-
const msgpack = new MsgpackEncoder()
|
|
11
|
-
|
|
12
10
|
function makeRequest (data, url, cb) {
|
|
13
11
|
const options = {
|
|
14
12
|
path: '/v0.1/pipeline_stats',
|
|
@@ -39,7 +37,7 @@ class DataStreamsWriter {
|
|
|
39
37
|
log.debug('Maximum number of active requests reached. Payload discarded: %j', payload)
|
|
40
38
|
return
|
|
41
39
|
}
|
|
42
|
-
const encodedPayload =
|
|
40
|
+
const encodedPayload = encodeMsgpack(payload)
|
|
43
41
|
|
|
44
42
|
zlib.gzip(encodedPayload, { level: 1 }, (err, compressedData) => {
|
|
45
43
|
if (err) {
|
|
@@ -6,7 +6,7 @@ module.exports = {
|
|
|
6
6
|
templateRequiresEvaluation,
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const identifierRegex = /^[
|
|
9
|
+
const identifierRegex = /^(@[\w$]+|[a-zA-Z_$][\w$]*)$/
|
|
10
10
|
|
|
11
11
|
// The following identifiers have purposefully not been included in this list:
|
|
12
12
|
// - The reserved words `this` and `super` as they can have valid use cases as `ref` values
|
|
@@ -99,14 +99,11 @@ function compile (node) {
|
|
|
99
99
|
? `(typeof ${compile(value[0])} === '${value[1]}')` // TODO: Is parenthesizing necessary?
|
|
100
100
|
: `Function.prototype[Symbol.hasInstance].call(${assertIdentifier(value[1])}, ${compile(value[0])})`
|
|
101
101
|
} else if (type === 'ref') {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return '$dd_key'
|
|
106
|
-
} else if (value === '@value') {
|
|
107
|
-
return '$dd_value'
|
|
102
|
+
const refValue = assertIdentifier(value)
|
|
103
|
+
if (refValue.startsWith('@')) {
|
|
104
|
+
return `$dd_${refValue.slice(1)}`
|
|
108
105
|
}
|
|
109
|
-
return
|
|
106
|
+
return refValue
|
|
110
107
|
} else if (Array.isArray(value)) {
|
|
111
108
|
const args = value.map(compile)
|
|
112
109
|
switch (type) {
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const getConfig = require('../config')
|
|
4
|
-
const { MsgpackChunk
|
|
4
|
+
const { MsgpackChunk } = require('../msgpack')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const { normalizeSpan } = require('./tags-processors')
|
|
7
7
|
|
|
8
8
|
const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
|
|
9
|
+
// Values longer than this byte threshold skip the `_stringMap` lookup and
|
|
10
|
+
// emit through `bytes.write` directly. Hashing a multi-KiB string for
|
|
11
|
+
// `Map.get` costs more than the cache hit saves on the inputs that produce
|
|
12
|
+
// strings this long (events JSON, stack traces, large query bodies) — they
|
|
13
|
+
// are unique per span, so the cache hit rate stays near zero anyway.
|
|
14
|
+
const STRING_CACHE_BYPASS_LIMIT = 1024
|
|
9
15
|
|
|
10
16
|
// Pre-encoded static keys + value-prefix bytes; the hot encode loop emits
|
|
11
17
|
// each via one Uint8Array.set instead of routing through the string cache.
|
|
@@ -43,6 +49,17 @@ const KEY_SERVICE = buildKey('service')
|
|
|
43
49
|
const KEY_ERROR = buildKey('error')
|
|
44
50
|
const KEY_START = buildKey('start')
|
|
45
51
|
const KEY_DURATION = buildKey('duration')
|
|
52
|
+
|
|
53
|
+
// Fused `[KEY_ERROR, fixint]` payloads. `error` is `0` or `1` on nearly every
|
|
54
|
+
// span (the boolean-shaped tracer field collapsed onto a single byte). One
|
|
55
|
+
// `bytes.set` writes the key and the value together instead of routing the
|
|
56
|
+
// value through `writeIntOrFloat`'s reserve + branch table.
|
|
57
|
+
const KEY_ERROR_0 = Buffer.concat([KEY_ERROR, Buffer.from([0x00])])
|
|
58
|
+
const KEY_ERROR_1 = Buffer.concat([KEY_ERROR, Buffer.from([0x01])])
|
|
59
|
+
// `[KEY_START, 0xCF]` — `start` is always a nanosecond timestamp ≥ 2³², so
|
|
60
|
+
// the msgpack u64 type byte is statically known and fuses with the key. The
|
|
61
|
+
// 8-byte value is written inline right after.
|
|
62
|
+
const KEY_START_PREFIX = buildKeyWithPrefix('start', 0xCF)
|
|
46
63
|
const KEY_SPAN_EVENTS = buildKey('span_events')
|
|
47
64
|
const KEY_META_STRUCT = buildKey('meta_struct')
|
|
48
65
|
const KEY_TRACE_ID_PREFIX = buildKeyWithPrefix('trace_id', 0xCF)
|
|
@@ -97,6 +114,8 @@ const ATTR_PAYLOAD_BOOL_FALSE = Buffer.concat([ATTR_PREFIX_BOOL, Buffer.from([0x
|
|
|
97
114
|
function formatSpanWithLegacyEvents (span) {
|
|
98
115
|
span = normalizeSpan(span)
|
|
99
116
|
if (span.span_events) {
|
|
117
|
+
// TODO: this is currently a main cost driver. By unifying it with the formatter
|
|
118
|
+
// it should be possible to improve performance significantly overall.
|
|
100
119
|
span.meta.events = stringifySpanEvents(span.span_events)
|
|
101
120
|
// `= undefined` over `delete` to keep the span's hidden class — `delete`
|
|
102
121
|
// would push every event-bearing span into V8 dictionary mode.
|
|
@@ -201,8 +220,12 @@ function escapeJsonString (value) {
|
|
|
201
220
|
return '"' + value + '"'
|
|
202
221
|
}
|
|
203
222
|
|
|
223
|
+
function lazyEncodedTraceBufferLogger (bytes, start, end) {
|
|
224
|
+
const hex = bytes.buffer.subarray(start, end).toString('hex').match(/../g).join(' ')
|
|
225
|
+
return `Adding encoded trace to buffer: ${hex}`
|
|
226
|
+
}
|
|
227
|
+
|
|
204
228
|
class AgentEncoder {
|
|
205
|
-
#msgpack = new MsgpackEncoder()
|
|
206
229
|
#limit
|
|
207
230
|
#writer
|
|
208
231
|
#config
|
|
@@ -239,11 +262,7 @@ class AgentEncoder {
|
|
|
239
262
|
|
|
240
263
|
if (this.#debugEncoding) {
|
|
241
264
|
const end = bytes.length
|
|
242
|
-
|
|
243
|
-
log.debug(() => {
|
|
244
|
-
const hex = bytes.buffer.subarray(start, end).toString('hex').match(/../g).join(' ')
|
|
245
|
-
return `Adding encoded trace to buffer: ${hex}`
|
|
246
|
-
})
|
|
265
|
+
log.debug(lazyEncodedTraceBufferLogger, bytes, start, end)
|
|
247
266
|
}
|
|
248
267
|
|
|
249
268
|
// Soft limit overshoot is fine — the agent caps at 50 MB.
|
|
@@ -269,7 +288,7 @@ class AgentEncoder {
|
|
|
269
288
|
}
|
|
270
289
|
|
|
271
290
|
_encode (bytes, trace) {
|
|
272
|
-
|
|
291
|
+
bytes.writeArrayPrefix(trace)
|
|
273
292
|
|
|
274
293
|
const formatSpan = this.#formatSpan
|
|
275
294
|
const stringMap = this._stringMap
|
|
@@ -286,10 +305,10 @@ class AgentEncoder {
|
|
|
286
305
|
if (span.span_events) mapSize++
|
|
287
306
|
|
|
288
307
|
// Pre-fetch the cached string entries up front and fuse the map prefix,
|
|
289
|
-
// optional `type`, three IDs,
|
|
308
|
+
// optional `type`, three IDs, `name` / `resource` / `service`, and —
|
|
309
|
+
// in the common fixint-error case — the error/start/duration_key
|
|
290
310
|
// emissions into a single `bytes.reserve` + sequential native writes.
|
|
291
|
-
// Replaces
|
|
292
|
-
// header, type, three IDs, three strings) with one.
|
|
311
|
+
// Replaces up to ten separate `bytes.reserve` calls per span with one.
|
|
293
312
|
let typeEntry
|
|
294
313
|
if (span.type) {
|
|
295
314
|
typeEntry = stringMap[span.type] ?? this._cacheString(span.type)
|
|
@@ -301,8 +320,17 @@ class AgentEncoder {
|
|
|
301
320
|
const resourceLen = resourceEntry.length
|
|
302
321
|
const serviceLen = serviceEntry.length
|
|
303
322
|
|
|
304
|
-
//
|
|
305
|
-
//
|
|
323
|
+
// Almost every span carries `error: 0` or `error: 1` AND a nanosecond
|
|
324
|
+
// `start` timestamp ≥ 2³² (so `start` always encodes as a u64). When
|
|
325
|
+
// both hold, the block fuses error key+value, the start key + 0xCF
|
|
326
|
+
// type byte + 8-byte timestamp, and the duration key into the per-span
|
|
327
|
+
// reserve. The fallback path covers synthetic/test inputs with small
|
|
328
|
+
// starts and rare non-binary error flags by keeping per-field emits so
|
|
329
|
+
// each integer picks the shortest msgpack encoding.
|
|
330
|
+
const errorIsFixint = span.error === 0 || span.error === 1
|
|
331
|
+
const startFitsU64 = span.start >= 0x1_00_00_00_00
|
|
332
|
+
const fuseTail = errorIsFixint && startFitsU64
|
|
333
|
+
|
|
306
334
|
let blockSize = 1 +
|
|
307
335
|
KEY_TRACE_ID_PREFIX.length + 8 +
|
|
308
336
|
KEY_SPAN_ID_PREFIX.length + 8 +
|
|
@@ -311,6 +339,9 @@ class AgentEncoder {
|
|
|
311
339
|
KEY_RESOURCE.length + resourceLen +
|
|
312
340
|
KEY_SERVICE.length + serviceLen
|
|
313
341
|
if (typeEntry) blockSize += KEY_TYPE.length + typeEntry.length
|
|
342
|
+
if (fuseTail) {
|
|
343
|
+
blockSize += KEY_ERROR_0.length + KEY_START_PREFIX.length + 8 + KEY_DURATION.length
|
|
344
|
+
}
|
|
314
345
|
|
|
315
346
|
const blockOffset = bytes.length
|
|
316
347
|
bytes.reserve(blockSize)
|
|
@@ -343,13 +374,35 @@ class AgentEncoder {
|
|
|
343
374
|
target.set(KEY_SERVICE, cursor)
|
|
344
375
|
cursor += KEY_SERVICE.length
|
|
345
376
|
target.set(serviceEntry, cursor)
|
|
377
|
+
cursor += serviceLen
|
|
378
|
+
|
|
379
|
+
if (fuseTail) {
|
|
380
|
+
target.set(span.error === 0 ? KEY_ERROR_0 : KEY_ERROR_1, cursor)
|
|
381
|
+
cursor += KEY_ERROR_0.length
|
|
346
382
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
383
|
+
target.set(KEY_START_PREFIX, cursor)
|
|
384
|
+
cursor += KEY_START_PREFIX.length
|
|
385
|
+
// Inline u64 write so the 0xCF type byte and the 8 timestamp bytes
|
|
386
|
+
// share the same reserve as the keys.
|
|
387
|
+
target.writeUInt32BE((span.start / 0x1_00_00_00_00) >>> 0, cursor)
|
|
388
|
+
target.writeUInt32BE(span.start >>> 0, cursor + 4)
|
|
389
|
+
cursor += 8
|
|
390
|
+
|
|
391
|
+
target.set(KEY_DURATION, cursor)
|
|
392
|
+
} else {
|
|
393
|
+
if (span.error === 0) {
|
|
394
|
+
bytes.set(KEY_ERROR_0)
|
|
395
|
+
} else if (span.error === 1) {
|
|
396
|
+
bytes.set(KEY_ERROR_1)
|
|
397
|
+
} else {
|
|
398
|
+
bytes.set(KEY_ERROR)
|
|
399
|
+
bytes.writeIntOrFloat(span.error)
|
|
400
|
+
}
|
|
401
|
+
bytes.set(KEY_START)
|
|
402
|
+
bytes.writeIntOrFloat(span.start)
|
|
403
|
+
bytes.set(KEY_DURATION)
|
|
404
|
+
}
|
|
405
|
+
bytes.writeIntOrFloat(span.duration)
|
|
353
406
|
|
|
354
407
|
this.#encodeMetaEntries(bytes, KEY_META_PREFIX, span.meta)
|
|
355
408
|
this.#encodeMetaEntries(bytes, KEY_METRICS_PREFIX, span.metrics)
|
|
@@ -390,34 +443,14 @@ class AgentEncoder {
|
|
|
390
443
|
|
|
391
444
|
_reset () {
|
|
392
445
|
this._traceCount = 0
|
|
393
|
-
this._traceBytes.
|
|
446
|
+
this._traceBytes.reset()
|
|
394
447
|
this._stringCount = 0
|
|
395
|
-
this._stringBytes.
|
|
448
|
+
this._stringBytes.reset()
|
|
396
449
|
this._stringMap = Object.create(null)
|
|
397
450
|
|
|
398
451
|
this._cacheString('')
|
|
399
452
|
}
|
|
400
453
|
|
|
401
|
-
_encodeBuffer (bytes, buffer) {
|
|
402
|
-
this.#msgpack.encodeBin(bytes, buffer)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
_encodeBool (bytes, value) {
|
|
406
|
-
this.#msgpack.encodeBoolean(bytes, value)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
_encodeArrayPrefix (bytes, value) {
|
|
410
|
-
this.#msgpack.encodeArrayPrefix(bytes, value)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
_encodeMapPrefix (bytes, keysLength) {
|
|
414
|
-
this.#msgpack.encodeMapPrefix(bytes, keysLength)
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
_encodeByte (bytes, value) {
|
|
418
|
-
this.#msgpack.encodeByte(bytes, value)
|
|
419
|
-
}
|
|
420
|
-
|
|
421
454
|
// TODO: Use BigInt instead.
|
|
422
455
|
_encodeId (bytes, identifier) {
|
|
423
456
|
const idBuffer = identifier.toBuffer()
|
|
@@ -438,18 +471,6 @@ class AgentEncoder {
|
|
|
438
471
|
target[offset + 8] = idBuffer[start + 7]
|
|
439
472
|
}
|
|
440
473
|
|
|
441
|
-
_encodeNumber (bytes, value) {
|
|
442
|
-
this.#msgpack.encodeNumber(bytes, value)
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
_encodeInteger (bytes, value) {
|
|
446
|
-
this.#msgpack.encodeInteger(bytes, value)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
_encodeLong (bytes, value) {
|
|
450
|
-
this.#msgpack.encodeLong(bytes, value)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
474
|
// Single pass: reserve the count slot, encode entries while counting, patch the count.
|
|
454
475
|
// Subclasses (0.5, CI visibility encoders) inherit this; the wire stays on float64
|
|
455
476
|
// for numeric values to keep their established trace / events intake unchanged.
|
|
@@ -467,7 +488,7 @@ class AgentEncoder {
|
|
|
467
488
|
count++
|
|
468
489
|
} else if (typeof entryValue === 'number') {
|
|
469
490
|
this._encodeString(bytes, key)
|
|
470
|
-
|
|
491
|
+
bytes.writeFloat(entryValue)
|
|
471
492
|
count++
|
|
472
493
|
}
|
|
473
494
|
}
|
|
@@ -480,6 +501,10 @@ class AgentEncoder {
|
|
|
480
501
|
}
|
|
481
502
|
|
|
482
503
|
_encodeString (bytes, value = '') {
|
|
504
|
+
if (value.length > STRING_CACHE_BYPASS_LIMIT) {
|
|
505
|
+
bytes.write(value)
|
|
506
|
+
return
|
|
507
|
+
}
|
|
483
508
|
const entry = this._stringMap[value] ?? this._cacheString(value)
|
|
484
509
|
const length = entry.length
|
|
485
510
|
const offset = bytes.length
|
|
@@ -540,6 +565,17 @@ class AgentEncoder {
|
|
|
540
565
|
const writeOffset = bytes.length
|
|
541
566
|
|
|
542
567
|
if (typeof entryValue === 'string') {
|
|
568
|
+
if (entryValue.length > STRING_CACHE_BYPASS_LIMIT) {
|
|
569
|
+
// Long values (events JSON, stack traces, large query bodies) are
|
|
570
|
+
// unique per span; hashing them for the cache lookup costs more
|
|
571
|
+
// than the lookup ever recovers. Emit the key from the cache and
|
|
572
|
+
// stream the value directly.
|
|
573
|
+
bytes.reserve(keyEntryLen)
|
|
574
|
+
bytes.buffer.set(keyEntry, writeOffset)
|
|
575
|
+
bytes.write(entryValue)
|
|
576
|
+
count++
|
|
577
|
+
continue
|
|
578
|
+
}
|
|
543
579
|
const valueEntry = stringMap[entryValue] ?? this._cacheString(entryValue)
|
|
544
580
|
const valueEntryLen = valueEntry.length
|
|
545
581
|
bytes.reserve(keyEntryLen + valueEntryLen)
|
|
@@ -547,9 +583,22 @@ class AgentEncoder {
|
|
|
547
583
|
target.set(keyEntry, writeOffset)
|
|
548
584
|
target.set(valueEntry, writeOffset + keyEntryLen)
|
|
549
585
|
} else {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
586
|
+
// Speculate that `entryValue` is a positive fixint (0..127): one
|
|
587
|
+
// reserve covers both the key and the value. The metrics map (sample
|
|
588
|
+
// rate, priority, `_dd.measured`, attribute counts) is mostly small
|
|
589
|
+
// unsigned integers, so the speculation wins on every entry that
|
|
590
|
+
// doesn't go through the slow `writeIntOrFloat` dispatch chain.
|
|
591
|
+
bytes.reserve(keyEntryLen + 1)
|
|
592
|
+
const target = bytes.buffer
|
|
593
|
+
target.set(keyEntry, writeOffset)
|
|
594
|
+
if (entryValue === (entryValue & 0x7F)) {
|
|
595
|
+
target[writeOffset + keyEntryLen] = entryValue
|
|
596
|
+
} else {
|
|
597
|
+
// Speculation missed; rewind the speculative byte and route the
|
|
598
|
+
// value through the full encoder so it picks the right type.
|
|
599
|
+
bytes.length = writeOffset + keyEntryLen
|
|
600
|
+
bytes.writeIntOrFloat(entryValue)
|
|
601
|
+
}
|
|
553
602
|
}
|
|
554
603
|
count++
|
|
555
604
|
}
|
|
@@ -589,41 +638,6 @@ class AgentEncoder {
|
|
|
589
638
|
return offset + 8
|
|
590
639
|
}
|
|
591
640
|
|
|
592
|
-
/**
|
|
593
|
-
* Emit `value` as the smallest valid msgpack number encoding: compact
|
|
594
|
-
* unsigned/signed int when integer, float64 otherwise. Unlike
|
|
595
|
-
* `MsgpackEncoder#encodeNumber`, NaN keeps its float64 bits instead of
|
|
596
|
-
* coercing to fixint 0.
|
|
597
|
-
*
|
|
598
|
-
* Underscore-protected so the 0.5 subclass can call it from its own
|
|
599
|
-
* `_encode` / `_encodeMap` overrides.
|
|
600
|
-
*
|
|
601
|
-
* @param {MsgpackChunk} bytes
|
|
602
|
-
* @param {number} value
|
|
603
|
-
*/
|
|
604
|
-
_encodeIntOrFloat (bytes, value) {
|
|
605
|
-
// Fast path: positive fixint (0..127). `value === (value & 0x7F)` is true
|
|
606
|
-
// iff `value` is an exact integer in that range — covers `error: 0/1`,
|
|
607
|
-
// priority flags, attribute counts, HTTP status codes mapped to numbers,
|
|
608
|
-
// and most small metrics. NaN, ±Infinity, negatives, and any non-integer
|
|
609
|
-
// float fall through.
|
|
610
|
-
if (value === (value & 0x7F)) {
|
|
611
|
-
const offset = bytes.length
|
|
612
|
-
bytes.reserve(1)
|
|
613
|
-
bytes.buffer[offset] = value
|
|
614
|
-
return
|
|
615
|
-
}
|
|
616
|
-
if (Number.isInteger(value)) {
|
|
617
|
-
if (value >= 0) {
|
|
618
|
-
this.#msgpack.encodeUnsigned(bytes, value)
|
|
619
|
-
} else {
|
|
620
|
-
this.#msgpack.encodeSigned(bytes, value)
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
623
|
-
this.#encodeFloat(bytes, value)
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
641
|
/**
|
|
628
642
|
* @param {MsgpackChunk} bytes
|
|
629
643
|
* @param {string | number | boolean} value
|
|
@@ -634,21 +648,17 @@ class AgentEncoder {
|
|
|
634
648
|
this._encodeString(bytes, value)
|
|
635
649
|
break
|
|
636
650
|
case 'number':
|
|
637
|
-
|
|
651
|
+
bytes.writeFloat(value)
|
|
638
652
|
break
|
|
639
653
|
case 'boolean':
|
|
640
|
-
|
|
654
|
+
bytes.writeBoolean(value)
|
|
641
655
|
break
|
|
642
656
|
}
|
|
643
657
|
}
|
|
644
658
|
|
|
645
|
-
#encodeFloat (bytes, value) {
|
|
646
|
-
this.#msgpack.encodeFloat(bytes, value)
|
|
647
|
-
}
|
|
648
|
-
|
|
649
659
|
#encodeMetaStruct (bytes, value) {
|
|
650
660
|
if (Array.isArray(value)) {
|
|
651
|
-
|
|
661
|
+
bytes.writeMapPrefix(0)
|
|
652
662
|
return
|
|
653
663
|
}
|
|
654
664
|
|
|
@@ -774,7 +784,7 @@ class AgentEncoder {
|
|
|
774
784
|
bytes.set(KEY_NAME)
|
|
775
785
|
this._encodeString(bytes, event.name)
|
|
776
786
|
bytes.set(KEY_EVENT_TIME)
|
|
777
|
-
|
|
787
|
+
bytes.writeFloat(event.time_unix_nano)
|
|
778
788
|
|
|
779
789
|
const attributes = event.attributes
|
|
780
790
|
if (attributes !== null && typeof attributes === 'object') {
|
|
@@ -844,7 +854,7 @@ class AgentEncoder {
|
|
|
844
854
|
if (typeof value === 'number') {
|
|
845
855
|
this._encodeString(bytes, key)
|
|
846
856
|
bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
|
|
847
|
-
|
|
857
|
+
bytes.writeIntOrFloat(value)
|
|
848
858
|
return true
|
|
849
859
|
}
|
|
850
860
|
if (typeof value === 'boolean') {
|
|
@@ -855,8 +865,11 @@ class AgentEncoder {
|
|
|
855
865
|
if (Array.isArray(value)) {
|
|
856
866
|
return this.#emitArrayAttribute(bytes, key, value)
|
|
857
867
|
}
|
|
858
|
-
memoizedLogDebug(
|
|
859
|
-
|
|
868
|
+
memoizedLogDebug(
|
|
869
|
+
key,
|
|
870
|
+
'Encountered unsupported data type for span event v0.4 encoding, key: ' +
|
|
871
|
+
'%s: with value: %s. Skipping encoding of pair.',
|
|
872
|
+
value
|
|
860
873
|
)
|
|
861
874
|
return false
|
|
862
875
|
}
|
|
@@ -914,7 +927,7 @@ class AgentEncoder {
|
|
|
914
927
|
}
|
|
915
928
|
if (typeof value === 'number') {
|
|
916
929
|
bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
|
|
917
|
-
|
|
930
|
+
bytes.writeIntOrFloat(value)
|
|
918
931
|
return true
|
|
919
932
|
}
|
|
920
933
|
if (typeof value === 'boolean') {
|
|
@@ -922,8 +935,11 @@ class AgentEncoder {
|
|
|
922
935
|
return true
|
|
923
936
|
}
|
|
924
937
|
if (Array.isArray(value)) {
|
|
925
|
-
memoizedLogDebug(
|
|
926
|
-
|
|
938
|
+
memoizedLogDebug(
|
|
939
|
+
key,
|
|
940
|
+
'Encountered nested array data type for span event v0.4 encoding. ' +
|
|
941
|
+
'Skipping encoding key: %s: with value: %s.',
|
|
942
|
+
value
|
|
927
943
|
)
|
|
928
944
|
}
|
|
929
945
|
return false
|
|
@@ -931,10 +947,10 @@ class AgentEncoder {
|
|
|
931
947
|
}
|
|
932
948
|
|
|
933
949
|
const seenKeys = new Set()
|
|
934
|
-
function memoizedLogDebug (key, message) {
|
|
950
|
+
function memoizedLogDebug (key, message, value) {
|
|
935
951
|
if (!seenKeys.has(key)) {
|
|
936
952
|
seenKeys.add(key)
|
|
937
|
-
log.debug(message)
|
|
953
|
+
log.debug(message, key, typeof value)
|
|
938
954
|
}
|
|
939
955
|
}
|
|
940
956
|
|
|
@@ -6,10 +6,18 @@ const { AgentEncoder: BaseEncoder, stringifySpanEvents } = require('./0.4')
|
|
|
6
6
|
const ARRAY_OF_TWO = 0x92
|
|
7
7
|
const ARRAY_OF_TWELVE = 0x9C
|
|
8
8
|
|
|
9
|
+
// Per-span fused head: `[0x9C, service-idx, name-idx, resource-idx,
|
|
10
|
+
// trace-id, span-id, parent-id]` — three uint32 indexes (5 bytes each) +
|
|
11
|
+
// three uint64 IDs (9 bytes each) + the array marker. Replaces seven
|
|
12
|
+
// separate reserves (`writeByte` + 3 × `writeInteger` + 3 × `_encodeId`)
|
|
13
|
+
// with one block-sized reserve per span.
|
|
14
|
+
const HEAD_BLOCK_SIZE = 1 + 5 * 3 + 9 * 3
|
|
15
|
+
|
|
9
16
|
function formatSpan (span) {
|
|
10
17
|
span = normalizeSpan(span)
|
|
11
18
|
// v0.5 has no native span_events slot; always serialize as a meta tag.
|
|
12
19
|
if (span.span_events) {
|
|
20
|
+
// TODO: this is a costly operation. Consolidate this with the formatter
|
|
13
21
|
span.meta.events = stringifySpanEvents(span.span_events)
|
|
14
22
|
// `= undefined` over `delete` to keep the span's hidden class.
|
|
15
23
|
span.span_events = undefined
|
|
@@ -35,20 +43,36 @@ class AgentEncoder extends BaseEncoder {
|
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
_encode (bytes, trace) {
|
|
38
|
-
|
|
46
|
+
bytes.writeArrayPrefix(trace)
|
|
47
|
+
|
|
48
|
+
const stringMap = this._stringMap
|
|
39
49
|
|
|
40
50
|
for (let span of trace) {
|
|
41
51
|
span = formatSpan(span)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
|
|
53
|
+
// Resolve the three head string indices up front. `_cacheString`
|
|
54
|
+
// writes into `_stringBytes`, an independent chunk, so the side
|
|
55
|
+
// effect is safe to interleave with the `_traceBytes` reserve
|
|
56
|
+
// below.
|
|
57
|
+
const serviceIndex = stringMap[span.service] ?? this._cacheString(span.service)
|
|
58
|
+
const nameIndex = stringMap[span.name] ?? this._cacheString(span.name)
|
|
59
|
+
const resourceIndex = stringMap[span.resource] ?? this._cacheString(span.resource)
|
|
60
|
+
|
|
61
|
+
const blockOffset = bytes.length
|
|
62
|
+
bytes.reserve(HEAD_BLOCK_SIZE)
|
|
63
|
+
const target = bytes.buffer
|
|
64
|
+
|
|
65
|
+
target[blockOffset] = ARRAY_OF_TWELVE
|
|
66
|
+
let cursor = this.#writeIndexAt(target, blockOffset + 1, serviceIndex)
|
|
67
|
+
cursor = this.#writeIndexAt(target, cursor, nameIndex)
|
|
68
|
+
cursor = this.#writeIndexAt(target, cursor, resourceIndex)
|
|
69
|
+
cursor = this.#writeIdAt(target, cursor, span.trace_id)
|
|
70
|
+
cursor = this.#writeIdAt(target, cursor, span.span_id)
|
|
71
|
+
this.#writeIdAt(target, cursor, span.parent_id)
|
|
72
|
+
|
|
73
|
+
bytes.writeIntOrFloat(span.start || 0)
|
|
74
|
+
bytes.writeIntOrFloat(span.duration || 0)
|
|
75
|
+
bytes.writeIntOrFloat(span.error)
|
|
52
76
|
this._encodeMap(bytes, span.meta || {})
|
|
53
77
|
this._encodeMap(bytes, span.metrics || {})
|
|
54
78
|
this._encodeString(bytes, span.type)
|
|
@@ -65,18 +89,41 @@ class AgentEncoder extends BaseEncoder {
|
|
|
65
89
|
bytes.reserve(5)
|
|
66
90
|
bytes.buffer[offset] = 0xDF
|
|
67
91
|
|
|
92
|
+
const stringMap = this._stringMap
|
|
68
93
|
let count = 0
|
|
69
94
|
for (const key of Object.keys(value)) {
|
|
70
95
|
const entryValue = value[key]
|
|
96
|
+
if (typeof entryValue !== 'string' && typeof entryValue !== 'number') continue
|
|
97
|
+
|
|
98
|
+
const keyIndex = stringMap[key] ?? this._cacheString(key)
|
|
99
|
+
const writeOffset = bytes.length
|
|
100
|
+
|
|
71
101
|
if (typeof entryValue === 'string') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this
|
|
78
|
-
|
|
102
|
+
// Both halves are uint32 indices on the v0.5 wire — known
|
|
103
|
+
// size, so the key and value pair fuses into one reserve.
|
|
104
|
+
const valueIndex = stringMap[entryValue] ?? this._cacheString(entryValue)
|
|
105
|
+
bytes.reserve(10)
|
|
106
|
+
const target = bytes.buffer
|
|
107
|
+
this.#writeIndexAt(target, writeOffset, keyIndex)
|
|
108
|
+
this.#writeIndexAt(target, writeOffset + 5, valueIndex)
|
|
109
|
+
} else {
|
|
110
|
+
// Speculate that the value is a positive fixint (0..127). The
|
|
111
|
+
// metrics map is mostly small unsigned integers (sample priority,
|
|
112
|
+
// `_dd.measured`, attribute counts), so one reserve covers the
|
|
113
|
+
// key (5 bytes) and the value (1 byte). Misses rewind the
|
|
114
|
+
// speculative value byte and route the value through the full
|
|
115
|
+
// encoder so the wire still picks the shortest valid encoding.
|
|
116
|
+
bytes.reserve(6)
|
|
117
|
+
const target = bytes.buffer
|
|
118
|
+
this.#writeIndexAt(target, writeOffset, keyIndex)
|
|
119
|
+
if (entryValue === (entryValue & 0x7F)) {
|
|
120
|
+
target[writeOffset + 5] = entryValue
|
|
121
|
+
} else {
|
|
122
|
+
bytes.length = writeOffset + 5
|
|
123
|
+
bytes.writeIntOrFloat(entryValue)
|
|
124
|
+
}
|
|
79
125
|
}
|
|
126
|
+
count++
|
|
80
127
|
}
|
|
81
128
|
|
|
82
129
|
const target = bytes.buffer
|
|
@@ -87,20 +134,18 @@ class AgentEncoder extends BaseEncoder {
|
|
|
87
134
|
}
|
|
88
135
|
|
|
89
136
|
_encodeString (bytes, value = '') {
|
|
137
|
+
const index = this._stringMap[value] ?? this._cacheString(value)
|
|
138
|
+
bytes.writeInteger(index)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
_cacheString (value) {
|
|
90
142
|
let index = this._stringMap[value]
|
|
91
143
|
if (index === undefined) {
|
|
92
144
|
index = this._stringCount++
|
|
93
145
|
this._stringMap[value] = index
|
|
94
146
|
this._stringBytes.write(value)
|
|
95
147
|
}
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
_cacheString (value) {
|
|
100
|
-
if (this._stringMap[value] === undefined) {
|
|
101
|
-
this._stringMap[value] = this._stringCount++
|
|
102
|
-
this._stringBytes.write(value)
|
|
103
|
-
}
|
|
148
|
+
return index
|
|
104
149
|
}
|
|
105
150
|
|
|
106
151
|
_writeStrings (buffer, offset) {
|
|
@@ -109,6 +154,49 @@ class AgentEncoder extends BaseEncoder {
|
|
|
109
154
|
|
|
110
155
|
return offset
|
|
111
156
|
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Write `[0xCE, uint32(index)]` into `target` at `offset` and return the
|
|
160
|
+
* new cursor. Caller is responsible for having reserved enough room.
|
|
161
|
+
*
|
|
162
|
+
* @param {Uint8Array} target
|
|
163
|
+
* @param {number} offset
|
|
164
|
+
* @param {number} index
|
|
165
|
+
* @returns {number}
|
|
166
|
+
*/
|
|
167
|
+
#writeIndexAt (target, offset, index) {
|
|
168
|
+
target[offset] = 0xCE
|
|
169
|
+
target[offset + 1] = index >> 24
|
|
170
|
+
target[offset + 2] = index >> 16
|
|
171
|
+
target[offset + 3] = index >> 8
|
|
172
|
+
target[offset + 4] = index
|
|
173
|
+
return offset + 5
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Write `[0xCF, uint64(id)]` into `target` at `offset` and return the
|
|
178
|
+
* new cursor. The id is truncated to the low 8 bytes, matching the
|
|
179
|
+
* inherited `_encodeId` behavior.
|
|
180
|
+
*
|
|
181
|
+
* @param {Uint8Array} target
|
|
182
|
+
* @param {number} offset
|
|
183
|
+
* @param {{ toBuffer: () => Uint8Array | number[] }} identifier
|
|
184
|
+
* @returns {number}
|
|
185
|
+
*/
|
|
186
|
+
#writeIdAt (target, offset, identifier) {
|
|
187
|
+
target[offset] = 0xCF
|
|
188
|
+
const idBuffer = identifier.toBuffer()
|
|
189
|
+
const start = idBuffer.length - 8
|
|
190
|
+
target[offset + 1] = idBuffer[start]
|
|
191
|
+
target[offset + 2] = idBuffer[start + 1]
|
|
192
|
+
target[offset + 3] = idBuffer[start + 2]
|
|
193
|
+
target[offset + 4] = idBuffer[start + 3]
|
|
194
|
+
target[offset + 5] = idBuffer[start + 4]
|
|
195
|
+
target[offset + 6] = idBuffer[start + 5]
|
|
196
|
+
target[offset + 7] = idBuffer[start + 6]
|
|
197
|
+
target[offset + 8] = idBuffer[start + 7]
|
|
198
|
+
return offset + 9
|
|
199
|
+
}
|
|
112
200
|
}
|
|
113
201
|
|
|
114
202
|
module.exports = { AgentEncoder }
|