analogger 1.26.1 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -12
- package/ana-logger.d.cts +1 -1
- package/browser/ana-logger.mjs +231 -5
- package/browser/imported/adm-zip/adm-zip.mjs +959 -0
- package/browser/imported/adm-zip/zipEntry.mjs +414 -0
- package/browser/imported/adm-zip/zipFile.mjs +455 -0
- package/demo.js +18 -2
- package/dist/analogger-browser.min.mjs +5 -5
- package/dist/html-to-image-plugin.min.mjs +5 -5
- package/esm/ana-logger.mjs +231 -5
- package/esm/node_modules/adm-zip/adm-zip.mjs +959 -0
- package/esm/node_modules/adm-zip/zipEntry.mjs +414 -0
- package/esm/node_modules/adm-zip/zipFile.mjs +455 -0
- package/package.json +3 -2
- package/src/ana-logger.cjs +231 -5
package/README.md
CHANGED
|
@@ -263,18 +263,23 @@ Display the browser native message box if run from it; otherwise, it displays th
|
|
|
263
263
|
### setOptions()
|
|
264
264
|
|
|
265
265
|
|
|
266
|
-
| **Options**
|
|
267
|
-
|
|
268
|
-
| silent
|
|
269
|
-
| hideLog
|
|
270
|
-
| hideError
|
|
271
|
-
| hideHookMessage
|
|
272
|
-
| hidePassingTests
|
|
273
|
-
| logToDom
|
|
274
|
-
| logToFile
|
|
275
|
-
| logToRemote
|
|
276
|
-
|
|
|
277
|
-
|
|
|
266
|
+
| **Options** | **default** | **Expect** | **Description** |
|
|
267
|
+
|---------------------|-------------|-----------------------------------|------------------------------------------------------------------------------------|
|
|
268
|
+
| silent | false | boolean | _Hide logs from console (not errors)_ |
|
|
269
|
+
| hideLog | false | boolean | _Same as above (silent has precedence over hideLog)_ |
|
|
270
|
+
| hideError | false | boolean | _Hide errors from console_ |
|
|
271
|
+
| hideHookMessage | false | boolean | _Hide the automatic message shown when some native console methods are overridden_ |
|
|
272
|
+
| hidePassingTests | false | boolean | _Hide Live test results_ |
|
|
273
|
+
| logToDom | false | string (DOM Selector) | _display log in a DOM container_ |
|
|
274
|
+
| logToFile | false | string (File path) | _write log to a file if running from Node_ |
|
|
275
|
+
| logToRemote | undefined | string (URL) | _Send log to a remote (more info in the next version)_ |
|
|
276
|
+
| logMaxSize | 0 | number | _Set maximum size for the log file_ |
|
|
277
|
+
| compressArchives | false | boolean | _Whether to archive and compress the logs after deleting an archive_ |
|
|
278
|
+
| compressionLevel | 1 | number | _Archive compression level (0 to 9 with 0 = no compression)_ |
|
|
279
|
+
| addArchiveTimestamp | true | boolean | _Whether to add a timestamp to the generated rotated logs_ |
|
|
280
|
+
| logMaxArchives | 3 | number | _Maximum number of log files to use_ |
|
|
281
|
+
| requiredLogLevel | "LOG" | "LOG" / "INFO" / "WARN" / "ERROR" | _Define the log level from which the system can show a log entry_ |
|
|
282
|
+
| enableDate | false | boolean | _Show date + time (instead of time only)_ |
|
|
278
283
|
|
|
279
284
|
<br/>
|
|
280
285
|
|
package/ana-logger.d.cts
CHANGED
|
@@ -110,7 +110,7 @@ declare class ____AnaLogger {
|
|
|
110
110
|
isBrowser(): boolean;
|
|
111
111
|
resetLogger(): void;
|
|
112
112
|
resetOptions(): void;
|
|
113
|
-
setOptions({ contextLenMax, idLenMax, lidLenMax, symbolLenMax, messageLenMax, hideLog, hideError, hideHookMessage, hidePassingTests, logToDom, logToFile, logToRemote, logToRemoteUrl, logToRemoteBinaryUrl, loopback, requiredLogLevel, oneConsolePerContext, silent, enableDate, protocol, host, port, pathname, binarypathname }?: any): void;
|
|
113
|
+
setOptions({ contextLenMax, idLenMax, lidLenMax, symbolLenMax, messageLenMax, hideLog, hideError, hideHookMessage, hidePassingTests, logToDom, logToFile, logMaxSize, logMaxArchives, logIndexArchive, addArchiveTimestamp, addArchiveIndex, compressArchives, compressionLevel, logToRemote, logToRemoteUrl, logToRemoteBinaryUrl, loopback, requiredLogLevel, oneConsolePerContext, silent, enableDate, protocol, host, port, pathname, binarypathname }?: any): void;
|
|
114
114
|
EOL: string;
|
|
115
115
|
getOptions(): {
|
|
116
116
|
hideHookMessage: boolean;
|
package/browser/ana-logger.mjs
CHANGED
|
@@ -15,7 +15,6 @@ import {CONSOLE_HEADER_CLASSNAME, CONSOLE_FOOTER_CLASSNAME} from "./constants.m
|
|
|
15
15
|
// to-ansi is also used by the browser
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
const DEFAULT = {
|
|
20
19
|
moduleName: "analogger",
|
|
21
20
|
// Default values for remote-logging
|
|
@@ -171,6 +170,166 @@ const symbolNames = {
|
|
|
171
170
|
// --------------------------------------------------
|
|
172
171
|
// Helpers
|
|
173
172
|
// --------------------------------------------------
|
|
173
|
+
function getFilePathProperties(filePath) {
|
|
174
|
+
try {
|
|
175
|
+
const ext = path.extname(filePath);
|
|
176
|
+
const basename = path.basename(filePath, ext);
|
|
177
|
+
const dirname = path.dirname(filePath);
|
|
178
|
+
const fPath = filePath.slice(0, filePath.length - ext.length);
|
|
179
|
+
return {
|
|
180
|
+
extension: ext, filePath: fPath, basename, dirname
|
|
181
|
+
};
|
|
182
|
+
} catch (e) {
|
|
183
|
+
console.error("FILEPATH_EXT_FAILURE: ", e.message);
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
extension: ".log", filePath
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getConsistentTimestamp() {
|
|
191
|
+
const now = new Date();
|
|
192
|
+
|
|
193
|
+
// ISO 8601 format with milliseconds and timezone offset
|
|
194
|
+
const isoString = now.toISOString();
|
|
195
|
+
|
|
196
|
+
return isoString.replace(/:/g, "-").replace(/\./g, "-");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Deletes all files in the given directory that match the specified filename prefix, index, and extension.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} directory - The directory containing the files.
|
|
203
|
+
* @param {string} filenamePrefix - The prefix of the filename (e.g., "demo").
|
|
204
|
+
* @param {string} index - The index to match (e.g., "01", "02", "03").
|
|
205
|
+
* @param {string} extension - The file extension (e.g., "log").
|
|
206
|
+
* @param archiveName
|
|
207
|
+
* @param compressionLevel
|
|
208
|
+
* @param {function} deletionCallback - A callback function to handle the result.
|
|
209
|
+
*/
|
|
210
|
+
function deleteFilesWithIndex(directory, filenamePrefix, index, extension, archiveName, compressionLevel, deletionCallback) {
|
|
211
|
+
fs.readdir(directory, (err, files) => {
|
|
212
|
+
if (err) {
|
|
213
|
+
deletionCallback(err, null);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const deletedFiles = [];
|
|
218
|
+
let filesProcessed = 0;
|
|
219
|
+
|
|
220
|
+
const removeFile = (filePath, callback) => {
|
|
221
|
+
if (!fs.existsSync(filePath)) {
|
|
222
|
+
callback(null, null);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
fs.unlink(filePath, (unlinkErr) => {
|
|
226
|
+
if (unlinkErr) {
|
|
227
|
+
console.error(`DELETION_FAILURE: Error deleting file ${filePath}: ${unlinkErr}`);
|
|
228
|
+
} else {
|
|
229
|
+
deletedFiles.push(filePath);
|
|
230
|
+
}
|
|
231
|
+
filesProcessed++;
|
|
232
|
+
if (filesProcessed === files.length) {
|
|
233
|
+
callback(null, deletedFiles);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const processFile = (file) => {
|
|
239
|
+
if (file.startsWith(filenamePrefix + ".") && file.endsWith(index + extension)) {
|
|
240
|
+
const filePath = path.join(directory, file);
|
|
241
|
+
|
|
242
|
+
if (archiveName)
|
|
243
|
+
{
|
|
244
|
+
createTarGzArchiveSync(filePath, archiveName, compressionLevel);
|
|
245
|
+
removeFile(filePath, deletionCallback);
|
|
246
|
+
}
|
|
247
|
+
else
|
|
248
|
+
{
|
|
249
|
+
removeFile(filePath, deletionCallback);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
} else {
|
|
253
|
+
filesProcessed++;
|
|
254
|
+
if (filesProcessed === files.length) {
|
|
255
|
+
deletionCallback(null, deletedFiles);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (files.length === 0) {
|
|
261
|
+
deletionCallback(null, deletedFiles); // Handle empty directory
|
|
262
|
+
} else {
|
|
263
|
+
files.forEach(processFile, compressionLevel);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Adds a log file to a tar.gz archive.
|
|
270
|
+
*
|
|
271
|
+
* @param {string} inputFile - The path to the log file to be added to the archive.
|
|
272
|
+
* @param {string} archivePath - The path to the tar.gz archive.
|
|
273
|
+
* @param compressionLevel
|
|
274
|
+
*/
|
|
275
|
+
function createTarGzArchiveSync(inputFile, archivePath, compressionLevel = 1) {
|
|
276
|
+
try {
|
|
277
|
+
return ;
|
|
278
|
+
|
|
279
|
+
// Check if the input file exists
|
|
280
|
+
if (!fs.existsSync(inputFile)) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Create the archive if it doesn't exist, otherwise append
|
|
285
|
+
if (!fs.existsSync(archivePath)) {
|
|
286
|
+
// Create a new archive with the single file at root
|
|
287
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tar-gz-init-'));
|
|
288
|
+
try {
|
|
289
|
+
const destFilePath = path.join(tempDir, path.basename(inputFile));
|
|
290
|
+
fs.copyFileSync(inputFile, destFilePath);
|
|
291
|
+
tar.c({
|
|
292
|
+
sync: true,
|
|
293
|
+
gzip: { level: compressionLevel },
|
|
294
|
+
file: archivePath,
|
|
295
|
+
cwd: tempDir,
|
|
296
|
+
portable: true,
|
|
297
|
+
}, [path.basename(inputFile)]);
|
|
298
|
+
} finally {
|
|
299
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// If archive exists, append
|
|
305
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tar-gz-append-'));
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
tar.x({
|
|
309
|
+
file: archivePath,
|
|
310
|
+
cwd: tempDir,
|
|
311
|
+
sync: true,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const destFilePath = path.join(tempDir, path.basename(inputFile));
|
|
315
|
+
fs.copyFileSync(inputFile, destFilePath);
|
|
316
|
+
|
|
317
|
+
tar.c({
|
|
318
|
+
gzip: true,
|
|
319
|
+
file: archivePath,
|
|
320
|
+
cwd: tempDir,
|
|
321
|
+
sync: true,
|
|
322
|
+
portable: true,
|
|
323
|
+
}, fs.readdirSync(tempDir));
|
|
324
|
+
|
|
325
|
+
} finally {
|
|
326
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
327
|
+
}
|
|
328
|
+
} catch (err) {
|
|
329
|
+
console.error(`ARCHIVE_FAILURE: ${e.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
174
333
|
/**
|
|
175
334
|
* https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser
|
|
176
335
|
* @returns {string}
|
|
@@ -449,10 +608,16 @@ class ____AnaLogger
|
|
|
449
608
|
this.options.oneConsolePerContext = true;
|
|
450
609
|
this.options.logToDom = undefined;
|
|
451
610
|
this.options.logToFile = undefined;
|
|
611
|
+
this.options.logMaxSize = 0;
|
|
612
|
+
this.options.logMaxArchives = 3;
|
|
613
|
+
this.options.logIndexArchive = 0;
|
|
452
614
|
this.options.logToRemote = undefined;
|
|
615
|
+
this.options.addArchiveTimestamp = true;
|
|
616
|
+
this.options.addArchiveIndex = true;
|
|
453
617
|
this.options.logToRemoteUrl = undefined;
|
|
454
618
|
this.options.logToRemoteBinaryUrl = undefined;
|
|
455
|
-
this.options.
|
|
619
|
+
this.options.compressArchives = false;
|
|
620
|
+
this.options.compressionLevel = 1;
|
|
456
621
|
this.options.protocol = undefined;
|
|
457
622
|
this.options.host = undefined;
|
|
458
623
|
this.options.port = undefined;
|
|
@@ -478,6 +643,13 @@ class ____AnaLogger
|
|
|
478
643
|
hidePassingTests = undefined,
|
|
479
644
|
logToDom = undefined,
|
|
480
645
|
logToFile = undefined,
|
|
646
|
+
logMaxSize = 0,
|
|
647
|
+
logMaxArchives = 3,
|
|
648
|
+
logIndexArchive = 0,
|
|
649
|
+
addArchiveTimestamp = true,
|
|
650
|
+
addArchiveIndex = true,
|
|
651
|
+
compressArchives = false,
|
|
652
|
+
compressionLevel = 1,
|
|
481
653
|
logToRemote = undefined,
|
|
482
654
|
logToRemoteUrl = undefined,
|
|
483
655
|
logToRemoteBinaryUrl = undefined,
|
|
@@ -500,6 +672,14 @@ class ____AnaLogger
|
|
|
500
672
|
this.options.messageLenMax = messageLenMax;
|
|
501
673
|
this.options.symbolLenMax = symbolLenMax;
|
|
502
674
|
|
|
675
|
+
this.options.logMaxSize = logMaxSize;
|
|
676
|
+
this.options.logMaxArchives = logMaxArchives;
|
|
677
|
+
this.options.logIndexArchive = logIndexArchive;
|
|
678
|
+
this.options.addArchiveTimestamp = addArchiveTimestamp;
|
|
679
|
+
this.options.addArchiveIndex = addArchiveIndex;
|
|
680
|
+
this.options.compressArchives = compressArchives;
|
|
681
|
+
this.options.compressionLevel = compressionLevel;
|
|
682
|
+
|
|
503
683
|
this.options.requiredLogLevel = requiredLogLevel;
|
|
504
684
|
|
|
505
685
|
// TODO: Make one of silent or hideToLog options obsolete
|
|
@@ -1357,10 +1537,56 @@ class ____AnaLogger
|
|
|
1357
1537
|
{
|
|
1358
1538
|
try
|
|
1359
1539
|
{
|
|
1540
|
+
if (!fs.existsSync(this.options.logToFilePath))
|
|
1541
|
+
{
|
|
1542
|
+
const dir = path.dirname(this.options.logToFilePath);
|
|
1543
|
+
if (!fs.existsSync(dir))
|
|
1544
|
+
{
|
|
1545
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1546
|
+
}
|
|
1547
|
+
fs.writeFileSync(this.options.logToFilePath, "");
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// Check filesize doesn't exceed the limit set by the user
|
|
1551
|
+
if (this.options.logMaxSize) {
|
|
1552
|
+
const stats = fs.statSync(this.options.logToFilePath);
|
|
1553
|
+
const fileSizeInBytes = stats.size;
|
|
1554
|
+
if (fileSizeInBytes > this.options.logMaxSize) {
|
|
1555
|
+
|
|
1556
|
+
if (this.options.logIndexArchive < this.options.logMaxArchives) {
|
|
1557
|
+
++this.options.logIndexArchive;
|
|
1558
|
+
} else {
|
|
1559
|
+
this.options.logIndexArchive = 1;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// Extract the archive name without the extension
|
|
1563
|
+
const padding = this.options.logMaxArchives.toString().length + 1;
|
|
1564
|
+
const {filePath, extension, basename, dirname} = getFilePathProperties(this.options.logToFilePath);
|
|
1565
|
+
|
|
1566
|
+
// Find index of the next archive
|
|
1567
|
+
let indexStr, timeStamp;
|
|
1568
|
+
indexStr = this.options.addArchiveIndex ? "." + this.options.logIndexArchive.toString().padStart(padding, "0") : "";
|
|
1569
|
+
timeStamp = this.options.addArchiveTimestamp ? "." + getConsistentTimestamp() : "";
|
|
1570
|
+
|
|
1571
|
+
// Deduce old log file name
|
|
1572
|
+
const oldFileName = `${filePath}${timeStamp}${indexStr}${extension}`;
|
|
1573
|
+
|
|
1574
|
+
// Deduce archive name
|
|
1575
|
+
const archiveFileName = this.options.compressArchives ? `${filePath}.tar.gz` : "";
|
|
1576
|
+
|
|
1577
|
+
// Delete old archives
|
|
1578
|
+
deleteFilesWithIndex(dirname, basename, indexStr, extension, archiveFileName, this.options.compressionLevel, (error/*, deletedFiles*/) => {
|
|
1579
|
+
if (error) {
|
|
1580
|
+
console.error(`DELETION_FAILURE: Failed to delete some files`);
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
fs.renameSync(this.options.logToFilePath, oldFileName);
|
|
1585
|
+
fs.writeFileSync(this.options.logToFilePath, "");
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1360
1588
|
fs.appendFileSync(this.options.logToFilePath, text + this.EOL);
|
|
1361
|
-
}
|
|
1362
|
-
catch (e)
|
|
1363
|
-
{
|
|
1589
|
+
} catch (e) {
|
|
1364
1590
|
/* istanbul ignore next */
|
|
1365
1591
|
console.rawError("LOG_TO_FILE_FAILURE: ", e.message);
|
|
1366
1592
|
}
|