@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.
- package/LICENSE +26 -0
- package/README.md +126 -0
- package/index.d.ts +464 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +97 -0
- package/lib/agent.js +1226 -0
- package/lib/apm-client/apm-client.js +107 -0
- package/lib/apm-client/channel-writer.js +334 -0
- package/lib/apm-client/jsonl-file-client.js +241 -0
- package/lib/apm-client/ndjson.js +20 -0
- package/lib/apm-client/noop-apm-client.js +79 -0
- package/lib/apm-client/s3-uploader.js +308 -0
- package/lib/apm-client/truncate.js +507 -0
- package/lib/async-hooks-polyfill.js +58 -0
- package/lib/cloud-metadata/aws.js +175 -0
- package/lib/cloud-metadata/azure.js +123 -0
- package/lib/cloud-metadata/callback-coordination.js +159 -0
- package/lib/cloud-metadata/gcp.js +133 -0
- package/lib/cloud-metadata/index.js +175 -0
- package/lib/config/config.js +431 -0
- package/lib/config/normalizers.js +649 -0
- package/lib/config/schema.js +946 -0
- package/lib/constants.js +35 -0
- package/lib/errors.js +303 -0
- package/lib/filters/sanitize-field-names.js +69 -0
- package/lib/http-request.js +249 -0
- package/lib/instrumentation/context.js +56 -0
- package/lib/instrumentation/dropped-spans-stats.js +112 -0
- package/lib/instrumentation/elasticsearch-shared.js +63 -0
- package/lib/instrumentation/express-utils.js +91 -0
- package/lib/instrumentation/generic-span.js +322 -0
- package/lib/instrumentation/http-shared.js +424 -0
- package/lib/instrumentation/ids.js +39 -0
- package/lib/instrumentation/index.js +1078 -0
- package/lib/instrumentation/modules/@apollo/server.js +39 -0
- package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
- package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
- package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
- package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
- package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
- package/lib/instrumentation/modules/apollo-server-core.js +49 -0
- package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
- package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
- package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
- package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
- package/lib/instrumentation/modules/aws-sdk.js +76 -0
- package/lib/instrumentation/modules/bluebird.js +93 -0
- package/lib/instrumentation/modules/cassandra-driver.js +280 -0
- package/lib/instrumentation/modules/elasticsearch.js +200 -0
- package/lib/instrumentation/modules/express-graphql.js +66 -0
- package/lib/instrumentation/modules/express-queue.js +28 -0
- package/lib/instrumentation/modules/express.js +162 -0
- package/lib/instrumentation/modules/fastify.js +179 -0
- package/lib/instrumentation/modules/finalhandler.js +41 -0
- package/lib/instrumentation/modules/generic-pool.js +85 -0
- package/lib/instrumentation/modules/graphql.js +256 -0
- package/lib/instrumentation/modules/handlebars.js +33 -0
- package/lib/instrumentation/modules/http.js +112 -0
- package/lib/instrumentation/modules/http2.js +320 -0
- package/lib/instrumentation/modules/https.js +68 -0
- package/lib/instrumentation/modules/ioredis.js +94 -0
- package/lib/instrumentation/modules/jade.js +29 -0
- package/lib/instrumentation/modules/kafkajs.js +476 -0
- package/lib/instrumentation/modules/knex.js +91 -0
- package/lib/instrumentation/modules/koa-router.js +74 -0
- package/lib/instrumentation/modules/koa.js +15 -0
- package/lib/instrumentation/modules/memcached.js +100 -0
- package/lib/instrumentation/modules/mimic-response.js +45 -0
- package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
- package/lib/instrumentation/modules/mongodb-core.js +206 -0
- package/lib/instrumentation/modules/mongodb.js +259 -0
- package/lib/instrumentation/modules/mysql.js +200 -0
- package/lib/instrumentation/modules/mysql2.js +140 -0
- package/lib/instrumentation/modules/pg.js +148 -0
- package/lib/instrumentation/modules/pug.js +29 -0
- package/lib/instrumentation/modules/redis.js +176 -0
- package/lib/instrumentation/modules/restify.js +52 -0
- package/lib/instrumentation/modules/tedious.js +159 -0
- package/lib/instrumentation/modules/undici.js +270 -0
- package/lib/instrumentation/modules/ws.js +59 -0
- package/lib/instrumentation/noop-transaction.js +81 -0
- package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
- package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
- package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
- package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
- package/lib/instrumentation/run-context/RunContext.js +151 -0
- package/lib/instrumentation/run-context/index.js +23 -0
- package/lib/instrumentation/shimmer.js +123 -0
- package/lib/instrumentation/span-compression.js +239 -0
- package/lib/instrumentation/span.js +621 -0
- package/lib/instrumentation/template-shared.js +43 -0
- package/lib/instrumentation/timer.js +84 -0
- package/lib/instrumentation/transaction.js +571 -0
- package/lib/load-source-map.js +100 -0
- package/lib/logging.js +212 -0
- package/lib/metrics/index.js +92 -0
- package/lib/metrics/platforms/generic/index.js +40 -0
- package/lib/metrics/platforms/generic/process-cpu.js +22 -0
- package/lib/metrics/platforms/generic/process-top.js +157 -0
- package/lib/metrics/platforms/generic/stats.js +34 -0
- package/lib/metrics/platforms/generic/system-cpu.js +51 -0
- package/lib/metrics/platforms/linux/index.js +19 -0
- package/lib/metrics/platforms/linux/stats.js +213 -0
- package/lib/metrics/queue.js +90 -0
- package/lib/metrics/registry.js +52 -0
- package/lib/metrics/reporter.js +119 -0
- package/lib/metrics/runtime.js +77 -0
- package/lib/middleware/connect.js +16 -0
- package/lib/parsers.js +225 -0
- package/lib/propwrap.js +147 -0
- package/lib/stacktraces.js +537 -0
- package/lib/symbols.js +15 -0
- package/lib/tracecontext/index.js +115 -0
- package/lib/tracecontext/traceparent.js +185 -0
- package/lib/tracecontext/tracestate.js +388 -0
- package/lib/wildcard-matcher.js +52 -0
- package/loader.mjs +7 -0
- package/package.json +98 -0
- package/start.d.ts +8 -0
- package/start.js +29 -0
- package/types/aws-lambda.d.ts +98 -0
- 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 };
|