@salesforce/core 3.30.14 → 3.31.7
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.txt +11 -11
- package/README.md +222 -227
- package/lib/config/aliasesConfig.d.ts +12 -12
- package/lib/config/aliasesConfig.js +27 -27
- package/lib/config/authInfoConfig.d.ts +19 -19
- package/lib/config/authInfoConfig.js +34 -34
- package/lib/config/config.d.ts +311 -311
- package/lib/config/config.js +574 -574
- package/lib/config/configAggregator.d.ts +232 -232
- package/lib/config/configAggregator.js +379 -379
- package/lib/config/configFile.d.ts +199 -199
- package/lib/config/configFile.js +340 -340
- package/lib/config/configGroup.d.ts +141 -141
- package/lib/config/configGroup.js +224 -224
- package/lib/config/configStore.d.ts +241 -241
- package/lib/config/configStore.js +352 -352
- package/lib/config/envVars.d.ts +101 -101
- package/lib/config/envVars.js +456 -456
- package/lib/config/orgUsersConfig.d.ts +31 -31
- package/lib/config/orgUsersConfig.js +41 -41
- package/lib/config/sandboxOrgConfig.d.ts +37 -37
- package/lib/config/sandboxOrgConfig.js +50 -50
- package/lib/config/sandboxProcessCache.d.ts +16 -16
- package/lib/config/sandboxProcessCache.js +37 -37
- package/lib/config/tokensConfig.d.ts +10 -10
- package/lib/config/tokensConfig.js +28 -28
- package/lib/config/ttlConfig.d.ts +34 -34
- package/lib/config/ttlConfig.js +54 -54
- package/lib/crypto/crypto.d.ts +54 -54
- package/lib/crypto/crypto.js +220 -220
- package/lib/crypto/keyChain.d.ts +8 -8
- package/lib/crypto/keyChain.js +61 -61
- package/lib/crypto/keyChainImpl.d.ts +116 -116
- package/lib/crypto/keyChainImpl.js +486 -486
- package/lib/crypto/secureBuffer.d.ts +46 -46
- package/lib/crypto/secureBuffer.js +82 -82
- package/lib/deviceOauthService.d.ts +71 -71
- package/lib/deviceOauthService.js +191 -191
- package/lib/exported.d.ts +38 -38
- package/lib/exported.js +118 -118
- package/lib/global.d.ts +70 -70
- package/lib/global.js +109 -109
- package/lib/lifecycleEvents.d.ts +93 -93
- package/lib/lifecycleEvents.js +188 -188
- package/lib/logger.d.ts +381 -381
- package/lib/logger.js +734 -734
- package/lib/messages.d.ts +291 -291
- package/lib/messages.js +543 -543
- package/lib/org/authInfo.d.ts +344 -344
- package/lib/org/authInfo.js +892 -892
- package/lib/org/authRemover.d.ts +88 -88
- package/lib/org/authRemover.js +182 -182
- package/lib/org/connection.d.ts +197 -197
- package/lib/org/connection.js +395 -395
- package/lib/org/index.d.ts +6 -6
- package/lib/org/index.js +28 -28
- package/lib/org/org.d.ts +558 -558
- package/lib/org/org.js +1267 -1267
- package/lib/org/orgConfigProperties.d.ts +69 -69
- package/lib/org/orgConfigProperties.js +136 -136
- package/lib/org/permissionSetAssignment.d.ts +35 -35
- package/lib/org/permissionSetAssignment.js +125 -125
- package/lib/org/scratchOrgCache.d.ts +20 -20
- package/lib/org/scratchOrgCache.js +32 -32
- package/lib/org/scratchOrgCreate.d.ts +54 -54
- package/lib/org/scratchOrgCreate.js +216 -216
- package/lib/org/scratchOrgErrorCodes.d.ts +10 -10
- package/lib/org/scratchOrgErrorCodes.js +88 -88
- package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -26
- package/lib/org/scratchOrgFeatureDeprecation.js +109 -109
- package/lib/org/scratchOrgInfoApi.d.ts +68 -68
- package/lib/org/scratchOrgInfoApi.js +413 -413
- package/lib/org/scratchOrgInfoGenerator.d.ts +64 -64
- package/lib/org/scratchOrgInfoGenerator.js +241 -241
- package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -10
- package/lib/org/scratchOrgLifecycleEvents.js +40 -40
- package/lib/org/scratchOrgSettingsGenerator.d.ts +78 -78
- package/lib/org/scratchOrgSettingsGenerator.js +276 -276
- package/lib/org/scratchOrgTypes.d.ts +43 -43
- package/lib/org/scratchOrgTypes.js +8 -8
- package/lib/org/user.d.ts +187 -187
- package/lib/org/user.js +448 -448
- package/lib/schema/printer.d.ts +79 -79
- package/lib/schema/printer.js +260 -260
- package/lib/schema/validator.d.ts +70 -70
- package/lib/schema/validator.js +169 -169
- package/lib/sfError.d.ts +73 -73
- package/lib/sfError.js +136 -136
- package/lib/sfProject.d.ts +357 -357
- package/lib/sfProject.js +671 -671
- package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -98
- package/lib/stateAggregator/accessors/aliasAccessor.js +145 -145
- package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -101
- package/lib/stateAggregator/accessors/orgAccessor.js +240 -240
- package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -8
- package/lib/stateAggregator/accessors/sandboxAccessor.js +27 -27
- package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -63
- package/lib/stateAggregator/accessors/tokenAccessor.js +79 -79
- package/lib/stateAggregator/index.d.ts +4 -4
- package/lib/stateAggregator/index.js +26 -26
- package/lib/stateAggregator/stateAggregator.d.ts +25 -25
- package/lib/stateAggregator/stateAggregator.js +45 -45
- package/lib/status/myDomainResolver.d.ts +66 -66
- package/lib/status/myDomainResolver.js +124 -124
- package/lib/status/pollingClient.d.ts +85 -85
- package/lib/status/pollingClient.js +115 -115
- package/lib/status/streamingClient.d.ts +244 -244
- package/lib/status/streamingClient.js +436 -436
- package/lib/status/types.d.ts +89 -89
- package/lib/status/types.js +17 -17
- package/lib/testSetup.d.ts +553 -530
- package/lib/testSetup.js +871 -727
- package/lib/util/cache.d.ts +11 -11
- package/lib/util/cache.js +69 -69
- package/lib/util/checkLightningDomain.d.ts +1 -1
- package/lib/util/checkLightningDomain.js +28 -28
- package/lib/util/directoryWriter.d.ts +12 -12
- package/lib/util/directoryWriter.js +53 -53
- package/lib/util/getJwtAudienceUrl.d.ts +4 -4
- package/lib/util/getJwtAudienceUrl.js +18 -18
- package/lib/util/internal.d.ts +58 -58
- package/lib/util/internal.js +118 -118
- package/lib/util/jsonXmlTools.d.ts +14 -14
- package/lib/util/jsonXmlTools.js +38 -38
- package/lib/util/mapKeys.d.ts +14 -14
- package/lib/util/mapKeys.js +51 -51
- package/lib/util/sfdc.d.ts +52 -52
- package/lib/util/sfdc.js +85 -85
- package/lib/util/sfdcUrl.d.ts +72 -72
- package/lib/util/sfdcUrl.js +215 -215
- package/lib/util/structuredWriter.d.ts +9 -9
- package/lib/util/structuredWriter.js +2 -2
- package/lib/util/zipWriter.d.ts +16 -16
- package/lib/util/zipWriter.js +67 -67
- package/lib/webOAuthServer.d.ts +156 -156
- package/lib/webOAuthServer.js +388 -388
- package/messages/auth.md +37 -37
- package/messages/config.md +156 -156
- package/messages/connection.md +30 -30
- package/messages/core.json +20 -20
- package/messages/core.md +67 -67
- package/messages/encryption.md +85 -85
- package/messages/envVars.md +303 -303
- package/messages/org.md +63 -63
- package/messages/permissionSetAssignment.md +31 -31
- package/messages/scratchOrgCreate.md +23 -23
- package/messages/scratchOrgErrorCodes.md +115 -115
- package/messages/scratchOrgFeatureDeprecation.md +11 -11
- package/messages/scratchOrgInfoApi.md +15 -15
- package/messages/scratchOrgInfoGenerator.md +23 -23
- package/messages/streaming.md +23 -23
- package/messages/user.md +35 -35
- package/package.json +97 -97
package/lib/logger.js
CHANGED
|
@@ -1,735 +1,735 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Logger = exports.LoggerFormat = exports.LoggerLevel = void 0;
|
|
4
|
-
/*
|
|
5
|
-
* Copyright (c) 2020, salesforce.com, inc.
|
|
6
|
-
* All rights reserved.
|
|
7
|
-
* Licensed under the BSD 3-Clause license.
|
|
8
|
-
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
|
-
*/
|
|
10
|
-
const events_1 = require("events");
|
|
11
|
-
const os = require("os");
|
|
12
|
-
const path = require("path");
|
|
13
|
-
const stream_1 = require("stream");
|
|
14
|
-
const fs = require("fs");
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
const Bunyan = require("@salesforce/bunyan");
|
|
18
|
-
const kit_1 = require("@salesforce/kit");
|
|
19
|
-
const ts_types_1 = require("@salesforce/ts-types");
|
|
20
|
-
const Debug = require("debug");
|
|
21
|
-
const global_1 = require("./global");
|
|
22
|
-
const sfError_1 = require("./sfError");
|
|
23
|
-
/**
|
|
24
|
-
* Standard `Logger` levels.
|
|
25
|
-
*
|
|
26
|
-
* **See** {@link https://github.com/forcedotcom/node-bunyan#levels|Bunyan Levels}
|
|
27
|
-
*/
|
|
28
|
-
var LoggerLevel;
|
|
29
|
-
(function (LoggerLevel) {
|
|
30
|
-
LoggerLevel[LoggerLevel["TRACE"] = 10] = "TRACE";
|
|
31
|
-
LoggerLevel[LoggerLevel["DEBUG"] = 20] = "DEBUG";
|
|
32
|
-
LoggerLevel[LoggerLevel["INFO"] = 30] = "INFO";
|
|
33
|
-
LoggerLevel[LoggerLevel["WARN"] = 40] = "WARN";
|
|
34
|
-
LoggerLevel[LoggerLevel["ERROR"] = 50] = "ERROR";
|
|
35
|
-
LoggerLevel[LoggerLevel["FATAL"] = 60] = "FATAL";
|
|
36
|
-
})(LoggerLevel = exports.LoggerLevel || (exports.LoggerLevel = {}));
|
|
37
|
-
/**
|
|
38
|
-
* `Logger` format types.
|
|
39
|
-
*/
|
|
40
|
-
var LoggerFormat;
|
|
41
|
-
(function (LoggerFormat) {
|
|
42
|
-
LoggerFormat[LoggerFormat["JSON"] = 0] = "JSON";
|
|
43
|
-
LoggerFormat[LoggerFormat["LOGFMT"] = 1] = "LOGFMT";
|
|
44
|
-
})(LoggerFormat = exports.LoggerFormat || (exports.LoggerFormat = {}));
|
|
45
|
-
/**
|
|
46
|
-
* A logging abstraction powered by {@link https://github.com/forcedotcom/node-bunyan|Bunyan} that provides both a default
|
|
47
|
-
* logger configuration that will log to `sfdx.log`, and a way to create custom loggers based on the same foundation.
|
|
48
|
-
*
|
|
49
|
-
* ```
|
|
50
|
-
* // Gets the root sfdx logger
|
|
51
|
-
* const logger = await Logger.root();
|
|
52
|
-
*
|
|
53
|
-
* // Creates a child logger of the root sfdx logger with custom fields applied
|
|
54
|
-
* const childLogger = await Logger.child('myRootChild', {tag: 'value'});
|
|
55
|
-
*
|
|
56
|
-
* // Creates a custom logger unaffiliated with the root logger
|
|
57
|
-
* const myCustomLogger = new Logger('myCustomLogger');
|
|
58
|
-
*
|
|
59
|
-
* // Creates a child of a custom logger unaffiliated with the root logger with custom fields applied
|
|
60
|
-
* const myCustomChildLogger = myCustomLogger.child('myCustomChild', {tag: 'value'});
|
|
61
|
-
* ```
|
|
62
|
-
* **See** https://github.com/forcedotcom/node-bunyan
|
|
63
|
-
*
|
|
64
|
-
* **See** https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_dev_cli_log_messages.htm
|
|
65
|
-
*/
|
|
66
|
-
class Logger {
|
|
67
|
-
/**
|
|
68
|
-
* Constructs a new `Logger`.
|
|
69
|
-
*
|
|
70
|
-
* @param optionsOrName A set of `LoggerOptions` or name to use with the default options.
|
|
71
|
-
*
|
|
72
|
-
* **Throws** *{@link SfError}{ name: 'RedundantRootLoggerError' }* More than one attempt is made to construct the root
|
|
73
|
-
* `Logger`.
|
|
74
|
-
*/
|
|
75
|
-
constructor(optionsOrName) {
|
|
76
|
-
/**
|
|
77
|
-
* The default rotation period for logs. Example '1d' will rotate logs daily (at midnight).
|
|
78
|
-
* See 'period' docs here: https://github.com/forcedotcom/node-bunyan#stream-type-rotating-file
|
|
79
|
-
*/
|
|
80
|
-
this.logRotationPeriod = new kit_1.Env().getString('SF_LOG_ROTATION_PERIOD') ?? '1d';
|
|
81
|
-
/**
|
|
82
|
-
* The number of backup rotated log files to keep.
|
|
83
|
-
* Example: '3' will have the base sf.log file, and the past 3 (period) log files.
|
|
84
|
-
* See 'count' docs here: https://github.com/forcedotcom/node-bunyan#stream-type-rotating-file
|
|
85
|
-
*/
|
|
86
|
-
this.logRotationCount = new kit_1.Env().getNumber('SF_LOG_ROTATION_COUNT') ?? 2;
|
|
87
|
-
/**
|
|
88
|
-
* Whether debug is enabled for this Logger.
|
|
89
|
-
*/
|
|
90
|
-
this.debugEnabled = false;
|
|
91
|
-
this.uncaughtExceptionHandler = (err) => {
|
|
92
|
-
// W-7558552
|
|
93
|
-
// Only log uncaught exceptions in root logger
|
|
94
|
-
if (this === Logger.rootLogger) {
|
|
95
|
-
// log the exception
|
|
96
|
-
// FIXME: good chance this won't be logged because
|
|
97
|
-
// process.exit was called before this is logged
|
|
98
|
-
// https://github.com/trentm/node-bunyan/issues/95
|
|
99
|
-
this.fatal(err);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
this.exitHandler = () => {
|
|
103
|
-
this.close();
|
|
104
|
-
};
|
|
105
|
-
let options;
|
|
106
|
-
if (typeof optionsOrName === 'string') {
|
|
107
|
-
options = {
|
|
108
|
-
name: optionsOrName,
|
|
109
|
-
level: Logger.DEFAULT_LEVEL,
|
|
110
|
-
serializers: Bunyan.stdSerializers,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
options = optionsOrName;
|
|
115
|
-
}
|
|
116
|
-
if (Logger.rootLogger && options.name === Logger.ROOT_NAME) {
|
|
117
|
-
throw new sfError_1.SfError('Can not create another root logger.', 'RedundantRootLoggerError');
|
|
118
|
-
}
|
|
119
|
-
// Inspect format to know what logging format to use then delete from options to
|
|
120
|
-
// ensure it doesn't conflict with Bunyan.
|
|
121
|
-
this.format = options.format ?? LoggerFormat.JSON;
|
|
122
|
-
delete options.format;
|
|
123
|
-
// If the log format is LOGFMT, we need to convert any stream(s) into a LOGFMT type stream.
|
|
124
|
-
if (this.format === LoggerFormat.LOGFMT && options.stream) {
|
|
125
|
-
const ls = this.createLogFmtFormatterStream({ stream: options.stream });
|
|
126
|
-
options.stream = ls.stream;
|
|
127
|
-
}
|
|
128
|
-
if (this.format === LoggerFormat.LOGFMT && options.streams) {
|
|
129
|
-
const logFmtConvertedStreams = [];
|
|
130
|
-
options.streams.forEach((ls) => {
|
|
131
|
-
logFmtConvertedStreams.push(this.createLogFmtFormatterStream(ls));
|
|
132
|
-
});
|
|
133
|
-
options.streams = logFmtConvertedStreams;
|
|
134
|
-
}
|
|
135
|
-
this.bunyan = new Bunyan(options);
|
|
136
|
-
this.bunyan.name = options.name;
|
|
137
|
-
this.bunyan.filters = [];
|
|
138
|
-
if (!options.streams && !options.stream) {
|
|
139
|
-
this.bunyan.streams = [];
|
|
140
|
-
}
|
|
141
|
-
// all SFDX loggers must filter sensitive data
|
|
142
|
-
this.addFilter((...args) => filterSecrets(...args));
|
|
143
|
-
if (global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
144
|
-
Logger.lifecycle.on('uncaughtException', this.uncaughtExceptionHandler);
|
|
145
|
-
Logger.lifecycle.on('exit', this.exitHandler);
|
|
146
|
-
}
|
|
147
|
-
this.trace(`Created '${this.getName()}' logger instance`);
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Gets the root logger with the default level, file stream, and DEBUG enabled.
|
|
151
|
-
*/
|
|
152
|
-
static async root() {
|
|
153
|
-
if (this.rootLogger) {
|
|
154
|
-
return this.rootLogger;
|
|
155
|
-
}
|
|
156
|
-
const rootLogger = (this.rootLogger = new Logger(Logger.ROOT_NAME).setLevel());
|
|
157
|
-
// disable log file writing, if applicable
|
|
158
|
-
const disableLogFile = new kit_1.Env().getString('SF_DISABLE_LOG_FILE');
|
|
159
|
-
if (disableLogFile !== 'true' && global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
160
|
-
await rootLogger.addLogFileStream(global_1.Global.LOG_FILE_PATH);
|
|
161
|
-
}
|
|
162
|
-
rootLogger.enableDEBUG();
|
|
163
|
-
return rootLogger;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Gets the root logger with the default level, file stream, and DEBUG enabled.
|
|
167
|
-
*/
|
|
168
|
-
static getRoot() {
|
|
169
|
-
if (this.rootLogger) {
|
|
170
|
-
return this.rootLogger;
|
|
171
|
-
}
|
|
172
|
-
const rootLogger = (this.rootLogger = new Logger(Logger.ROOT_NAME).setLevel());
|
|
173
|
-
// disable log file writing, if applicable
|
|
174
|
-
if (process.env.SFDX_DISABLE_LOG_FILE !== 'true' && global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
175
|
-
rootLogger.addLogFileStreamSync(global_1.Global.LOG_FILE_PATH);
|
|
176
|
-
}
|
|
177
|
-
rootLogger.enableDEBUG();
|
|
178
|
-
return rootLogger;
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Destroys the root `Logger`.
|
|
182
|
-
*
|
|
183
|
-
* @ignore
|
|
184
|
-
*/
|
|
185
|
-
static destroyRoot() {
|
|
186
|
-
if (this.rootLogger) {
|
|
187
|
-
this.rootLogger.close();
|
|
188
|
-
this.rootLogger = undefined;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Create a child of the root logger, inheriting this instance's configuration such as `level`, `streams`, etc.
|
|
193
|
-
*
|
|
194
|
-
* @param name The name of the child logger.
|
|
195
|
-
* @param fields Additional fields included in all log lines.
|
|
196
|
-
*/
|
|
197
|
-
static async child(name, fields) {
|
|
198
|
-
return (await Logger.root()).child(name, fields);
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Create a child of the root logger, inheriting this instance's configuration such as `level`, `streams`, etc.
|
|
202
|
-
*
|
|
203
|
-
* @param name The name of the child logger.
|
|
204
|
-
* @param fields Additional fields included in all log lines.
|
|
205
|
-
*/
|
|
206
|
-
static childFromRoot(name, fields) {
|
|
207
|
-
return Logger.getRoot().child(name, fields);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Gets a numeric `LoggerLevel` value by string name.
|
|
211
|
-
*
|
|
212
|
-
* @param {string} levelName The level name to convert to a `LoggerLevel` enum value.
|
|
213
|
-
*
|
|
214
|
-
* **Throws** *{@link SfError}{ name: 'UnrecognizedLoggerLevelNameError' }* The level name was not case-insensitively recognized as a valid `LoggerLevel` value.
|
|
215
|
-
* @see {@Link LoggerLevel}
|
|
216
|
-
*/
|
|
217
|
-
static getLevelByName(levelName) {
|
|
218
|
-
levelName = levelName.toUpperCase();
|
|
219
|
-
if (!(0, ts_types_1.isKeyOf)(LoggerLevel, levelName)) {
|
|
220
|
-
throw new sfError_1.SfError(`Invalid log level "${levelName}".`, 'UnrecognizedLoggerLevelNameError');
|
|
221
|
-
}
|
|
222
|
-
return LoggerLevel[levelName];
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Adds a stream.
|
|
226
|
-
*
|
|
227
|
-
* @param stream The stream configuration to add.
|
|
228
|
-
* @param defaultLevel The default level of the stream.
|
|
229
|
-
*/
|
|
230
|
-
addStream(stream, defaultLevel) {
|
|
231
|
-
if (this.format === LoggerFormat.LOGFMT) {
|
|
232
|
-
stream = this.createLogFmtFormatterStream(stream);
|
|
233
|
-
}
|
|
234
|
-
this.bunyan.addStream(stream, defaultLevel);
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Adds a file stream to this logger. Resolved or rejected upon completion of the addition.
|
|
238
|
-
*
|
|
239
|
-
* @param logFile The path to the log file. If it doesn't exist it will be created.
|
|
240
|
-
*/
|
|
241
|
-
async addLogFileStream(logFile) {
|
|
242
|
-
try {
|
|
243
|
-
// Check if we have write access to the log file (i.e., we created it already)
|
|
244
|
-
await fs.promises.access(logFile, fs.constants.W_OK);
|
|
245
|
-
}
|
|
246
|
-
catch (err1) {
|
|
247
|
-
try {
|
|
248
|
-
if (process.platform === 'win32') {
|
|
249
|
-
await fs.promises.mkdir(path.dirname(logFile), { recursive: true });
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
await fs.promises.mkdir(path.dirname(logFile), { recursive: true, mode: 0o700 });
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch (err2) {
|
|
256
|
-
throw sfError_1.SfError.wrap(err2);
|
|
257
|
-
}
|
|
258
|
-
try {
|
|
259
|
-
await fs.promises.writeFile(logFile, '', { mode: '600' });
|
|
260
|
-
}
|
|
261
|
-
catch (err3) {
|
|
262
|
-
throw sfError_1.SfError.wrap(err3);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// avoid multiple streams to same log file
|
|
266
|
-
if (!this.bunyan.streams.find(
|
|
267
|
-
// No bunyan typings
|
|
268
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
269
|
-
(stream) => stream.type === 'rotating-file' && stream.path === logFile)) {
|
|
270
|
-
this.addStream({
|
|
271
|
-
type: 'rotating-file',
|
|
272
|
-
path: logFile,
|
|
273
|
-
period: this.logRotationPeriod,
|
|
274
|
-
count: this.logRotationCount,
|
|
275
|
-
level: this.bunyan.level(),
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Adds a file stream to this logger. Resolved or rejected upon completion of the addition.
|
|
281
|
-
*
|
|
282
|
-
* @param logFile The path to the log file. If it doesn't exist it will be created.
|
|
283
|
-
*/
|
|
284
|
-
addLogFileStreamSync(logFile) {
|
|
285
|
-
try {
|
|
286
|
-
// Check if we have write access to the log file (i.e., we created it already)
|
|
287
|
-
fs.accessSync(logFile, fs.constants.W_OK);
|
|
288
|
-
}
|
|
289
|
-
catch (err1) {
|
|
290
|
-
try {
|
|
291
|
-
if (process.platform === 'win32') {
|
|
292
|
-
fs.mkdirSync(path.dirname(logFile));
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
fs.mkdirSync(path.dirname(logFile), { mode: 0o700 });
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
catch (err2) {
|
|
299
|
-
throw sfError_1.SfError.wrap(err2);
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
fs.writeFileSync(logFile, '', { mode: '600' });
|
|
303
|
-
}
|
|
304
|
-
catch (err3) {
|
|
305
|
-
throw sfError_1.SfError.wrap(err3);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// avoid multiple streams to same log file
|
|
309
|
-
if (!this.bunyan.streams.find(
|
|
310
|
-
// No bunyan typings
|
|
311
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
|
-
(stream) => stream.type === 'rotating-file' && stream.path === logFile)) {
|
|
313
|
-
this.addStream({
|
|
314
|
-
type: 'rotating-file',
|
|
315
|
-
path: logFile,
|
|
316
|
-
period: this.logRotationPeriod,
|
|
317
|
-
count: this.logRotationCount,
|
|
318
|
-
level: this.bunyan.level(),
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Gets the name of this logger.
|
|
324
|
-
*/
|
|
325
|
-
getName() {
|
|
326
|
-
return this.bunyan.name;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Gets the current level of this logger.
|
|
330
|
-
*/
|
|
331
|
-
getLevel() {
|
|
332
|
-
return this.bunyan.level();
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Set the logging level of all streams for this logger. If a specific `level` is not provided, this method will
|
|
336
|
-
* attempt to read it from the environment variable `SFDX_LOG_LEVEL`, and if not found,
|
|
337
|
-
* {@link Logger.DEFAULT_LOG_LEVEL} will be used instead. For convenience `this` object is returned.
|
|
338
|
-
*
|
|
339
|
-
* @param {LoggerLevelValue} [level] The logger level.
|
|
340
|
-
*
|
|
341
|
-
* **Throws** *{@link SfError}{ name: 'UnrecognizedLoggerLevelNameError' }* A value of `level` read from `SFDX_LOG_LEVEL`
|
|
342
|
-
* was invalid.
|
|
343
|
-
*
|
|
344
|
-
* ```
|
|
345
|
-
* // Sets the level from the environment or default value
|
|
346
|
-
* logger.setLevel()
|
|
347
|
-
*
|
|
348
|
-
* // Set the level from the INFO enum
|
|
349
|
-
* logger.setLevel(LoggerLevel.INFO)
|
|
350
|
-
*
|
|
351
|
-
* // Sets the level case-insensitively from a string value
|
|
352
|
-
* logger.setLevel(Logger.getLevelByName('info'))
|
|
353
|
-
* ```
|
|
354
|
-
*/
|
|
355
|
-
setLevel(level) {
|
|
356
|
-
if (level == null) {
|
|
357
|
-
const logLevelFromEnvVar = new kit_1.Env().getString('SF_LOG_LEVEL');
|
|
358
|
-
level = logLevelFromEnvVar ? Logger.getLevelByName(logLevelFromEnvVar) : Logger.DEFAULT_LEVEL;
|
|
359
|
-
}
|
|
360
|
-
this.bunyan.level(level);
|
|
361
|
-
return this;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Gets the underlying Bunyan logger.
|
|
365
|
-
*/
|
|
366
|
-
// leave this typed as any to keep if from trying to export the type from the untyped bunyan module
|
|
367
|
-
// this prevents consumers from getting node_modules/@salesforce/core/lib/logger.d.ts:281:24 - error TS2304: Cannot find name 'Bunyan'.
|
|
368
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
369
|
-
getBunyanLogger() {
|
|
370
|
-
return this.bunyan;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Compares the requested log level with the current log level. Returns true if
|
|
374
|
-
* the requested log level is greater than or equal to the current log level.
|
|
375
|
-
*
|
|
376
|
-
* @param level The requested log level to compare against the currently set log level.
|
|
377
|
-
*/
|
|
378
|
-
shouldLog(level) {
|
|
379
|
-
if (typeof level === 'string') {
|
|
380
|
-
level = Bunyan.levelFromName(level);
|
|
381
|
-
}
|
|
382
|
-
return level >= this.getLevel();
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Use in-memory logging for this logger instance instead of any parent streams. Useful for testing.
|
|
386
|
-
* For convenience this object is returned.
|
|
387
|
-
*
|
|
388
|
-
* **WARNING: This cannot be undone for this logger instance.**
|
|
389
|
-
*/
|
|
390
|
-
useMemoryLogging() {
|
|
391
|
-
this.bunyan.streams = [];
|
|
392
|
-
this.bunyan.ringBuffer = new Bunyan.RingBuffer({ limit: 5000 });
|
|
393
|
-
this.addStream({
|
|
394
|
-
type: 'raw',
|
|
395
|
-
stream: this.bunyan.ringBuffer,
|
|
396
|
-
level: this.bunyan.level(),
|
|
397
|
-
});
|
|
398
|
-
return this;
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Gets an array of log line objects. Each element is an object that corresponds to a log line.
|
|
402
|
-
*/
|
|
403
|
-
getBufferedRecords() {
|
|
404
|
-
if (this.bunyan.ringBuffer) {
|
|
405
|
-
return this.bunyan.ringBuffer.records;
|
|
406
|
-
}
|
|
407
|
-
return [];
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Reads a text blob of all the log lines contained in memory or the log file.
|
|
411
|
-
*/
|
|
412
|
-
readLogContentsAsText() {
|
|
413
|
-
if (this.bunyan.ringBuffer) {
|
|
414
|
-
return this.getBufferedRecords().reduce((accum, line) => {
|
|
415
|
-
accum += JSON.stringify(line) + os.EOL;
|
|
416
|
-
return accum;
|
|
417
|
-
}, '');
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
let content = '';
|
|
421
|
-
// No bunyan typings
|
|
422
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
423
|
-
this.bunyan.streams.forEach(async (stream) => {
|
|
424
|
-
if (stream.type === 'file') {
|
|
425
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
426
|
-
content += await fs.promises.readFile(stream.path, 'utf8');
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
return content;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Adds a filter to be applied to all logged messages.
|
|
434
|
-
*
|
|
435
|
-
* @param filter A function with signature `(...args: any[]) => any[]` that transforms log message arguments.
|
|
436
|
-
*/
|
|
437
|
-
addFilter(filter) {
|
|
438
|
-
if (!this.bunyan.filters) {
|
|
439
|
-
this.bunyan.filters = [];
|
|
440
|
-
}
|
|
441
|
-
this.bunyan.filters.push(filter);
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Close the logger, including any streams, and remove all listeners.
|
|
445
|
-
*
|
|
446
|
-
* @param fn A function with signature `(stream: LoggerStream) => void` to call for each stream with the stream as an arg.
|
|
447
|
-
*/
|
|
448
|
-
close(fn) {
|
|
449
|
-
if (this.bunyan.streams) {
|
|
450
|
-
try {
|
|
451
|
-
this.bunyan.streams.forEach((entry) => {
|
|
452
|
-
if (fn) {
|
|
453
|
-
fn(entry);
|
|
454
|
-
}
|
|
455
|
-
// close file streams, flush buffer to disk
|
|
456
|
-
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
457
|
-
if (entry.type === 'file' && entry.stream && (0, ts_types_1.isFunction)(entry.stream.end)) {
|
|
458
|
-
entry.stream.end();
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
finally {
|
|
463
|
-
Logger.lifecycle.removeListener('uncaughtException', this.uncaughtExceptionHandler);
|
|
464
|
-
Logger.lifecycle.removeListener('exit', this.exitHandler);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Create a child logger, typically to add a few log record fields. For convenience this object is returned.
|
|
470
|
-
*
|
|
471
|
-
* @param name The name of the child logger that is emitted w/ log line as `log:<name>`.
|
|
472
|
-
* @param fields Additional fields included in all log lines for the child logger.
|
|
473
|
-
*/
|
|
474
|
-
child(name, fields = {}) {
|
|
475
|
-
if (!name) {
|
|
476
|
-
throw new sfError_1.SfError('LoggerNameRequired');
|
|
477
|
-
}
|
|
478
|
-
fields.log = name;
|
|
479
|
-
const child = new Logger(name);
|
|
480
|
-
// only support including additional fields on log line (no config)
|
|
481
|
-
child.bunyan = this.bunyan.child(fields, true);
|
|
482
|
-
child.bunyan.name = name;
|
|
483
|
-
child.bunyan.filters = this.bunyan.filters;
|
|
484
|
-
this.trace(`Setup child '${name}' logger instance`);
|
|
485
|
-
return child;
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* Add a field to all log lines for this logger. For convenience `this` object is returned.
|
|
489
|
-
*
|
|
490
|
-
* @param name The name of the field to add.
|
|
491
|
-
* @param value The value of the field to be logged.
|
|
492
|
-
*/
|
|
493
|
-
addField(name, value) {
|
|
494
|
-
this.bunyan.fields[name] = value;
|
|
495
|
-
return this;
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Logs at `trace` level with filtering applied. For convenience `this` object is returned.
|
|
499
|
-
*
|
|
500
|
-
* @param args Any number of arguments to be logged.
|
|
501
|
-
*/
|
|
502
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
503
|
-
trace(...args) {
|
|
504
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
505
|
-
this.bunyan.trace(this.applyFilters(LoggerLevel.TRACE, ...args));
|
|
506
|
-
return this;
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* Logs at `debug` level with filtering applied. For convenience `this` object is returned.
|
|
510
|
-
*
|
|
511
|
-
* @param args Any number of arguments to be logged.
|
|
512
|
-
*/
|
|
513
|
-
debug(...args) {
|
|
514
|
-
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...args));
|
|
515
|
-
return this;
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Logs at `debug` level with filtering applied.
|
|
519
|
-
*
|
|
520
|
-
* @param cb A callback that returns on array objects to be logged.
|
|
521
|
-
*/
|
|
522
|
-
debugCallback(cb) {
|
|
523
|
-
if (this.getLevel() === LoggerLevel.DEBUG || process.env.DEBUG) {
|
|
524
|
-
const result = cb();
|
|
525
|
-
if ((0, ts_types_1.isArray)(result)) {
|
|
526
|
-
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...result));
|
|
527
|
-
}
|
|
528
|
-
else {
|
|
529
|
-
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...[result]));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Logs at `info` level with filtering applied. For convenience `this` object is returned.
|
|
535
|
-
*
|
|
536
|
-
* @param args Any number of arguments to be logged.
|
|
537
|
-
*/
|
|
538
|
-
info(...args) {
|
|
539
|
-
this.bunyan.info(this.applyFilters(LoggerLevel.INFO, ...args));
|
|
540
|
-
return this;
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Logs at `warn` level with filtering applied. For convenience `this` object is returned.
|
|
544
|
-
*
|
|
545
|
-
* @param args Any number of arguments to be logged.
|
|
546
|
-
*/
|
|
547
|
-
warn(...args) {
|
|
548
|
-
this.bunyan.warn(this.applyFilters(LoggerLevel.WARN, ...args));
|
|
549
|
-
return this;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Logs at `error` level with filtering applied. For convenience `this` object is returned.
|
|
553
|
-
*
|
|
554
|
-
* @param args Any number of arguments to be logged.
|
|
555
|
-
*/
|
|
556
|
-
error(...args) {
|
|
557
|
-
this.bunyan.error(this.applyFilters(LoggerLevel.ERROR, ...args));
|
|
558
|
-
return this;
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* Logs at `fatal` level with filtering applied. For convenience `this` object is returned.
|
|
562
|
-
*
|
|
563
|
-
* @param args Any number of arguments to be logged.
|
|
564
|
-
*/
|
|
565
|
-
fatal(...args) {
|
|
566
|
-
// always show fatal to stderr
|
|
567
|
-
// eslint-disable-next-line no-console
|
|
568
|
-
console.error(...args);
|
|
569
|
-
this.bunyan.fatal(this.applyFilters(LoggerLevel.FATAL, ...args));
|
|
570
|
-
return this;
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Enables logging to stdout when the DEBUG environment variable is used. It uses the logger
|
|
574
|
-
* name as the debug name, so you can do DEBUG=<logger-name> to filter the results to your logger.
|
|
575
|
-
*/
|
|
576
|
-
enableDEBUG() {
|
|
577
|
-
// The debug library does this for you, but no point setting up the stream if it isn't there
|
|
578
|
-
if (process.env.DEBUG && !this.debugEnabled) {
|
|
579
|
-
const debuggers = {};
|
|
580
|
-
debuggers.core = Debug(`${this.getName()}:core`);
|
|
581
|
-
this.addStream({
|
|
582
|
-
name: 'debug',
|
|
583
|
-
stream: new stream_1.Writable({
|
|
584
|
-
write: (chunk, encoding, next) => {
|
|
585
|
-
try {
|
|
586
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
587
|
-
const json = (0, kit_1.parseJsonMap)(chunk.toString());
|
|
588
|
-
const logLevel = (0, ts_types_1.ensureNumber)(json.level);
|
|
589
|
-
if (this.getLevel() <= logLevel) {
|
|
590
|
-
let debuggerName = 'core';
|
|
591
|
-
if ((0, ts_types_1.isString)(json.log)) {
|
|
592
|
-
debuggerName = json.log;
|
|
593
|
-
if (!debuggers[debuggerName]) {
|
|
594
|
-
debuggers[debuggerName] = Debug(`${this.getName()}:${debuggerName}`);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
const level = LoggerLevel[logLevel];
|
|
598
|
-
(0, ts_types_1.ensure)(debuggers[debuggerName])(`${level} ${json.msg}`);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
catch (err) {
|
|
602
|
-
// do nothing
|
|
603
|
-
}
|
|
604
|
-
next();
|
|
605
|
-
},
|
|
606
|
-
}),
|
|
607
|
-
// Consume all levels
|
|
608
|
-
level: 0,
|
|
609
|
-
});
|
|
610
|
-
this.debugEnabled = true;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
applyFilters(logLevel, ...args) {
|
|
614
|
-
if (this.shouldLog(logLevel)) {
|
|
615
|
-
// No bunyan typings
|
|
616
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
617
|
-
this.bunyan.filters.forEach((filter) => (args = filter(...args)));
|
|
618
|
-
}
|
|
619
|
-
return args && args.length === 1 ? args[0] : args;
|
|
620
|
-
}
|
|
621
|
-
// eslint-disable-next-line class-methods-use-this
|
|
622
|
-
createLogFmtFormatterStream(loggerStream) {
|
|
623
|
-
const logFmtWriteableStream = new stream_1.Writable({
|
|
624
|
-
write: (chunk, enc, cb) => {
|
|
625
|
-
try {
|
|
626
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
627
|
-
const parsedJSON = JSON.parse(chunk.toString());
|
|
628
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
629
|
-
const keys = Object.keys(parsedJSON);
|
|
630
|
-
let logEntry = '';
|
|
631
|
-
keys.forEach((key) => {
|
|
632
|
-
let logMsg = `${parsedJSON[key]}`;
|
|
633
|
-
if (logMsg.trim().includes(' ')) {
|
|
634
|
-
logMsg = `"${logMsg}"`;
|
|
635
|
-
}
|
|
636
|
-
logEntry += `${key}=${logMsg} `;
|
|
637
|
-
});
|
|
638
|
-
if (loggerStream.stream) {
|
|
639
|
-
loggerStream.stream.write(logEntry.trimRight() + '\n');
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
catch (error) {
|
|
643
|
-
if (loggerStream.stream) {
|
|
644
|
-
loggerStream.stream.write(chunk.toString());
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
cb(null);
|
|
648
|
-
},
|
|
649
|
-
});
|
|
650
|
-
return Object.assign({}, loggerStream, { stream: logFmtWriteableStream });
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
exports.Logger = Logger;
|
|
654
|
-
/**
|
|
655
|
-
* The name of the root sfdx `Logger`.
|
|
656
|
-
*/
|
|
657
|
-
Logger.ROOT_NAME = 'sf';
|
|
658
|
-
/**
|
|
659
|
-
* The default `LoggerLevel` when constructing new `Logger` instances.
|
|
660
|
-
*/
|
|
661
|
-
Logger.DEFAULT_LEVEL = LoggerLevel.WARN;
|
|
662
|
-
/**
|
|
663
|
-
* A list of all lower case `LoggerLevel` names.
|
|
664
|
-
*
|
|
665
|
-
* **See** {@link LoggerLevel}
|
|
666
|
-
*/
|
|
667
|
-
Logger.LEVEL_NAMES = Object.values(LoggerLevel)
|
|
668
|
-
.filter(ts_types_1.isString)
|
|
669
|
-
.map((v) => v.toLowerCase());
|
|
670
|
-
// Rollup all instance-specific process event listeners together to prevent global `MaxListenersExceededWarning`s.
|
|
671
|
-
Logger.lifecycle = (() => {
|
|
672
|
-
const events = new events_1.EventEmitter();
|
|
673
|
-
events.setMaxListeners(0); // never warn on listener counts
|
|
674
|
-
process.on('uncaughtException', (err) => events.emit('uncaughtException', err));
|
|
675
|
-
process.on('exit', () => events.emit('exit'));
|
|
676
|
-
return events;
|
|
677
|
-
})();
|
|
678
|
-
// Ok to log clientid
|
|
679
|
-
const FILTERED_KEYS = [
|
|
680
|
-
'sid',
|
|
681
|
-
'Authorization',
|
|
682
|
-
// Any json attribute that contains the words "access" and "token" will have the attribute/value hidden
|
|
683
|
-
{ name: 'access_token', regex: 'access[^\'"]*token' },
|
|
684
|
-
// Any json attribute that contains the words "refresh" and "token" will have the attribute/value hidden
|
|
685
|
-
{ name: 'refresh_token', regex: 'refresh[^\'"]*token' },
|
|
686
|
-
'clientsecret',
|
|
687
|
-
// Any json attribute that contains the words "sfdx", "auth", and "url" will have the attribute/value hidden
|
|
688
|
-
{ name: 'sfdxauthurl', regex: 'sfdx[^\'"]*auth[^\'"]*url' },
|
|
689
|
-
];
|
|
690
|
-
// SFDX code and plugins should never show tokens or connect app information in the logs
|
|
691
|
-
const filterSecrets = (...args) => args.map((arg) => {
|
|
692
|
-
if ((0, ts_types_1.isArray)(arg)) {
|
|
693
|
-
return filterSecrets(...arg);
|
|
694
|
-
}
|
|
695
|
-
if (arg) {
|
|
696
|
-
let mutableArg;
|
|
697
|
-
// Normalize all objects into a string. This include errors.
|
|
698
|
-
if (arg instanceof Buffer) {
|
|
699
|
-
mutableArg = '<Buffer>';
|
|
700
|
-
}
|
|
701
|
-
else if ((0, ts_types_1.isObject)(arg)) {
|
|
702
|
-
mutableArg = JSON.stringify(arg);
|
|
703
|
-
}
|
|
704
|
-
else if ((0, ts_types_1.isString)(arg)) {
|
|
705
|
-
mutableArg = arg;
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
mutableArg = '';
|
|
709
|
-
}
|
|
710
|
-
const HIDDEN = 'HIDDEN';
|
|
711
|
-
FILTERED_KEYS.forEach((key) => {
|
|
712
|
-
let expElement = key;
|
|
713
|
-
let expName = key;
|
|
714
|
-
// Filtered keys can be strings or objects containing regular expression components.
|
|
715
|
-
if ((0, ts_types_1.isPlainObject)(key)) {
|
|
716
|
-
expElement = key.regex;
|
|
717
|
-
expName = key.name;
|
|
718
|
-
}
|
|
719
|
-
const hiddenAttrMessage = `"<${expName} - ${HIDDEN}>"`;
|
|
720
|
-
// Match all json attribute values case insensitive: ex. {" Access*^&(*()^* Token " : " 45143075913458901348905 \n\t" ...}
|
|
721
|
-
const regexTokens = new RegExp(`(['"][^'"]*${expElement}[^'"]*['"]\\s*:\\s*)['"][^'"]*['"]`, 'gi');
|
|
722
|
-
mutableArg = mutableArg.replace(regexTokens, `$1${hiddenAttrMessage}`);
|
|
723
|
-
// Match all key value attribute case insensitive: ex. {" key\t" : ' access_token ' , " value " : " dsafgasr431 " ....}
|
|
724
|
-
const keyRegex = new RegExp(`(['"]\\s*key\\s*['"]\\s*:)\\s*['"]\\s*${expElement}\\s*['"]\\s*.\\s*['"]\\s*value\\s*['"]\\s*:\\s*['"]\\s*[^'"]*['"]`, 'gi');
|
|
725
|
-
mutableArg = mutableArg.replace(keyRegex, `$1${hiddenAttrMessage}`);
|
|
726
|
-
});
|
|
727
|
-
mutableArg = mutableArg.replace(/(00D\w{12,15})![.\w]*/, `<${HIDDEN}>`);
|
|
728
|
-
// return an object if an object was logged; otherwise return the filtered string.
|
|
729
|
-
return (0, ts_types_1.isObject)(arg) ? (0, kit_1.parseJson)(mutableArg) : mutableArg;
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
return arg;
|
|
733
|
-
}
|
|
734
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logger = exports.LoggerFormat = exports.LoggerLevel = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2020, salesforce.com, inc.
|
|
6
|
+
* All rights reserved.
|
|
7
|
+
* Licensed under the BSD 3-Clause license.
|
|
8
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
|
+
*/
|
|
10
|
+
const events_1 = require("events");
|
|
11
|
+
const os = require("os");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const stream_1 = require("stream");
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
const Bunyan = require("@salesforce/bunyan");
|
|
18
|
+
const kit_1 = require("@salesforce/kit");
|
|
19
|
+
const ts_types_1 = require("@salesforce/ts-types");
|
|
20
|
+
const Debug = require("debug");
|
|
21
|
+
const global_1 = require("./global");
|
|
22
|
+
const sfError_1 = require("./sfError");
|
|
23
|
+
/**
|
|
24
|
+
* Standard `Logger` levels.
|
|
25
|
+
*
|
|
26
|
+
* **See** {@link https://github.com/forcedotcom/node-bunyan#levels|Bunyan Levels}
|
|
27
|
+
*/
|
|
28
|
+
var LoggerLevel;
|
|
29
|
+
(function (LoggerLevel) {
|
|
30
|
+
LoggerLevel[LoggerLevel["TRACE"] = 10] = "TRACE";
|
|
31
|
+
LoggerLevel[LoggerLevel["DEBUG"] = 20] = "DEBUG";
|
|
32
|
+
LoggerLevel[LoggerLevel["INFO"] = 30] = "INFO";
|
|
33
|
+
LoggerLevel[LoggerLevel["WARN"] = 40] = "WARN";
|
|
34
|
+
LoggerLevel[LoggerLevel["ERROR"] = 50] = "ERROR";
|
|
35
|
+
LoggerLevel[LoggerLevel["FATAL"] = 60] = "FATAL";
|
|
36
|
+
})(LoggerLevel = exports.LoggerLevel || (exports.LoggerLevel = {}));
|
|
37
|
+
/**
|
|
38
|
+
* `Logger` format types.
|
|
39
|
+
*/
|
|
40
|
+
var LoggerFormat;
|
|
41
|
+
(function (LoggerFormat) {
|
|
42
|
+
LoggerFormat[LoggerFormat["JSON"] = 0] = "JSON";
|
|
43
|
+
LoggerFormat[LoggerFormat["LOGFMT"] = 1] = "LOGFMT";
|
|
44
|
+
})(LoggerFormat = exports.LoggerFormat || (exports.LoggerFormat = {}));
|
|
45
|
+
/**
|
|
46
|
+
* A logging abstraction powered by {@link https://github.com/forcedotcom/node-bunyan|Bunyan} that provides both a default
|
|
47
|
+
* logger configuration that will log to `sfdx.log`, and a way to create custom loggers based on the same foundation.
|
|
48
|
+
*
|
|
49
|
+
* ```
|
|
50
|
+
* // Gets the root sfdx logger
|
|
51
|
+
* const logger = await Logger.root();
|
|
52
|
+
*
|
|
53
|
+
* // Creates a child logger of the root sfdx logger with custom fields applied
|
|
54
|
+
* const childLogger = await Logger.child('myRootChild', {tag: 'value'});
|
|
55
|
+
*
|
|
56
|
+
* // Creates a custom logger unaffiliated with the root logger
|
|
57
|
+
* const myCustomLogger = new Logger('myCustomLogger');
|
|
58
|
+
*
|
|
59
|
+
* // Creates a child of a custom logger unaffiliated with the root logger with custom fields applied
|
|
60
|
+
* const myCustomChildLogger = myCustomLogger.child('myCustomChild', {tag: 'value'});
|
|
61
|
+
* ```
|
|
62
|
+
* **See** https://github.com/forcedotcom/node-bunyan
|
|
63
|
+
*
|
|
64
|
+
* **See** https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_dev_cli_log_messages.htm
|
|
65
|
+
*/
|
|
66
|
+
class Logger {
|
|
67
|
+
/**
|
|
68
|
+
* Constructs a new `Logger`.
|
|
69
|
+
*
|
|
70
|
+
* @param optionsOrName A set of `LoggerOptions` or name to use with the default options.
|
|
71
|
+
*
|
|
72
|
+
* **Throws** *{@link SfError}{ name: 'RedundantRootLoggerError' }* More than one attempt is made to construct the root
|
|
73
|
+
* `Logger`.
|
|
74
|
+
*/
|
|
75
|
+
constructor(optionsOrName) {
|
|
76
|
+
/**
|
|
77
|
+
* The default rotation period for logs. Example '1d' will rotate logs daily (at midnight).
|
|
78
|
+
* See 'period' docs here: https://github.com/forcedotcom/node-bunyan#stream-type-rotating-file
|
|
79
|
+
*/
|
|
80
|
+
this.logRotationPeriod = new kit_1.Env().getString('SF_LOG_ROTATION_PERIOD') ?? '1d';
|
|
81
|
+
/**
|
|
82
|
+
* The number of backup rotated log files to keep.
|
|
83
|
+
* Example: '3' will have the base sf.log file, and the past 3 (period) log files.
|
|
84
|
+
* See 'count' docs here: https://github.com/forcedotcom/node-bunyan#stream-type-rotating-file
|
|
85
|
+
*/
|
|
86
|
+
this.logRotationCount = new kit_1.Env().getNumber('SF_LOG_ROTATION_COUNT') ?? 2;
|
|
87
|
+
/**
|
|
88
|
+
* Whether debug is enabled for this Logger.
|
|
89
|
+
*/
|
|
90
|
+
this.debugEnabled = false;
|
|
91
|
+
this.uncaughtExceptionHandler = (err) => {
|
|
92
|
+
// W-7558552
|
|
93
|
+
// Only log uncaught exceptions in root logger
|
|
94
|
+
if (this === Logger.rootLogger) {
|
|
95
|
+
// log the exception
|
|
96
|
+
// FIXME: good chance this won't be logged because
|
|
97
|
+
// process.exit was called before this is logged
|
|
98
|
+
// https://github.com/trentm/node-bunyan/issues/95
|
|
99
|
+
this.fatal(err);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
this.exitHandler = () => {
|
|
103
|
+
this.close();
|
|
104
|
+
};
|
|
105
|
+
let options;
|
|
106
|
+
if (typeof optionsOrName === 'string') {
|
|
107
|
+
options = {
|
|
108
|
+
name: optionsOrName,
|
|
109
|
+
level: Logger.DEFAULT_LEVEL,
|
|
110
|
+
serializers: Bunyan.stdSerializers,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
options = optionsOrName;
|
|
115
|
+
}
|
|
116
|
+
if (Logger.rootLogger && options.name === Logger.ROOT_NAME) {
|
|
117
|
+
throw new sfError_1.SfError('Can not create another root logger.', 'RedundantRootLoggerError');
|
|
118
|
+
}
|
|
119
|
+
// Inspect format to know what logging format to use then delete from options to
|
|
120
|
+
// ensure it doesn't conflict with Bunyan.
|
|
121
|
+
this.format = options.format ?? LoggerFormat.JSON;
|
|
122
|
+
delete options.format;
|
|
123
|
+
// If the log format is LOGFMT, we need to convert any stream(s) into a LOGFMT type stream.
|
|
124
|
+
if (this.format === LoggerFormat.LOGFMT && options.stream) {
|
|
125
|
+
const ls = this.createLogFmtFormatterStream({ stream: options.stream });
|
|
126
|
+
options.stream = ls.stream;
|
|
127
|
+
}
|
|
128
|
+
if (this.format === LoggerFormat.LOGFMT && options.streams) {
|
|
129
|
+
const logFmtConvertedStreams = [];
|
|
130
|
+
options.streams.forEach((ls) => {
|
|
131
|
+
logFmtConvertedStreams.push(this.createLogFmtFormatterStream(ls));
|
|
132
|
+
});
|
|
133
|
+
options.streams = logFmtConvertedStreams;
|
|
134
|
+
}
|
|
135
|
+
this.bunyan = new Bunyan(options);
|
|
136
|
+
this.bunyan.name = options.name;
|
|
137
|
+
this.bunyan.filters = [];
|
|
138
|
+
if (!options.streams && !options.stream) {
|
|
139
|
+
this.bunyan.streams = [];
|
|
140
|
+
}
|
|
141
|
+
// all SFDX loggers must filter sensitive data
|
|
142
|
+
this.addFilter((...args) => filterSecrets(...args));
|
|
143
|
+
if (global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
144
|
+
Logger.lifecycle.on('uncaughtException', this.uncaughtExceptionHandler);
|
|
145
|
+
Logger.lifecycle.on('exit', this.exitHandler);
|
|
146
|
+
}
|
|
147
|
+
this.trace(`Created '${this.getName()}' logger instance`);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Gets the root logger with the default level, file stream, and DEBUG enabled.
|
|
151
|
+
*/
|
|
152
|
+
static async root() {
|
|
153
|
+
if (this.rootLogger) {
|
|
154
|
+
return this.rootLogger;
|
|
155
|
+
}
|
|
156
|
+
const rootLogger = (this.rootLogger = new Logger(Logger.ROOT_NAME).setLevel());
|
|
157
|
+
// disable log file writing, if applicable
|
|
158
|
+
const disableLogFile = new kit_1.Env().getString('SF_DISABLE_LOG_FILE');
|
|
159
|
+
if (disableLogFile !== 'true' && global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
160
|
+
await rootLogger.addLogFileStream(global_1.Global.LOG_FILE_PATH);
|
|
161
|
+
}
|
|
162
|
+
rootLogger.enableDEBUG();
|
|
163
|
+
return rootLogger;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Gets the root logger with the default level, file stream, and DEBUG enabled.
|
|
167
|
+
*/
|
|
168
|
+
static getRoot() {
|
|
169
|
+
if (this.rootLogger) {
|
|
170
|
+
return this.rootLogger;
|
|
171
|
+
}
|
|
172
|
+
const rootLogger = (this.rootLogger = new Logger(Logger.ROOT_NAME).setLevel());
|
|
173
|
+
// disable log file writing, if applicable
|
|
174
|
+
if (process.env.SFDX_DISABLE_LOG_FILE !== 'true' && global_1.Global.getEnvironmentMode() !== global_1.Mode.TEST) {
|
|
175
|
+
rootLogger.addLogFileStreamSync(global_1.Global.LOG_FILE_PATH);
|
|
176
|
+
}
|
|
177
|
+
rootLogger.enableDEBUG();
|
|
178
|
+
return rootLogger;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Destroys the root `Logger`.
|
|
182
|
+
*
|
|
183
|
+
* @ignore
|
|
184
|
+
*/
|
|
185
|
+
static destroyRoot() {
|
|
186
|
+
if (this.rootLogger) {
|
|
187
|
+
this.rootLogger.close();
|
|
188
|
+
this.rootLogger = undefined;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a child of the root logger, inheriting this instance's configuration such as `level`, `streams`, etc.
|
|
193
|
+
*
|
|
194
|
+
* @param name The name of the child logger.
|
|
195
|
+
* @param fields Additional fields included in all log lines.
|
|
196
|
+
*/
|
|
197
|
+
static async child(name, fields) {
|
|
198
|
+
return (await Logger.root()).child(name, fields);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a child of the root logger, inheriting this instance's configuration such as `level`, `streams`, etc.
|
|
202
|
+
*
|
|
203
|
+
* @param name The name of the child logger.
|
|
204
|
+
* @param fields Additional fields included in all log lines.
|
|
205
|
+
*/
|
|
206
|
+
static childFromRoot(name, fields) {
|
|
207
|
+
return Logger.getRoot().child(name, fields);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Gets a numeric `LoggerLevel` value by string name.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} levelName The level name to convert to a `LoggerLevel` enum value.
|
|
213
|
+
*
|
|
214
|
+
* **Throws** *{@link SfError}{ name: 'UnrecognizedLoggerLevelNameError' }* The level name was not case-insensitively recognized as a valid `LoggerLevel` value.
|
|
215
|
+
* @see {@Link LoggerLevel}
|
|
216
|
+
*/
|
|
217
|
+
static getLevelByName(levelName) {
|
|
218
|
+
levelName = levelName.toUpperCase();
|
|
219
|
+
if (!(0, ts_types_1.isKeyOf)(LoggerLevel, levelName)) {
|
|
220
|
+
throw new sfError_1.SfError(`Invalid log level "${levelName}".`, 'UnrecognizedLoggerLevelNameError');
|
|
221
|
+
}
|
|
222
|
+
return LoggerLevel[levelName];
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Adds a stream.
|
|
226
|
+
*
|
|
227
|
+
* @param stream The stream configuration to add.
|
|
228
|
+
* @param defaultLevel The default level of the stream.
|
|
229
|
+
*/
|
|
230
|
+
addStream(stream, defaultLevel) {
|
|
231
|
+
if (this.format === LoggerFormat.LOGFMT) {
|
|
232
|
+
stream = this.createLogFmtFormatterStream(stream);
|
|
233
|
+
}
|
|
234
|
+
this.bunyan.addStream(stream, defaultLevel);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Adds a file stream to this logger. Resolved or rejected upon completion of the addition.
|
|
238
|
+
*
|
|
239
|
+
* @param logFile The path to the log file. If it doesn't exist it will be created.
|
|
240
|
+
*/
|
|
241
|
+
async addLogFileStream(logFile) {
|
|
242
|
+
try {
|
|
243
|
+
// Check if we have write access to the log file (i.e., we created it already)
|
|
244
|
+
await fs.promises.access(logFile, fs.constants.W_OK);
|
|
245
|
+
}
|
|
246
|
+
catch (err1) {
|
|
247
|
+
try {
|
|
248
|
+
if (process.platform === 'win32') {
|
|
249
|
+
await fs.promises.mkdir(path.dirname(logFile), { recursive: true });
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
await fs.promises.mkdir(path.dirname(logFile), { recursive: true, mode: 0o700 });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err2) {
|
|
256
|
+
throw sfError_1.SfError.wrap(err2);
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
await fs.promises.writeFile(logFile, '', { mode: '600' });
|
|
260
|
+
}
|
|
261
|
+
catch (err3) {
|
|
262
|
+
throw sfError_1.SfError.wrap(err3);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// avoid multiple streams to same log file
|
|
266
|
+
if (!this.bunyan.streams.find(
|
|
267
|
+
// No bunyan typings
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
269
|
+
(stream) => stream.type === 'rotating-file' && stream.path === logFile)) {
|
|
270
|
+
this.addStream({
|
|
271
|
+
type: 'rotating-file',
|
|
272
|
+
path: logFile,
|
|
273
|
+
period: this.logRotationPeriod,
|
|
274
|
+
count: this.logRotationCount,
|
|
275
|
+
level: this.bunyan.level(),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Adds a file stream to this logger. Resolved or rejected upon completion of the addition.
|
|
281
|
+
*
|
|
282
|
+
* @param logFile The path to the log file. If it doesn't exist it will be created.
|
|
283
|
+
*/
|
|
284
|
+
addLogFileStreamSync(logFile) {
|
|
285
|
+
try {
|
|
286
|
+
// Check if we have write access to the log file (i.e., we created it already)
|
|
287
|
+
fs.accessSync(logFile, fs.constants.W_OK);
|
|
288
|
+
}
|
|
289
|
+
catch (err1) {
|
|
290
|
+
try {
|
|
291
|
+
if (process.platform === 'win32') {
|
|
292
|
+
fs.mkdirSync(path.dirname(logFile));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
fs.mkdirSync(path.dirname(logFile), { mode: 0o700 });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err2) {
|
|
299
|
+
throw sfError_1.SfError.wrap(err2);
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
fs.writeFileSync(logFile, '', { mode: '600' });
|
|
303
|
+
}
|
|
304
|
+
catch (err3) {
|
|
305
|
+
throw sfError_1.SfError.wrap(err3);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// avoid multiple streams to same log file
|
|
309
|
+
if (!this.bunyan.streams.find(
|
|
310
|
+
// No bunyan typings
|
|
311
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
|
+
(stream) => stream.type === 'rotating-file' && stream.path === logFile)) {
|
|
313
|
+
this.addStream({
|
|
314
|
+
type: 'rotating-file',
|
|
315
|
+
path: logFile,
|
|
316
|
+
period: this.logRotationPeriod,
|
|
317
|
+
count: this.logRotationCount,
|
|
318
|
+
level: this.bunyan.level(),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Gets the name of this logger.
|
|
324
|
+
*/
|
|
325
|
+
getName() {
|
|
326
|
+
return this.bunyan.name;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Gets the current level of this logger.
|
|
330
|
+
*/
|
|
331
|
+
getLevel() {
|
|
332
|
+
return this.bunyan.level();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Set the logging level of all streams for this logger. If a specific `level` is not provided, this method will
|
|
336
|
+
* attempt to read it from the environment variable `SFDX_LOG_LEVEL`, and if not found,
|
|
337
|
+
* {@link Logger.DEFAULT_LOG_LEVEL} will be used instead. For convenience `this` object is returned.
|
|
338
|
+
*
|
|
339
|
+
* @param {LoggerLevelValue} [level] The logger level.
|
|
340
|
+
*
|
|
341
|
+
* **Throws** *{@link SfError}{ name: 'UnrecognizedLoggerLevelNameError' }* A value of `level` read from `SFDX_LOG_LEVEL`
|
|
342
|
+
* was invalid.
|
|
343
|
+
*
|
|
344
|
+
* ```
|
|
345
|
+
* // Sets the level from the environment or default value
|
|
346
|
+
* logger.setLevel()
|
|
347
|
+
*
|
|
348
|
+
* // Set the level from the INFO enum
|
|
349
|
+
* logger.setLevel(LoggerLevel.INFO)
|
|
350
|
+
*
|
|
351
|
+
* // Sets the level case-insensitively from a string value
|
|
352
|
+
* logger.setLevel(Logger.getLevelByName('info'))
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
setLevel(level) {
|
|
356
|
+
if (level == null) {
|
|
357
|
+
const logLevelFromEnvVar = new kit_1.Env().getString('SF_LOG_LEVEL');
|
|
358
|
+
level = logLevelFromEnvVar ? Logger.getLevelByName(logLevelFromEnvVar) : Logger.DEFAULT_LEVEL;
|
|
359
|
+
}
|
|
360
|
+
this.bunyan.level(level);
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Gets the underlying Bunyan logger.
|
|
365
|
+
*/
|
|
366
|
+
// leave this typed as any to keep if from trying to export the type from the untyped bunyan module
|
|
367
|
+
// this prevents consumers from getting node_modules/@salesforce/core/lib/logger.d.ts:281:24 - error TS2304: Cannot find name 'Bunyan'.
|
|
368
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
|
369
|
+
getBunyanLogger() {
|
|
370
|
+
return this.bunyan;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Compares the requested log level with the current log level. Returns true if
|
|
374
|
+
* the requested log level is greater than or equal to the current log level.
|
|
375
|
+
*
|
|
376
|
+
* @param level The requested log level to compare against the currently set log level.
|
|
377
|
+
*/
|
|
378
|
+
shouldLog(level) {
|
|
379
|
+
if (typeof level === 'string') {
|
|
380
|
+
level = Bunyan.levelFromName(level);
|
|
381
|
+
}
|
|
382
|
+
return level >= this.getLevel();
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Use in-memory logging for this logger instance instead of any parent streams. Useful for testing.
|
|
386
|
+
* For convenience this object is returned.
|
|
387
|
+
*
|
|
388
|
+
* **WARNING: This cannot be undone for this logger instance.**
|
|
389
|
+
*/
|
|
390
|
+
useMemoryLogging() {
|
|
391
|
+
this.bunyan.streams = [];
|
|
392
|
+
this.bunyan.ringBuffer = new Bunyan.RingBuffer({ limit: 5000 });
|
|
393
|
+
this.addStream({
|
|
394
|
+
type: 'raw',
|
|
395
|
+
stream: this.bunyan.ringBuffer,
|
|
396
|
+
level: this.bunyan.level(),
|
|
397
|
+
});
|
|
398
|
+
return this;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Gets an array of log line objects. Each element is an object that corresponds to a log line.
|
|
402
|
+
*/
|
|
403
|
+
getBufferedRecords() {
|
|
404
|
+
if (this.bunyan.ringBuffer) {
|
|
405
|
+
return this.bunyan.ringBuffer.records;
|
|
406
|
+
}
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Reads a text blob of all the log lines contained in memory or the log file.
|
|
411
|
+
*/
|
|
412
|
+
readLogContentsAsText() {
|
|
413
|
+
if (this.bunyan.ringBuffer) {
|
|
414
|
+
return this.getBufferedRecords().reduce((accum, line) => {
|
|
415
|
+
accum += JSON.stringify(line) + os.EOL;
|
|
416
|
+
return accum;
|
|
417
|
+
}, '');
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
let content = '';
|
|
421
|
+
// No bunyan typings
|
|
422
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
423
|
+
this.bunyan.streams.forEach(async (stream) => {
|
|
424
|
+
if (stream.type === 'file') {
|
|
425
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
426
|
+
content += await fs.promises.readFile(stream.path, 'utf8');
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
return content;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Adds a filter to be applied to all logged messages.
|
|
434
|
+
*
|
|
435
|
+
* @param filter A function with signature `(...args: any[]) => any[]` that transforms log message arguments.
|
|
436
|
+
*/
|
|
437
|
+
addFilter(filter) {
|
|
438
|
+
if (!this.bunyan.filters) {
|
|
439
|
+
this.bunyan.filters = [];
|
|
440
|
+
}
|
|
441
|
+
this.bunyan.filters.push(filter);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Close the logger, including any streams, and remove all listeners.
|
|
445
|
+
*
|
|
446
|
+
* @param fn A function with signature `(stream: LoggerStream) => void` to call for each stream with the stream as an arg.
|
|
447
|
+
*/
|
|
448
|
+
close(fn) {
|
|
449
|
+
if (this.bunyan.streams) {
|
|
450
|
+
try {
|
|
451
|
+
this.bunyan.streams.forEach((entry) => {
|
|
452
|
+
if (fn) {
|
|
453
|
+
fn(entry);
|
|
454
|
+
}
|
|
455
|
+
// close file streams, flush buffer to disk
|
|
456
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
457
|
+
if (entry.type === 'file' && entry.stream && (0, ts_types_1.isFunction)(entry.stream.end)) {
|
|
458
|
+
entry.stream.end();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
finally {
|
|
463
|
+
Logger.lifecycle.removeListener('uncaughtException', this.uncaughtExceptionHandler);
|
|
464
|
+
Logger.lifecycle.removeListener('exit', this.exitHandler);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Create a child logger, typically to add a few log record fields. For convenience this object is returned.
|
|
470
|
+
*
|
|
471
|
+
* @param name The name of the child logger that is emitted w/ log line as `log:<name>`.
|
|
472
|
+
* @param fields Additional fields included in all log lines for the child logger.
|
|
473
|
+
*/
|
|
474
|
+
child(name, fields = {}) {
|
|
475
|
+
if (!name) {
|
|
476
|
+
throw new sfError_1.SfError('LoggerNameRequired');
|
|
477
|
+
}
|
|
478
|
+
fields.log = name;
|
|
479
|
+
const child = new Logger(name);
|
|
480
|
+
// only support including additional fields on log line (no config)
|
|
481
|
+
child.bunyan = this.bunyan.child(fields, true);
|
|
482
|
+
child.bunyan.name = name;
|
|
483
|
+
child.bunyan.filters = this.bunyan.filters;
|
|
484
|
+
this.trace(`Setup child '${name}' logger instance`);
|
|
485
|
+
return child;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Add a field to all log lines for this logger. For convenience `this` object is returned.
|
|
489
|
+
*
|
|
490
|
+
* @param name The name of the field to add.
|
|
491
|
+
* @param value The value of the field to be logged.
|
|
492
|
+
*/
|
|
493
|
+
addField(name, value) {
|
|
494
|
+
this.bunyan.fields[name] = value;
|
|
495
|
+
return this;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Logs at `trace` level with filtering applied. For convenience `this` object is returned.
|
|
499
|
+
*
|
|
500
|
+
* @param args Any number of arguments to be logged.
|
|
501
|
+
*/
|
|
502
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
503
|
+
trace(...args) {
|
|
504
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
505
|
+
this.bunyan.trace(this.applyFilters(LoggerLevel.TRACE, ...args));
|
|
506
|
+
return this;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Logs at `debug` level with filtering applied. For convenience `this` object is returned.
|
|
510
|
+
*
|
|
511
|
+
* @param args Any number of arguments to be logged.
|
|
512
|
+
*/
|
|
513
|
+
debug(...args) {
|
|
514
|
+
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...args));
|
|
515
|
+
return this;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Logs at `debug` level with filtering applied.
|
|
519
|
+
*
|
|
520
|
+
* @param cb A callback that returns on array objects to be logged.
|
|
521
|
+
*/
|
|
522
|
+
debugCallback(cb) {
|
|
523
|
+
if (this.getLevel() === LoggerLevel.DEBUG || process.env.DEBUG) {
|
|
524
|
+
const result = cb();
|
|
525
|
+
if ((0, ts_types_1.isArray)(result)) {
|
|
526
|
+
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...result));
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
this.bunyan.debug(this.applyFilters(LoggerLevel.DEBUG, ...[result]));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Logs at `info` level with filtering applied. For convenience `this` object is returned.
|
|
535
|
+
*
|
|
536
|
+
* @param args Any number of arguments to be logged.
|
|
537
|
+
*/
|
|
538
|
+
info(...args) {
|
|
539
|
+
this.bunyan.info(this.applyFilters(LoggerLevel.INFO, ...args));
|
|
540
|
+
return this;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Logs at `warn` level with filtering applied. For convenience `this` object is returned.
|
|
544
|
+
*
|
|
545
|
+
* @param args Any number of arguments to be logged.
|
|
546
|
+
*/
|
|
547
|
+
warn(...args) {
|
|
548
|
+
this.bunyan.warn(this.applyFilters(LoggerLevel.WARN, ...args));
|
|
549
|
+
return this;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Logs at `error` level with filtering applied. For convenience `this` object is returned.
|
|
553
|
+
*
|
|
554
|
+
* @param args Any number of arguments to be logged.
|
|
555
|
+
*/
|
|
556
|
+
error(...args) {
|
|
557
|
+
this.bunyan.error(this.applyFilters(LoggerLevel.ERROR, ...args));
|
|
558
|
+
return this;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Logs at `fatal` level with filtering applied. For convenience `this` object is returned.
|
|
562
|
+
*
|
|
563
|
+
* @param args Any number of arguments to be logged.
|
|
564
|
+
*/
|
|
565
|
+
fatal(...args) {
|
|
566
|
+
// always show fatal to stderr
|
|
567
|
+
// eslint-disable-next-line no-console
|
|
568
|
+
console.error(...args);
|
|
569
|
+
this.bunyan.fatal(this.applyFilters(LoggerLevel.FATAL, ...args));
|
|
570
|
+
return this;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Enables logging to stdout when the DEBUG environment variable is used. It uses the logger
|
|
574
|
+
* name as the debug name, so you can do DEBUG=<logger-name> to filter the results to your logger.
|
|
575
|
+
*/
|
|
576
|
+
enableDEBUG() {
|
|
577
|
+
// The debug library does this for you, but no point setting up the stream if it isn't there
|
|
578
|
+
if (process.env.DEBUG && !this.debugEnabled) {
|
|
579
|
+
const debuggers = {};
|
|
580
|
+
debuggers.core = Debug(`${this.getName()}:core`);
|
|
581
|
+
this.addStream({
|
|
582
|
+
name: 'debug',
|
|
583
|
+
stream: new stream_1.Writable({
|
|
584
|
+
write: (chunk, encoding, next) => {
|
|
585
|
+
try {
|
|
586
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
587
|
+
const json = (0, kit_1.parseJsonMap)(chunk.toString());
|
|
588
|
+
const logLevel = (0, ts_types_1.ensureNumber)(json.level);
|
|
589
|
+
if (this.getLevel() <= logLevel) {
|
|
590
|
+
let debuggerName = 'core';
|
|
591
|
+
if ((0, ts_types_1.isString)(json.log)) {
|
|
592
|
+
debuggerName = json.log;
|
|
593
|
+
if (!debuggers[debuggerName]) {
|
|
594
|
+
debuggers[debuggerName] = Debug(`${this.getName()}:${debuggerName}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
const level = LoggerLevel[logLevel];
|
|
598
|
+
(0, ts_types_1.ensure)(debuggers[debuggerName])(`${level} ${json.msg}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
catch (err) {
|
|
602
|
+
// do nothing
|
|
603
|
+
}
|
|
604
|
+
next();
|
|
605
|
+
},
|
|
606
|
+
}),
|
|
607
|
+
// Consume all levels
|
|
608
|
+
level: 0,
|
|
609
|
+
});
|
|
610
|
+
this.debugEnabled = true;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
applyFilters(logLevel, ...args) {
|
|
614
|
+
if (this.shouldLog(logLevel)) {
|
|
615
|
+
// No bunyan typings
|
|
616
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
617
|
+
this.bunyan.filters.forEach((filter) => (args = filter(...args)));
|
|
618
|
+
}
|
|
619
|
+
return args && args.length === 1 ? args[0] : args;
|
|
620
|
+
}
|
|
621
|
+
// eslint-disable-next-line class-methods-use-this
|
|
622
|
+
createLogFmtFormatterStream(loggerStream) {
|
|
623
|
+
const logFmtWriteableStream = new stream_1.Writable({
|
|
624
|
+
write: (chunk, enc, cb) => {
|
|
625
|
+
try {
|
|
626
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
627
|
+
const parsedJSON = JSON.parse(chunk.toString());
|
|
628
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
629
|
+
const keys = Object.keys(parsedJSON);
|
|
630
|
+
let logEntry = '';
|
|
631
|
+
keys.forEach((key) => {
|
|
632
|
+
let logMsg = `${parsedJSON[key]}`;
|
|
633
|
+
if (logMsg.trim().includes(' ')) {
|
|
634
|
+
logMsg = `"${logMsg}"`;
|
|
635
|
+
}
|
|
636
|
+
logEntry += `${key}=${logMsg} `;
|
|
637
|
+
});
|
|
638
|
+
if (loggerStream.stream) {
|
|
639
|
+
loggerStream.stream.write(logEntry.trimRight() + '\n');
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
catch (error) {
|
|
643
|
+
if (loggerStream.stream) {
|
|
644
|
+
loggerStream.stream.write(chunk.toString());
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
cb(null);
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
return Object.assign({}, loggerStream, { stream: logFmtWriteableStream });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
exports.Logger = Logger;
|
|
654
|
+
/**
|
|
655
|
+
* The name of the root sfdx `Logger`.
|
|
656
|
+
*/
|
|
657
|
+
Logger.ROOT_NAME = 'sf';
|
|
658
|
+
/**
|
|
659
|
+
* The default `LoggerLevel` when constructing new `Logger` instances.
|
|
660
|
+
*/
|
|
661
|
+
Logger.DEFAULT_LEVEL = LoggerLevel.WARN;
|
|
662
|
+
/**
|
|
663
|
+
* A list of all lower case `LoggerLevel` names.
|
|
664
|
+
*
|
|
665
|
+
* **See** {@link LoggerLevel}
|
|
666
|
+
*/
|
|
667
|
+
Logger.LEVEL_NAMES = Object.values(LoggerLevel)
|
|
668
|
+
.filter(ts_types_1.isString)
|
|
669
|
+
.map((v) => v.toLowerCase());
|
|
670
|
+
// Rollup all instance-specific process event listeners together to prevent global `MaxListenersExceededWarning`s.
|
|
671
|
+
Logger.lifecycle = (() => {
|
|
672
|
+
const events = new events_1.EventEmitter();
|
|
673
|
+
events.setMaxListeners(0); // never warn on listener counts
|
|
674
|
+
process.on('uncaughtException', (err) => events.emit('uncaughtException', err));
|
|
675
|
+
process.on('exit', () => events.emit('exit'));
|
|
676
|
+
return events;
|
|
677
|
+
})();
|
|
678
|
+
// Ok to log clientid
|
|
679
|
+
const FILTERED_KEYS = [
|
|
680
|
+
'sid',
|
|
681
|
+
'Authorization',
|
|
682
|
+
// Any json attribute that contains the words "access" and "token" will have the attribute/value hidden
|
|
683
|
+
{ name: 'access_token', regex: 'access[^\'"]*token' },
|
|
684
|
+
// Any json attribute that contains the words "refresh" and "token" will have the attribute/value hidden
|
|
685
|
+
{ name: 'refresh_token', regex: 'refresh[^\'"]*token' },
|
|
686
|
+
'clientsecret',
|
|
687
|
+
// Any json attribute that contains the words "sfdx", "auth", and "url" will have the attribute/value hidden
|
|
688
|
+
{ name: 'sfdxauthurl', regex: 'sfdx[^\'"]*auth[^\'"]*url' },
|
|
689
|
+
];
|
|
690
|
+
// SFDX code and plugins should never show tokens or connect app information in the logs
|
|
691
|
+
const filterSecrets = (...args) => args.map((arg) => {
|
|
692
|
+
if ((0, ts_types_1.isArray)(arg)) {
|
|
693
|
+
return filterSecrets(...arg);
|
|
694
|
+
}
|
|
695
|
+
if (arg) {
|
|
696
|
+
let mutableArg;
|
|
697
|
+
// Normalize all objects into a string. This include errors.
|
|
698
|
+
if (arg instanceof Buffer) {
|
|
699
|
+
mutableArg = '<Buffer>';
|
|
700
|
+
}
|
|
701
|
+
else if ((0, ts_types_1.isObject)(arg)) {
|
|
702
|
+
mutableArg = JSON.stringify(arg);
|
|
703
|
+
}
|
|
704
|
+
else if ((0, ts_types_1.isString)(arg)) {
|
|
705
|
+
mutableArg = arg;
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
mutableArg = '';
|
|
709
|
+
}
|
|
710
|
+
const HIDDEN = 'HIDDEN';
|
|
711
|
+
FILTERED_KEYS.forEach((key) => {
|
|
712
|
+
let expElement = key;
|
|
713
|
+
let expName = key;
|
|
714
|
+
// Filtered keys can be strings or objects containing regular expression components.
|
|
715
|
+
if ((0, ts_types_1.isPlainObject)(key)) {
|
|
716
|
+
expElement = key.regex;
|
|
717
|
+
expName = key.name;
|
|
718
|
+
}
|
|
719
|
+
const hiddenAttrMessage = `"<${expName} - ${HIDDEN}>"`;
|
|
720
|
+
// Match all json attribute values case insensitive: ex. {" Access*^&(*()^* Token " : " 45143075913458901348905 \n\t" ...}
|
|
721
|
+
const regexTokens = new RegExp(`(['"][^'"]*${expElement}[^'"]*['"]\\s*:\\s*)['"][^'"]*['"]`, 'gi');
|
|
722
|
+
mutableArg = mutableArg.replace(regexTokens, `$1${hiddenAttrMessage}`);
|
|
723
|
+
// Match all key value attribute case insensitive: ex. {" key\t" : ' access_token ' , " value " : " dsafgasr431 " ....}
|
|
724
|
+
const keyRegex = new RegExp(`(['"]\\s*key\\s*['"]\\s*:)\\s*['"]\\s*${expElement}\\s*['"]\\s*.\\s*['"]\\s*value\\s*['"]\\s*:\\s*['"]\\s*[^'"]*['"]`, 'gi');
|
|
725
|
+
mutableArg = mutableArg.replace(keyRegex, `$1${hiddenAttrMessage}`);
|
|
726
|
+
});
|
|
727
|
+
mutableArg = mutableArg.replace(/(00D\w{12,15})![.\w]*/, `<${HIDDEN}>`);
|
|
728
|
+
// return an object if an object was logged; otherwise return the filtered string.
|
|
729
|
+
return (0, ts_types_1.isObject)(arg) ? (0, kit_1.parseJson)(mutableArg) : mutableArg;
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
return arg;
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
735
|
//# sourceMappingURL=logger.js.map
|