@phystack/phy-logger 4.3.40-dev

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.
@@ -0,0 +1,55 @@
1
+ export declare enum LogLevel {
2
+ DEBUG = 0,
3
+ INFO = 1,
4
+ WARN = 2,
5
+ ERROR = 3
6
+ }
7
+ export interface LogOptions {
8
+ level?: LogLevel;
9
+ logToFile?: boolean;
10
+ logDirectory?: string;
11
+ timestampFormat?: string;
12
+ includeTrace?: boolean;
13
+ namespace?: string;
14
+ rotationPeriodInDays?: number;
15
+ logToConsole?: boolean;
16
+ }
17
+ export interface LogEntry {
18
+ timestamp: string;
19
+ level: string;
20
+ message: string;
21
+ data?: any;
22
+ trace?: string;
23
+ }
24
+ export declare class PhyLogger {
25
+ private level;
26
+ private logToFile;
27
+ private logDirectory;
28
+ private timestampFormat;
29
+ private includeTrace;
30
+ private currentLogPath;
31
+ private namespace;
32
+ private rotationPeriodInDays;
33
+ private logToConsole;
34
+ constructor(options?: LogOptions);
35
+ private generateLogFilePath;
36
+ private ensureLogDirectory;
37
+ private getCallerPackageName;
38
+ private getTimestamp;
39
+ private formatMessage;
40
+ private safeJsonStringify;
41
+ private writeToFile;
42
+ private removeOldLogs;
43
+ private getConsoleMethod;
44
+ private log;
45
+ debug(message: string, data?: any): void;
46
+ info(message: string, data?: any): void;
47
+ warn(message: string, data?: any): void;
48
+ error(message: string, data?: any): void;
49
+ setLevel(level: LogLevel): void;
50
+ clearCurrentLog(): Promise<void>;
51
+ getCurrentLogPath(): Promise<string>;
52
+ getLogs(lastLines?: number): Promise<string[]>;
53
+ getAllLogFiles(): Promise<string[]>;
54
+ }
55
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,oBAAY,QAAQ;IAChB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACZ;AAED,MAAM,WAAW,UAAU;IACvB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IAClB,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,YAAY,CAAU;gBAElB,OAAO,GAAE,UAAe;IA4BpC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,oBAAoB;IA0C5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,iBAAiB;YAiBX,WAAW;YAmBX,aAAa;IAqB3B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,GAAG;IAkBJ,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIxC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIvC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIvC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIxC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAIzB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAMhC,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIpC,OAAO,CAAC,SAAS,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQnD,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAOnD"}
package/dist/index.js ADDED
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PhyLogger = exports.LogLevel = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ var LogLevel;
10
+ (function (LogLevel) {
11
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
12
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
13
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
14
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
15
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
16
+ class PhyLogger {
17
+ constructor(options = {}) {
18
+ var _a, _b, _c, _d, _e, _f;
19
+ const baseDirectory = '/var/log';
20
+ this.level = (_a = options.level) !== null && _a !== void 0 ? _a : LogLevel.INFO;
21
+ this.logToFile = (_b = options.logToFile) !== null && _b !== void 0 ? _b : false;
22
+ if (options.logDirectory === 'logs') {
23
+ this.logDirectory = baseDirectory;
24
+ }
25
+ else {
26
+ this.logDirectory = path_1.default.join(baseDirectory, options.logDirectory || '');
27
+ }
28
+ this.timestampFormat = (_c = options.timestampFormat) !== null && _c !== void 0 ? _c : 'ISO';
29
+ this.includeTrace = (_d = options.includeTrace) !== null && _d !== void 0 ? _d : true;
30
+ let callerNamespace = this.getCallerPackageName() || 'phylogger';
31
+ if (callerNamespace.startsWith('@phystack/')) {
32
+ callerNamespace = `phylogger-${callerNamespace.substring('@phystack/'.length)}`;
33
+ }
34
+ this.currentLogPath = this.generateLogFilePath(callerNamespace);
35
+ this.namespace = callerNamespace ? `[${callerNamespace}] ` : '';
36
+ this.rotationPeriodInDays = (_e = options.rotationPeriodInDays) !== null && _e !== void 0 ? _e : 3;
37
+ this.logToConsole = (_f = options.logToConsole) !== null && _f !== void 0 ? _f : true;
38
+ if (this.logToFile) {
39
+ this.ensureLogDirectory();
40
+ }
41
+ }
42
+ generateLogFilePath(namespace) {
43
+ return path_1.default.join(this.logDirectory, `${namespace}.log`);
44
+ }
45
+ ensureLogDirectory() {
46
+ try {
47
+ if (!fs_1.default.existsSync(this.logDirectory)) {
48
+ fs_1.default.mkdirSync(this.logDirectory, { recursive: true });
49
+ fs_1.default.chmodSync(this.logDirectory, 0o700);
50
+ }
51
+ }
52
+ catch (error) {
53
+ console.error('Failed to ensure log directory:', error);
54
+ }
55
+ }
56
+ getCallerPackageName() {
57
+ const originalPrepareStackTrace = Error.prepareStackTrace;
58
+ try {
59
+ Error.prepareStackTrace = (_, stack) => stack;
60
+ const err = new Error();
61
+ const stack = err.stack;
62
+ let callerFile;
63
+ for (const callSite of stack) {
64
+ const fileName = callSite.getFileName();
65
+ if (fileName && !fileName.includes('phy-logger')) {
66
+ callerFile = fileName;
67
+ break;
68
+ }
69
+ }
70
+ if (callerFile) {
71
+ let dir = path_1.default.dirname(callerFile);
72
+ const root = path_1.default.parse(dir).root;
73
+ while (dir !== root) {
74
+ const pkgPath = path_1.default.join(dir, 'package.json');
75
+ if (fs_1.default.existsSync(pkgPath)) {
76
+ try {
77
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
78
+ return pkg.name || '';
79
+ }
80
+ catch (e) {
81
+ console.error('Error parsing package.json:', e);
82
+ }
83
+ }
84
+ dir = path_1.default.dirname(dir);
85
+ }
86
+ }
87
+ }
88
+ catch (error) {
89
+ console.error('Error while getting caller package name:', error);
90
+ }
91
+ finally {
92
+ Error.prepareStackTrace = originalPrepareStackTrace;
93
+ }
94
+ return '';
95
+ }
96
+ getTimestamp() {
97
+ const now = new Date();
98
+ if (this.timestampFormat === 'ISO') {
99
+ return now.toISOString();
100
+ }
101
+ return now.toLocaleString();
102
+ }
103
+ formatMessage(level, message, data) {
104
+ var _a;
105
+ const logEntry = {
106
+ timestamp: this.getTimestamp(),
107
+ level,
108
+ message: `${this.namespace}${message}`,
109
+ data: data,
110
+ };
111
+ if (this.includeTrace && (level === 'ERROR' || level === 'WARN')) {
112
+ logEntry.trace = (_a = new Error().stack) === null || _a === void 0 ? void 0 : _a.split('\n').slice(3).join('\n');
113
+ }
114
+ return logEntry;
115
+ }
116
+ safeJsonStringify(data) {
117
+ try {
118
+ const seen = new WeakSet();
119
+ return JSON.stringify(data, (_key, value) => {
120
+ if (typeof value === 'object' && value !== null) {
121
+ if (seen.has(value)) {
122
+ return '[Circular]';
123
+ }
124
+ seen.add(value);
125
+ }
126
+ return value;
127
+ });
128
+ }
129
+ catch (err) {
130
+ return '[Unserializable Data]';
131
+ }
132
+ }
133
+ async writeToFile(entry) {
134
+ try {
135
+ await this.removeOldLogs();
136
+ const logLine = `${entry.timestamp} [${entry.level}] ${entry.message}${entry.data ? ' ' + this.safeJsonStringify(entry.data) : ''}${entry.trace ? '\nStack Trace:\n' + entry.trace : ''}\n`;
137
+ if (!fs_1.default.existsSync(this.currentLogPath)) {
138
+ await fs_1.default.promises.writeFile(this.currentLogPath, '');
139
+ await fs_1.default.promises.chmod(this.currentLogPath, 0o660);
140
+ }
141
+ await fs_1.default.promises.appendFile(this.currentLogPath, logLine);
142
+ }
143
+ catch (error) {
144
+ console.error('Failed to write to log file:', error);
145
+ }
146
+ }
147
+ async removeOldLogs() {
148
+ try {
149
+ const files = await fs_1.default.promises.readdir(this.logDirectory);
150
+ const thresholdDate = new Date();
151
+ thresholdDate.setDate(thresholdDate.getDate() - this.rotationPeriodInDays);
152
+ for (const file of files) {
153
+ if (file.endsWith('.log')) {
154
+ const filePath = path_1.default.join(this.logDirectory, file);
155
+ const stats = await fs_1.default.promises.stat(filePath);
156
+ const fileDate = new Date(stats.mtime);
157
+ if (fileDate < thresholdDate) {
158
+ await fs_1.default.promises.unlink(filePath);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.error('Error deleting old log files:', error);
165
+ }
166
+ }
167
+ getConsoleMethod(level) {
168
+ switch (level) {
169
+ case 'DEBUG': return 'log';
170
+ case 'INFO': return 'info';
171
+ case 'WARN': return 'warn';
172
+ case 'ERROR': return 'error';
173
+ default: return 'log';
174
+ }
175
+ }
176
+ log(level, message, data) {
177
+ if (LogLevel[level] < this.level) {
178
+ return;
179
+ }
180
+ const entry = this.formatMessage(level, message, data);
181
+ if (this.logToConsole) {
182
+ const consoleMethod = this.getConsoleMethod(level);
183
+ const consoleMessage = `${entry.timestamp} [${entry.level}] ${entry.message}`;
184
+ data ? console[consoleMethod](consoleMessage, data) : console[consoleMethod](consoleMessage);
185
+ if (entry.trace) {
186
+ console[consoleMethod]('Stack Trace:', entry.trace);
187
+ }
188
+ }
189
+ if (this.logToFile) {
190
+ this.writeToFile(entry);
191
+ }
192
+ }
193
+ debug(message, data) {
194
+ this.log('DEBUG', message, data);
195
+ }
196
+ info(message, data) {
197
+ this.log('INFO', message, data);
198
+ }
199
+ warn(message, data) {
200
+ this.log('WARN', message, data);
201
+ }
202
+ error(message, data) {
203
+ this.log('ERROR', message, data);
204
+ }
205
+ setLevel(level) {
206
+ this.level = level;
207
+ }
208
+ async clearCurrentLog() {
209
+ if (this.logToFile && fs_1.default.existsSync(this.currentLogPath)) {
210
+ await fs_1.default.promises.writeFile(this.currentLogPath, '');
211
+ }
212
+ }
213
+ async getCurrentLogPath() {
214
+ return this.currentLogPath;
215
+ }
216
+ async getLogs(lastLines = 100) {
217
+ if (!this.logToFile || !fs_1.default.existsSync(this.currentLogPath)) {
218
+ return [];
219
+ }
220
+ const fileContent = await fs_1.default.promises.readFile(this.currentLogPath, 'utf-8');
221
+ return fileContent.split('\n').slice(-lastLines);
222
+ }
223
+ async getAllLogFiles() {
224
+ if (!fs_1.default.existsSync(this.logDirectory)) {
225
+ return [];
226
+ }
227
+ const files = await fs_1.default.promises.readdir(this.logDirectory);
228
+ return files.filter(file => file.endsWith('.log')).sort().reverse();
229
+ }
230
+ }
231
+ exports.PhyLogger = PhyLogger;
232
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@phystack/phy-logger",
3
+ "version": "4.3.40-dev",
4
+ "main": "dist/index.js",
5
+ "license": "UNLICENSED",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "build": "rimraf dist && tsc",
11
+ "build:watch": "tsc -w"
12
+ },
13
+ "devDependencies": {
14
+ "rimraf": "^6.0.1",
15
+ "typescript": "^5.4.5"
16
+ },
17
+ "test": "react-scripts test",
18
+ "test:coverage": "npm run test -- --coverage --watchAll=false",
19
+ "gitHead": "53c505b3ecad47ebd4f38819c07b0ec1227214a6"
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,279 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export enum LogLevel {
5
+ DEBUG = 0,
6
+ INFO = 1,
7
+ WARN = 2,
8
+ ERROR = 3,
9
+ }
10
+
11
+ export interface LogOptions {
12
+ level?: LogLevel;
13
+ logToFile?: boolean;
14
+ logDirectory?: string;
15
+ timestampFormat?: string;
16
+ includeTrace?: boolean;
17
+ namespace?: string;
18
+ rotationPeriodInDays?: number;
19
+ logToConsole?: boolean;
20
+ }
21
+
22
+ export interface LogEntry {
23
+ timestamp: string;
24
+ level: string;
25
+ message: string;
26
+ data?: any;
27
+ trace?: string;
28
+ }
29
+
30
+ export class PhyLogger {
31
+ private level: LogLevel;
32
+ private logToFile: boolean;
33
+ private logDirectory: string;
34
+ private timestampFormat: string;
35
+ private includeTrace: boolean;
36
+ private currentLogPath: string;
37
+ private namespace: string;
38
+ private rotationPeriodInDays: number;
39
+ private logToConsole: boolean;
40
+
41
+ constructor(options: LogOptions = {}) {
42
+ const baseDirectory = '/var/log';
43
+
44
+ this.level = options.level ?? LogLevel.INFO;
45
+ this.logToFile = options.logToFile ?? false;
46
+ if (options.logDirectory === 'logs') {
47
+ this.logDirectory = baseDirectory;
48
+ } else {
49
+ this.logDirectory = path.join(baseDirectory, options.logDirectory || '');
50
+ }
51
+ this.timestampFormat = options.timestampFormat ?? 'ISO';
52
+ this.includeTrace = options.includeTrace ?? true;
53
+
54
+ // Determine the caller namespace solely from package.json.
55
+ let callerNamespace = this.getCallerPackageName() || 'phylogger';
56
+ if (callerNamespace.startsWith('@phystack/')) {
57
+ callerNamespace = `phylogger-${callerNamespace.substring('@phystack/'.length)}`;
58
+ }
59
+ this.currentLogPath = this.generateLogFilePath(callerNamespace);
60
+ this.namespace = callerNamespace ? `[${callerNamespace}] ` : '';
61
+ this.rotationPeriodInDays = options.rotationPeriodInDays ?? 3;
62
+ this.logToConsole = options.logToConsole ?? true;
63
+
64
+ if (this.logToFile) {
65
+ this.ensureLogDirectory();
66
+ }
67
+ }
68
+
69
+ private generateLogFilePath(namespace: string): string {
70
+ return path.join(this.logDirectory, `${namespace}.log`);
71
+ }
72
+
73
+ private ensureLogDirectory(): void {
74
+ try {
75
+ if (!fs.existsSync(this.logDirectory)) {
76
+ fs.mkdirSync(this.logDirectory, { recursive: true });
77
+ fs.chmodSync(this.logDirectory, 0o700);
78
+ }
79
+ } catch (error) {
80
+ console.error('Failed to ensure log directory:', error);
81
+ }
82
+ }
83
+
84
+ // This helper inspects the call stack to determine the caller file and finds the package name.
85
+ private getCallerPackageName(): string {
86
+ const originalPrepareStackTrace = Error.prepareStackTrace;
87
+ try {
88
+ // Prepare the error to get a structured stack
89
+ Error.prepareStackTrace = (_, stack) => stack;
90
+ const err = new Error();
91
+ const stack = err.stack as unknown as NodeJS.CallSite[];
92
+
93
+ let callerFile: string | undefined;
94
+ // Start scanning the stack frames, skipping frames inside the logger itself.
95
+ for (const callSite of stack) {
96
+ const fileName = callSite.getFileName();
97
+ if (fileName && !fileName.includes('phy-logger')) {
98
+ callerFile = fileName;
99
+ break;
100
+ }
101
+ }
102
+
103
+ if (callerFile) {
104
+ let dir = path.dirname(callerFile);
105
+ const root = path.parse(dir).root;
106
+ while (dir !== root) {
107
+ const pkgPath = path.join(dir, 'package.json');
108
+ if (fs.existsSync(pkgPath)) {
109
+ try {
110
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
111
+ return pkg.name || '';
112
+ } catch (e) {
113
+ console.error('Error parsing package.json:', e);
114
+ }
115
+ }
116
+ dir = path.dirname(dir);
117
+ }
118
+ }
119
+ } catch (error) {
120
+ console.error('Error while getting caller package name:', error);
121
+ } finally {
122
+ Error.prepareStackTrace = originalPrepareStackTrace;
123
+ }
124
+ return '';
125
+ }
126
+
127
+ private getTimestamp(): string {
128
+ const now = new Date();
129
+ if (this.timestampFormat === 'ISO') {
130
+ return now.toISOString();
131
+ }
132
+ return now.toLocaleString();
133
+ }
134
+
135
+ private formatMessage(level: string, message: string, data?: any): LogEntry {
136
+ const logEntry: LogEntry = {
137
+ timestamp: this.getTimestamp(),
138
+ level,
139
+ message: `${this.namespace}${message}`,
140
+ data: data,
141
+ };
142
+
143
+ if (this.includeTrace && (level === 'ERROR' || level === 'WARN')) {
144
+ logEntry.trace = new Error().stack?.split('\n').slice(3).join('\n');
145
+ }
146
+ return logEntry;
147
+ }
148
+
149
+ private safeJsonStringify(data: any): string {
150
+ try {
151
+ const seen = new WeakSet();
152
+ return JSON.stringify(data, (_key, value) => {
153
+ if (typeof value === 'object' && value !== null) {
154
+ if (seen.has(value)) {
155
+ return '[Circular]';
156
+ }
157
+ seen.add(value);
158
+ }
159
+ return value;
160
+ });
161
+ } catch (err) {
162
+ return '[Unserializable Data]';
163
+ }
164
+ }
165
+
166
+ private async writeToFile(entry: LogEntry): Promise<void> {
167
+ try {
168
+ await this.removeOldLogs();
169
+
170
+ const logLine = `${entry.timestamp} [${entry.level}] ${entry.message}${
171
+ entry.data ? ' ' + this.safeJsonStringify(entry.data) : ''
172
+ }${entry.trace ? '\nStack Trace:\n' + entry.trace : ''}\n`;
173
+
174
+ if (!fs.existsSync(this.currentLogPath)) {
175
+ await fs.promises.writeFile(this.currentLogPath, '');
176
+ await fs.promises.chmod(this.currentLogPath, 0o660);
177
+ }
178
+
179
+ await fs.promises.appendFile(this.currentLogPath, logLine);
180
+ } catch (error) {
181
+ console.error('Failed to write to log file:', error);
182
+ }
183
+ }
184
+
185
+ private async removeOldLogs(): Promise<void> {
186
+ try {
187
+ const files = await fs.promises.readdir(this.logDirectory);
188
+ const thresholdDate = new Date();
189
+ thresholdDate.setDate(thresholdDate.getDate() - this.rotationPeriodInDays);
190
+
191
+ for (const file of files) {
192
+ if (file.endsWith('.log')) {
193
+ const filePath = path.join(this.logDirectory, file);
194
+ const stats = await fs.promises.stat(filePath);
195
+ const fileDate = new Date(stats.mtime);
196
+ if (fileDate < thresholdDate) {
197
+ await fs.promises.unlink(filePath);
198
+ }
199
+ }
200
+ }
201
+ } catch (error) {
202
+ console.error('Error deleting old log files:', error);
203
+ }
204
+ }
205
+
206
+ private getConsoleMethod(level: string): 'log' | 'info' | 'warn' | 'error' {
207
+ switch (level) {
208
+ case 'DEBUG': return 'log';
209
+ case 'INFO': return 'info';
210
+ case 'WARN': return 'warn';
211
+ case 'ERROR': return 'error';
212
+ default: return 'log';
213
+ }
214
+ }
215
+
216
+ private log(level: string, message: string, data?: any): void {
217
+ if (LogLevel[level as keyof typeof LogLevel] < this.level) {
218
+ return;
219
+ }
220
+ const entry = this.formatMessage(level, message, data);
221
+ if (this.logToConsole) {
222
+ const consoleMethod = this.getConsoleMethod(level);
223
+ const consoleMessage = `${entry.timestamp} [${entry.level}] ${entry.message}`;
224
+ data ? console[consoleMethod](consoleMessage, data) : console[consoleMethod](consoleMessage);
225
+ if (entry.trace) {
226
+ console[consoleMethod]('Stack Trace:', entry.trace);
227
+ }
228
+ }
229
+ if (this.logToFile) {
230
+ this.writeToFile(entry);
231
+ }
232
+ }
233
+
234
+ public debug(message: string, data?: any): void {
235
+ this.log('DEBUG', message, data);
236
+ }
237
+
238
+ public info(message: string, data?: any): void {
239
+ this.log('INFO', message, data);
240
+ }
241
+
242
+ public warn(message: string, data?: any): void {
243
+ this.log('WARN', message, data);
244
+ }
245
+
246
+ public error(message: string, data?: any): void {
247
+ this.log('ERROR', message, data);
248
+ }
249
+
250
+ public setLevel(level: LogLevel): void {
251
+ this.level = level;
252
+ }
253
+
254
+ public async clearCurrentLog(): Promise<void> {
255
+ if (this.logToFile && fs.existsSync(this.currentLogPath)) {
256
+ await fs.promises.writeFile(this.currentLogPath, '');
257
+ }
258
+ }
259
+
260
+ public async getCurrentLogPath(): Promise<string> {
261
+ return this.currentLogPath;
262
+ }
263
+
264
+ public async getLogs(lastLines: number = 100): Promise<string[]> {
265
+ if (!this.logToFile || !fs.existsSync(this.currentLogPath)) {
266
+ return [];
267
+ }
268
+ const fileContent = await fs.promises.readFile(this.currentLogPath, 'utf-8');
269
+ return fileContent.split('\n').slice(-lastLines);
270
+ }
271
+
272
+ public async getAllLogFiles(): Promise<string[]> {
273
+ if (!fs.existsSync(this.logDirectory)) {
274
+ return [];
275
+ }
276
+ const files = await fs.promises.readdir(this.logDirectory);
277
+ return files.filter(file => file.endsWith('.log')).sort().reverse();
278
+ }
279
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "es2018",
5
+ "outDir": "dist",
6
+ "rootDir": "./src",
7
+ "baseUrl": "src",
8
+ "removeComments": true,
9
+ "moduleResolution": "node",
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "alwaysStrict": true,
14
+ "allowJs": true,
15
+ "noEmitOnError": true,
16
+ "noFallthroughCasesInSwitch": true,
17
+ "noImplicitAny": true,
18
+ "noImplicitReturns": false,
19
+ "noImplicitThis": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "strictBindCallApply": true,
23
+ "strictNullChecks": true,
24
+ "allowSyntheticDefaultImports": true,
25
+ "resolveJsonModule": true,
26
+ "esModuleInterop": true,
27
+ "declarationDir": "dist",
28
+ "declarationMap": true,
29
+ "declaration": true,
30
+ "paths": {
31
+ "@/*": ["*"]
32
+ }
33
+ },
34
+ "include": [
35
+ "./src/**/*"
36
+ , "src/__tests__" ],
37
+ "exclude": [
38
+ "node_modules/**/*",
39
+ ".serverless/**/*",
40
+ ".webpack/**/*",
41
+ "_warmup/**/*",
42
+ "dist/**/*",
43
+ ".vscode/**/*",
44
+ ],
45
+ }