@jetstart/logs 1.5.2
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/.eslintrc.json +6 -0
- package/README.md +122 -0
- package/dist/cli/formatter.d.ts +8 -0
- package/dist/cli/formatter.js +60 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +24 -0
- package/dist/cli/viewer.d.ts +13 -0
- package/dist/cli/viewer.js +63 -0
- package/dist/filters/index.d.ts +10 -0
- package/dist/filters/index.js +45 -0
- package/dist/filters/level.d.ts +8 -0
- package/dist/filters/level.js +28 -0
- package/dist/filters/search.d.ts +7 -0
- package/dist/filters/search.js +29 -0
- package/dist/filters/source.d.ts +7 -0
- package/dist/filters/source.js +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +26 -0
- package/dist/server/index.d.ts +26 -0
- package/dist/server/index.js +119 -0
- package/dist/server/storage.d.ts +15 -0
- package/dist/server/storage.js +70 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +6 -0
- package/dist/utils/colors.d.ts +9 -0
- package/dist/utils/colors.js +50 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +21 -0
- package/package.json +65 -0
- package/src/cli/formatter.ts +63 -0
- package/src/cli/index.ts +24 -0
- package/src/cli/viewer.ts +64 -0
- package/src/filters/index.ts +51 -0
- package/src/filters/level.ts +28 -0
- package/src/filters/search.ts +32 -0
- package/src/filters/source.ts +10 -0
- package/src/index.ts +19 -0
- package/src/server/index.ts +134 -0
- package/src/server/storage.ts +77 -0
- package/src/types/index.ts +15 -0
- package/src/utils/colors.ts +45 -0
- package/src/utils/index.ts +5 -0
- package/tests/filters.test.ts +81 -0
- package/tests/storage.test.ts +89 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Storage
|
|
3
|
+
* In-memory log storage with size limits
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { LogEntry, LogLevel, LogSource, LogStats } from '@jetstart/shared';
|
|
7
|
+
|
|
8
|
+
export class LogStorage {
|
|
9
|
+
private logs: LogEntry[] = [];
|
|
10
|
+
private maxEntries: number;
|
|
11
|
+
|
|
12
|
+
constructor(maxEntries: number = 10000) {
|
|
13
|
+
this.maxEntries = maxEntries;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
add(entry: LogEntry): void {
|
|
17
|
+
this.logs.push(entry);
|
|
18
|
+
|
|
19
|
+
// Trim if exceeds max
|
|
20
|
+
if (this.logs.length > this.maxEntries) {
|
|
21
|
+
this.logs.shift();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getAll(): LogEntry[] {
|
|
26
|
+
return [...this.logs];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
clear(): void {
|
|
30
|
+
this.logs = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getStats(): LogStats {
|
|
34
|
+
const stats: LogStats = {
|
|
35
|
+
totalLogs: this.logs.length,
|
|
36
|
+
errorCount: 0,
|
|
37
|
+
warningCount: 0,
|
|
38
|
+
lastLogTime: this.logs.length > 0 ? this.logs[this.logs.length - 1].timestamp : 0,
|
|
39
|
+
logsByLevel: {
|
|
40
|
+
[LogLevel.VERBOSE]: 0,
|
|
41
|
+
[LogLevel.DEBUG]: 0,
|
|
42
|
+
[LogLevel.INFO]: 0,
|
|
43
|
+
[LogLevel.WARN]: 0,
|
|
44
|
+
[LogLevel.ERROR]: 0,
|
|
45
|
+
[LogLevel.FATAL]: 0,
|
|
46
|
+
},
|
|
47
|
+
logsBySource: {
|
|
48
|
+
[LogSource.CLI]: 0,
|
|
49
|
+
[LogSource.CORE]: 0,
|
|
50
|
+
[LogSource.CLIENT]: 0,
|
|
51
|
+
[LogSource.BUILD]: 0,
|
|
52
|
+
[LogSource.NETWORK]: 0,
|
|
53
|
+
[LogSource.SYSTEM]: 0,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
this.logs.forEach(log => {
|
|
58
|
+
// Count by level
|
|
59
|
+
stats.logsByLevel[log.level]++;
|
|
60
|
+
|
|
61
|
+
// Count errors and warnings
|
|
62
|
+
if (log.level === LogLevel.ERROR || log.level === LogLevel.FATAL) {
|
|
63
|
+
stats.errorCount++;
|
|
64
|
+
}
|
|
65
|
+
if (log.level === LogLevel.WARN) {
|
|
66
|
+
stats.warningCount++;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Count by source
|
|
70
|
+
if (log.source in stats.logsBySource) {
|
|
71
|
+
stats.logsBySource[log.source]++;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return stats;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Utilities
|
|
3
|
+
* Colors for different log levels and sources
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { LogLevel, LogSource } from '@jetstart/shared';
|
|
8
|
+
|
|
9
|
+
export function getColorForLevel(level: LogLevel): chalk.Chalk {
|
|
10
|
+
switch (level) {
|
|
11
|
+
case LogLevel.VERBOSE:
|
|
12
|
+
return chalk.gray;
|
|
13
|
+
case LogLevel.DEBUG:
|
|
14
|
+
return chalk.blue;
|
|
15
|
+
case LogLevel.INFO:
|
|
16
|
+
return chalk.green;
|
|
17
|
+
case LogLevel.WARN:
|
|
18
|
+
return chalk.yellow;
|
|
19
|
+
case LogLevel.ERROR:
|
|
20
|
+
return chalk.red;
|
|
21
|
+
case LogLevel.FATAL:
|
|
22
|
+
return chalk.bgRed.white;
|
|
23
|
+
default:
|
|
24
|
+
return chalk.white;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getColorForSource(source: LogSource): chalk.Chalk {
|
|
29
|
+
switch (source) {
|
|
30
|
+
case LogSource.CLI:
|
|
31
|
+
return chalk.cyan;
|
|
32
|
+
case LogSource.CORE:
|
|
33
|
+
return chalk.magenta;
|
|
34
|
+
case LogSource.CLIENT:
|
|
35
|
+
return chalk.green;
|
|
36
|
+
case LogSource.BUILD:
|
|
37
|
+
return chalk.yellow;
|
|
38
|
+
case LogSource.NETWORK:
|
|
39
|
+
return chalk.blue;
|
|
40
|
+
case LogSource.SYSTEM:
|
|
41
|
+
return chalk.gray;
|
|
42
|
+
default:
|
|
43
|
+
return chalk.white;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for log filters
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { filterByLevel, filterBySource, filterBySearch, applyFilters } from '../src/filters';
|
|
6
|
+
import { LogEntry, LogLevel, LogSource } from '@jetstart/shared';
|
|
7
|
+
|
|
8
|
+
describe('Log Filters', () => {
|
|
9
|
+
const sampleLogs: LogEntry[] = [
|
|
10
|
+
{
|
|
11
|
+
id: '1',
|
|
12
|
+
timestamp: Date.now(),
|
|
13
|
+
level: LogLevel.INFO,
|
|
14
|
+
tag: 'Test',
|
|
15
|
+
message: 'Info message',
|
|
16
|
+
source: LogSource.CLI,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: '2',
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
level: LogLevel.ERROR,
|
|
22
|
+
tag: 'Test',
|
|
23
|
+
message: 'Error message',
|
|
24
|
+
source: LogSource.BUILD,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: '3',
|
|
28
|
+
timestamp: Date.now(),
|
|
29
|
+
level: LogLevel.WARN,
|
|
30
|
+
tag: 'Test',
|
|
31
|
+
message: 'Warning message',
|
|
32
|
+
source: LogSource.CORE,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
describe('filterByLevel', () => {
|
|
37
|
+
it('should filter logs by level', () => {
|
|
38
|
+
const filtered = filterByLevel(sampleLogs, [LogLevel.ERROR]);
|
|
39
|
+
expect(filtered.length).toBe(1);
|
|
40
|
+
expect(filtered[0].level).toBe(LogLevel.ERROR);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should filter by multiple levels', () => {
|
|
44
|
+
const filtered = filterByLevel(sampleLogs, [LogLevel.ERROR, LogLevel.WARN]);
|
|
45
|
+
expect(filtered.length).toBe(2);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('filterBySource', () => {
|
|
50
|
+
it('should filter logs by source', () => {
|
|
51
|
+
const filtered = filterBySource(sampleLogs, [LogSource.BUILD]);
|
|
52
|
+
expect(filtered.length).toBe(1);
|
|
53
|
+
expect(filtered[0].source).toBe(LogSource.BUILD);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('filterBySearch', () => {
|
|
58
|
+
it('should search in message', () => {
|
|
59
|
+
const filtered = filterBySearch(sampleLogs, 'error');
|
|
60
|
+
expect(filtered.length).toBe(1);
|
|
61
|
+
expect(filtered[0].message).toContain('Error');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should be case-insensitive', () => {
|
|
65
|
+
const filtered = filterBySearch(sampleLogs, 'ERROR');
|
|
66
|
+
expect(filtered.length).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('applyFilters', () => {
|
|
71
|
+
it('should apply multiple filters', () => {
|
|
72
|
+
const filtered = applyFilters(sampleLogs, {
|
|
73
|
+
levels: [LogLevel.ERROR, LogLevel.WARN],
|
|
74
|
+
sources: [LogSource.BUILD],
|
|
75
|
+
});
|
|
76
|
+
expect(filtered.length).toBe(1);
|
|
77
|
+
expect(filtered[0].level).toBe(LogLevel.ERROR);
|
|
78
|
+
expect(filtered[0].source).toBe(LogSource.BUILD);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for log storage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { LogStorage } from '../src/server/storage';
|
|
6
|
+
import { LogEntry, LogLevel, LogSource } from '@jetstart/shared';
|
|
7
|
+
|
|
8
|
+
describe('LogStorage', () => {
|
|
9
|
+
let storage: LogStorage;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
storage = new LogStorage(100);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should add and retrieve logs', () => {
|
|
16
|
+
const log: LogEntry = {
|
|
17
|
+
id: '1',
|
|
18
|
+
timestamp: Date.now(),
|
|
19
|
+
level: LogLevel.INFO,
|
|
20
|
+
tag: 'Test',
|
|
21
|
+
message: 'Test message',
|
|
22
|
+
source: LogSource.CLI,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
storage.add(log);
|
|
26
|
+
const logs = storage.getAll();
|
|
27
|
+
|
|
28
|
+
expect(logs.length).toBe(1);
|
|
29
|
+
expect(logs[0]).toEqual(log);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should enforce max entries limit', () => {
|
|
33
|
+
const smallStorage = new LogStorage(5);
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < 10; i++) {
|
|
36
|
+
smallStorage.add({
|
|
37
|
+
id: String(i),
|
|
38
|
+
timestamp: Date.now(),
|
|
39
|
+
level: LogLevel.INFO,
|
|
40
|
+
tag: 'Test',
|
|
41
|
+
message: `Message ${i}`,
|
|
42
|
+
source: LogSource.CLI,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const logs = smallStorage.getAll();
|
|
47
|
+
expect(logs.length).toBe(5);
|
|
48
|
+
expect(logs[0].id).toBe('5'); // First 5 removed
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should clear all logs', () => {
|
|
52
|
+
storage.add({
|
|
53
|
+
id: '1',
|
|
54
|
+
timestamp: Date.now(),
|
|
55
|
+
level: LogLevel.INFO,
|
|
56
|
+
tag: 'Test',
|
|
57
|
+
message: 'Test',
|
|
58
|
+
source: LogSource.CLI,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
storage.clear();
|
|
62
|
+
expect(storage.getAll().length).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should generate statistics', () => {
|
|
66
|
+
storage.add({
|
|
67
|
+
id: '1',
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
level: LogLevel.ERROR,
|
|
70
|
+
tag: 'Test',
|
|
71
|
+
message: 'Error',
|
|
72
|
+
source: LogSource.BUILD,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
storage.add({
|
|
76
|
+
id: '2',
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
level: LogLevel.WARN,
|
|
79
|
+
tag: 'Test',
|
|
80
|
+
message: 'Warning',
|
|
81
|
+
source: LogSource.CORE,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const stats = storage.getStats();
|
|
85
|
+
expect(stats.totalLogs).toBe(2);
|
|
86
|
+
expect(stats.errorCount).toBe(1);
|
|
87
|
+
expect(stats.warningCount).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"composite": true,
|
|
17
|
+
"outDir": "./dist",
|
|
18
|
+
"rootDir": "./src"
|
|
19
|
+
},
|
|
20
|
+
"references": [
|
|
21
|
+
{ "path": "../shared" }
|
|
22
|
+
],
|
|
23
|
+
"include": ["src/**/*"],
|
|
24
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
25
|
+
}
|