vloggo 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/README.md +274 -0
- package/build/config/config.d.ts +159 -0
- package/build/config/config.js +291 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -0
- package/build/interfaces/interfaces.d.ts +36 -0
- package/build/interfaces/interfaces.js +1 -0
- package/build/internal/emailService.d.ts +57 -0
- package/build/internal/emailService.js +132 -0
- package/build/internal/fileService.d.ts +68 -0
- package/build/internal/fileService.js +189 -0
- package/build/internal/formatService.d.ts +64 -0
- package/build/internal/formatService.js +135 -0
- package/build/services/logService.d.ts +140 -0
- package/build/services/logService.js +211 -0
- package/package.json +52 -0
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface VLoggoConfig {
|
|
2
|
+
client?: string;
|
|
3
|
+
json?: boolean;
|
|
4
|
+
debug?: boolean;
|
|
5
|
+
console?: boolean;
|
|
6
|
+
directory?: VLoggoDirectory;
|
|
7
|
+
filecount?: VLoggoFilecount;
|
|
8
|
+
notify?: boolean;
|
|
9
|
+
smtp?: VLoggoSMTPConfig;
|
|
10
|
+
throttle?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface VLoggoDirectory {
|
|
13
|
+
txt?: string;
|
|
14
|
+
json?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface VLoggoFilecount {
|
|
17
|
+
txt?: number;
|
|
18
|
+
json?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface VLoggoSMTPConfig {
|
|
21
|
+
host: string;
|
|
22
|
+
port: number;
|
|
23
|
+
username: string;
|
|
24
|
+
password: string;
|
|
25
|
+
from: string;
|
|
26
|
+
to: string | string[];
|
|
27
|
+
secure: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type LogLevel = "INFO" | "WARN" | "ERROR" | "FATAL" | "DEBUG";
|
|
30
|
+
export interface LogEntry {
|
|
31
|
+
level: LogLevel;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
code: string;
|
|
34
|
+
caller: string;
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Config from "../config/config";
|
|
2
|
+
/**
|
|
3
|
+
* Service responsible for sending email notifications via SMTP.
|
|
4
|
+
* Handles error notifications with throttling to prevent spam.
|
|
5
|
+
*
|
|
6
|
+
* @class EmailService
|
|
7
|
+
*/
|
|
8
|
+
declare class EmailService {
|
|
9
|
+
private config;
|
|
10
|
+
private format;
|
|
11
|
+
private transporter;
|
|
12
|
+
private _ready;
|
|
13
|
+
private lastEmailSent;
|
|
14
|
+
/**
|
|
15
|
+
* Creates an instance of EmailService and initializes the SMTP transport.
|
|
16
|
+
*
|
|
17
|
+
* @param {Config} config - Configuration object containing SMTP settings
|
|
18
|
+
*/
|
|
19
|
+
constructor(config: Config);
|
|
20
|
+
/**
|
|
21
|
+
* Initializes SMTP transport using nodemailer.
|
|
22
|
+
* Sets up connection pooling and authentication.
|
|
23
|
+
* Logs initialization status if debug mode is enabled.
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
private initialize;
|
|
29
|
+
/**
|
|
30
|
+
* Sends an error notification email to configured recipients.
|
|
31
|
+
* Implements throttling based on config.throttle to prevent email spam.
|
|
32
|
+
*
|
|
33
|
+
* @async
|
|
34
|
+
* @param {string} client - Name of the client/application reporting the error
|
|
35
|
+
* @param {string} code - Error code identifier
|
|
36
|
+
* @param {string} error - Detailed error message
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* await emailService.sendErrorNotification(
|
|
42
|
+
* "MyApp",
|
|
43
|
+
* "ERR_DATABASE",
|
|
44
|
+
* "Failed to connect to database"
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
sendErrorNotification(client: string, code: string, error: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Gets the ready status of the email service.
|
|
51
|
+
*
|
|
52
|
+
* @readonly
|
|
53
|
+
* @returns {boolean} True if the service is initialized and ready to send emails
|
|
54
|
+
*/
|
|
55
|
+
get ready(): boolean;
|
|
56
|
+
}
|
|
57
|
+
export default EmailService;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import nodemailer from "nodemailer";
|
|
2
|
+
import FormatService from "./formatService";
|
|
3
|
+
/**
|
|
4
|
+
* Service responsible for sending email notifications via SMTP.
|
|
5
|
+
* Handles error notifications with throttling to prevent spam.
|
|
6
|
+
*
|
|
7
|
+
* @class EmailService
|
|
8
|
+
*/
|
|
9
|
+
class EmailService {
|
|
10
|
+
config;
|
|
11
|
+
format = new FormatService();
|
|
12
|
+
transporter = null;
|
|
13
|
+
_ready = false;
|
|
14
|
+
lastEmailSent = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Creates an instance of EmailService and initializes the SMTP transport.
|
|
17
|
+
*
|
|
18
|
+
* @param {Config} config - Configuration object containing SMTP settings
|
|
19
|
+
*/
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.initialize();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Initializes SMTP transport using nodemailer.
|
|
26
|
+
* Sets up connection pooling and authentication.
|
|
27
|
+
* Logs initialization status if debug mode is enabled.
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
* @returns {void}
|
|
31
|
+
*/
|
|
32
|
+
initialize() {
|
|
33
|
+
if (!this.config.smtp) {
|
|
34
|
+
if (this.config.console) {
|
|
35
|
+
console.info(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [INFO] : notification service disabled > missing configuration`);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
this.transporter = nodemailer.createTransport({
|
|
41
|
+
host: this.config.smtp.host,
|
|
42
|
+
port: this.config.smtp.port,
|
|
43
|
+
secure: this.config.smtp.secure ?? this.config.smtp.port === 465,
|
|
44
|
+
pool: true,
|
|
45
|
+
auth: {
|
|
46
|
+
user: this.config.smtp.username,
|
|
47
|
+
pass: this.config.smtp.password,
|
|
48
|
+
},
|
|
49
|
+
debug: this.config.debug,
|
|
50
|
+
logger: this.config.debug,
|
|
51
|
+
});
|
|
52
|
+
this._ready = true;
|
|
53
|
+
if (this.config.debug) {
|
|
54
|
+
console.info(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [INFO] : notification service initialized successfully`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error initializing notification service > ${error.message}`);
|
|
59
|
+
this.transporter = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sends an error notification email to configured recipients.
|
|
64
|
+
* Implements throttling based on config.throttle to prevent email spam.
|
|
65
|
+
*
|
|
66
|
+
* @async
|
|
67
|
+
* @param {string} client - Name of the client/application reporting the error
|
|
68
|
+
* @param {string} code - Error code identifier
|
|
69
|
+
* @param {string} error - Detailed error message
|
|
70
|
+
* @returns {Promise<void>}
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* await emailService.sendErrorNotification(
|
|
75
|
+
* "MyApp",
|
|
76
|
+
* "ERR_DATABASE",
|
|
77
|
+
* "Failed to connect to database"
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
async sendErrorNotification(client, code, error) {
|
|
82
|
+
if (!this.transporter || !this.config.smtp) {
|
|
83
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : failed to send error message > email service not initialized`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
if (now - this.lastEmailSent < this.config.throttle) {
|
|
88
|
+
if (this.config.debug) {
|
|
89
|
+
console.info(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [INFO] : email throttled > ${now - this.lastEmailSent} ms remaining`);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.lastEmailSent = now;
|
|
94
|
+
const recipients = Array.isArray(this.config.smtp.to)
|
|
95
|
+
? this.config.smtp.to.join(", ")
|
|
96
|
+
: this.config.smtp.to;
|
|
97
|
+
const caller = this.format.caller(3);
|
|
98
|
+
const emailContent = {
|
|
99
|
+
from: `"${client}" <${this.config.smtp.from}>`,
|
|
100
|
+
to: recipients,
|
|
101
|
+
subject: `[${client}] Error Alert - ${code}`,
|
|
102
|
+
html: `
|
|
103
|
+
<h2>Error Report</h2>
|
|
104
|
+
<p><strong>Client:</strong> ${client}</p>
|
|
105
|
+
<p><strong>Error Code:</strong> ${code}</p>
|
|
106
|
+
<p><strong>Caller:</strong> ${caller}</p>
|
|
107
|
+
<p><strong>Error Message:</strong> ${error}</p>
|
|
108
|
+
<p><strong>Timestamp:</strong> ${new Date().toLocaleString("pt-BR")}</p>
|
|
109
|
+
<hr>
|
|
110
|
+
`,
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
await this.transporter.sendMail(emailContent);
|
|
114
|
+
if (this.config.debug) {
|
|
115
|
+
console.info(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [INFO] : error email sent successfully to ${recipients}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : failed to send error message > ${error.message}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Gets the ready status of the email service.
|
|
124
|
+
*
|
|
125
|
+
* @readonly
|
|
126
|
+
* @returns {boolean} True if the service is initialized and ready to send emails
|
|
127
|
+
*/
|
|
128
|
+
get ready() {
|
|
129
|
+
return this._ready;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export default EmailService;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import Config from "../config/config";
|
|
2
|
+
/**
|
|
3
|
+
* Service responsible for managing log file operations.
|
|
4
|
+
* Handles file creation, writing, rotation, and cleanup based on retention policies.
|
|
5
|
+
*
|
|
6
|
+
* @class FileService
|
|
7
|
+
*/
|
|
8
|
+
declare class FileService {
|
|
9
|
+
private config;
|
|
10
|
+
private _txtFilename;
|
|
11
|
+
private _jsonFilename;
|
|
12
|
+
private _currentDay;
|
|
13
|
+
private _initialized;
|
|
14
|
+
private format;
|
|
15
|
+
/**
|
|
16
|
+
* Creates an instance of FileService.
|
|
17
|
+
*
|
|
18
|
+
* @param {Config} config - Configuration object containing directory and file retention settings
|
|
19
|
+
*/
|
|
20
|
+
constructor(config: Config);
|
|
21
|
+
/**
|
|
22
|
+
* Initializes the file service by creating the log directory and first log file.
|
|
23
|
+
* Sets up the current day tracking for rotation purposes.
|
|
24
|
+
* This method is idempotent - calling it multiple times has no effect after first initialization.
|
|
25
|
+
*
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
initialize(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Writes a line to the current log file.
|
|
31
|
+
* Checks if the service is initialized before writing.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} line - Log line to write (should include newline character)
|
|
34
|
+
* @returns {void}
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* fileService.write("[INFO] User logged in\n");
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
write(line: string, json?: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Verifies if log rotation is needed by checking if the day has changed.
|
|
44
|
+
* If rotation is needed, creates a new log file and triggers cleanup of old files.
|
|
45
|
+
* This method should be called before each write operation to ensure proper rotation.
|
|
46
|
+
*
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
verify(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Rotates log files by removing old files that exceed the retention count.
|
|
52
|
+
* Scans the log directory, sorts files by modification time (newest first),
|
|
53
|
+
* and deletes the oldest files when the count exceeds config.filecount.
|
|
54
|
+
*
|
|
55
|
+
* @private
|
|
56
|
+
* @async
|
|
57
|
+
* @returns {Promise<void>}
|
|
58
|
+
*/
|
|
59
|
+
private rotate;
|
|
60
|
+
/**
|
|
61
|
+
* Gets the initialization status of the file service.
|
|
62
|
+
*
|
|
63
|
+
* @readonly
|
|
64
|
+
* @returns {boolean} True if the service is initialized and ready to write logs
|
|
65
|
+
*/
|
|
66
|
+
get initialized(): boolean;
|
|
67
|
+
}
|
|
68
|
+
export default FileService;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as fsp from "fs/promises";
|
|
4
|
+
import FormatService from "./formatService";
|
|
5
|
+
/**
|
|
6
|
+
* Service responsible for managing log file operations.
|
|
7
|
+
* Handles file creation, writing, rotation, and cleanup based on retention policies.
|
|
8
|
+
*
|
|
9
|
+
* @class FileService
|
|
10
|
+
*/
|
|
11
|
+
class FileService {
|
|
12
|
+
config;
|
|
13
|
+
_txtFilename = "";
|
|
14
|
+
_jsonFilename = "";
|
|
15
|
+
_currentDay = 0;
|
|
16
|
+
_initialized = false;
|
|
17
|
+
format;
|
|
18
|
+
/**
|
|
19
|
+
* Creates an instance of FileService.
|
|
20
|
+
*
|
|
21
|
+
* @param {Config} config - Configuration object containing directory and file retention settings
|
|
22
|
+
*/
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.format = new FormatService(this.config.client);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Initializes the file service by creating the log directory and first log file.
|
|
29
|
+
* Sets up the current day tracking for rotation purposes.
|
|
30
|
+
* This method is idempotent - calling it multiple times has no effect after first initialization.
|
|
31
|
+
*
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
initialize() {
|
|
35
|
+
if (this._initialized) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
this._currentDay = new Date().getDate();
|
|
40
|
+
const txtDirectory = this.config.directory.txt;
|
|
41
|
+
fs.mkdirSync(txtDirectory, { recursive: true });
|
|
42
|
+
this._txtFilename = path.resolve(txtDirectory, this.format.filename());
|
|
43
|
+
fs.appendFileSync(this._txtFilename, this.format.separator());
|
|
44
|
+
if (this.config.json) {
|
|
45
|
+
const jsonDirectory = this.config.directory.json;
|
|
46
|
+
fs.mkdirSync(jsonDirectory, { recursive: true });
|
|
47
|
+
this._jsonFilename = path.resolve(jsonDirectory, this.format.jsonFilename());
|
|
48
|
+
fs.appendFileSync(this._jsonFilename, this.format.jsonSeparator());
|
|
49
|
+
}
|
|
50
|
+
this._initialized = true;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error initializing VLoggo > ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Writes a line to the current log file.
|
|
58
|
+
* Checks if the service is initialized before writing.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} line - Log line to write (should include newline character)
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* fileService.write("[INFO] User logged in\n");
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
write(line, json) {
|
|
69
|
+
if (!this.initialized) {
|
|
70
|
+
console.warn(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [WARN] : file service not initialized`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
fs.appendFileSync(this._txtFilename, line);
|
|
75
|
+
if (this.config.json && json != undefined) {
|
|
76
|
+
fs.appendFileSync(this._jsonFilename, json);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : failed to write to log file > ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Verifies if log rotation is needed by checking if the day has changed.
|
|
85
|
+
* If rotation is needed, creates a new log file and triggers cleanup of old files.
|
|
86
|
+
* This method should be called before each write operation to ensure proper rotation.
|
|
87
|
+
*
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
90
|
+
verify() {
|
|
91
|
+
const today = new Date().getDate();
|
|
92
|
+
if (today === this._currentDay) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
fs.mkdirSync(this.config.directory.txt, { recursive: true });
|
|
97
|
+
this._txtFilename = path.resolve(this.config.directory.txt, this.format.filename());
|
|
98
|
+
fs.appendFileSync(this._txtFilename, this.format.separator());
|
|
99
|
+
if (this.config.json) {
|
|
100
|
+
fs.mkdirSync(this.config.directory.json, { recursive: true });
|
|
101
|
+
this._jsonFilename = path.resolve(this.config.directory.json, this.format.jsonFilename());
|
|
102
|
+
fs.appendFileSync(this._jsonFilename, this.format.jsonSeparator());
|
|
103
|
+
}
|
|
104
|
+
this._initialized = true;
|
|
105
|
+
this.rotate().catch((error) => console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error rotating VLoggo > ${error.message}`));
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error verifying VLoggo rotation > ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Rotates log files by removing old files that exceed the retention count.
|
|
113
|
+
* Scans the log directory, sorts files by modification time (newest first),
|
|
114
|
+
* and deletes the oldest files when the count exceeds config.filecount.
|
|
115
|
+
*
|
|
116
|
+
* @private
|
|
117
|
+
* @async
|
|
118
|
+
* @returns {Promise<void>}
|
|
119
|
+
*/
|
|
120
|
+
async rotate() {
|
|
121
|
+
try {
|
|
122
|
+
const txtDirectory = this.config.directory.txt;
|
|
123
|
+
const txtFiles = await fsp.readdir(txtDirectory);
|
|
124
|
+
const logFilesPromises = txtFiles
|
|
125
|
+
.filter((file) => file.endsWith(".txt"))
|
|
126
|
+
.map(async (file) => {
|
|
127
|
+
const filePath = path.join(txtDirectory, file);
|
|
128
|
+
const stats = await fsp.stat(filePath);
|
|
129
|
+
return {
|
|
130
|
+
name: file,
|
|
131
|
+
path: filePath,
|
|
132
|
+
mtime: stats.mtime.getTime(),
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
const logFiles = (await Promise.all(logFilesPromises)).sort((a, b) => b.mtime - a.mtime);
|
|
136
|
+
if (logFiles.length > this.config.filecount.txt) {
|
|
137
|
+
const filesToDelete = logFiles.slice(this.config.filecount.txt);
|
|
138
|
+
await Promise.allSettled(filesToDelete.map(async (file) => {
|
|
139
|
+
try {
|
|
140
|
+
await fsp.unlink(file.path);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error deleting old .txt file > ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
if (this.config.json) {
|
|
148
|
+
const jsonDirectory = this.config.directory.json;
|
|
149
|
+
const jsonFiles = await fsp.readdir(jsonDirectory);
|
|
150
|
+
const logFilesPromises = jsonFiles
|
|
151
|
+
.filter((file) => file.endsWith(".jsonl"))
|
|
152
|
+
.map(async (file) => {
|
|
153
|
+
const filePath = path.join(jsonDirectory, file);
|
|
154
|
+
const stats = await fsp.stat(filePath);
|
|
155
|
+
return {
|
|
156
|
+
name: file,
|
|
157
|
+
path: filePath,
|
|
158
|
+
mtime: stats.mtime.getTime(),
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
const logFiles = (await Promise.all(logFilesPromises)).sort((a, b) => b.mtime - a.mtime);
|
|
162
|
+
if (logFiles.length > this.config.filecount.json) {
|
|
163
|
+
const filesToDelete = logFiles.slice(this.config.filecount.json);
|
|
164
|
+
await Promise.allSettled(filesToDelete.map(async (file) => {
|
|
165
|
+
try {
|
|
166
|
+
await fsp.unlink(file.path);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : error deleting old .json file > ${error.message}`);
|
|
170
|
+
}
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error(`[VLoggo] > [${this.config.client}] [${this.format.date()}] [ERROR] : VLoggo cleanup failed > ${error.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Gets the initialization status of the file service.
|
|
181
|
+
*
|
|
182
|
+
* @readonly
|
|
183
|
+
* @returns {boolean} True if the service is initialized and ready to write logs
|
|
184
|
+
*/
|
|
185
|
+
get initialized() {
|
|
186
|
+
return this._initialized;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export default FileService;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { LogEntry } from "../interfaces/interfaces";
|
|
2
|
+
/**
|
|
3
|
+
* Service responsible for formatting log-related strings.
|
|
4
|
+
* Provides utilities for date formatting, filename generation, separators, and caller traceability.
|
|
5
|
+
*
|
|
6
|
+
* @class FormatService
|
|
7
|
+
*/
|
|
8
|
+
declare class FormatService {
|
|
9
|
+
private client?;
|
|
10
|
+
constructor(client?: string | undefined);
|
|
11
|
+
/**
|
|
12
|
+
* Formats a date object into DD/MM/YYYY HH:mm:ss format.
|
|
13
|
+
* @param date - Date to format
|
|
14
|
+
* @returns Formatted date string
|
|
15
|
+
*/
|
|
16
|
+
date(date?: Date): string;
|
|
17
|
+
/**
|
|
18
|
+
* Formats a date into ISO 8601 format for JSON logs.
|
|
19
|
+
* @param date - Date to format
|
|
20
|
+
* @returns ISO formatted date string
|
|
21
|
+
*/
|
|
22
|
+
isoDate(date?: Date): string;
|
|
23
|
+
/**
|
|
24
|
+
* Formats a filename based on the current date (YYYY-MM-DD).
|
|
25
|
+
* @returns Filename in format log-YYYY-MM-DD.txt
|
|
26
|
+
*/
|
|
27
|
+
filename(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Formats a JSON filename based on the current date.
|
|
30
|
+
* @returns Filename in format log-YYYY-MM-DD.jsonl
|
|
31
|
+
*/
|
|
32
|
+
jsonFilename(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Formats a complete log entry into a single line.
|
|
35
|
+
* @param entry - Log entry to format
|
|
36
|
+
* @returns Formatted log line
|
|
37
|
+
*/
|
|
38
|
+
line(entry: LogEntry): string;
|
|
39
|
+
/**
|
|
40
|
+
* Formats a log entry as JSON.
|
|
41
|
+
* @param entry - Log entry to format
|
|
42
|
+
* @param pretty - Whether to pretty-print the JSON
|
|
43
|
+
* @returns JSON string with newline
|
|
44
|
+
*/
|
|
45
|
+
jsonLine(entry: LogEntry, pretty?: boolean): string;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a separator line for log file initialization.
|
|
48
|
+
* @param message - Optional message to include
|
|
49
|
+
* @returns Formatted separator with message
|
|
50
|
+
*/
|
|
51
|
+
separator(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a JSON separator for log file initialization.
|
|
54
|
+
* @returns JSON formatted initialization entry
|
|
55
|
+
*/
|
|
56
|
+
jsonSeparator(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Gets caller location as "file.ts:line"
|
|
59
|
+
* @param skip - Number of stack frames to skip (default: 0)
|
|
60
|
+
* @returns Formatted string like "server.ts:123" or empty string if failed
|
|
61
|
+
*/
|
|
62
|
+
caller(skip?: number): string;
|
|
63
|
+
}
|
|
64
|
+
export default FormatService;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service responsible for formatting log-related strings.
|
|
3
|
+
* Provides utilities for date formatting, filename generation, separators, and caller traceability.
|
|
4
|
+
*
|
|
5
|
+
* @class FormatService
|
|
6
|
+
*/
|
|
7
|
+
class FormatService {
|
|
8
|
+
client;
|
|
9
|
+
constructor(client) {
|
|
10
|
+
this.client = client;
|
|
11
|
+
if (!client) {
|
|
12
|
+
client = "VLoggo";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Formats a date object into DD/MM/YYYY HH:mm:ss format.
|
|
17
|
+
* @param date - Date to format
|
|
18
|
+
* @returns Formatted date string
|
|
19
|
+
*/
|
|
20
|
+
date(date = new Date()) {
|
|
21
|
+
const pad = (num) => num.toString().padStart(2, "0");
|
|
22
|
+
const d = pad(date.getDate());
|
|
23
|
+
const m = pad(date.getMonth() + 1);
|
|
24
|
+
const y = date.getFullYear();
|
|
25
|
+
const h = pad(date.getHours());
|
|
26
|
+
const min = pad(date.getMinutes());
|
|
27
|
+
const s = pad(date.getSeconds());
|
|
28
|
+
return `${d}/${m}/${y} ${h}:${min}:${s}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Formats a date into ISO 8601 format for JSON logs.
|
|
32
|
+
* @param date - Date to format
|
|
33
|
+
* @returns ISO formatted date string
|
|
34
|
+
*/
|
|
35
|
+
isoDate(date = new Date()) {
|
|
36
|
+
return date.toISOString();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Formats a filename based on the current date (YYYY-MM-DD).
|
|
40
|
+
* @returns Filename in format log-YYYY-MM-DD.txt
|
|
41
|
+
*/
|
|
42
|
+
filename() {
|
|
43
|
+
const now = new Date();
|
|
44
|
+
const year = now.getFullYear();
|
|
45
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
46
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
47
|
+
return `log-${year}-${month}-${day}.txt`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Formats a JSON filename based on the current date.
|
|
51
|
+
* @returns Filename in format log-YYYY-MM-DD.jsonl
|
|
52
|
+
*/
|
|
53
|
+
jsonFilename() {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const year = now.getFullYear();
|
|
56
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
57
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
58
|
+
return `log-${year}-${month}-${day}.jsonl`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Formats a complete log entry into a single line.
|
|
62
|
+
* @param entry - Log entry to format
|
|
63
|
+
* @returns Formatted log line
|
|
64
|
+
*/
|
|
65
|
+
line(entry) {
|
|
66
|
+
const timestamp = this.date(new Date());
|
|
67
|
+
return `[${this.client}] [${timestamp}] [${entry.level}] [${entry.code}] [${entry.caller}]: ${entry.message}\n`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Formats a log entry as JSON.
|
|
71
|
+
* @param entry - Log entry to format
|
|
72
|
+
* @param pretty - Whether to pretty-print the JSON
|
|
73
|
+
* @returns JSON string with newline
|
|
74
|
+
*/
|
|
75
|
+
jsonLine(entry, pretty = true) {
|
|
76
|
+
const jsonEntry = {
|
|
77
|
+
client: this.client,
|
|
78
|
+
timestamp: this.isoDate(new Date()),
|
|
79
|
+
level: entry.level,
|
|
80
|
+
code: entry.code,
|
|
81
|
+
caller: entry.caller,
|
|
82
|
+
message: entry.message,
|
|
83
|
+
};
|
|
84
|
+
return JSON.stringify(jsonEntry) + "\n";
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Creates a separator line for log file initialization.
|
|
88
|
+
* @param message - Optional message to include
|
|
89
|
+
* @returns Formatted separator with message
|
|
90
|
+
*/
|
|
91
|
+
separator() {
|
|
92
|
+
const separator = "\n" + "_".repeat(50) + "\n\n";
|
|
93
|
+
const timestamp = this.date();
|
|
94
|
+
return `${separator}[${this.client}] [${timestamp}] [INIT] : VLoggo initialized successfully \n`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a JSON separator for log file initialization.
|
|
98
|
+
* @returns JSON formatted initialization entry
|
|
99
|
+
*/
|
|
100
|
+
jsonSeparator() {
|
|
101
|
+
const initEntry = {
|
|
102
|
+
client: this.client,
|
|
103
|
+
timestamp: this.isoDate(new Date()),
|
|
104
|
+
level: "INIT",
|
|
105
|
+
message: "VLoggo initialized successfully",
|
|
106
|
+
};
|
|
107
|
+
return JSON.stringify(initEntry) + "\n";
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Gets caller location as "file.ts:line"
|
|
111
|
+
* @param skip - Number of stack frames to skip (default: 0)
|
|
112
|
+
* @returns Formatted string like "server.ts:123" or empty string if failed
|
|
113
|
+
*/
|
|
114
|
+
caller(skip = 0) {
|
|
115
|
+
const err = new Error();
|
|
116
|
+
const stack = err.stack;
|
|
117
|
+
if (!stack) {
|
|
118
|
+
return "(unknown:0)";
|
|
119
|
+
}
|
|
120
|
+
const lines = stack.split("\n");
|
|
121
|
+
const targetLine = lines[skip + 2];
|
|
122
|
+
if (!targetLine) {
|
|
123
|
+
return "(unknown:0)";
|
|
124
|
+
}
|
|
125
|
+
const match = targetLine.match(/\((.+?):(\d+):\d+\)/) ||
|
|
126
|
+
targetLine.match(/at\s+(.+?):(\d+):\d+/);
|
|
127
|
+
if (!match) {
|
|
128
|
+
return "(unknown:0)";
|
|
129
|
+
}
|
|
130
|
+
const [, filepath, line] = match;
|
|
131
|
+
const filename = filepath.split(/[/\\]/).pop() || filepath;
|
|
132
|
+
return `${filename}:${line}`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export default FormatService;
|