@redthreadlabs/tracelog 1.4.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.
Files changed (127) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +126 -0
  3. package/index.d.ts +464 -0
  4. package/index.js +11 -0
  5. package/lib/InflightEventSet.js +53 -0
  6. package/lib/activation-method.js +97 -0
  7. package/lib/agent.js +1226 -0
  8. package/lib/apm-client/apm-client.js +107 -0
  9. package/lib/apm-client/channel-writer.js +334 -0
  10. package/lib/apm-client/jsonl-file-client.js +241 -0
  11. package/lib/apm-client/ndjson.js +20 -0
  12. package/lib/apm-client/noop-apm-client.js +79 -0
  13. package/lib/apm-client/s3-uploader.js +308 -0
  14. package/lib/apm-client/truncate.js +507 -0
  15. package/lib/async-hooks-polyfill.js +58 -0
  16. package/lib/cloud-metadata/aws.js +175 -0
  17. package/lib/cloud-metadata/azure.js +123 -0
  18. package/lib/cloud-metadata/callback-coordination.js +159 -0
  19. package/lib/cloud-metadata/gcp.js +133 -0
  20. package/lib/cloud-metadata/index.js +175 -0
  21. package/lib/config/config.js +431 -0
  22. package/lib/config/normalizers.js +649 -0
  23. package/lib/config/schema.js +946 -0
  24. package/lib/constants.js +35 -0
  25. package/lib/errors.js +303 -0
  26. package/lib/filters/sanitize-field-names.js +69 -0
  27. package/lib/http-request.js +249 -0
  28. package/lib/instrumentation/context.js +56 -0
  29. package/lib/instrumentation/dropped-spans-stats.js +112 -0
  30. package/lib/instrumentation/elasticsearch-shared.js +63 -0
  31. package/lib/instrumentation/express-utils.js +91 -0
  32. package/lib/instrumentation/generic-span.js +322 -0
  33. package/lib/instrumentation/http-shared.js +424 -0
  34. package/lib/instrumentation/ids.js +39 -0
  35. package/lib/instrumentation/index.js +1078 -0
  36. package/lib/instrumentation/modules/@apollo/server.js +39 -0
  37. package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
  38. package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
  39. package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
  40. package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
  41. package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
  42. package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
  43. package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
  44. package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
  45. package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
  46. package/lib/instrumentation/modules/apollo-server-core.js +49 -0
  47. package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
  48. package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
  49. package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
  50. package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
  51. package/lib/instrumentation/modules/aws-sdk.js +76 -0
  52. package/lib/instrumentation/modules/bluebird.js +93 -0
  53. package/lib/instrumentation/modules/cassandra-driver.js +280 -0
  54. package/lib/instrumentation/modules/elasticsearch.js +200 -0
  55. package/lib/instrumentation/modules/express-graphql.js +66 -0
  56. package/lib/instrumentation/modules/express-queue.js +28 -0
  57. package/lib/instrumentation/modules/express.js +162 -0
  58. package/lib/instrumentation/modules/fastify.js +179 -0
  59. package/lib/instrumentation/modules/finalhandler.js +41 -0
  60. package/lib/instrumentation/modules/generic-pool.js +85 -0
  61. package/lib/instrumentation/modules/graphql.js +256 -0
  62. package/lib/instrumentation/modules/handlebars.js +33 -0
  63. package/lib/instrumentation/modules/http.js +112 -0
  64. package/lib/instrumentation/modules/http2.js +320 -0
  65. package/lib/instrumentation/modules/https.js +68 -0
  66. package/lib/instrumentation/modules/ioredis.js +94 -0
  67. package/lib/instrumentation/modules/jade.js +29 -0
  68. package/lib/instrumentation/modules/kafkajs.js +476 -0
  69. package/lib/instrumentation/modules/knex.js +91 -0
  70. package/lib/instrumentation/modules/koa-router.js +74 -0
  71. package/lib/instrumentation/modules/koa.js +15 -0
  72. package/lib/instrumentation/modules/memcached.js +100 -0
  73. package/lib/instrumentation/modules/mimic-response.js +45 -0
  74. package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
  75. package/lib/instrumentation/modules/mongodb-core.js +206 -0
  76. package/lib/instrumentation/modules/mongodb.js +259 -0
  77. package/lib/instrumentation/modules/mysql.js +200 -0
  78. package/lib/instrumentation/modules/mysql2.js +140 -0
  79. package/lib/instrumentation/modules/pg.js +148 -0
  80. package/lib/instrumentation/modules/pug.js +29 -0
  81. package/lib/instrumentation/modules/redis.js +176 -0
  82. package/lib/instrumentation/modules/restify.js +52 -0
  83. package/lib/instrumentation/modules/tedious.js +159 -0
  84. package/lib/instrumentation/modules/undici.js +270 -0
  85. package/lib/instrumentation/modules/ws.js +59 -0
  86. package/lib/instrumentation/noop-transaction.js +81 -0
  87. package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
  88. package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
  89. package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
  90. package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
  91. package/lib/instrumentation/run-context/RunContext.js +151 -0
  92. package/lib/instrumentation/run-context/index.js +23 -0
  93. package/lib/instrumentation/shimmer.js +123 -0
  94. package/lib/instrumentation/span-compression.js +239 -0
  95. package/lib/instrumentation/span.js +621 -0
  96. package/lib/instrumentation/template-shared.js +43 -0
  97. package/lib/instrumentation/timer.js +84 -0
  98. package/lib/instrumentation/transaction.js +571 -0
  99. package/lib/load-source-map.js +100 -0
  100. package/lib/logging.js +212 -0
  101. package/lib/metrics/index.js +92 -0
  102. package/lib/metrics/platforms/generic/index.js +40 -0
  103. package/lib/metrics/platforms/generic/process-cpu.js +22 -0
  104. package/lib/metrics/platforms/generic/process-top.js +157 -0
  105. package/lib/metrics/platforms/generic/stats.js +34 -0
  106. package/lib/metrics/platforms/generic/system-cpu.js +51 -0
  107. package/lib/metrics/platforms/linux/index.js +19 -0
  108. package/lib/metrics/platforms/linux/stats.js +213 -0
  109. package/lib/metrics/queue.js +90 -0
  110. package/lib/metrics/registry.js +52 -0
  111. package/lib/metrics/reporter.js +119 -0
  112. package/lib/metrics/runtime.js +77 -0
  113. package/lib/middleware/connect.js +16 -0
  114. package/lib/parsers.js +225 -0
  115. package/lib/propwrap.js +147 -0
  116. package/lib/stacktraces.js +537 -0
  117. package/lib/symbols.js +15 -0
  118. package/lib/tracecontext/index.js +115 -0
  119. package/lib/tracecontext/traceparent.js +185 -0
  120. package/lib/tracecontext/tracestate.js +388 -0
  121. package/lib/wildcard-matcher.js +52 -0
  122. package/loader.mjs +7 -0
  123. package/package.json +98 -0
  124. package/start.d.ts +8 -0
  125. package/start.js +29 -0
  126. package/types/aws-lambda.d.ts +98 -0
  127. package/types/connect.d.ts +23 -0
