dd-trace 5.101.0 → 5.103.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/ext/exporters.js +1 -0
- package/package.json +20 -17
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/aerospike.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +9 -9
- package/packages/datadog-instrumentations/src/amqplib.js +6 -7
- package/packages/datadog-instrumentations/src/anthropic.js +10 -10
- package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
- package/packages/datadog-instrumentations/src/avsc.js +6 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
- package/packages/datadog-instrumentations/src/bluebird.js +2 -2
- package/packages/datadog-instrumentations/src/body-parser.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
- package/packages/datadog-instrumentations/src/child_process.js +12 -12
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +41 -24
- package/packages/datadog-instrumentations/src/connect.js +7 -7
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cookie.js +2 -2
- package/packages/datadog-instrumentations/src/couchbase.js +73 -238
- package/packages/datadog-instrumentations/src/crypto.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +78 -17
- package/packages/datadog-instrumentations/src/dns.js +0 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
- package/packages/datadog-instrumentations/src/express-session.js +4 -4
- package/packages/datadog-instrumentations/src/express.js +10 -11
- package/packages/datadog-instrumentations/src/fastify.js +2 -2
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/fs.js +14 -14
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
- package/packages/datadog-instrumentations/src/google-genai.js +4 -4
- package/packages/datadog-instrumentations/src/graphql.js +13 -12
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +9 -9
- package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
- package/packages/datadog-instrumentations/src/helpers/promise.js +2 -2
- package/packages/datadog-instrumentations/src/hono.js +2 -2
- package/packages/datadog-instrumentations/src/http/client.js +6 -6
- package/packages/datadog-instrumentations/src/http/server.js +9 -9
- package/packages/datadog-instrumentations/src/ioredis.js +16 -12
- package/packages/datadog-instrumentations/src/jest.js +382 -81
- package/packages/datadog-instrumentations/src/kafkajs.js +165 -174
- package/packages/datadog-instrumentations/src/knex.js +17 -17
- package/packages/datadog-instrumentations/src/koa.js +12 -12
- package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
- package/packages/datadog-instrumentations/src/lodash.js +4 -4
- package/packages/datadog-instrumentations/src/mariadb.js +13 -13
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/common.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +85 -11
- package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +42 -30
- package/packages/datadog-instrumentations/src/mongodb.js +5 -5
- package/packages/datadog-instrumentations/src/mongoose.js +21 -21
- package/packages/datadog-instrumentations/src/mquery.js +5 -5
- package/packages/datadog-instrumentations/src/multer.js +4 -4
- package/packages/datadog-instrumentations/src/mysql.js +16 -16
- package/packages/datadog-instrumentations/src/mysql2.js +4 -4
- package/packages/datadog-instrumentations/src/net.js +14 -8
- package/packages/datadog-instrumentations/src/nyc.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +19 -19
- package/packages/datadog-instrumentations/src/oracledb.js +6 -6
- package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
- package/packages/datadog-instrumentations/src/pg.js +39 -25
- package/packages/datadog-instrumentations/src/pino.js +6 -10
- package/packages/datadog-instrumentations/src/playwright.js +445 -68
- package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
- package/packages/datadog-instrumentations/src/redis.js +20 -12
- package/packages/datadog-instrumentations/src/restify.js +2 -2
- package/packages/datadog-instrumentations/src/router.js +12 -12
- package/packages/datadog-instrumentations/src/stripe.js +12 -12
- package/packages/datadog-instrumentations/src/vitest.js +107 -26
- package/packages/datadog-instrumentations/src/winston.js +4 -4
- package/packages/datadog-instrumentations/src/ws.js +7 -7
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +70 -28
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +20 -13
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +46 -36
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +34 -23
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +14 -15
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +74 -55
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +20 -18
- package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +5 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +215 -26
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- package/packages/datadog-plugin-electron/src/index.js +17 -0
- package/packages/datadog-plugin-electron/src/ipc.js +143 -0
- package/packages/datadog-plugin-electron/src/net.js +82 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -5
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
- package/packages/datadog-plugin-graphql/src/execute.js +6 -28
- package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
- package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
- package/packages/datadog-plugin-graphql/src/utils.js +29 -0
- package/packages/datadog-plugin-grpc/src/client.js +6 -7
- package/packages/datadog-plugin-grpc/src/util.js +57 -22
- package/packages/datadog-plugin-http/src/client.js +3 -7
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-jest/src/util.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +5 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +36 -69
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-openai/src/tracing.js +12 -23
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +5 -1
- package/packages/datadog-plugin-redis/src/index.js +18 -23
- package/packages/datadog-plugin-vitest/src/index.js +8 -1
- package/packages/datadog-shimmer/src/shimmer.js +7 -1
- package/packages/dd-trace/src/aiguard/index.js +3 -1
- package/packages/dd-trace/src/aiguard/sdk.js +36 -30
- package/packages/dd-trace/src/aiguard/tags.js +20 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +4 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +21 -24
- package/packages/dd-trace/src/appsec/reporter.js +3 -1
- package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
- package/packages/dd-trace/src/azure_metadata.js +17 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
- package/packages/dd-trace/src/config/git_properties.js +2 -2
- package/packages/dd-trace/src/config/helper.js +4 -0
- package/packages/dd-trace/src/config/index.js +2 -2
- package/packages/dd-trace/src/config/major-overrides.js +98 -0
- package/packages/dd-trace/src/config/parsers.js +7 -1
- package/packages/dd-trace/src/config/supported-configurations.json +51 -38
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/index.js +2 -1
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -4
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
- package/packages/dd-trace/src/debugger/index.js +7 -7
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +748 -232
- package/packages/dd-trace/src/encode/0.5.js +47 -10
- package/packages/dd-trace/src/encode/agentless-json.js +1 -1
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/electron/index.js +49 -0
- package/packages/dd-trace/src/external-logger/src/index.js +2 -1
- package/packages/dd-trace/src/git_metadata.js +10 -8
- package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
- package/packages/dd-trace/src/lambda/index.js +62 -14
- package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
- package/packages/dd-trace/src/llmobs/index.js +13 -2
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +6 -3
- package/packages/dd-trace/src/llmobs/sdk.js +24 -26
- package/packages/dd-trace/src/llmobs/span_processor.js +25 -5
- package/packages/dd-trace/src/llmobs/util.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/msgpack/chunk.js +6 -3
- package/packages/dd-trace/src/openfeature/noop.js +40 -36
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
- package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
- package/packages/dd-trace/src/plugins/database.js +54 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/plugin.js +2 -4
- package/packages/dd-trace/src/plugins/util/ci.js +9 -9
- package/packages/dd-trace/src/plugins/util/git-cache.js +23 -23
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +56 -12
- package/packages/dd-trace/src/plugins/util/url.js +1 -3
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +18 -16
- package/packages/dd-trace/src/plugins/util/web.js +5 -7
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -23
- package/packages/dd-trace/src/profiling/profilers/wall.js +5 -6
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
- package/packages/dd-trace/src/ritm.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/index.js +2 -2
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/scope.js +3 -10
- package/packages/dd-trace/src/serverless.js +6 -6
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +27 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +24 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
- package/packages/dd-trace/src/tracer.js +7 -7
- package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
|
@@ -7,30 +7,222 @@ const { normalizeSpan } = require('./tags-processors')
|
|
|
7
7
|
|
|
8
8
|
const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// Pre-encoded static keys + value-prefix bytes; the hot encode loop emits
|
|
11
|
+
// each via one Uint8Array.set instead of routing through the string cache.
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} key fixstr key, must be < 32 UTF-8 bytes.
|
|
15
|
+
* @returns {Buffer}
|
|
16
|
+
*/
|
|
17
|
+
function buildKey (key) {
|
|
18
|
+
const length = Buffer.byteLength(key)
|
|
19
|
+
const buffer = Buffer.allocUnsafe(length + 1)
|
|
20
|
+
buffer[0] = length | 0xA0
|
|
21
|
+
buffer.utf8Write(key, 1, length)
|
|
22
|
+
return buffer
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} key fixstr key, must be < 32 UTF-8 bytes.
|
|
27
|
+
* @param {number} prefix msgpack prefix byte for the value that follows the key.
|
|
28
|
+
* @returns {Buffer}
|
|
29
|
+
*/
|
|
30
|
+
function buildKeyWithPrefix (key, prefix) {
|
|
31
|
+
const length = Buffer.byteLength(key)
|
|
32
|
+
const buffer = Buffer.allocUnsafe(length + 2)
|
|
33
|
+
buffer[0] = length | 0xA0
|
|
34
|
+
buffer.utf8Write(key, 1, length)
|
|
35
|
+
buffer[length + 1] = prefix
|
|
36
|
+
return buffer
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const KEY_TYPE = buildKey('type')
|
|
40
|
+
const KEY_NAME = buildKey('name')
|
|
41
|
+
const KEY_RESOURCE = buildKey('resource')
|
|
42
|
+
const KEY_SERVICE = buildKey('service')
|
|
43
|
+
const KEY_ERROR = buildKey('error')
|
|
44
|
+
const KEY_START = buildKey('start')
|
|
45
|
+
const KEY_DURATION = buildKey('duration')
|
|
46
|
+
const KEY_SPAN_EVENTS = buildKey('span_events')
|
|
47
|
+
const KEY_META_STRUCT = buildKey('meta_struct')
|
|
48
|
+
const KEY_TRACE_ID_PREFIX = buildKeyWithPrefix('trace_id', 0xCF)
|
|
49
|
+
const KEY_SPAN_ID_PREFIX = buildKeyWithPrefix('span_id', 0xCF)
|
|
50
|
+
const KEY_PARENT_ID_PREFIX = buildKeyWithPrefix('parent_id', 0xCF)
|
|
51
|
+
const KEY_META_PREFIX = buildKeyWithPrefix('meta', 0xDF)
|
|
52
|
+
const KEY_METRICS_PREFIX = buildKeyWithPrefix('metrics', 0xDF)
|
|
53
|
+
|
|
54
|
+
// Span-event field keys — `name` is shared with the span-level KEY_NAME.
|
|
55
|
+
const KEY_EVENT_TIME = buildKey('time_unix_nano')
|
|
56
|
+
const KEY_EVENT_ATTRIBUTES = buildKey('attributes')
|
|
57
|
+
|
|
58
|
+
// Pre-encoded prefix for a span-event-attribute typed wrapper:
|
|
59
|
+
// `[0x82 fixmap(2), 'type' fixstr, type fixint, '<value>_value' fixstr]`.
|
|
60
|
+
// The hot path emits one of these constants + the raw value, so the encoder
|
|
61
|
+
// never has to allocate `{ type: N, *_value: ... }` wrappers.
|
|
62
|
+
function buildAttrPrefix (typeByte, valueKey) {
|
|
63
|
+
const valueKeyBuf = buildKey(valueKey)
|
|
64
|
+
const buf = Buffer.allocUnsafe(7 + valueKeyBuf.length)
|
|
65
|
+
buf[0] = 0x82
|
|
66
|
+
buf[1] = 0xA4
|
|
67
|
+
buf[2] = 0x74 // t
|
|
68
|
+
buf[3] = 0x79 // y
|
|
69
|
+
buf[4] = 0x70 // p
|
|
70
|
+
buf[5] = 0x65 // e
|
|
71
|
+
buf[6] = typeByte
|
|
72
|
+
valueKeyBuf.copy(buf, 7)
|
|
73
|
+
return buf
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ATTR_PREFIX_STRING = buildAttrPrefix(0x00, 'string_value')
|
|
77
|
+
const ATTR_PREFIX_BOOL = buildAttrPrefix(0x01, 'bool_value')
|
|
78
|
+
const ATTR_PREFIX_INT = buildAttrPrefix(0x02, 'int_value')
|
|
79
|
+
const ATTR_PREFIX_DOUBLE = buildAttrPrefix(0x03, 'double_value')
|
|
80
|
+
|
|
81
|
+
// Outer array attribute is the only nested case: `[0x82, 'type', 4,
|
|
82
|
+
// 'array_value', 0x81 fixmap(1), 'values', 0xDD array32-prefix]`. The 4-byte
|
|
83
|
+
// length slot follows.
|
|
84
|
+
const ATTR_PREFIX_ARRAY = Buffer.concat([
|
|
85
|
+
buildAttrPrefix(0x04, 'array_value'),
|
|
86
|
+
Buffer.from([0x81]),
|
|
87
|
+
buildKey('values'),
|
|
88
|
+
Buffer.from([0xDD]),
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
// Pre-encoded boolean payloads: `[ATTR_PREFIX_BOOL, 0xC3 / 0xC2]`. Used by
|
|
92
|
+
// `#emitAttribute` and `#emitArrayItem` to emit the whole bool wrapper in a
|
|
93
|
+
// single `bytes.set`.
|
|
94
|
+
const ATTR_PAYLOAD_BOOL_TRUE = Buffer.concat([ATTR_PREFIX_BOOL, Buffer.from([0xC3])])
|
|
95
|
+
const ATTR_PAYLOAD_BOOL_FALSE = Buffer.concat([ATTR_PREFIX_BOOL, Buffer.from([0xC2])])
|
|
96
|
+
|
|
97
|
+
function formatSpanWithLegacyEvents (span) {
|
|
11
98
|
span = normalizeSpan(span)
|
|
12
99
|
if (span.span_events) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
100
|
+
span.meta.events = stringifySpanEvents(span.span_events)
|
|
101
|
+
// `= undefined` over `delete` to keep the span's hidden class — `delete`
|
|
102
|
+
// would push every event-bearing span into V8 dictionary mode.
|
|
103
|
+
span.span_events = undefined
|
|
104
|
+
}
|
|
105
|
+
return span
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Hand-written stringifier for `span.span_events`. The shape is fixed by
|
|
110
|
+
* `extractSpanEvents` (`{ name, time_unix_nano, attributes? }`) and attribute
|
|
111
|
+
* values are pre-sanitized to primitives or arrays of primitives, so we can
|
|
112
|
+
* skip everything `JSON.stringify` does for the generic case (toJSON probing,
|
|
113
|
+
* key iteration over the prototype chain, replacer hooks). Output matches
|
|
114
|
+
* `JSON.stringify(spanEvents)` byte-for-byte for the post-sanitization shape.
|
|
115
|
+
*
|
|
116
|
+
* @param {Array<{ name: string, time_unix_nano: number, attributes?: object }>} spanEvents
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
function stringifySpanEvents (spanEvents) {
|
|
120
|
+
let result = '['
|
|
121
|
+
for (let index = 0; index < spanEvents.length; index++) {
|
|
122
|
+
if (index > 0) result += ','
|
|
123
|
+
const event = spanEvents[index]
|
|
124
|
+
// `addEvent` does not type-check `name`; defer the unusual cases to
|
|
125
|
+
// `JSON.stringify` so non-string names match the prior behaviour
|
|
126
|
+
// instead of throwing in `escapeJsonString`.
|
|
127
|
+
if (typeof event.name !== 'string') {
|
|
128
|
+
result += JSON.stringify(event)
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
result += '{"name":' + escapeJsonString(event.name) +
|
|
132
|
+
',"time_unix_nano":' + jsonNumber(event.time_unix_nano)
|
|
133
|
+
if (event.attributes) {
|
|
134
|
+
result += ',"attributes":' + stringifyAttributes(event.attributes)
|
|
135
|
+
}
|
|
136
|
+
result += '}'
|
|
137
|
+
}
|
|
138
|
+
return result + ']'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function stringifyAttributes (attributes) {
|
|
142
|
+
let result = '{'
|
|
143
|
+
let first = true
|
|
144
|
+
for (const key of Object.keys(attributes)) {
|
|
145
|
+
if (first) {
|
|
146
|
+
first = false
|
|
16
147
|
} else {
|
|
17
|
-
|
|
18
|
-
delete span.span_events
|
|
148
|
+
result += ','
|
|
19
149
|
}
|
|
150
|
+
result += escapeJsonString(key) + ':' + stringifyAttributeValue(attributes[key])
|
|
20
151
|
}
|
|
21
|
-
return
|
|
152
|
+
return result + '}'
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function stringifyAttributeValue (value) {
|
|
156
|
+
if (typeof value === 'string') return escapeJsonString(value)
|
|
157
|
+
if (typeof value === 'number') return jsonNumber(value)
|
|
158
|
+
if (typeof value === 'boolean') return value ? 'true' : 'false'
|
|
159
|
+
if (Array.isArray(value)) {
|
|
160
|
+
let result = '['
|
|
161
|
+
for (let index = 0; index < value.length; index++) {
|
|
162
|
+
if (index > 0) result += ','
|
|
163
|
+
result += stringifyAttributeValue(value[index])
|
|
164
|
+
}
|
|
165
|
+
return result + ']'
|
|
166
|
+
}
|
|
167
|
+
// Sanitization rejects everything else, but keep the safety net.
|
|
168
|
+
return 'null'
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Match `JSON.stringify` for numbers: `NaN` and `±Infinity` collapse to the
|
|
173
|
+
* literal `null`, everything else uses ECMAScript's default `Number → String`
|
|
174
|
+
* conversion (which is what `JSON.stringify` calls internally).
|
|
175
|
+
*
|
|
176
|
+
* @param {number} value
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
function jsonNumber (value) {
|
|
180
|
+
if (Number.isFinite(value)) return String(value)
|
|
181
|
+
return 'null'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Fast path: scan once, and if no character in the string requires JSON
|
|
186
|
+
* escaping, emit `"<str>"` as-is. The scanned chars are `"`, `\`, and any
|
|
187
|
+
* control char in the U+0000–U+001F range. Anything else delegates to
|
|
188
|
+
* `JSON.stringify` for full spec-compliant escaping (surrogate pairs,
|
|
189
|
+
* lone surrogates, etc.).
|
|
190
|
+
*
|
|
191
|
+
* @param {string} value
|
|
192
|
+
* @returns {string}
|
|
193
|
+
*/
|
|
194
|
+
function escapeJsonString (value) {
|
|
195
|
+
for (let index = 0; index < value.length; index++) {
|
|
196
|
+
const code = value.charCodeAt(index)
|
|
197
|
+
if (code < 0x20 || code === 0x22 || code === 0x5C) {
|
|
198
|
+
return JSON.stringify(value)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return '"' + value + '"'
|
|
22
202
|
}
|
|
23
203
|
|
|
24
204
|
class AgentEncoder {
|
|
205
|
+
#msgpack = new MsgpackEncoder()
|
|
206
|
+
#limit
|
|
207
|
+
#writer
|
|
208
|
+
#config
|
|
209
|
+
#debugEncoding
|
|
210
|
+
#formatSpan
|
|
211
|
+
|
|
25
212
|
constructor (writer, limit = SOFT_LIMIT) {
|
|
26
|
-
this
|
|
27
|
-
this._limit = limit
|
|
213
|
+
this.#limit = limit
|
|
28
214
|
this._traceBytes = new MsgpackChunk()
|
|
29
215
|
this._stringBytes = new MsgpackChunk()
|
|
30
|
-
this
|
|
216
|
+
this.#writer = writer
|
|
31
217
|
this._reset()
|
|
32
|
-
this
|
|
33
|
-
this
|
|
218
|
+
this.#config = getConfig()
|
|
219
|
+
this.#debugEncoding = this.#config.DD_TRACE_ENCODING_DEBUG
|
|
220
|
+
// Pick the per-span formatter once so the hot loop pays no per-span
|
|
221
|
+
// config check. The native path doesn't need to reshape `span_events`
|
|
222
|
+
// because `#encodeSpanEvents` works directly on the raw attributes.
|
|
223
|
+
this.#formatSpan = this.#config.DD_TRACE_NATIVE_SPAN_EVENTS
|
|
224
|
+
? normalizeSpan
|
|
225
|
+
: formatSpanWithLegacyEvents
|
|
34
226
|
}
|
|
35
227
|
|
|
36
228
|
count () {
|
|
@@ -45,21 +237,19 @@ class AgentEncoder {
|
|
|
45
237
|
|
|
46
238
|
this._encode(bytes, trace)
|
|
47
239
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (this._debugEncoding) {
|
|
240
|
+
if (this.#debugEncoding) {
|
|
241
|
+
const end = bytes.length
|
|
51
242
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
52
243
|
log.debug(() => {
|
|
53
244
|
const hex = bytes.buffer.subarray(start, end).toString('hex').match(/../g).join(' ')
|
|
54
|
-
|
|
55
245
|
return `Adding encoded trace to buffer: ${hex}`
|
|
56
246
|
})
|
|
57
247
|
}
|
|
58
248
|
|
|
59
|
-
//
|
|
60
|
-
if (this._traceBytes.length > this
|
|
249
|
+
// Soft limit overshoot is fine — the agent caps at 50 MB.
|
|
250
|
+
if (this._traceBytes.length > this.#limit || this._stringBytes.length > this.#limit) {
|
|
61
251
|
log.debug('Buffer went over soft limit, flushing')
|
|
62
|
-
this.
|
|
252
|
+
this.#writer.flush()
|
|
63
253
|
}
|
|
64
254
|
}
|
|
65
255
|
|
|
@@ -81,56 +271,121 @@ class AgentEncoder {
|
|
|
81
271
|
_encode (bytes, trace) {
|
|
82
272
|
this._encodeArrayPrefix(bytes, trace)
|
|
83
273
|
|
|
274
|
+
const formatSpan = this.#formatSpan
|
|
275
|
+
const stringMap = this._stringMap
|
|
276
|
+
// Snapshot the string buffer so we can detect a mid-encode resize and
|
|
277
|
+
// release the old backing store afterwards (see `#refreshStringCache`).
|
|
278
|
+
const stringBufferAtStart = this._stringBytes.buffer
|
|
279
|
+
|
|
84
280
|
for (let span of trace) {
|
|
85
|
-
span = formatSpan(span
|
|
86
|
-
bytes.reserve(1)
|
|
281
|
+
span = formatSpan(span)
|
|
87
282
|
|
|
88
|
-
// this is the original size of the fixed map for span attributes that always exist
|
|
89
283
|
let mapSize = 11
|
|
284
|
+
if (span.type) mapSize++
|
|
285
|
+
if (span.meta_struct) mapSize++
|
|
286
|
+
if (span.span_events) mapSize++
|
|
287
|
+
|
|
288
|
+
// Pre-fetch the cached string entries up front and fuse the map prefix,
|
|
289
|
+
// optional `type`, three IDs, and `name` / `resource` / `service`
|
|
290
|
+
// emissions into a single `bytes.reserve` + sequential native writes.
|
|
291
|
+
// Replaces seven `bytes.reserve` calls per span (one each for the
|
|
292
|
+
// header, type, three IDs, three strings) with one.
|
|
293
|
+
let typeEntry
|
|
294
|
+
if (span.type) {
|
|
295
|
+
typeEntry = stringMap[span.type] ?? this._cacheString(span.type)
|
|
296
|
+
}
|
|
297
|
+
const nameEntry = stringMap[span.name] ?? this._cacheString(span.name)
|
|
298
|
+
const resourceEntry = stringMap[span.resource] ?? this._cacheString(span.resource)
|
|
299
|
+
const serviceEntry = stringMap[span.service] ?? this._cacheString(span.service)
|
|
300
|
+
const nameLen = nameEntry.length
|
|
301
|
+
const resourceLen = resourceEntry.length
|
|
302
|
+
const serviceLen = serviceEntry.length
|
|
303
|
+
|
|
304
|
+
// 1 byte map prefix + 3 ID fields (10/9/11 bytes prefix + 8 bytes value
|
|
305
|
+
// each) + the three string fields.
|
|
306
|
+
let blockSize = 1 +
|
|
307
|
+
KEY_TRACE_ID_PREFIX.length + 8 +
|
|
308
|
+
KEY_SPAN_ID_PREFIX.length + 8 +
|
|
309
|
+
KEY_PARENT_ID_PREFIX.length + 8 +
|
|
310
|
+
KEY_NAME.length + nameLen +
|
|
311
|
+
KEY_RESOURCE.length + resourceLen +
|
|
312
|
+
KEY_SERVICE.length + serviceLen
|
|
313
|
+
if (typeEntry) blockSize += KEY_TYPE.length + typeEntry.length
|
|
314
|
+
|
|
315
|
+
const blockOffset = bytes.length
|
|
316
|
+
bytes.reserve(blockSize)
|
|
317
|
+
const target = bytes.buffer
|
|
318
|
+
let cursor = blockOffset
|
|
319
|
+
|
|
320
|
+
target[cursor++] = 0x80 + mapSize
|
|
321
|
+
|
|
322
|
+
if (typeEntry) {
|
|
323
|
+
target.set(KEY_TYPE, cursor)
|
|
324
|
+
cursor += KEY_TYPE.length
|
|
325
|
+
target.set(typeEntry, cursor)
|
|
326
|
+
cursor += typeEntry.length
|
|
327
|
+
}
|
|
90
328
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (span.span_events) mapSize += 1
|
|
329
|
+
cursor = this.#writeIdAt(target, cursor, KEY_TRACE_ID_PREFIX, span.trace_id)
|
|
330
|
+
cursor = this.#writeIdAt(target, cursor, KEY_SPAN_ID_PREFIX, span.span_id)
|
|
331
|
+
cursor = this.#writeIdAt(target, cursor, KEY_PARENT_ID_PREFIX, span.parent_id)
|
|
95
332
|
|
|
96
|
-
|
|
333
|
+
target.set(KEY_NAME, cursor)
|
|
334
|
+
cursor += KEY_NAME.length
|
|
335
|
+
target.set(nameEntry, cursor)
|
|
336
|
+
cursor += nameLen
|
|
97
337
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
338
|
+
target.set(KEY_RESOURCE, cursor)
|
|
339
|
+
cursor += KEY_RESOURCE.length
|
|
340
|
+
target.set(resourceEntry, cursor)
|
|
341
|
+
cursor += resourceLen
|
|
342
|
+
|
|
343
|
+
target.set(KEY_SERVICE, cursor)
|
|
344
|
+
cursor += KEY_SERVICE.length
|
|
345
|
+
target.set(serviceEntry, cursor)
|
|
346
|
+
|
|
347
|
+
bytes.set(KEY_ERROR)
|
|
348
|
+
this._encodeIntOrFloat(bytes, span.error)
|
|
349
|
+
bytes.set(KEY_START)
|
|
350
|
+
this._encodeIntOrFloat(bytes, span.start)
|
|
351
|
+
bytes.set(KEY_DURATION)
|
|
352
|
+
this._encodeIntOrFloat(bytes, span.duration)
|
|
353
|
+
|
|
354
|
+
this.#encodeMetaEntries(bytes, KEY_META_PREFIX, span.meta)
|
|
355
|
+
this.#encodeMetaEntries(bytes, KEY_METRICS_PREFIX, span.metrics)
|
|
102
356
|
|
|
103
|
-
this._encodeString(bytes, 'trace_id')
|
|
104
|
-
this._encodeId(bytes, span.trace_id)
|
|
105
|
-
this._encodeString(bytes, 'span_id')
|
|
106
|
-
this._encodeId(bytes, span.span_id)
|
|
107
|
-
this._encodeString(bytes, 'parent_id')
|
|
108
|
-
this._encodeId(bytes, span.parent_id)
|
|
109
|
-
this._encodeString(bytes, 'name')
|
|
110
|
-
this._encodeString(bytes, span.name)
|
|
111
|
-
this._encodeString(bytes, 'resource')
|
|
112
|
-
this._encodeString(bytes, span.resource)
|
|
113
|
-
this._encodeString(bytes, 'service')
|
|
114
|
-
this._encodeString(bytes, span.service)
|
|
115
|
-
this._encodeString(bytes, 'error')
|
|
116
|
-
this._encodeInteger(bytes, span.error)
|
|
117
|
-
this._encodeString(bytes, 'start')
|
|
118
|
-
this._encodeLong(bytes, span.start)
|
|
119
|
-
this._encodeString(bytes, 'duration')
|
|
120
|
-
this._encodeLong(bytes, span.duration)
|
|
121
|
-
this._encodeString(bytes, 'meta')
|
|
122
|
-
this._encodeMap(bytes, span.meta)
|
|
123
|
-
this._encodeString(bytes, 'metrics')
|
|
124
|
-
this._encodeMap(bytes, span.metrics)
|
|
125
357
|
if (span.span_events) {
|
|
126
|
-
|
|
127
|
-
this
|
|
358
|
+
bytes.set(KEY_SPAN_EVENTS)
|
|
359
|
+
this.#encodeSpanEvents(bytes, span.span_events)
|
|
128
360
|
}
|
|
129
361
|
if (span.meta_struct) {
|
|
130
|
-
|
|
131
|
-
this
|
|
362
|
+
bytes.set(KEY_META_STRUCT)
|
|
363
|
+
this.#encodeMetaStruct(bytes, span.meta_struct)
|
|
132
364
|
}
|
|
133
365
|
}
|
|
366
|
+
|
|
367
|
+
if (this._stringBytes.buffer !== stringBufferAtStart) {
|
|
368
|
+
this.#refreshStringCache()
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Rebuild the cached string subarrays in `_stringMap` against the current
|
|
374
|
+
* `_stringBytes.buffer`. After `MsgpackChunk.reserve()` resizes, the prior
|
|
375
|
+
* subarrays still reference the old `Buffer`'s `ArrayBuffer` and pin it
|
|
376
|
+
* until `_reset()` clears the map; for a payload that grows 2 → 4 → 8 MB
|
|
377
|
+
* that is up to 6 MB of avoidable peak memory. `Buffer.allocUnsafe(>= 2
|
|
378
|
+
* MB)` bypasses the small-allocation pool and starts at offset 0 in its
|
|
379
|
+
* `ArrayBuffer`, so the old subarray's `byteOffset` is the entry's start
|
|
380
|
+
* position in the new buffer.
|
|
381
|
+
*/
|
|
382
|
+
#refreshStringCache () {
|
|
383
|
+
const newBuffer = this._stringBytes.buffer
|
|
384
|
+
const stringMap = this._stringMap
|
|
385
|
+
for (const key of Object.keys(stringMap)) {
|
|
386
|
+
const old = stringMap[key]
|
|
387
|
+
stringMap[key] = newBuffer.subarray(old.byteOffset, old.byteOffset + old.length)
|
|
388
|
+
}
|
|
134
389
|
}
|
|
135
390
|
|
|
136
391
|
_reset () {
|
|
@@ -144,123 +399,290 @@ class AgentEncoder {
|
|
|
144
399
|
}
|
|
145
400
|
|
|
146
401
|
_encodeBuffer (bytes, buffer) {
|
|
147
|
-
this.
|
|
402
|
+
this.#msgpack.encodeBin(bytes, buffer)
|
|
148
403
|
}
|
|
149
404
|
|
|
150
405
|
_encodeBool (bytes, value) {
|
|
151
|
-
this.
|
|
406
|
+
this.#msgpack.encodeBoolean(bytes, value)
|
|
152
407
|
}
|
|
153
408
|
|
|
154
409
|
_encodeArrayPrefix (bytes, value) {
|
|
155
|
-
this.
|
|
410
|
+
this.#msgpack.encodeArrayPrefix(bytes, value)
|
|
156
411
|
}
|
|
157
412
|
|
|
158
413
|
_encodeMapPrefix (bytes, keysLength) {
|
|
159
|
-
this.
|
|
414
|
+
this.#msgpack.encodeMapPrefix(bytes, keysLength)
|
|
160
415
|
}
|
|
161
416
|
|
|
162
417
|
_encodeByte (bytes, value) {
|
|
163
|
-
this.
|
|
418
|
+
this.#msgpack.encodeByte(bytes, value)
|
|
164
419
|
}
|
|
165
420
|
|
|
166
421
|
// TODO: Use BigInt instead.
|
|
167
|
-
_encodeId (bytes,
|
|
422
|
+
_encodeId (bytes, identifier) {
|
|
423
|
+
const idBuffer = identifier.toBuffer()
|
|
424
|
+
const start = idBuffer.length - 8
|
|
168
425
|
const offset = bytes.length
|
|
169
426
|
|
|
170
427
|
bytes.reserve(9)
|
|
171
428
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
bytes.buffer[offset + 8] = id[7]
|
|
429
|
+
const target = bytes.buffer
|
|
430
|
+
target[offset] = 0xCF
|
|
431
|
+
target[offset + 1] = idBuffer[start]
|
|
432
|
+
target[offset + 2] = idBuffer[start + 1]
|
|
433
|
+
target[offset + 3] = idBuffer[start + 2]
|
|
434
|
+
target[offset + 4] = idBuffer[start + 3]
|
|
435
|
+
target[offset + 5] = idBuffer[start + 4]
|
|
436
|
+
target[offset + 6] = idBuffer[start + 5]
|
|
437
|
+
target[offset + 7] = idBuffer[start + 6]
|
|
438
|
+
target[offset + 8] = idBuffer[start + 7]
|
|
183
439
|
}
|
|
184
440
|
|
|
185
441
|
_encodeNumber (bytes, value) {
|
|
186
|
-
this.
|
|
442
|
+
this.#msgpack.encodeNumber(bytes, value)
|
|
187
443
|
}
|
|
188
444
|
|
|
189
445
|
_encodeInteger (bytes, value) {
|
|
190
|
-
this.
|
|
446
|
+
this.#msgpack.encodeInteger(bytes, value)
|
|
191
447
|
}
|
|
192
448
|
|
|
193
449
|
_encodeLong (bytes, value) {
|
|
194
|
-
this.
|
|
450
|
+
this.#msgpack.encodeLong(bytes, value)
|
|
195
451
|
}
|
|
196
452
|
|
|
453
|
+
// Single pass: reserve the count slot, encode entries while counting, patch the count.
|
|
454
|
+
// Subclasses (0.5, CI visibility encoders) inherit this; the wire stays on float64
|
|
455
|
+
// for numeric values to keep their established trace / events intake unchanged.
|
|
197
456
|
_encodeMap (bytes, value) {
|
|
198
|
-
const
|
|
199
|
-
|
|
457
|
+
const offset = bytes.length
|
|
458
|
+
bytes.reserve(5)
|
|
459
|
+
bytes.buffer[offset] = 0xDF
|
|
460
|
+
|
|
461
|
+
let count = 0
|
|
462
|
+
for (const key of Object.keys(value)) {
|
|
463
|
+
const entryValue = value[key]
|
|
464
|
+
if (typeof entryValue === 'string') {
|
|
465
|
+
this._encodeString(bytes, key)
|
|
466
|
+
this._encodeString(bytes, entryValue)
|
|
467
|
+
count++
|
|
468
|
+
} else if (typeof entryValue === 'number') {
|
|
469
|
+
this._encodeString(bytes, key)
|
|
470
|
+
this.#encodeFloat(bytes, entryValue)
|
|
471
|
+
count++
|
|
472
|
+
}
|
|
473
|
+
}
|
|
200
474
|
|
|
201
|
-
|
|
475
|
+
const target = bytes.buffer
|
|
476
|
+
target[offset + 1] = count >>> 24
|
|
477
|
+
target[offset + 2] = count >>> 16
|
|
478
|
+
target[offset + 3] = count >>> 8
|
|
479
|
+
target[offset + 4] = count
|
|
480
|
+
}
|
|
202
481
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
482
|
+
_encodeString (bytes, value = '') {
|
|
483
|
+
const entry = this._stringMap[value] ?? this._cacheString(value)
|
|
484
|
+
const length = entry.length
|
|
485
|
+
const offset = bytes.length
|
|
486
|
+
bytes.reserve(length)
|
|
487
|
+
bytes.buffer.set(entry, offset)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
_cacheString (value) {
|
|
491
|
+
let entry = this._stringMap[value]
|
|
492
|
+
if (entry === undefined) {
|
|
493
|
+
this._stringCount++
|
|
494
|
+
const start = this._stringBytes.length
|
|
495
|
+
const written = this._stringBytes.write(value)
|
|
496
|
+
entry = this._stringBytes.buffer.subarray(start, start + written)
|
|
497
|
+
this._stringMap[value] = entry
|
|
498
|
+
}
|
|
499
|
+
return entry
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
_writeArrayPrefix (buffer, offset, count) {
|
|
503
|
+
buffer[offset++] = 0xDD
|
|
504
|
+
buffer.writeUInt32BE(count, offset)
|
|
505
|
+
|
|
506
|
+
return offset + 4
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
_writeTraces (buffer, offset = 0) {
|
|
510
|
+
offset = this._writeArrayPrefix(buffer, offset, this._traceCount)
|
|
511
|
+
offset += this._traceBytes.buffer.copy(buffer, offset, 0, this._traceBytes.length)
|
|
512
|
+
|
|
513
|
+
return offset
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Fast path for `span.meta` / `span.metrics`. Inlines the string cache so
|
|
518
|
+
* each entry is one reserve (not two) and skips the polymorphic dispatch.
|
|
519
|
+
*
|
|
520
|
+
* @param {MsgpackChunk} bytes
|
|
521
|
+
* @param {Buffer} keyPrefix Precomputed `[key, 0xDF]`.
|
|
522
|
+
* @param {Record<string, unknown>} value
|
|
523
|
+
*/
|
|
524
|
+
#encodeMetaEntries (bytes, keyPrefix, value) {
|
|
525
|
+
const keyPrefixLen = keyPrefix.length
|
|
526
|
+
const headerOffset = bytes.length
|
|
527
|
+
bytes.reserve(keyPrefixLen + 4)
|
|
528
|
+
bytes.buffer.set(keyPrefix, headerOffset)
|
|
529
|
+
const countOffset = headerOffset + keyPrefixLen
|
|
530
|
+
|
|
531
|
+
const stringMap = this._stringMap
|
|
532
|
+
let count = 0
|
|
533
|
+
|
|
534
|
+
for (const key of Object.keys(value)) {
|
|
535
|
+
const entryValue = value[key]
|
|
536
|
+
if (typeof entryValue !== 'string' && typeof entryValue !== 'number') continue
|
|
537
|
+
|
|
538
|
+
const keyEntry = stringMap[key] ?? this._cacheString(key)
|
|
539
|
+
const keyEntryLen = keyEntry.length
|
|
540
|
+
const writeOffset = bytes.length
|
|
541
|
+
|
|
542
|
+
if (typeof entryValue === 'string') {
|
|
543
|
+
const valueEntry = stringMap[entryValue] ?? this._cacheString(entryValue)
|
|
544
|
+
const valueEntryLen = valueEntry.length
|
|
545
|
+
bytes.reserve(keyEntryLen + valueEntryLen)
|
|
546
|
+
const target = bytes.buffer
|
|
547
|
+
target.set(keyEntry, writeOffset)
|
|
548
|
+
target.set(valueEntry, writeOffset + keyEntryLen)
|
|
549
|
+
} else {
|
|
550
|
+
bytes.reserve(keyEntryLen)
|
|
551
|
+
bytes.buffer.set(keyEntry, writeOffset)
|
|
552
|
+
this._encodeIntOrFloat(bytes, entryValue)
|
|
553
|
+
}
|
|
554
|
+
count++
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const target = bytes.buffer
|
|
558
|
+
target[countOffset] = count >>> 24
|
|
559
|
+
target[countOffset + 1] = count >>> 16
|
|
560
|
+
target[countOffset + 2] = count >>> 8
|
|
561
|
+
target[countOffset + 3] = count
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Write `[keyPrefix, 8-byte uint64 id]` into `target` at `offset` and
|
|
566
|
+
* return the new cursor. Caller is responsible for having reserved enough
|
|
567
|
+
* room — this is the no-reserve variant used inside `_encode`'s combined
|
|
568
|
+
* fixed-fields block.
|
|
569
|
+
*
|
|
570
|
+
* @param {Uint8Array} target
|
|
571
|
+
* @param {number} offset
|
|
572
|
+
* @param {Buffer} keyPrefix Precomputed `[key, 0xCF]`.
|
|
573
|
+
* @param {{ toBuffer: () => Uint8Array | number[] }} identifier
|
|
574
|
+
* @returns {number}
|
|
575
|
+
*/
|
|
576
|
+
#writeIdAt (target, offset, keyPrefix, identifier) {
|
|
577
|
+
target.set(keyPrefix, offset)
|
|
578
|
+
offset += keyPrefix.length
|
|
579
|
+
const idBuffer = identifier.toBuffer()
|
|
580
|
+
const start = idBuffer.length - 8
|
|
581
|
+
target[offset] = idBuffer[start]
|
|
582
|
+
target[offset + 1] = idBuffer[start + 1]
|
|
583
|
+
target[offset + 2] = idBuffer[start + 2]
|
|
584
|
+
target[offset + 3] = idBuffer[start + 3]
|
|
585
|
+
target[offset + 4] = idBuffer[start + 4]
|
|
586
|
+
target[offset + 5] = idBuffer[start + 5]
|
|
587
|
+
target[offset + 6] = idBuffer[start + 6]
|
|
588
|
+
target[offset + 7] = idBuffer[start + 7]
|
|
589
|
+
return offset + 8
|
|
590
|
+
}
|
|
591
|
+
|
|
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)
|
|
206
624
|
}
|
|
207
625
|
}
|
|
208
626
|
|
|
209
|
-
|
|
627
|
+
/**
|
|
628
|
+
* @param {MsgpackChunk} bytes
|
|
629
|
+
* @param {string | number | boolean} value
|
|
630
|
+
*/
|
|
631
|
+
#encodeValue (bytes, value) {
|
|
210
632
|
switch (typeof value) {
|
|
211
633
|
case 'string':
|
|
212
634
|
this._encodeString(bytes, value)
|
|
213
635
|
break
|
|
214
636
|
case 'number':
|
|
215
|
-
this
|
|
637
|
+
this.#encodeFloat(bytes, value)
|
|
216
638
|
break
|
|
217
639
|
case 'boolean':
|
|
218
640
|
this._encodeBool(bytes, value)
|
|
219
641
|
break
|
|
220
|
-
default:
|
|
221
|
-
// should not happen
|
|
222
642
|
}
|
|
223
643
|
}
|
|
224
644
|
|
|
225
|
-
|
|
226
|
-
this.
|
|
227
|
-
|
|
228
|
-
const { start, end } = this._stringMap[value]
|
|
229
|
-
|
|
230
|
-
this._stringBytes.copy(bytes, start, end)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
_encodeFloat (bytes, value) {
|
|
234
|
-
this._msgpack.encodeFloat(bytes, value)
|
|
645
|
+
#encodeFloat (bytes, value) {
|
|
646
|
+
this.#msgpack.encodeFloat(bytes, value)
|
|
235
647
|
}
|
|
236
648
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
typeof v === 'number' ||
|
|
243
|
-
(v !== null && typeof v === 'object')
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
this._encodeMapPrefix(bytes, validKeys.length)
|
|
649
|
+
#encodeMetaStruct (bytes, value) {
|
|
650
|
+
if (Array.isArray(value)) {
|
|
651
|
+
this._encodeMapPrefix(bytes, 0)
|
|
652
|
+
return
|
|
653
|
+
}
|
|
247
654
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
655
|
+
const offset = bytes.length
|
|
656
|
+
bytes.reserve(5)
|
|
657
|
+
bytes.buffer[offset] = 0xDF
|
|
658
|
+
|
|
659
|
+
let count = 0
|
|
660
|
+
for (const key of Object.keys(value)) {
|
|
661
|
+
const entryValue = value[key]
|
|
662
|
+
if (typeof entryValue === 'string' || typeof entryValue === 'number' ||
|
|
663
|
+
(entryValue !== null && typeof entryValue === 'object')) {
|
|
664
|
+
this._encodeString(bytes, key)
|
|
665
|
+
this.#encodeObjectAsByteArray(bytes, entryValue)
|
|
666
|
+
count++
|
|
667
|
+
}
|
|
252
668
|
}
|
|
669
|
+
|
|
670
|
+
const target = bytes.buffer
|
|
671
|
+
target[offset + 1] = count >>> 24
|
|
672
|
+
target[offset + 2] = count >>> 16
|
|
673
|
+
target[offset + 3] = count >>> 8
|
|
674
|
+
target[offset + 4] = count
|
|
253
675
|
}
|
|
254
676
|
|
|
255
|
-
|
|
677
|
+
#encodeObjectAsByteArray (bytes, value) {
|
|
256
678
|
const prefixLength = 5
|
|
257
679
|
const offset = bytes.length
|
|
258
680
|
|
|
259
681
|
bytes.reserve(prefixLength)
|
|
260
682
|
|
|
261
|
-
this
|
|
683
|
+
this.#encodeObject(bytes, value)
|
|
262
684
|
|
|
263
|
-
//
|
|
685
|
+
// The byte length isn't known until the inner object has been encoded.
|
|
264
686
|
const length = bytes.length - offset - prefixLength
|
|
265
687
|
bytes.buffer[offset] = 0xC6
|
|
266
688
|
bytes.buffer[offset + 1] = length >> 24
|
|
@@ -269,157 +691,251 @@ class AgentEncoder {
|
|
|
269
691
|
bytes.buffer[offset + 4] = length
|
|
270
692
|
}
|
|
271
693
|
|
|
272
|
-
|
|
694
|
+
#encodeObject (bytes, value, circularReferencesDetector = new Set()) {
|
|
273
695
|
circularReferencesDetector.add(value)
|
|
274
696
|
if (Array.isArray(value)) {
|
|
275
|
-
this
|
|
697
|
+
this.#encodeObjectAsArray(bytes, value, circularReferencesDetector)
|
|
276
698
|
} else if (value !== null && typeof value === 'object') {
|
|
277
|
-
this
|
|
699
|
+
this.#encodeObjectAsMap(bytes, value, circularReferencesDetector)
|
|
278
700
|
} else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
279
|
-
this
|
|
701
|
+
this.#encodeValue(bytes, value)
|
|
280
702
|
}
|
|
281
703
|
}
|
|
282
704
|
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
705
|
+
#encodeObjectAsMap (bytes, value, circularReferencesDetector) {
|
|
706
|
+
const offset = bytes.length
|
|
707
|
+
bytes.reserve(5)
|
|
708
|
+
bytes.buffer[offset] = 0xDF
|
|
709
|
+
|
|
710
|
+
let count = 0
|
|
711
|
+
for (const key of Object.keys(value)) {
|
|
712
|
+
const entryValue = value[key]
|
|
713
|
+
if (typeof entryValue === 'string' || typeof entryValue === 'number' || typeof entryValue === 'boolean' ||
|
|
714
|
+
(entryValue !== null && typeof entryValue === 'object' &&
|
|
715
|
+
!circularReferencesDetector.has(entryValue))) {
|
|
716
|
+
this._encodeString(bytes, key)
|
|
717
|
+
this.#encodeObject(bytes, entryValue, circularReferencesDetector)
|
|
718
|
+
count++
|
|
719
|
+
}
|
|
298
720
|
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
_encodeObjectAsArray (bytes, value, circularReferencesDetector) {
|
|
302
|
-
const validValue = value.filter(item =>
|
|
303
|
-
typeof item === 'string' ||
|
|
304
|
-
typeof item === 'number' ||
|
|
305
|
-
(item !== null && typeof item === 'object' && !circularReferencesDetector.has(item)))
|
|
306
|
-
|
|
307
|
-
this._encodeArrayPrefix(bytes, validValue)
|
|
308
721
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
722
|
+
const target = bytes.buffer
|
|
723
|
+
target[offset + 1] = count >>> 24
|
|
724
|
+
target[offset + 2] = count >>> 16
|
|
725
|
+
target[offset + 3] = count >>> 8
|
|
726
|
+
target[offset + 4] = count
|
|
312
727
|
}
|
|
313
728
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
729
|
+
#encodeObjectAsArray (bytes, value, circularReferencesDetector) {
|
|
730
|
+
const offset = bytes.length
|
|
731
|
+
bytes.reserve(5)
|
|
732
|
+
bytes.buffer[offset] = 0xDD
|
|
733
|
+
|
|
734
|
+
let count = 0
|
|
735
|
+
for (const item of value) {
|
|
736
|
+
if (typeof item === 'string' || typeof item === 'number' ||
|
|
737
|
+
(item !== null && typeof item === 'object' && !circularReferencesDetector.has(item))) {
|
|
738
|
+
this.#encodeObject(bytes, item, circularReferencesDetector)
|
|
739
|
+
count++
|
|
320
740
|
}
|
|
321
741
|
}
|
|
322
|
-
}
|
|
323
742
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
743
|
+
const target = bytes.buffer
|
|
744
|
+
target[offset + 1] = count >>> 24
|
|
745
|
+
target[offset + 2] = count >>> 16
|
|
746
|
+
target[offset + 3] = count >>> 8
|
|
747
|
+
target[offset + 4] = count
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Specialized encoder for `span.span_events`. Walks the events directly,
|
|
752
|
+
* emitting OTel attribute typed wrappers inline from the raw attribute
|
|
753
|
+
* values — no `formatSpanEvents` pre-pass and no recursive generic walk.
|
|
754
|
+
*
|
|
755
|
+
* @param {MsgpackChunk} bytes
|
|
756
|
+
* @param {Array<{ name: string, time_unix_nano: number, attributes?: object }>} spanEvents
|
|
757
|
+
*/
|
|
758
|
+
#encodeSpanEvents (bytes, spanEvents) {
|
|
759
|
+
const offset = bytes.length
|
|
760
|
+
bytes.reserve(5)
|
|
761
|
+
bytes.buffer[offset] = 0xDD
|
|
327
762
|
|
|
328
|
-
|
|
329
|
-
|
|
763
|
+
let arrayCount = 0
|
|
764
|
+
for (const event of spanEvents) {
|
|
765
|
+
// `addEvent` and the OTel bridge do not type-check `name`, and a
|
|
766
|
+
// non-string would throw downstream in `Buffer.byteLength`. Drop the
|
|
767
|
+
// bad event silently so the rest of the trace still encodes.
|
|
768
|
+
if (event === null || typeof event !== 'object' || typeof event.name !== 'string') continue
|
|
330
769
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
770
|
+
const eventHeaderOffset = bytes.length
|
|
771
|
+
bytes.reserve(1)
|
|
772
|
+
bytes.buffer[eventHeaderOffset] = 0x82
|
|
334
773
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
774
|
+
bytes.set(KEY_NAME)
|
|
775
|
+
this._encodeString(bytes, event.name)
|
|
776
|
+
bytes.set(KEY_EVENT_TIME)
|
|
777
|
+
this.#encodeFloat(bytes, event.time_unix_nano)
|
|
338
778
|
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
seenKeys.add(key)
|
|
343
|
-
log.debug(message)
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function formatSpanEvents (span) {
|
|
348
|
-
for (const spanEvent of span.span_events) {
|
|
349
|
-
if (spanEvent.attributes) {
|
|
350
|
-
let hasAttributes = false
|
|
351
|
-
for (const [key, value] of Object.entries(spanEvent.attributes)) {
|
|
352
|
-
const newValue = convertSpanEventAttributeValues(key, value)
|
|
353
|
-
if (newValue === undefined) {
|
|
354
|
-
delete spanEvent.attributes[key] // delete from attributes if undefined
|
|
355
|
-
} else {
|
|
356
|
-
hasAttributes = true
|
|
357
|
-
spanEvent.attributes[key] = newValue
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (!hasAttributes) {
|
|
361
|
-
delete spanEvent.attributes
|
|
779
|
+
const attributes = event.attributes
|
|
780
|
+
if (attributes !== null && typeof attributes === 'object') {
|
|
781
|
+
this.#encodeAttributesIfAny(bytes, attributes, eventHeaderOffset)
|
|
362
782
|
}
|
|
783
|
+
arrayCount++
|
|
363
784
|
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
785
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
786
|
+
const target = bytes.buffer
|
|
787
|
+
target[offset + 1] = arrayCount >>> 24
|
|
788
|
+
target[offset + 2] = arrayCount >>> 16
|
|
789
|
+
target[offset + 3] = arrayCount >>> 8
|
|
790
|
+
target[offset + 4] = arrayCount
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Optimistically emits the `'attributes'` slot for an event. If every entry
|
|
795
|
+
* filters out, the partial output is rewound and the event's map header
|
|
796
|
+
* stays at 2 entries.
|
|
797
|
+
*
|
|
798
|
+
* @param {MsgpackChunk} bytes
|
|
799
|
+
* @param {Record<string, unknown>} attributes
|
|
800
|
+
* @param {number} eventHeaderOffset
|
|
801
|
+
*/
|
|
802
|
+
#encodeAttributesIfAny (bytes, attributes, eventHeaderOffset) {
|
|
803
|
+
const sectionStart = bytes.length
|
|
804
|
+
|
|
805
|
+
bytes.set(KEY_EVENT_ATTRIBUTES)
|
|
806
|
+
const countOffset = bytes.length
|
|
807
|
+
bytes.reserve(5)
|
|
808
|
+
bytes.buffer[countOffset] = 0xDF
|
|
809
|
+
|
|
810
|
+
let count = 0
|
|
811
|
+
for (const key of Object.keys(attributes)) {
|
|
812
|
+
if (this.#emitAttribute(bytes, key, attributes[key])) count++
|
|
372
813
|
}
|
|
373
|
-
}
|
|
374
814
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
bool_value: value,
|
|
815
|
+
if (count === 0) {
|
|
816
|
+
bytes.length = sectionStart
|
|
817
|
+
return
|
|
379
818
|
}
|
|
380
|
-
}
|
|
381
819
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
820
|
+
const target = bytes.buffer
|
|
821
|
+
target[countOffset + 1] = count >>> 24
|
|
822
|
+
target[countOffset + 2] = count >>> 16
|
|
823
|
+
target[countOffset + 3] = count >>> 8
|
|
824
|
+
target[countOffset + 4] = count
|
|
825
|
+
bytes.buffer[eventHeaderOffset] = 0x83
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Emit `<key, typed_wrapper>` for a single attribute. Returns true on a
|
|
830
|
+
* supported type, false (with a memoized debug log) otherwise.
|
|
831
|
+
*
|
|
832
|
+
* @param {MsgpackChunk} bytes
|
|
833
|
+
* @param {string} key
|
|
834
|
+
* @param {unknown} value
|
|
835
|
+
* @returns {boolean}
|
|
836
|
+
*/
|
|
837
|
+
#emitAttribute (bytes, key, value) {
|
|
838
|
+
if (typeof value === 'string') {
|
|
839
|
+
this._encodeString(bytes, key)
|
|
840
|
+
bytes.set(ATTR_PREFIX_STRING)
|
|
841
|
+
this._encodeString(bytes, value)
|
|
842
|
+
return true
|
|
388
843
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
844
|
+
if (typeof value === 'number') {
|
|
845
|
+
this._encodeString(bytes, key)
|
|
846
|
+
bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
|
|
847
|
+
this._encodeIntOrFloat(bytes, value)
|
|
848
|
+
return true
|
|
849
|
+
}
|
|
850
|
+
if (typeof value === 'boolean') {
|
|
851
|
+
this._encodeString(bytes, key)
|
|
852
|
+
bytes.set(value ? ATTR_PAYLOAD_BOOL_TRUE : ATTR_PAYLOAD_BOOL_FALSE)
|
|
853
|
+
return true
|
|
854
|
+
}
|
|
855
|
+
if (Array.isArray(value)) {
|
|
856
|
+
return this.#emitArrayAttribute(bytes, key, value)
|
|
857
|
+
}
|
|
858
|
+
memoizedLogDebug(key, 'Encountered unsupported data type for span event v0.4 encoding, key: ' +
|
|
859
|
+
`${key}: with value: ${typeof value}. Skipping encoding of pair.`
|
|
860
|
+
)
|
|
861
|
+
return false
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Emit `<key, { type: 4, array_value: { values: [...] } }>` from a raw
|
|
866
|
+
* array of primitives. Filters nested arrays / unsupported items; if no
|
|
867
|
+
* items remain the whole entry is rewound.
|
|
868
|
+
*
|
|
869
|
+
* @param {MsgpackChunk} bytes
|
|
870
|
+
* @param {string} key
|
|
871
|
+
* @param {Array<unknown>} array
|
|
872
|
+
* @returns {boolean}
|
|
873
|
+
*/
|
|
874
|
+
#emitArrayAttribute (bytes, key, array) {
|
|
875
|
+
const sectionStart = bytes.length
|
|
876
|
+
|
|
877
|
+
this._encodeString(bytes, key)
|
|
878
|
+
bytes.set(ATTR_PREFIX_ARRAY)
|
|
879
|
+
const arrayCountOffset = bytes.length
|
|
880
|
+
bytes.reserve(4)
|
|
881
|
+
|
|
882
|
+
let count = 0
|
|
883
|
+
for (const item of array) {
|
|
884
|
+
if (this.#emitArrayItem(bytes, key, item)) count++
|
|
392
885
|
}
|
|
393
|
-
}
|
|
394
886
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const convertedVal = convertSpanEventAttributeValues(key, val, 1)
|
|
400
|
-
if (convertedVal !== undefined) {
|
|
401
|
-
convertedArray.push(convertedVal)
|
|
402
|
-
}
|
|
403
|
-
}
|
|
887
|
+
if (count === 0) {
|
|
888
|
+
bytes.length = sectionStart
|
|
889
|
+
return false
|
|
890
|
+
}
|
|
404
891
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
892
|
+
const target = bytes.buffer
|
|
893
|
+
target[arrayCountOffset] = count >>> 24
|
|
894
|
+
target[arrayCountOffset + 1] = count >>> 16
|
|
895
|
+
target[arrayCountOffset + 2] = count >>> 8
|
|
896
|
+
target[arrayCountOffset + 3] = count
|
|
897
|
+
return true
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Emit a single typed wrapper inside an `array_value.values` array. No
|
|
902
|
+
* recursion: nested arrays are dropped with a memoized debug log.
|
|
903
|
+
*
|
|
904
|
+
* @param {MsgpackChunk} bytes
|
|
905
|
+
* @param {string} key
|
|
906
|
+
* @param {unknown} value
|
|
907
|
+
* @returns {boolean}
|
|
908
|
+
*/
|
|
909
|
+
#emitArrayItem (bytes, key, value) {
|
|
910
|
+
if (typeof value === 'string') {
|
|
911
|
+
bytes.set(ATTR_PREFIX_STRING)
|
|
912
|
+
this._encodeString(bytes, value)
|
|
913
|
+
return true
|
|
914
|
+
}
|
|
915
|
+
if (typeof value === 'number') {
|
|
916
|
+
bytes.set(Number.isInteger(value) ? ATTR_PREFIX_INT : ATTR_PREFIX_DOUBLE)
|
|
917
|
+
this._encodeIntOrFloat(bytes, value)
|
|
918
|
+
return true
|
|
919
|
+
}
|
|
920
|
+
if (typeof value === 'boolean') {
|
|
921
|
+
bytes.set(value ? ATTR_PAYLOAD_BOOL_TRUE : ATTR_PAYLOAD_BOOL_FALSE)
|
|
922
|
+
return true
|
|
923
|
+
}
|
|
924
|
+
if (Array.isArray(value)) {
|
|
414
925
|
memoizedLogDebug(key, 'Encountered nested array data type for span event v0.4 encoding. ' +
|
|
415
926
|
`Skipping encoding key: ${key}: with value: ${typeof value}.`
|
|
416
927
|
)
|
|
417
928
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
929
|
+
return false
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const seenKeys = new Set()
|
|
934
|
+
function memoizedLogDebug (key, message) {
|
|
935
|
+
if (!seenKeys.has(key)) {
|
|
936
|
+
seenKeys.add(key)
|
|
937
|
+
log.debug(message)
|
|
422
938
|
}
|
|
423
939
|
}
|
|
424
940
|
|
|
425
|
-
module.exports = { AgentEncoder }
|
|
941
|
+
module.exports = { AgentEncoder, stringifySpanEvents }
|