@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.
Files changed (153) hide show
  1. package/LICENSE.txt +11 -11
  2. package/README.md +222 -227
  3. package/lib/config/aliasesConfig.d.ts +12 -12
  4. package/lib/config/aliasesConfig.js +27 -27
  5. package/lib/config/authInfoConfig.d.ts +19 -19
  6. package/lib/config/authInfoConfig.js +34 -34
  7. package/lib/config/config.d.ts +311 -311
  8. package/lib/config/config.js +574 -574
  9. package/lib/config/configAggregator.d.ts +232 -232
  10. package/lib/config/configAggregator.js +379 -379
  11. package/lib/config/configFile.d.ts +199 -199
  12. package/lib/config/configFile.js +340 -340
  13. package/lib/config/configGroup.d.ts +141 -141
  14. package/lib/config/configGroup.js +224 -224
  15. package/lib/config/configStore.d.ts +241 -241
  16. package/lib/config/configStore.js +352 -352
  17. package/lib/config/envVars.d.ts +101 -101
  18. package/lib/config/envVars.js +456 -456
  19. package/lib/config/orgUsersConfig.d.ts +31 -31
  20. package/lib/config/orgUsersConfig.js +41 -41
  21. package/lib/config/sandboxOrgConfig.d.ts +37 -37
  22. package/lib/config/sandboxOrgConfig.js +50 -50
  23. package/lib/config/sandboxProcessCache.d.ts +16 -16
  24. package/lib/config/sandboxProcessCache.js +37 -37
  25. package/lib/config/tokensConfig.d.ts +10 -10
  26. package/lib/config/tokensConfig.js +28 -28
  27. package/lib/config/ttlConfig.d.ts +34 -34
  28. package/lib/config/ttlConfig.js +54 -54
  29. package/lib/crypto/crypto.d.ts +54 -54
  30. package/lib/crypto/crypto.js +220 -220
  31. package/lib/crypto/keyChain.d.ts +8 -8
  32. package/lib/crypto/keyChain.js +61 -61
  33. package/lib/crypto/keyChainImpl.d.ts +116 -116
  34. package/lib/crypto/keyChainImpl.js +486 -486
  35. package/lib/crypto/secureBuffer.d.ts +46 -46
  36. package/lib/crypto/secureBuffer.js +82 -82
  37. package/lib/deviceOauthService.d.ts +71 -71
  38. package/lib/deviceOauthService.js +191 -191
  39. package/lib/exported.d.ts +38 -38
  40. package/lib/exported.js +118 -118
  41. package/lib/global.d.ts +70 -70
  42. package/lib/global.js +109 -109
  43. package/lib/lifecycleEvents.d.ts +93 -93
  44. package/lib/lifecycleEvents.js +188 -188
  45. package/lib/logger.d.ts +381 -381
  46. package/lib/logger.js +734 -734
  47. package/lib/messages.d.ts +291 -291
  48. package/lib/messages.js +543 -543
  49. package/lib/org/authInfo.d.ts +344 -344
  50. package/lib/org/authInfo.js +892 -892
  51. package/lib/org/authRemover.d.ts +88 -88
  52. package/lib/org/authRemover.js +182 -182
  53. package/lib/org/connection.d.ts +197 -197
  54. package/lib/org/connection.js +395 -395
  55. package/lib/org/index.d.ts +6 -6
  56. package/lib/org/index.js +28 -28
  57. package/lib/org/org.d.ts +558 -558
  58. package/lib/org/org.js +1267 -1267
  59. package/lib/org/orgConfigProperties.d.ts +69 -69
  60. package/lib/org/orgConfigProperties.js +136 -136
  61. package/lib/org/permissionSetAssignment.d.ts +35 -35
  62. package/lib/org/permissionSetAssignment.js +125 -125
  63. package/lib/org/scratchOrgCache.d.ts +20 -20
  64. package/lib/org/scratchOrgCache.js +32 -32
  65. package/lib/org/scratchOrgCreate.d.ts +54 -54
  66. package/lib/org/scratchOrgCreate.js +216 -216
  67. package/lib/org/scratchOrgErrorCodes.d.ts +10 -10
  68. package/lib/org/scratchOrgErrorCodes.js +88 -88
  69. package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -26
  70. package/lib/org/scratchOrgFeatureDeprecation.js +109 -109
  71. package/lib/org/scratchOrgInfoApi.d.ts +68 -68
  72. package/lib/org/scratchOrgInfoApi.js +413 -413
  73. package/lib/org/scratchOrgInfoGenerator.d.ts +64 -64
  74. package/lib/org/scratchOrgInfoGenerator.js +241 -241
  75. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -10
  76. package/lib/org/scratchOrgLifecycleEvents.js +40 -40
  77. package/lib/org/scratchOrgSettingsGenerator.d.ts +78 -78
  78. package/lib/org/scratchOrgSettingsGenerator.js +276 -276
  79. package/lib/org/scratchOrgTypes.d.ts +43 -43
  80. package/lib/org/scratchOrgTypes.js +8 -8
  81. package/lib/org/user.d.ts +187 -187
  82. package/lib/org/user.js +448 -448
  83. package/lib/schema/printer.d.ts +79 -79
  84. package/lib/schema/printer.js +260 -260
  85. package/lib/schema/validator.d.ts +70 -70
  86. package/lib/schema/validator.js +169 -169
  87. package/lib/sfError.d.ts +73 -73
  88. package/lib/sfError.js +136 -136
  89. package/lib/sfProject.d.ts +357 -357
  90. package/lib/sfProject.js +671 -671
  91. package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -98
  92. package/lib/stateAggregator/accessors/aliasAccessor.js +145 -145
  93. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -101
  94. package/lib/stateAggregator/accessors/orgAccessor.js +240 -240
  95. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -8
  96. package/lib/stateAggregator/accessors/sandboxAccessor.js +27 -27
  97. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -63
  98. package/lib/stateAggregator/accessors/tokenAccessor.js +79 -79
  99. package/lib/stateAggregator/index.d.ts +4 -4
  100. package/lib/stateAggregator/index.js +26 -26
  101. package/lib/stateAggregator/stateAggregator.d.ts +25 -25
  102. package/lib/stateAggregator/stateAggregator.js +45 -45
  103. package/lib/status/myDomainResolver.d.ts +66 -66
  104. package/lib/status/myDomainResolver.js +124 -124
  105. package/lib/status/pollingClient.d.ts +85 -85
  106. package/lib/status/pollingClient.js +115 -115
  107. package/lib/status/streamingClient.d.ts +244 -244
  108. package/lib/status/streamingClient.js +436 -436
  109. package/lib/status/types.d.ts +89 -89
  110. package/lib/status/types.js +17 -17
  111. package/lib/testSetup.d.ts +553 -530
  112. package/lib/testSetup.js +871 -727
  113. package/lib/util/cache.d.ts +11 -11
  114. package/lib/util/cache.js +69 -69
  115. package/lib/util/checkLightningDomain.d.ts +1 -1
  116. package/lib/util/checkLightningDomain.js +28 -28
  117. package/lib/util/directoryWriter.d.ts +12 -12
  118. package/lib/util/directoryWriter.js +53 -53
  119. package/lib/util/getJwtAudienceUrl.d.ts +4 -4
  120. package/lib/util/getJwtAudienceUrl.js +18 -18
  121. package/lib/util/internal.d.ts +58 -58
  122. package/lib/util/internal.js +118 -118
  123. package/lib/util/jsonXmlTools.d.ts +14 -14
  124. package/lib/util/jsonXmlTools.js +38 -38
  125. package/lib/util/mapKeys.d.ts +14 -14
  126. package/lib/util/mapKeys.js +51 -51
  127. package/lib/util/sfdc.d.ts +52 -52
  128. package/lib/util/sfdc.js +85 -85
  129. package/lib/util/sfdcUrl.d.ts +72 -72
  130. package/lib/util/sfdcUrl.js +215 -215
  131. package/lib/util/structuredWriter.d.ts +9 -9
  132. package/lib/util/structuredWriter.js +2 -2
  133. package/lib/util/zipWriter.d.ts +16 -16
  134. package/lib/util/zipWriter.js +67 -67
  135. package/lib/webOAuthServer.d.ts +156 -156
  136. package/lib/webOAuthServer.js +388 -388
  137. package/messages/auth.md +37 -37
  138. package/messages/config.md +156 -156
  139. package/messages/connection.md +30 -30
  140. package/messages/core.json +20 -20
  141. package/messages/core.md +67 -67
  142. package/messages/encryption.md +85 -85
  143. package/messages/envVars.md +303 -303
  144. package/messages/org.md +63 -63
  145. package/messages/permissionSetAssignment.md +31 -31
  146. package/messages/scratchOrgCreate.md +23 -23
  147. package/messages/scratchOrgErrorCodes.md +115 -115
  148. package/messages/scratchOrgFeatureDeprecation.md +11 -11
  149. package/messages/scratchOrgInfoApi.md +15 -15
  150. package/messages/scratchOrgInfoGenerator.md +23 -23
  151. package/messages/streaming.md +23 -23
  152. package/messages/user.md +35 -35
  153. 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