@@ -0,0 +1,79 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and other contributors where applicable.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ // A no-op (does nothing) APM Client -- i.e. the APM server client API
10
+ // provided by elastic-apm-http-client.
11
+ //
12
+ // This is used for some configurations (when `disableSend=true` or when
13
+ // `contextPropagationOnly=true`) and in some tests.
14
+
15
+ class NoopApmClient {
16
+ config(opts) {}
17
+
18
+ addMetadataFilter(fn) {}
19
+ setExtraMetadata(metadata) {}
20
+
21
+ lambdaStart() {}
22
+ lambdaShouldRegisterTransactions() {
23
+ return true;
24
+ }
25
+ lambdaRegisterTransaction(trans, awsRequestId) {}
26
+
27
+ sendSpan(span, cb) {
28
+ if (cb) {
29
+ process.nextTick(cb);
30
+ }
31
+ }
32
+
33
+ sendTransaction(transaction, cb) {
34
+ if (cb) {
35
+ process.nextTick(cb);
36
+ }
37
+ }
38
+
39
+ sendError(_error, cb) {
40
+ if (cb) {
41
+ process.nextTick(cb);
42
+ }
43
+ }
44
+
45
+ sendMetricSet(metricset, cb) {
46
+ if (cb) {
47
+ process.nextTick(cb);
48
+ }
49
+ }
50
+
51
+ sendEvent(event, cb) {
52
+ if (cb) {
53
+ process.nextTick(cb);
54
+ }
55
+ }
56
+
57
+ flush(opts, cb) {
58
+ if (typeof opts === 'function') {
59
+ cb = opts;
60
+ opts = {};
61
+ } else if (!opts) {
62
+ opts = {};
63
+ }
64
+ if (cb) {
65
+ process.nextTick(cb);
66
+ }
67
+ }
68
+
69
+ supportsKeepingUnsampledTransaction() {
70
+ return true;
71
+ }
72
+
73
+ // Inherited from Writable, called in agent.js.
74
+ destroy() {}
75
+ }
76
+
77
+ module.exports = {
78
+ NoopApmClient,
79
+ };
@@ -0,0 +1,308 @@
1
+ /*
2
+ * Copyright Shaxpir Inc. All rights reserved.
3
+ * Licensed under the BSD 2-Clause License; you may not use this file except in
4
+ * compliance with the BSD 2-Clause License.
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const fs = require('fs');
10
+ const os = require('os');
11
+ const path = require('path');
12
+ const zlib = require('zlib');
13
+ const { createGzip } = require('zlib');
14
+ const { pipeline } = require('stream');
15
+ const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
16
+
17
+ class S3Uploader {
18
+ /**
19
+ * @param {Object} opts
20
+ * @param {string} opts.bucket - S3 bucket name
21
+ * @param {string} opts.keyTemplate - S3 key template with {variable} placeholders
22
+ * @param {string} [opts.region] - AWS region
23
+ * @param {string} [opts.accessKeyId] - AWS access key ID
24
+ * @param {string} [opts.secretAccessKey] - AWS secret access key
25
+ * @param {string} [opts.sessionToken] - AWS session token
26
+ * @param {string} opts.serviceName - Service name for template variables
27
+ * @param {string} [opts.environment] - Environment for template variables
28
+ * @param {boolean} [opts.gzipCompleted=true] - Gzip completed files before upload
29
+ * @param {boolean} [opts.gzipCurrent=true] - Gzip current files before upload
30
+ * @param {Object} [opts.logger] - Logger instance
31
+ * @param {Object} [opts.s3Client] - S3 client instance (must have a send() method).
32
+ * Defaults to a real S3Client from @aws-sdk/client-s3. Inject a mock for testing.
33
+ * @param {Function} [opts.clock] - Clock provider for testability. Returns a Date.
34
+ */
35
+ constructor(opts) {
36
+ this._bucket = opts.bucket;
37
+ this._keyTemplate = opts.keyTemplate;
38
+ this._serviceName = opts.serviceName || 'unknown';
39
+ this._environment = opts.environment || 'development';
40
+ this._gzipCompleted = opts.gzipCompleted !== false;
41
+ this._gzipCurrent = opts.gzipCurrent !== false;
42
+ this._log = opts.logger || null;
43
+
44
+ // Clock provider for testability.
45
+ this._clock = opts.clock || (() => new Date());
46
+
47
+ // S3 client abstraction: inject a mock for testing, or use the real SDK.
48
+ if (opts.s3Client) {
49
+ this._s3 = opts.s3Client;
50
+ } else {
51
+ const clientOpts = {};
52
+ if (opts.region) {
53
+ clientOpts.region = opts.region;
54
+ }
55
+ if (opts.accessKeyId && opts.secretAccessKey) {
56
+ clientOpts.credentials = {
57
+ accessKeyId: opts.accessKeyId,
58
+ secretAccessKey: opts.secretAccessKey,
59
+ };
60
+ if (opts.sessionToken) {
61
+ clientOpts.credentials.sessionToken = opts.sessionToken;
62
+ }
63
+ }
64
+ this._s3 = new S3Client(clientOpts);
65
+ }
66
+
67
+ this._pendingUploads = 0;
68
+ }
69
+
70
+ /**
71
+ * Upload a completed (rotated) file, then delete local on success.
72
+ * @param {string} filePath - Path to the completed JSONL file
73
+ * @param {Object} vars - Template variables (channel, interval)
74
+ */
75
+ uploadCompleted(filePath, vars) {
76
+ if (!fs.existsSync(filePath)) return;
77
+
78
+ if (this._gzipCompleted) {
79
+ this._uploadCompletedGzipped(filePath, vars);
80
+ } else {
81
+ this._uploadCompletedRaw(filePath, vars);
82
+ }
83
+ }
84
+
85
+ _uploadCompletedGzipped(filePath, vars) {
86
+ const key = this._resolveKey(vars) + '.gz';
87
+ const gzPath = filePath + '.gz';
88
+
89
+ this._pendingUploads++;
90
+
91
+ const readStream = fs.createReadStream(filePath);
92
+ const gzip = createGzip();
93
+ const writeStream = fs.createWriteStream(gzPath);
94
+
95
+ pipeline(readStream, gzip, writeStream, (err) => {
96
+ if (err) {
97
+ this._logError('Failed to gzip %s: %s', filePath, err.message);
98
+ this._pendingUploads--;
99
+ return;
100
+ }
101
+
102
+ const body = fs.createReadStream(gzPath);
103
+ const command = new PutObjectCommand({
104
+ Bucket: this._bucket,
105
+ Key: key,
106
+ Body: body,
107
+ ContentType: 'application/x-ndjson',
108
+ ContentEncoding: 'gzip',
109
+ });
110
+
111
+ this._s3
112
+ .send(command)
113
+ .then(() => {
114
+ if (this._log) {
115
+ this._log.debug('Uploaded completed log to s3://%s/%s', this._bucket, key);
116
+ }
117
+ try { fs.unlinkSync(filePath); } catch (e) { /* ignore */ }
118
+ try { fs.unlinkSync(gzPath); } catch (e) { /* ignore */ }
119
+ })
120
+ .catch((uploadErr) => {
121
+ this._logError(
122
+ 'Failed to upload %s to S3: %s',
123
+ filePath,
124
+ uploadErr.message,
125
+ );
126
+ try { fs.unlinkSync(gzPath); } catch (e) { /* ignore */ }
127
+ })
128
+ .finally(() => {
129
+ this._pendingUploads--;
130
+ });
131
+ });
132
+ }
133
+
134
+ _uploadCompletedRaw(filePath, vars) {
135
+ const key = this._resolveKey(vars);
136
+
137
+ this._pendingUploads++;
138
+
139
+ const body = fs.createReadStream(filePath);
140
+ const command = new PutObjectCommand({
141
+ Bucket: this._bucket,
142
+ Key: key,
143
+ Body: body,
144
+ ContentType: 'application/x-ndjson',
145
+ });
146
+
147
+ this._s3
148
+ .send(command)
149
+ .then(() => {
150
+ if (this._log) {
151
+ this._log.debug('Uploaded completed log to s3://%s/%s', this._bucket, key);
152
+ }
153
+ try { fs.unlinkSync(filePath); } catch (e) { /* ignore */ }
154
+ })
155
+ .catch((uploadErr) => {
156
+ this._logError(
157
+ 'Failed to upload %s to S3: %s',
158
+ filePath,
159
+ uploadErr.message,
160
+ );
161
+ })
162
+ .finally(() => {
163
+ this._pendingUploads--;
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Upload the current (incomplete) file without deletion.
169
+ * Uses a stable S3 key (based on the filename, not a timestamp) so that
170
+ * each upload overwrites the previous snapshot instead of creating duplicates.
171
+ * @param {string} filePath - Path to the current JSONL file
172
+ * @param {Function} [cb] - Callback when upload completes
173
+ */
174
+ /**
175
+ * Upload the current (incomplete) file without deletion.
176
+ * Uses 'current' as the interval so periodic uploads overwrite the same object.
177
+ * @param {string} filePath - Path to the current JSONL file
178
+ * @param {Object} vars - Template variables (channel); interval is set to 'current'
179
+ * @param {Function} [cb] - Callback when upload completes
180
+ */
181
+ uploadCurrent(filePath, vars, cb) {
182
+ if (typeof vars === 'function') {
183
+ cb = vars;
184
+ vars = {};
185
+ }
186
+ if (!fs.existsSync(filePath)) {
187
+ if (cb) process.nextTick(cb);
188
+ return;
189
+ }
190
+
191
+ const currentVars = { ...vars, interval: 'current' };
192
+ const rawBody = fs.readFileSync(filePath);
193
+
194
+ if (this._gzipCurrent) {
195
+ this._uploadCurrentGzipped(filePath, rawBody, currentVars, cb);
196
+ } else {
197
+ this._uploadCurrentRaw(filePath, rawBody, currentVars, cb);
198
+ }
199
+ }
200
+
201
+ _uploadCurrentGzipped(filePath, rawBody, vars, cb) {
202
+ const key = this._resolveKey(vars) + '.gz';
203
+
204
+ this._pendingUploads++;
205
+
206
+ zlib.gzip(rawBody, (err, compressed) => {
207
+ if (err) {
208
+ this._logError('Failed to gzip current log: %s', err.message);
209
+ this._pendingUploads--;
210
+ if (cb) cb();
211
+ return;
212
+ }
213
+
214
+ const command = new PutObjectCommand({
215
+ Bucket: this._bucket,
216
+ Key: key,
217
+ Body: compressed,
218
+ ContentType: 'application/x-ndjson',
219
+ ContentEncoding: 'gzip',
220
+ });
221
+
222
+ this._s3
223
+ .send(command)
224
+ .then(() => {
225
+ if (this._log) {
226
+ this._log.debug('Uploaded current log to s3://%s/%s', this._bucket, key);
227
+ }
228
+ })
229
+ .catch((uploadErr) => {
230
+ this._logError(
231
+ 'Failed to upload current log to S3: %s',
232
+ uploadErr.message,
233
+ );
234
+ })
235
+ .finally(() => {
236
+ this._pendingUploads--;
237
+ if (cb) cb();
238
+ });
239
+ });
240
+ }
241
+
242
+ _uploadCurrentRaw(filePath, rawBody, vars, cb) {
243
+ const key = this._resolveKey(vars);
244
+
245
+ this._pendingUploads++;
246
+
247
+ const command = new PutObjectCommand({
248
+ Bucket: this._bucket,
249
+ Key: key,
250
+ Body: rawBody,
251
+ ContentType: 'application/x-ndjson',
252
+ });
253
+
254
+ this._s3
255
+ .send(command)
256
+ .then(() => {
257
+ if (this._log) {
258
+ this._log.debug('Uploaded current log to s3://%s/%s', this._bucket, key);
259
+ }
260
+ })
261
+ .catch((uploadErr) => {
262
+ this._logError(
263
+ 'Failed to upload current log to S3: %s',
264
+ uploadErr.message,
265
+ );
266
+ })
267
+ .finally(() => {
268
+ this._pendingUploads--;
269
+ if (cb) cb();
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Resolve an S3 key from the template.
275
+ *
276
+ * @param {Object} vars - Template variables:
277
+ * - {string} channel - Channel name (e.g. 'server', 'client')
278
+ * - {string} interval - Period label (e.g. '2026-03-17') or 'current'
279
+ */
280
+ _resolveKey(vars) {
281
+ const allVars = {
282
+ serviceName: this._serviceName,
283
+ environment: this._environment,
284
+ hostname: os.hostname(),
285
+ ...vars,
286
+ };
287
+
288
+ return this._keyTemplate.replace(/\{(\w+)\}/g, (match, name) => {
289
+ return allVars[name] !== undefined ? allVars[name] : match;
290
+ });
291
+ }
292
+
293
+ _logError(fmt, ...args) {
294
+ if (this._log) {
295
+ this._log.error(fmt, ...args);
296
+ }
297
+ }
298
+ }
299
+
300
+ function _pad2(n) {
301
+ return String(n).padStart(2, '0');
302
+ }
303
+
304
+ function _fmtDate(d) {
305
+ return `${d.getFullYear()}-${_pad2(d.getMonth() + 1)}-${_pad2(d.getDate())}`;
306
+ }
307
+
308
+ module.exports = { S3Uploader };