@flowrdesk/silo 1.0.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/src/logs.js ADDED
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Copyright 2026 John Spriggs (Flowrdesk LLC)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+
18
+ /*
19
+ * File that handles everything related to the logging part of our app
20
+ *
21
+ */
22
+ // local dependencies
23
+ import os from 'node:os'
24
+ import path from 'node:path'
25
+ import { createWriteStream } from 'node:fs'
26
+
27
+ // local settings
28
+ import { _config } from './settings.js'
29
+
30
+ // local functions
31
+ import { isString, isNumber, colors, verifyAndFormatLog, createDir, indexFile, checkTheFileSize, getTimeStamp, getDate} from './functions.js'
32
+
33
+ export default class Logs {
34
+ constructor({filename, level = null, maxSize = 250, txtColor, bgColor, benchmark = false, toFile = true, toTerminal = true, terminalRaw = false, maxQueueDepth = 50_000}){
35
+ if(!isString(filename)) throw new Error(`JS-Log_Manager supports 'Strings' for the filename only!`)
36
+ if(level !== null && !isString(level) && !isNumber(level)) throw new Error(`JS-Log_Manager supports 'Strings' or 'Numbers (zero or greater)' for the level only!`)
37
+ if(!isNumber(maxSize) || maxSize <= 0) throw new Error(`JS-Log_Manager supports 'Numbers (greater than zero)' for the maxSize only!`)
38
+
39
+ Object.assign(this, { filename, level, benchmark, toFile, toTerminal, terminalRaw })
40
+
41
+ this.txtColor = isString(txtColor) && colors.txt.hasOwnProperty(txtColor) ? colors.txt[txtColor] : null
42
+ this.bgColor = isString(bgColor) && colors.bg.hasOwnProperty(bgColor) ? colors.bg[bgColor] : null
43
+
44
+ // variables needed to handle the processing of the terminal logs
45
+ this.terminalQueue = []
46
+ this.terminalStream = null
47
+ this.isTerminalDraining = false
48
+ this.terminalProcessing = false
49
+
50
+ // variables needed to handle the processing of the raw terminal logs
51
+ this.rawTerminalQueue = []
52
+ this.rawTerminalStream = null
53
+ this.isRawTerminalDraining = false
54
+ this.rawTerminalProcessing = false
55
+
56
+ // variables needed to handle the processing of the file logs
57
+ this.doesDirecttoryExist = false
58
+ this.writeStream = null
59
+ this.streamFilename = null
60
+ this.isDraining = false
61
+ this.maxBufferSizeByKB = 1024 * _config.maxBufferSize
62
+ this.fileQueue = []
63
+ this.maxFileSizeByMB = 1024 * 1024 * maxSize
64
+ this.cachedFileSize = 0
65
+ this.isIndexing = false
66
+ this.checkedFileSize = false
67
+ this.endingWriteStream = false
68
+ this.fileProcessing = false
69
+
70
+ this.maxQueueDepth = maxQueueDepth
71
+ this.drainThreshold = Math.floor(maxQueueDepth * 0.5)
72
+ this.fileQueueWaiters = []
73
+ // ─────────────────────────────────────────────────────
74
+
75
+ // variables needed to handle flush (benefit for benchmarking)
76
+ this.flushPromise = null
77
+ this.terminalFlushPromise = null
78
+ this.rawTerminalFlushPromise = null
79
+ this.resolveFlush = null
80
+ this.terminalResolveFlush = null
81
+ this.rawTerminalResolveFlush = null
82
+
83
+ // holding metadata
84
+ this.hostname = os.hostname()
85
+ this.pid = process.pid
86
+
87
+ // the start up function that handles all the processes needed when the app starts
88
+ this.initStart()
89
+ this.count = 0
90
+ }
91
+
92
+ waitForQueueSpace = () => {
93
+ if (this.fileQueue.length < this.maxQueueDepth) {
94
+ return Promise.resolve()
95
+ }
96
+ return new Promise(resolve => {
97
+ this.fileQueueWaiters.push(resolve)
98
+ })
99
+ }
100
+
101
+ _releaseWaiters = () => {
102
+ if (this.fileQueueWaiters.length === 0) return
103
+ const waiters = this.fileQueueWaiters.splice(0)
104
+ for (const resolve of waiters) resolve()
105
+ }
106
+ // ─────────────────────────────────────────────────────────
107
+
108
+ // creates a stream to handle processing the terminal's queue
109
+ startTerminalStream = () => {
110
+ const system = os.platform()
111
+
112
+ const streamPath = this.benchmark && system === 'win32' ? 'NUL'
113
+ : this.benchmark && (system === 'darwin' || system === 'linux') ? '/dev/null'
114
+ : process.stdout
115
+
116
+ const settings = streamPath === '/dev/null' || streamPath === 'NUL'
117
+ ? {flags: 'w', highWaterMark: this.maxBufferSizeByKB}
118
+ : {fd: 1, highWaterMark: this.maxBufferSizeByKB}
119
+
120
+ this.terminalStream = createWriteStream(streamPath, settings)
121
+ this.processTerminalQue()
122
+
123
+ this.terminalStream.on('drain', () => {
124
+ if(this.isTerminalDraining) this.isTerminalDraining = false
125
+ setImmediate(() => {
126
+ this.processTerminalQue()
127
+ })
128
+ })
129
+ }
130
+
131
+ // creates a stream to handle processing the raw terminal queue
132
+ startRawTerminalStream = () => {
133
+ const system = os.platform()
134
+
135
+ const streamPath = this.benchmark && system === 'win32' ? 'NUL'
136
+ : this.benchmark && (system === 'darwin' || system === 'linux') ? '/dev/null'
137
+ : process.stdout
138
+
139
+ const settings = streamPath === '/dev/null' || streamPath === 'NUL'
140
+ ? {flags: 'w', highWaterMark: this.maxBufferSizeByKB}
141
+ : {fd: 1, highWaterMark: this.maxBufferSizeByKB}
142
+
143
+ this.rawTerminalStream = createWriteStream(streamPath, settings)
144
+ this.processRawTerminalQue()
145
+
146
+ this.rawTerminalStream.on('drain', () => {
147
+ if(this.isRawTerminalDraining) this.isRawTerminalDraining = false
148
+ setImmediate(() => this.processRawTerminalQue())
149
+ })
150
+ }
151
+
152
+ // function to end the Terminal stream
153
+ endTerminalStream = (callback = () => {}) => {
154
+ if(!this.terminalStream) return
155
+ this.terminalStream.end(() => {
156
+ this.terminalStream = null
157
+ callback()
158
+ })
159
+ }
160
+
161
+ // function to end the Raw Terminal stream
162
+ endRawTerminalStream = (callback = () => {}) => {
163
+ if(!this.rawTerminalStream) return
164
+ this.rawTerminalStream.end(() => {
165
+ this.rawTerminalStream = null
166
+ callback()
167
+ })
168
+ }
169
+
170
+ // function that prints to the terminal with ANSI colors if provided in settings
171
+ terminal = (userLog, manualTime = null) => {
172
+ const timeStamp = manualTime ? manualTime : getTimeStamp()
173
+ const level = this.level ? (typeof(this.level) === 'string' ? `"level":"${this.level}",` : `"level":${this.level},`) : ''
174
+ const metadata = !this.benchmark
175
+ ? `"logTime":"${timeStamp}","hostname":"${this.hostname}","pid":${this.pid},`
176
+ : ''
177
+ const verifiedLog = verifyAndFormatLog(userLog)
178
+ let colorCode = ''
179
+ if(this.txtColor) colorCode += '\x1b[' + this.txtColor + 'm'
180
+ if(this.bgColor) colorCode += '\x1b[' + this.bgColor + 'm'
181
+ const fullLog = colorCode + '{' + level + metadata + verifiedLog + '}' + colors.reset
182
+
183
+ this.terminalQueue.push(fullLog + '\n')
184
+ if(!this.terminalProecssing) {
185
+ this.terminalProecssing = true
186
+ setImmediate(() => {
187
+ this.processTerminalQue()
188
+ })
189
+ }
190
+ }
191
+
192
+ // function to process the terminal queue
193
+ processTerminalQue = () => {
194
+ if(this.terminalQueue.length === 0 || this.isTerminalDraining) return
195
+ if(!this.terminalStream){
196
+ this.startTerminalStream()
197
+ return
198
+ }
199
+ while(!this.isTerminalDraining && this.terminalQueue.length > 0){
200
+ const terminalBackPressure = this.terminalStream.write(this.terminalQueue.shift())
201
+ if(!terminalBackPressure) {
202
+ this.isTerminalDraining = true
203
+ return
204
+ }
205
+ }
206
+ }
207
+
208
+ // function that prints to the terminal without formatting (mainly used for benchmarking)
209
+ terminal_raw = (userLog, manualTime = null) => {
210
+ this.count++
211
+ const level = this.level ? (typeof(this.level) === 'string' ? `"level":"${this.level}",` : `"level":${this.level},`) : ''
212
+ const time = manualTime ? Date.now(_config.todayDate + manualTime) : Date.now()
213
+ const metadata = !this.benchmark
214
+ ? `"logTime":"${time}","hostname":"${this.hostname}","pid":${this.pid},`
215
+ : ''
216
+ const verifiedLog = verifyAndFormatLog(userLog)
217
+ this.rawTerminalQueue.push('{' + level + metadata + verifiedLog + '}' + '\n')
218
+ if(!this.rawTerminalProcessing) {
219
+ this.rawTerminalProcessing = true
220
+ setImmediate(() => {
221
+ this.processRawTerminalQue()
222
+ })
223
+ }
224
+ }
225
+
226
+ // function to process the raw terminal queue
227
+ processRawTerminalQue = () => {
228
+ if((this.rawTerminalQueue.length === 0 && !this.rawTerminalProcessing) || this.isRawTerminalDraining) return
229
+ if(!this.rawTerminalStream){
230
+ this.startRawTerminalStream()
231
+ return
232
+ }
233
+
234
+ let str = ''
235
+ const target = this.maxBufferSizeByKB * .75
236
+
237
+ while(!this.isRawTerminalDraining && this.rawTerminalQueue.length > 0){
238
+ const grab = this.rawTerminalQueue.length > 5000 ? 5000 : this.rawTerminalQueue.length
239
+ const chucks = this.rawTerminalQueue.splice(0, grab)
240
+ str += chucks.join('')
241
+
242
+ if(str.length >= target || (this.rawTerminalQueue.length === 0 && str.length > 0)) {
243
+ const rawTerminalBackPressure = this.rawTerminalStream.write(str)
244
+ if(!rawTerminalBackPressure) {
245
+ this.isRawTerminalDraining = true
246
+ return
247
+ }
248
+ str = ''
249
+ }
250
+ }
251
+
252
+ if(this.rawTerminalQueue.length > 0 && !this.isRawTerminalDraining) {
253
+ setImmediate(() => this.processRawTerminalQue())
254
+ } else if(this.rawTerminalQueue.length === 0) {
255
+ this.rawTerminalProcessing = false
256
+ if(this.rawTerminalResolveFlush) {
257
+ this.rawTerminalFlush()
258
+ }
259
+ }
260
+ }
261
+
262
+ // creates the directory for the file log folder
263
+ _createDir = async () => {
264
+ if(this.doesDirecttoryExist) return
265
+ await createDir()
266
+ this.doesDirecttoryExist = true
267
+ }
268
+
269
+ // creates a writable stream and handles draining
270
+ startWriteStream = () => {
271
+ if(this.writeStream || this.endingWriteStream) return
272
+
273
+ const fileDate = getDate()
274
+ this.streamFilename = this.filename + '_' + fileDate + '.log'
275
+ const filePath = path.join(_config.baseDir, this.streamFilename)
276
+
277
+ this.writeStream = createWriteStream(filePath, {
278
+ highWaterMark: this.maxBufferSizeByKB,
279
+ flags: 'a'
280
+ })
281
+
282
+ setImmediate(() => this.processFileQue())
283
+
284
+ this.writeStream.on('drain', () => {
285
+ this.isDraining = false
286
+ setImmediate(() => this.processFileQue())
287
+ })
288
+ }
289
+
290
+ // function to end the write stream
291
+ endWriteStream = (callback = () => {}) => {
292
+ if(!this.writeStream || this.endingWriteStream) return
293
+ if(!this.endingWriteStream) this.endingWriteStream = true
294
+ this.writeStream.end(() => {
295
+ this.writeStream = null
296
+ if(this.endingWriteStream) this.endingWriteStream = false
297
+ callback()
298
+ })
299
+ }
300
+
301
+ file = async (userLog, manualTime = null) => {
302
+ const timeStamp = manualTime ? manualTime : getTimeStamp()
303
+ const level = this.level ? (typeof(this.level) === 'string' ? `"level":"${this.level}",` : `"level":${this.level},`) : ''
304
+ const metadata = !this.benchmark
305
+ ? `"logTime":"${timeStamp}","hostname":"${this.hostname}","pid":${this.pid},`
306
+ : ''
307
+ const verifiedLog = verifyAndFormatLog(userLog)
308
+
309
+ this.fileQueue.push('{' + level + metadata + verifiedLog + '}' + '\n')
310
+
311
+ if(this.fileQueue.length >= this.maxQueueDepth){
312
+ await this.waitForQueueSpace()
313
+ }
314
+
315
+ if(!this.fileProcessing) {
316
+ this.fileProcessing = true
317
+ setImmediate(() => {
318
+ this.processFileQue()
319
+ })
320
+ }
321
+ }
322
+
323
+ processFileQue = () => {
324
+ if(this.fileQueue.length === 0 && !this.fileProecssing) {
325
+ if(this.resolveFlush) this.flush()
326
+ return
327
+ }
328
+ if(this.isDraining || this.isIndexing || !this.doesDirecttoryExist || this.endingWriteStream) return
329
+ if(!this.writeStream) {
330
+ this.startWriteStream()
331
+ return
332
+ }
333
+
334
+ let str = ''
335
+ const targetSize = this.maxBufferSizeByKB * .80
336
+
337
+ while(!this.isDraining && !this.isIndexing && !this.endingWriteStream && this.fileQueue.length > 0) {
338
+ const grab = this.fileQueue.length > 5000 ? 5000 : this.fileQueue.length
339
+ const chunks = this.fileQueue.splice(0, grab)
340
+ str += chunks.join('')
341
+
342
+ if(str.length >= targetSize
343
+ || (this.cachedFileSize + str.length) >= this.maxFileSizeByMB
344
+ || (this.fileQueue.length === 0 && str.length > 0)) {
345
+
346
+ const noBackPressure = this.writeStream.write(str)
347
+ this.cachedFileSize += str.length
348
+
349
+ if(noBackPressure === false) {
350
+ this.isDraining = true
351
+ }
352
+
353
+ if(this.cachedFileSize >= this.maxFileSizeByMB) {
354
+ this._indexFile(this.streamFilename)
355
+ }
356
+
357
+ str = ''
358
+ }
359
+
360
+ // ── Release parked callers once queue drains to threshold ──
361
+ if(this.fileQueue.length <= this.drainThreshold) {
362
+ this._releaseWaiters()
363
+ }
364
+ // ──────────────────────────────────────────────────────────
365
+ }
366
+
367
+ if(this.fileQueue.length > 0 && !this.isDraining && !this.isIndexing) {
368
+ setImmediate(() => this.processFileQue())
369
+ } else if(this.fileQueue.length === 0) {
370
+ this.fileProecssing = false
371
+ // Release any remaining waiters on full drain
372
+ this._releaseWaiters()
373
+ if(this.resolveFlush) {
374
+ this.flush()
375
+ }
376
+ }
377
+ }
378
+
379
+ // The Unified Logging Function
380
+ logg = async (data) => {
381
+ const timeStamp = getTimeStamp()
382
+
383
+ if (this.toFile) await this.file(data, timeStamp)
384
+
385
+ if (this.toTerminal) {
386
+ if (this.terminalRaw) {
387
+ this.terminal_raw(data, timeStamp)
388
+ }else {
389
+ this.terminal(data, timeStamp)
390
+ }
391
+ }
392
+ }
393
+
394
+ // index the file and add the index to the filename
395
+ _indexFile = (filename) => {
396
+ if(this.isIndexing) return
397
+ this.isIndexing = true
398
+ this.streamFilename = null
399
+ this.endWriteStream(async () => {
400
+ if(this.writeStream === null) {
401
+ try {
402
+ await indexFile(filename)
403
+ } catch (error) {
404
+ console.error(`[Error Handling Indexing]: ${error.message}`)
405
+ } finally {
406
+ this.isIndexing = false
407
+ this.cachedFileSize = 0
408
+ if(this.isDraining) this.isDraining = false
409
+ setImmediate(() => this.startWriteStream())
410
+ }
411
+ }
412
+ })
413
+ }
414
+
415
+ // checks the size of the file if it exists and updates the cache
416
+ _checkTheFileSize = async () => {
417
+ if(this.checkedFileSize) return
418
+ const fileDate = getDate()
419
+ const filename = this.filename + '_' + fileDate + '.log'
420
+ const size = await checkTheFileSize(filename)
421
+ if(size > 0) this.cachedFileSize = size
422
+ this.checkedFileSize = true
423
+ }
424
+
425
+ // function that starts all the upfront processes
426
+ initStart = async () => {
427
+ await this._createDir()
428
+ await this._checkTheFileSize()
429
+ }
430
+
431
+ flush = () => {
432
+ if(!this.flushPromise){
433
+ this.flushPromise = new Promise(resolve => {
434
+ this.resolveFlush = resolve
435
+ })
436
+ }
437
+ if(this.fileQueue.length === 0) {
438
+ if(this.writeStream){
439
+ this.endWriteStream(() => {
440
+ this.resolveFlush()
441
+ this.flushPromise = null
442
+ this.resolveFlush = null
443
+ })
444
+ } else {
445
+ this.resolveFlush()
446
+ this.flushPromise = null
447
+ this.resolveFlush = null
448
+ }
449
+ } else {
450
+ if(!this.fileProecssing) {
451
+ this.fileProecssing = true
452
+ setImmediate(() => this.processFileQue())
453
+ }
454
+ }
455
+ return this.flushPromise
456
+ }
457
+
458
+ terminalFlush = () => {
459
+ if(!this.terminalFlushPromise) {
460
+ this.terminalFlushPromise = new Promise(resolve => {
461
+ this.terminalResolveFlush = resolve
462
+ })
463
+ }
464
+ if(this.terminalQueue.length === 0) {
465
+ if(this.terminalStream) {
466
+ this.endTerminalStream(() => {
467
+ this.terminalResolveFlush()
468
+ this.terminalFlushPromise = null
469
+ this.terminalResolveFlush = null
470
+ })
471
+ } else {
472
+ this.terminalResolveFlush()
473
+ this.terminalFlushPromise = null
474
+ this.terminalResolveFlush = null
475
+ }
476
+ } else {
477
+ if(!this.terminalProcessing) {
478
+ this.terminalProcessing = true
479
+ setImmediate(() => this.processTerminalQue())
480
+ }
481
+ }
482
+ return this.terminalFlushPromise
483
+ }
484
+
485
+ rawTerminalFlush = () => {
486
+ if(!this.rawTerminalFlushPromise) {
487
+ this.rawTerminalFlushPromise = new Promise(resolve => {
488
+ this.rawTerminalResolveFlush = resolve
489
+ })
490
+ }
491
+ if(this.rawTerminalQueue.length === 0) {
492
+ if(this.rawTerminalStream) {
493
+ this.endRawTerminalStream(() => {
494
+ this.rawTerminalResolveFlush()
495
+ this.rawTerminalFlushPromise = null
496
+ this.rawTerminalResolveFlush = null
497
+ })
498
+ } else {
499
+ this.rawTerminalResolveFlush()
500
+ this.rawTerminalFlushPromise = null
501
+ this.rawTerminalResolveFlush = null
502
+ }
503
+ } else {
504
+ if(!this.rawTerminalProcessing) {
505
+ this.rawTerminalProcessing = true
506
+ setImmediate(() => this.processRawTerminalQue())
507
+ }
508
+ }
509
+ return this.rawTerminalFlushPromise
510
+ }
511
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Copyright 2026 John Spriggs (Flowrdesk LLC)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ // local dependencies
18
+ import path from "node:path"
19
+ import os from "node:os"
20
+
21
+ // local functions
22
+ import { isString, isNumber, isFilenameSafe } from "./functions.js"
23
+
24
+ export const _config = {
25
+ baseDir: path.join(process.cwd(), '.logs'),
26
+ maxBufferSize: 256, //kb
27
+ }
28
+
29
+ const appSetting = {
30
+ setDir: dir => {
31
+ if(!isString(dir)) throw new Error(`JS-Log-Manager support strings for the directory update`)
32
+
33
+ if(!isFilenameSafe(dir)) {
34
+ const prohibited = ['/', '\\', ':', '*', '?', '"', '<', '>', '|', '.'];
35
+ throw new Error(`Directory name is unsafe or contains prohibited characters. The following are NOT allowed: ${prohibited.join(' ')}`);
36
+ } else {
37
+ _config.baseDir = path.join(process.cwd(), dir)
38
+ return _config
39
+ }
40
+ },
41
+
42
+ setBufferSize: (num) => {
43
+ if(!isNumber(num) || num <= 0) throw new Error(`The Buffer Size(KB) must be a number that is greater than zero!`)
44
+
45
+ _config.maxBufferSize = num
46
+ return _config
47
+ }
48
+ }
49
+
50
+ export const bufferAutoTune = () => {
51
+
52
+ const budget = 64,
53
+ balanced = 128,
54
+ extreme = 256
55
+
56
+ const model = os.cpus()[0].model
57
+
58
+ // 1. Check for high-end "Extreme" chips
59
+ const isExtreme = /[iR][79]-/.test(model) || /Max|Ultra|Threadripper|EPYC/i.test(model);
60
+
61
+ // 2. Check for mid-range "Balanced" chips
62
+ const isBalanced = /[iR]5-/.test(model) || /Apple M[1-3](?! (Max|Ultra))/.test(model);
63
+
64
+ if(isExtreme) {
65
+ _config.maxBufferSize = extreme
66
+ console.log(`JS-Log-Manager has set your write buffer to 256kb (Extreme Mode)!`)
67
+ } else if(isBalanced) {
68
+ _config.maxBufferSize = balanced
69
+ console.log(`JS-Log-Manager has set your write buffer to 128kb (Balance Mode)!`)
70
+ } else {
71
+ _config.maxBufferSize = budget
72
+ console.log(`JS-Log-Manager has set your write buffer to 64kb (Budget Mode)!`)
73
+ }
74
+ return _config;
75
+ }
76
+
77
+ export const configuration = obj => {
78
+ for(const key in obj) {
79
+ if(key in appSetting) {
80
+ appSetting[key](obj[key])
81
+ }
82
+ }
83
+ }
package/tests/demo.js ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright 2026 John Spriggs (Flowrdesk LLC)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Logs from '../index.js';
18
+
19
+ const LOG_COUNT = parseInt(process.argv.slice(2)) || 1_000_000;
20
+
21
+ const clearMemory = () => {
22
+ if (global.gc) {
23
+ global.gc();
24
+ } else {
25
+ console.warn("Warming: GC not exposed. Run with --expose-gc for consistent results.");
26
+ }
27
+ };
28
+
29
+ const run = async () => {
30
+ const myLogger = new Logs({
31
+ filename: 'silo_demo_log',
32
+ level: 30,
33
+ benchmark: true
34
+ });
35
+
36
+ const startUsage = process.cpuUsage();
37
+ const startMem = process.memoryUsage().heapUsed;
38
+ const startTime = process.hrtime.bigint();
39
+
40
+ try {
41
+ clearMemory()
42
+ console.log('🧹 GC Clearing Memory')
43
+ console.log(`🚀 SILO: Executing ${LOG_COUNT.toLocaleString()} logs...`);
44
+ for (let i = 0; i < LOG_COUNT; i++) {
45
+ await myLogger.file({ iteration: (i + 1), mode: 'minimalist' });
46
+ }
47
+ await myLogger.flush();
48
+
49
+ const endTime = process.hrtime.bigint();
50
+ const endMem = process.memoryUsage().heapUsed;
51
+ const timeInSecs = Number(endTime - startTime) / 1e9;
52
+ const endUsage = process.cpuUsage(startUsage);
53
+
54
+ console.log(`✅ SILO PASSED`);
55
+ console.table({
56
+ time: timeInSecs.toFixed(4),
57
+ lps: Math.round(LOG_COUNT / timeInSecs).toLocaleString(),
58
+ cpu: (((endUsage.user + endUsage.system) / (timeInSecs * 1000000))).toFixed(2) + '%',
59
+ mem: ((endMem - startMem) / 1024 / 1024).toFixed(2) + ' MB'
60
+ });
61
+ } catch (err) {
62
+ console.error(`❌ SILO FAILED`);
63
+ console.table({
64
+ Status: "FAILED",
65
+ Time_Elapsed: timeInSecs.toFixed(4) + "s",
66
+ Mem_at_Failure: ((endMem - startMem) / 1024 / 1024).toFixed(2) + " MB",
67
+ CPU_Usage: (((endUsage.user + endUsage.system) / (timeInSecs * 1000000)) * 100).toFixed(2) + "%"
68
+ });
69
+ console.error(err);
70
+ }
71
+ };
72
+
73
+ run();