@quartz-labs/logger 1.0.0 → 1.0.1
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/dist/config.d.ts +9 -0
- package/{src/config.ts → dist/config.js} +11 -12
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/{src/logger.ts → dist/logger.js} +15 -40
- package/dist/logger.js.map +1 -0
- package/{src/types/ErrorCacheEntry.interface.ts → dist/types/ErrorCacheEntry.interface.d.ts} +1 -1
- package/dist/types/ErrorCacheEntry.interface.js +2 -0
- package/dist/types/ErrorCacheEntry.interface.js.map +1 -0
- package/package.json +14 -2
- package/src/index.ts +0 -1
- package/tsconfig.json +0 -21
package/dist/config.d.ts
ADDED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
3
|
dotenv.config();
|
|
5
|
-
|
|
6
4
|
const envSchema = z.object({
|
|
7
5
|
EMAIL_TO: z.string()
|
|
8
6
|
.transform((str) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
try {
|
|
8
|
+
const emails = str.split(',').map(email => email.trim());
|
|
9
|
+
if (!emails.every(email => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)))
|
|
10
|
+
throw new Error();
|
|
11
|
+
return emails;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new Error("Invalid email list format: must be comma-separated email addresses");
|
|
15
|
+
}
|
|
16
|
+
}),
|
|
17
17
|
EMAIL_FROM: z.string().email(),
|
|
18
18
|
EMAIL_HOST: z.string(),
|
|
19
19
|
EMAIL_PORT: z.coerce.number().min(0),
|
|
20
20
|
EMAIL_USER: z.string().email(),
|
|
21
21
|
EMAIL_PASSWORD: z.string(),
|
|
22
22
|
});
|
|
23
|
-
|
|
24
23
|
const config = envSchema.parse({
|
|
25
24
|
EMAIL_TO: process.env.EMAIL_TO,
|
|
26
25
|
EMAIL_FROM: process.env.EMAIL_FROM,
|
|
@@ -29,5 +28,5 @@ const config = envSchema.parse({
|
|
|
29
28
|
EMAIL_USER: process.env.EMAIL_USER,
|
|
30
29
|
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD,
|
|
31
30
|
});
|
|
32
|
-
|
|
33
31
|
export default config;
|
|
32
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;SACf,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,EAAE,CAAC;YACxF,OAAO,MAAM,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAC1F,CAAC;IACL,CAAC,CAAC;IACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IAC9B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IAC9B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;CAC7B,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;IAC9B,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAClC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAClC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAClC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;IAClC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;CAC7C,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AppLogger } from "./logger.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Logger } from "winston";
|
|
2
|
+
export interface AppLoggerOptions {
|
|
3
|
+
name: string;
|
|
4
|
+
dailyErrorCacheTimeMs: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class AppLogger {
|
|
7
|
+
protected logger: Logger;
|
|
8
|
+
private dailyErrorCache;
|
|
9
|
+
private dailyErrorCacheTimeMs;
|
|
10
|
+
constructor(options: AppLoggerOptions);
|
|
11
|
+
}
|
|
@@ -1,37 +1,21 @@
|
|
|
1
1
|
import { Writable } from "node:stream";
|
|
2
|
-
import winston, { createLogger,
|
|
2
|
+
import winston, { createLogger, transports } from "winston";
|
|
3
3
|
import nodemailer from "nodemailer";
|
|
4
4
|
import config from "./config.js";
|
|
5
|
-
import type { ErrorCacheEntry } from "./types/ErrorCacheEntry.interface.js";
|
|
6
|
-
|
|
7
|
-
export interface AppLoggerOptions {
|
|
8
|
-
name: string;
|
|
9
|
-
dailyErrorCacheTimeMs: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
5
|
export class AppLogger {
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
logger;
|
|
7
|
+
dailyErrorCache = new Map();
|
|
15
8
|
// TODO: Clear cache once a day
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
else
|
|
21
|
-
|
|
22
|
-
const consoleFormat = winston.format.combine(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return `[${timestamp}] ${level}: ${message}`;
|
|
27
|
-
})
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
const mailFormat = winston.format.combine(
|
|
31
|
-
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
32
|
-
winston.format.json({ space: 2})
|
|
33
|
-
)
|
|
34
|
-
|
|
9
|
+
dailyErrorCacheTimeMs;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
if (options.dailyErrorCacheTimeMs < 0)
|
|
12
|
+
this.dailyErrorCacheTimeMs = 0;
|
|
13
|
+
else
|
|
14
|
+
this.dailyErrorCacheTimeMs = options.dailyErrorCacheTimeMs;
|
|
15
|
+
const consoleFormat = winston.format.combine(winston.format.colorize(), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf(({ level, message, timestamp }) => {
|
|
16
|
+
return `[${timestamp}] ${level}: ${message}`;
|
|
17
|
+
}));
|
|
18
|
+
const mailFormat = winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.json({ space: 2 }));
|
|
35
19
|
const mailTransporter = nodemailer.createTransport({
|
|
36
20
|
host: config.EMAIL_HOST,
|
|
37
21
|
port: config.EMAIL_PORT,
|
|
@@ -41,24 +25,20 @@ export class AppLogger {
|
|
|
41
25
|
pass: config.EMAIL_PASSWORD,
|
|
42
26
|
},
|
|
43
27
|
});
|
|
44
|
-
|
|
45
28
|
const dailyErrorCacheTimeMinutes = Math.round(this.dailyErrorCacheTimeMs / 1000 / 60);
|
|
46
29
|
const mailTransportInstance = new winston.transports.Stream({
|
|
47
30
|
stream: new Writable({
|
|
48
31
|
write: (message, _, callback) => {
|
|
49
32
|
const parsedMessage = JSON.parse(message);
|
|
50
33
|
const cacheKey = `${parsedMessage.level}: ${parsedMessage.message}`;
|
|
51
|
-
|
|
52
34
|
const now = Date.now();
|
|
53
35
|
const cacheEntry = this.dailyErrorCache.get(cacheKey) || { count: 0, lastSent: 0 };
|
|
54
36
|
cacheEntry.count++;
|
|
55
|
-
|
|
56
37
|
// Only send email if error has not been sent in the last errorEmailCacheTime seconds
|
|
57
38
|
if (now - cacheEntry.lastSent > this.dailyErrorCacheTimeMs) {
|
|
58
39
|
const emailSubject = this.dailyErrorCacheTimeMs > 0
|
|
59
40
|
? `${options.name} Error (${cacheEntry.count} occurrences in past ${dailyErrorCacheTimeMinutes} minutes)`
|
|
60
41
|
: `${options.name} Error`;
|
|
61
|
-
|
|
62
42
|
for (const admin of config.EMAIL_TO) {
|
|
63
43
|
mailTransporter.sendMail({
|
|
64
44
|
from: config.EMAIL_FROM,
|
|
@@ -69,13 +49,10 @@ export class AppLogger {
|
|
|
69
49
|
console.error("Failed to send error email: ", error);
|
|
70
50
|
});
|
|
71
51
|
}
|
|
72
|
-
|
|
73
52
|
cacheEntry.count = 0;
|
|
74
53
|
cacheEntry.lastSent = now;
|
|
75
54
|
}
|
|
76
|
-
|
|
77
55
|
this.dailyErrorCache.set(cacheKey, cacheEntry);
|
|
78
|
-
|
|
79
56
|
callback();
|
|
80
57
|
return true;
|
|
81
58
|
}
|
|
@@ -83,7 +60,6 @@ export class AppLogger {
|
|
|
83
60
|
level: 'error',
|
|
84
61
|
format: mailFormat,
|
|
85
62
|
});
|
|
86
|
-
|
|
87
63
|
this.logger = createLogger({
|
|
88
64
|
level: 'info',
|
|
89
65
|
transports: [
|
|
@@ -99,13 +75,12 @@ export class AppLogger {
|
|
|
99
75
|
mailTransportInstance
|
|
100
76
|
],
|
|
101
77
|
});
|
|
102
|
-
|
|
103
78
|
process.on("uncaughtException", (error) => {
|
|
104
79
|
this.logger.error(error.message);
|
|
105
80
|
});
|
|
106
|
-
|
|
107
81
|
process.on("unhandledRejection", (reason) => {
|
|
108
82
|
this.logger.error(reason);
|
|
109
83
|
});
|
|
110
84
|
}
|
|
111
|
-
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorCacheEntry.interface.js","sourceRoot":"","sources":["../../src/types/ErrorCacheEntry.interface.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quartz-labs/logger",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Logger class for outputting logs and emailing any errors to admins",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
"*": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
6
18
|
"type": "module",
|
|
7
19
|
"repository": "https://github.com/quartz-labs/logger.git",
|
|
8
20
|
"author": "Quartz Labs",
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { AppLogger } from "./logger.js";
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"esModuleInterop": true,
|
|
4
|
-
"skipLibCheck": true,
|
|
5
|
-
"target": "es2022",
|
|
6
|
-
"allowJs": true,
|
|
7
|
-
"resolveJsonModule": true,
|
|
8
|
-
"moduleDetection": "force",
|
|
9
|
-
"isolatedModules": true,
|
|
10
|
-
"verbatimModuleSyntax": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"noUncheckedIndexedAccess": true,
|
|
13
|
-
"noImplicitOverride": true,
|
|
14
|
-
"module": "NodeNext",
|
|
15
|
-
"outDir": "dist",
|
|
16
|
-
"rootDir": "src",
|
|
17
|
-
"sourceMap": true,
|
|
18
|
-
"declaration": true,
|
|
19
|
-
"lib": ["es2022"],
|
|
20
|
-
}
|
|
21
|
-
}
|