@appbuildersph/logger 0.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/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @appbuildersph/logger
2
+
3
+ Enterprise logger for the App Builders PH SDK.
4
+
5
+ ## Levels
6
+
7
+ `debug`, `info`, `warning`, `error`, `critical`, `audit`, `security` — each has a
8
+ convenience method (`logger.error(...)`) and all funnel through `logger.log(level, ...)`.
9
+
10
+ ## Transports
11
+
12
+ - `ConsoleTransport` — colored stdout/stderr output
13
+ - `JsonTransport` — ndjson file output for log shipping
14
+ - `FileTransport` — plain-text file output with size-based rotation and gzip compression
15
+
16
+ Implement the `Transport` interface for remote/cloud shipping (Datadog, App Builders Cloud, etc.).
17
+
18
+ ## Usage
19
+
20
+ ```ts
21
+ import { Logger, ConsoleTransport, FileTransport } from "@appbuildersph/logger";
22
+
23
+ const logger = new Logger({
24
+ minLevel: "info",
25
+ transports: [new ConsoleTransport(), new FileTransport({ filePath: "./logs/app.log" })],
26
+ });
27
+
28
+ logger.info("server started");
29
+ logger.child("database").error("connection lost");
30
+
31
+ logger.query({ level: "error", search: "connection" });
32
+ logger.groupByCategory();
33
+ logger.export("json");
34
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,205 @@
1
+ 'use strict';
2
+
3
+ var shared = require('@appbuildersph/shared');
4
+ var promises = require('fs/promises');
5
+ var path = require('path');
6
+ var fs = require('fs');
7
+ var zlib = require('zlib');
8
+ var promises$1 = require('stream/promises');
9
+
10
+ // src/logger.ts
11
+
12
+ // src/level.ts
13
+ var LEVEL_WEIGHT = {
14
+ debug: 0,
15
+ info: 1,
16
+ warning: 2,
17
+ error: 3,
18
+ audit: 4,
19
+ security: 4,
20
+ critical: 5
21
+ };
22
+ function meetsMinLevel(level, minLevel) {
23
+ return LEVEL_WEIGHT[level] >= LEVEL_WEIGHT[minLevel];
24
+ }
25
+
26
+ // src/transports/console.ts
27
+ var COLOR = {
28
+ debug: "\x1B[90m",
29
+ info: "\x1B[36m",
30
+ warning: "\x1B[33m",
31
+ error: "\x1B[31m",
32
+ critical: "\x1B[41m",
33
+ audit: "\x1B[35m",
34
+ security: "\x1B[35m"
35
+ };
36
+ var RESET = "\x1B[0m";
37
+ var ConsoleTransport = class {
38
+ name = "console";
39
+ colors;
40
+ constructor(options = {}) {
41
+ this.colors = options.colors ?? true;
42
+ }
43
+ write(entry) {
44
+ const tag = `[${entry.level.toUpperCase()}]${entry.category ? ` (${entry.category})` : ""}`;
45
+ const line = `${entry.timestamp} ${tag} ${entry.message}`;
46
+ const output = this.colors ? `${COLOR[entry.level] ?? ""}${line}${RESET}` : line;
47
+ const method = entry.level === "critical" || entry.level === "error" ? console.error : entry.level === "warning" ? console.warn : console.log;
48
+ method(output, entry.context ?? "");
49
+ }
50
+ };
51
+
52
+ // src/logger.ts
53
+ var Logger = class _Logger {
54
+ transports;
55
+ minLevel;
56
+ bufferSize;
57
+ category;
58
+ buffer = [];
59
+ constructor(options = {}) {
60
+ this.transports = options.transports ?? [new ConsoleTransport()];
61
+ this.minLevel = options.minLevel ?? "debug";
62
+ this.bufferSize = options.bufferSize ?? 1e3;
63
+ this.category = options.category;
64
+ }
65
+ /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */
66
+ child(category) {
67
+ const scoped = new _Logger({
68
+ transports: this.transports,
69
+ minLevel: this.minLevel,
70
+ bufferSize: this.bufferSize,
71
+ category
72
+ });
73
+ scoped.buffer = this.buffer;
74
+ return scoped;
75
+ }
76
+ log(level, message, context) {
77
+ if (!meetsMinLevel(level, this.minLevel)) return;
78
+ const entry = {
79
+ level,
80
+ message,
81
+ context,
82
+ category: this.category,
83
+ timestamp: shared.nowIso()
84
+ };
85
+ this.buffer.push(entry);
86
+ if (this.buffer.length > this.bufferSize) this.buffer.shift();
87
+ for (const transport of this.transports) {
88
+ void transport.write(entry);
89
+ }
90
+ }
91
+ debug(message, context) {
92
+ this.log("debug", message, context);
93
+ }
94
+ info(message, context) {
95
+ this.log("info", message, context);
96
+ }
97
+ warning(message, context) {
98
+ this.log("warning", message, context);
99
+ }
100
+ error(message, context) {
101
+ this.log("error", message, context);
102
+ }
103
+ critical(message, context) {
104
+ this.log("critical", message, context);
105
+ }
106
+ audit(message, context) {
107
+ this.log("audit", message, context);
108
+ }
109
+ security(message, context) {
110
+ this.log("security", message, context);
111
+ }
112
+ /** Search/filter over the in-memory buffer (level, category, free-text, time range). */
113
+ query(query = {}) {
114
+ const from = query.from ? new Date(query.from) : void 0;
115
+ const to = query.to ? new Date(query.to) : void 0;
116
+ return this.buffer.filter((entry) => {
117
+ if (query.level && entry.level !== query.level) return false;
118
+ if (query.category && entry.category !== query.category) return false;
119
+ if (query.search && !entry.message.toLowerCase().includes(query.search.toLowerCase())) {
120
+ return false;
121
+ }
122
+ const entryTime = new Date(entry.timestamp);
123
+ if (from && entryTime < from) return false;
124
+ if (to && entryTime > to) return false;
125
+ return true;
126
+ });
127
+ }
128
+ /** Groups buffered entries by category (or "uncategorized"). */
129
+ groupByCategory() {
130
+ return this.buffer.reduce((groups, entry) => {
131
+ const key = entry.category ?? "uncategorized";
132
+ (groups[key] ??= []).push(entry);
133
+ return groups;
134
+ }, {});
135
+ }
136
+ export(format = "json") {
137
+ if (format === "json") return JSON.stringify(this.buffer, null, 2);
138
+ return this.buffer.map((e) => `${e.timestamp} [${e.level.toUpperCase()}] ${e.message}`).join("\n");
139
+ }
140
+ clear() {
141
+ this.buffer = [];
142
+ }
143
+ };
144
+ var JsonTransport = class {
145
+ name = "json";
146
+ filePath;
147
+ ready;
148
+ constructor(options) {
149
+ this.filePath = options.filePath;
150
+ this.ready = promises.mkdir(path.dirname(this.filePath), { recursive: true }).then(() => void 0);
151
+ }
152
+ async write(entry) {
153
+ await this.ready;
154
+ await promises.appendFile(this.filePath, `${JSON.stringify(entry)}
155
+ `, "utf8");
156
+ }
157
+ };
158
+ var FileTransport = class {
159
+ name = "file";
160
+ filePath;
161
+ maxBytes;
162
+ compress;
163
+ ready;
164
+ rotating = Promise.resolve();
165
+ constructor(options) {
166
+ this.filePath = options.filePath;
167
+ this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;
168
+ this.compress = options.compress ?? true;
169
+ this.ready = promises.mkdir(path.dirname(this.filePath), { recursive: true }).then(() => void 0);
170
+ }
171
+ async write(entry) {
172
+ await this.ready;
173
+ await this.rotating;
174
+ await this.rotateIfNeeded();
175
+ const line = `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}${entry.context ? ` ${JSON.stringify(entry.context)}` : ""}
176
+ `;
177
+ await promises.appendFile(this.filePath, line, "utf8");
178
+ }
179
+ async rotateIfNeeded() {
180
+ const size = await promises.stat(this.filePath).then((s) => s.size).catch(() => 0);
181
+ if (size < this.maxBytes) return;
182
+ this.rotating = this.rotate();
183
+ await this.rotating;
184
+ }
185
+ async rotate() {
186
+ const rotatedPath = `${this.filePath}.${Date.now()}`;
187
+ await promises.rename(this.filePath, rotatedPath);
188
+ if (this.compress) {
189
+ await promises$1.pipeline(
190
+ fs.createReadStream(rotatedPath),
191
+ zlib.createGzip(),
192
+ fs.createWriteStream(`${rotatedPath}.gz`)
193
+ );
194
+ const { unlink } = await import('fs/promises');
195
+ await unlink(rotatedPath);
196
+ }
197
+ }
198
+ };
199
+
200
+ exports.ConsoleTransport = ConsoleTransport;
201
+ exports.FileTransport = FileTransport;
202
+ exports.JsonTransport = JsonTransport;
203
+ exports.Logger = Logger;
204
+ //# sourceMappingURL=index.cjs.map
205
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/level.ts","../src/transports/console.ts","../src/logger.ts","../src/transports/json.ts","../src/transports/file.ts"],"names":["nowIso","mkdir","dirname","appendFile","stat","rename","pipeline","createReadStream","createGzip","createWriteStream"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { LogLevel } from \"@appbuildersph/shared\";\n\n/** Severity order, low to high. \"audit\" and \"security\" are treated as high-priority. */\nconst LEVEL_WEIGHT: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warning: 2,\n error: 3,\n audit: 4,\n security: 4,\n critical: 5,\n};\n\nexport function meetsMinLevel(level: LogLevel, minLevel: LogLevel): boolean {\n return LEVEL_WEIGHT[level] >= LEVEL_WEIGHT[minLevel];\n}\n","import type { LogLevel } from \"@appbuildersph/shared\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nconst COLOR: Partial<Record<LogLevel, string>> = {\n debug: \"\\x1b[90m\",\n info: \"\\x1b[36m\",\n warning: \"\\x1b[33m\",\n error: \"\\x1b[31m\",\n critical: \"\\x1b[41m\",\n audit: \"\\x1b[35m\",\n security: \"\\x1b[35m\",\n};\nconst RESET = \"\\x1b[0m\";\n\nexport interface ConsoleTransportOptions {\n colors?: boolean;\n}\n\nexport class ConsoleTransport implements Transport {\n readonly name = \"console\";\n private readonly colors: boolean;\n\n constructor(options: ConsoleTransportOptions = {}) {\n this.colors = options.colors ?? true;\n }\n\n write(entry: LoggerEntry): void {\n const tag = `[${entry.level.toUpperCase()}]${entry.category ? ` (${entry.category})` : \"\"}`;\n const line = `${entry.timestamp} ${tag} ${entry.message}`;\n const output = this.colors ? `${COLOR[entry.level] ?? \"\"}${line}${RESET}` : line;\n\n const method =\n entry.level === \"critical\" || entry.level === \"error\"\n ? console.error\n : entry.level === \"warning\"\n ? console.warn\n : console.log;\n\n method(output, entry.context ?? \"\");\n }\n}\n","import type { LogLevel } from \"@appbuildersph/shared\";\nimport { nowIso } from \"@appbuildersph/shared\";\nimport { meetsMinLevel } from \"./level.js\";\nimport { ConsoleTransport } from \"./transports/console.js\";\nimport type { LogCategory, LoggerEntry, LoggerOptions, LogQuery, Transport } from \"./types.js\";\n\nexport class Logger {\n private readonly transports: Transport[];\n private readonly minLevel: LogLevel;\n private readonly bufferSize: number;\n private readonly category?: LogCategory;\n private buffer: LoggerEntry[] = [];\n\n constructor(options: LoggerOptions = {}) {\n this.transports = options.transports ?? [new ConsoleTransport()];\n this.minLevel = options.minLevel ?? \"debug\";\n this.bufferSize = options.bufferSize ?? 1000;\n this.category = options.category;\n }\n\n /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */\n child(category: LogCategory): Logger {\n const scoped = new Logger({\n transports: this.transports,\n minLevel: this.minLevel,\n bufferSize: this.bufferSize,\n category,\n });\n scoped.buffer = this.buffer;\n return scoped;\n }\n\n log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n if (!meetsMinLevel(level, this.minLevel)) return;\n\n const entry: LoggerEntry = {\n level,\n message,\n context,\n category: this.category,\n timestamp: nowIso(),\n };\n\n this.buffer.push(entry);\n if (this.buffer.length > this.bufferSize) this.buffer.shift();\n\n for (const transport of this.transports) {\n void transport.write(entry);\n }\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.log(\"debug\", message, context);\n }\n info(message: string, context?: Record<string, unknown>): void {\n this.log(\"info\", message, context);\n }\n warning(message: string, context?: Record<string, unknown>): void {\n this.log(\"warning\", message, context);\n }\n error(message: string, context?: Record<string, unknown>): void {\n this.log(\"error\", message, context);\n }\n critical(message: string, context?: Record<string, unknown>): void {\n this.log(\"critical\", message, context);\n }\n audit(message: string, context?: Record<string, unknown>): void {\n this.log(\"audit\", message, context);\n }\n security(message: string, context?: Record<string, unknown>): void {\n this.log(\"security\", message, context);\n }\n\n /** Search/filter over the in-memory buffer (level, category, free-text, time range). */\n query(query: LogQuery = {}): LoggerEntry[] {\n const from = query.from ? new Date(query.from) : undefined;\n const to = query.to ? new Date(query.to) : undefined;\n\n return this.buffer.filter((entry) => {\n if (query.level && entry.level !== query.level) return false;\n if (query.category && entry.category !== query.category) return false;\n if (query.search && !entry.message.toLowerCase().includes(query.search.toLowerCase())) {\n return false;\n }\n const entryTime = new Date(entry.timestamp);\n if (from && entryTime < from) return false;\n if (to && entryTime > to) return false;\n return true;\n });\n }\n\n /** Groups buffered entries by category (or \"uncategorized\"). */\n groupByCategory(): Record<string, LoggerEntry[]> {\n return this.buffer.reduce<Record<string, LoggerEntry[]>>((groups, entry) => {\n const key = entry.category ?? \"uncategorized\";\n (groups[key] ??= []).push(entry);\n return groups;\n }, {});\n }\n\n export(format: \"json\" | \"text\" = \"json\"): string {\n if (format === \"json\") return JSON.stringify(this.buffer, null, 2);\n return this.buffer\n .map((e) => `${e.timestamp} [${e.level.toUpperCase()}] ${e.message}`)\n .join(\"\\n\");\n }\n\n clear(): void {\n this.buffer = [];\n }\n}\n","import { appendFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nexport interface JsonTransportOptions {\n filePath: string;\n}\n\n/** Writes one JSON object per line (ndjson) — suited for log shipping/ingestion. */\nexport class JsonTransport implements Transport {\n readonly name = \"json\";\n private readonly filePath: string;\n private ready: Promise<void>;\n\n constructor(options: JsonTransportOptions) {\n this.filePath = options.filePath;\n this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => undefined);\n }\n\n async write(entry: LoggerEntry): Promise<void> {\n await this.ready;\n await appendFile(this.filePath, `${JSON.stringify(entry)}\\n`, \"utf8\");\n }\n}\n","import { appendFile, mkdir, rename, stat } from \"node:fs/promises\";\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport { createGzip } from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport { dirname } from \"node:path\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nexport interface FileTransportOptions {\n filePath: string;\n /** Rotate once the file exceeds this many bytes. Default 10 MiB. */\n maxBytes?: number;\n /** Gzip the rotated file. Default true. */\n compress?: boolean;\n}\n\n/** Plain-text file transport with size-based rotation and optional gzip compression. */\nexport class FileTransport implements Transport {\n readonly name = \"file\";\n private readonly filePath: string;\n private readonly maxBytes: number;\n private readonly compress: boolean;\n private ready: Promise<void>;\n private rotating: Promise<void> = Promise.resolve();\n\n constructor(options: FileTransportOptions) {\n this.filePath = options.filePath;\n this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;\n this.compress = options.compress ?? true;\n this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => undefined);\n }\n\n async write(entry: LoggerEntry): Promise<void> {\n await this.ready;\n await this.rotating;\n await this.rotateIfNeeded();\n\n const line = `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}${\n entry.context ? ` ${JSON.stringify(entry.context)}` : \"\"\n }\\n`;\n await appendFile(this.filePath, line, \"utf8\");\n }\n\n private async rotateIfNeeded(): Promise<void> {\n const size = await stat(this.filePath)\n .then((s) => s.size)\n .catch(() => 0);\n if (size < this.maxBytes) return;\n\n this.rotating = this.rotate();\n await this.rotating;\n }\n\n private async rotate(): Promise<void> {\n const rotatedPath = `${this.filePath}.${Date.now()}`;\n await rename(this.filePath, rotatedPath);\n\n if (this.compress) {\n await pipeline(\n createReadStream(rotatedPath),\n createGzip(),\n createWriteStream(`${rotatedPath}.gz`),\n );\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(rotatedPath);\n }\n }\n}\n"]}
@@ -0,0 +1,95 @@
1
+ import { LogEntry, LogLevel } from '@appbuildersph/shared';
2
+
3
+ type LogCategory = "authentication" | "database" | "api" | "performance" | "system" | "crash" | string;
4
+ interface LoggerEntry extends LogEntry {
5
+ category?: LogCategory;
6
+ }
7
+ interface Transport {
8
+ name: string;
9
+ write(entry: LoggerEntry): void | Promise<void>;
10
+ }
11
+ interface LoggerOptions {
12
+ /** Minimum severity that reaches transports. Entries below this are dropped. */
13
+ minLevel?: LogLevel;
14
+ transports?: Transport[];
15
+ /** Max entries kept in the in-memory ring buffer for query()/export(). */
16
+ bufferSize?: number;
17
+ category?: LogCategory;
18
+ }
19
+ interface LogQuery {
20
+ level?: LogLevel;
21
+ category?: LogCategory;
22
+ search?: string;
23
+ from?: Date | string;
24
+ to?: Date | string;
25
+ }
26
+
27
+ declare class Logger {
28
+ private readonly transports;
29
+ private readonly minLevel;
30
+ private readonly bufferSize;
31
+ private readonly category?;
32
+ private buffer;
33
+ constructor(options?: LoggerOptions);
34
+ /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */
35
+ child(category: LogCategory): Logger;
36
+ log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
37
+ debug(message: string, context?: Record<string, unknown>): void;
38
+ info(message: string, context?: Record<string, unknown>): void;
39
+ warning(message: string, context?: Record<string, unknown>): void;
40
+ error(message: string, context?: Record<string, unknown>): void;
41
+ critical(message: string, context?: Record<string, unknown>): void;
42
+ audit(message: string, context?: Record<string, unknown>): void;
43
+ security(message: string, context?: Record<string, unknown>): void;
44
+ /** Search/filter over the in-memory buffer (level, category, free-text, time range). */
45
+ query(query?: LogQuery): LoggerEntry[];
46
+ /** Groups buffered entries by category (or "uncategorized"). */
47
+ groupByCategory(): Record<string, LoggerEntry[]>;
48
+ export(format?: "json" | "text"): string;
49
+ clear(): void;
50
+ }
51
+
52
+ interface ConsoleTransportOptions {
53
+ colors?: boolean;
54
+ }
55
+ declare class ConsoleTransport implements Transport {
56
+ readonly name = "console";
57
+ private readonly colors;
58
+ constructor(options?: ConsoleTransportOptions);
59
+ write(entry: LoggerEntry): void;
60
+ }
61
+
62
+ interface JsonTransportOptions {
63
+ filePath: string;
64
+ }
65
+ /** Writes one JSON object per line (ndjson) — suited for log shipping/ingestion. */
66
+ declare class JsonTransport implements Transport {
67
+ readonly name = "json";
68
+ private readonly filePath;
69
+ private ready;
70
+ constructor(options: JsonTransportOptions);
71
+ write(entry: LoggerEntry): Promise<void>;
72
+ }
73
+
74
+ interface FileTransportOptions {
75
+ filePath: string;
76
+ /** Rotate once the file exceeds this many bytes. Default 10 MiB. */
77
+ maxBytes?: number;
78
+ /** Gzip the rotated file. Default true. */
79
+ compress?: boolean;
80
+ }
81
+ /** Plain-text file transport with size-based rotation and optional gzip compression. */
82
+ declare class FileTransport implements Transport {
83
+ readonly name = "file";
84
+ private readonly filePath;
85
+ private readonly maxBytes;
86
+ private readonly compress;
87
+ private ready;
88
+ private rotating;
89
+ constructor(options: FileTransportOptions);
90
+ write(entry: LoggerEntry): Promise<void>;
91
+ private rotateIfNeeded;
92
+ private rotate;
93
+ }
94
+
95
+ export { ConsoleTransport, FileTransport, JsonTransport, type LogCategory, type LogQuery, Logger, type LoggerEntry, type LoggerOptions, type Transport };
@@ -0,0 +1,95 @@
1
+ import { LogEntry, LogLevel } from '@appbuildersph/shared';
2
+
3
+ type LogCategory = "authentication" | "database" | "api" | "performance" | "system" | "crash" | string;
4
+ interface LoggerEntry extends LogEntry {
5
+ category?: LogCategory;
6
+ }
7
+ interface Transport {
8
+ name: string;
9
+ write(entry: LoggerEntry): void | Promise<void>;
10
+ }
11
+ interface LoggerOptions {
12
+ /** Minimum severity that reaches transports. Entries below this are dropped. */
13
+ minLevel?: LogLevel;
14
+ transports?: Transport[];
15
+ /** Max entries kept in the in-memory ring buffer for query()/export(). */
16
+ bufferSize?: number;
17
+ category?: LogCategory;
18
+ }
19
+ interface LogQuery {
20
+ level?: LogLevel;
21
+ category?: LogCategory;
22
+ search?: string;
23
+ from?: Date | string;
24
+ to?: Date | string;
25
+ }
26
+
27
+ declare class Logger {
28
+ private readonly transports;
29
+ private readonly minLevel;
30
+ private readonly bufferSize;
31
+ private readonly category?;
32
+ private buffer;
33
+ constructor(options?: LoggerOptions);
34
+ /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */
35
+ child(category: LogCategory): Logger;
36
+ log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
37
+ debug(message: string, context?: Record<string, unknown>): void;
38
+ info(message: string, context?: Record<string, unknown>): void;
39
+ warning(message: string, context?: Record<string, unknown>): void;
40
+ error(message: string, context?: Record<string, unknown>): void;
41
+ critical(message: string, context?: Record<string, unknown>): void;
42
+ audit(message: string, context?: Record<string, unknown>): void;
43
+ security(message: string, context?: Record<string, unknown>): void;
44
+ /** Search/filter over the in-memory buffer (level, category, free-text, time range). */
45
+ query(query?: LogQuery): LoggerEntry[];
46
+ /** Groups buffered entries by category (or "uncategorized"). */
47
+ groupByCategory(): Record<string, LoggerEntry[]>;
48
+ export(format?: "json" | "text"): string;
49
+ clear(): void;
50
+ }
51
+
52
+ interface ConsoleTransportOptions {
53
+ colors?: boolean;
54
+ }
55
+ declare class ConsoleTransport implements Transport {
56
+ readonly name = "console";
57
+ private readonly colors;
58
+ constructor(options?: ConsoleTransportOptions);
59
+ write(entry: LoggerEntry): void;
60
+ }
61
+
62
+ interface JsonTransportOptions {
63
+ filePath: string;
64
+ }
65
+ /** Writes one JSON object per line (ndjson) — suited for log shipping/ingestion. */
66
+ declare class JsonTransport implements Transport {
67
+ readonly name = "json";
68
+ private readonly filePath;
69
+ private ready;
70
+ constructor(options: JsonTransportOptions);
71
+ write(entry: LoggerEntry): Promise<void>;
72
+ }
73
+
74
+ interface FileTransportOptions {
75
+ filePath: string;
76
+ /** Rotate once the file exceeds this many bytes. Default 10 MiB. */
77
+ maxBytes?: number;
78
+ /** Gzip the rotated file. Default true. */
79
+ compress?: boolean;
80
+ }
81
+ /** Plain-text file transport with size-based rotation and optional gzip compression. */
82
+ declare class FileTransport implements Transport {
83
+ readonly name = "file";
84
+ private readonly filePath;
85
+ private readonly maxBytes;
86
+ private readonly compress;
87
+ private ready;
88
+ private rotating;
89
+ constructor(options: FileTransportOptions);
90
+ write(entry: LoggerEntry): Promise<void>;
91
+ private rotateIfNeeded;
92
+ private rotate;
93
+ }
94
+
95
+ export { ConsoleTransport, FileTransport, JsonTransport, type LogCategory, type LogQuery, Logger, type LoggerEntry, type LoggerOptions, type Transport };
package/dist/index.js ADDED
@@ -0,0 +1,200 @@
1
+ import { nowIso } from '@appbuildersph/shared';
2
+ import { mkdir, appendFile, stat, rename } from 'fs/promises';
3
+ import { dirname } from 'path';
4
+ import { createReadStream, createWriteStream } from 'fs';
5
+ import { createGzip } from 'zlib';
6
+ import { pipeline } from 'stream/promises';
7
+
8
+ // src/logger.ts
9
+
10
+ // src/level.ts
11
+ var LEVEL_WEIGHT = {
12
+ debug: 0,
13
+ info: 1,
14
+ warning: 2,
15
+ error: 3,
16
+ audit: 4,
17
+ security: 4,
18
+ critical: 5
19
+ };
20
+ function meetsMinLevel(level, minLevel) {
21
+ return LEVEL_WEIGHT[level] >= LEVEL_WEIGHT[minLevel];
22
+ }
23
+
24
+ // src/transports/console.ts
25
+ var COLOR = {
26
+ debug: "\x1B[90m",
27
+ info: "\x1B[36m",
28
+ warning: "\x1B[33m",
29
+ error: "\x1B[31m",
30
+ critical: "\x1B[41m",
31
+ audit: "\x1B[35m",
32
+ security: "\x1B[35m"
33
+ };
34
+ var RESET = "\x1B[0m";
35
+ var ConsoleTransport = class {
36
+ name = "console";
37
+ colors;
38
+ constructor(options = {}) {
39
+ this.colors = options.colors ?? true;
40
+ }
41
+ write(entry) {
42
+ const tag = `[${entry.level.toUpperCase()}]${entry.category ? ` (${entry.category})` : ""}`;
43
+ const line = `${entry.timestamp} ${tag} ${entry.message}`;
44
+ const output = this.colors ? `${COLOR[entry.level] ?? ""}${line}${RESET}` : line;
45
+ const method = entry.level === "critical" || entry.level === "error" ? console.error : entry.level === "warning" ? console.warn : console.log;
46
+ method(output, entry.context ?? "");
47
+ }
48
+ };
49
+
50
+ // src/logger.ts
51
+ var Logger = class _Logger {
52
+ transports;
53
+ minLevel;
54
+ bufferSize;
55
+ category;
56
+ buffer = [];
57
+ constructor(options = {}) {
58
+ this.transports = options.transports ?? [new ConsoleTransport()];
59
+ this.minLevel = options.minLevel ?? "debug";
60
+ this.bufferSize = options.bufferSize ?? 1e3;
61
+ this.category = options.category;
62
+ }
63
+ /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */
64
+ child(category) {
65
+ const scoped = new _Logger({
66
+ transports: this.transports,
67
+ minLevel: this.minLevel,
68
+ bufferSize: this.bufferSize,
69
+ category
70
+ });
71
+ scoped.buffer = this.buffer;
72
+ return scoped;
73
+ }
74
+ log(level, message, context) {
75
+ if (!meetsMinLevel(level, this.minLevel)) return;
76
+ const entry = {
77
+ level,
78
+ message,
79
+ context,
80
+ category: this.category,
81
+ timestamp: nowIso()
82
+ };
83
+ this.buffer.push(entry);
84
+ if (this.buffer.length > this.bufferSize) this.buffer.shift();
85
+ for (const transport of this.transports) {
86
+ void transport.write(entry);
87
+ }
88
+ }
89
+ debug(message, context) {
90
+ this.log("debug", message, context);
91
+ }
92
+ info(message, context) {
93
+ this.log("info", message, context);
94
+ }
95
+ warning(message, context) {
96
+ this.log("warning", message, context);
97
+ }
98
+ error(message, context) {
99
+ this.log("error", message, context);
100
+ }
101
+ critical(message, context) {
102
+ this.log("critical", message, context);
103
+ }
104
+ audit(message, context) {
105
+ this.log("audit", message, context);
106
+ }
107
+ security(message, context) {
108
+ this.log("security", message, context);
109
+ }
110
+ /** Search/filter over the in-memory buffer (level, category, free-text, time range). */
111
+ query(query = {}) {
112
+ const from = query.from ? new Date(query.from) : void 0;
113
+ const to = query.to ? new Date(query.to) : void 0;
114
+ return this.buffer.filter((entry) => {
115
+ if (query.level && entry.level !== query.level) return false;
116
+ if (query.category && entry.category !== query.category) return false;
117
+ if (query.search && !entry.message.toLowerCase().includes(query.search.toLowerCase())) {
118
+ return false;
119
+ }
120
+ const entryTime = new Date(entry.timestamp);
121
+ if (from && entryTime < from) return false;
122
+ if (to && entryTime > to) return false;
123
+ return true;
124
+ });
125
+ }
126
+ /** Groups buffered entries by category (or "uncategorized"). */
127
+ groupByCategory() {
128
+ return this.buffer.reduce((groups, entry) => {
129
+ const key = entry.category ?? "uncategorized";
130
+ (groups[key] ??= []).push(entry);
131
+ return groups;
132
+ }, {});
133
+ }
134
+ export(format = "json") {
135
+ if (format === "json") return JSON.stringify(this.buffer, null, 2);
136
+ return this.buffer.map((e) => `${e.timestamp} [${e.level.toUpperCase()}] ${e.message}`).join("\n");
137
+ }
138
+ clear() {
139
+ this.buffer = [];
140
+ }
141
+ };
142
+ var JsonTransport = class {
143
+ name = "json";
144
+ filePath;
145
+ ready;
146
+ constructor(options) {
147
+ this.filePath = options.filePath;
148
+ this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => void 0);
149
+ }
150
+ async write(entry) {
151
+ await this.ready;
152
+ await appendFile(this.filePath, `${JSON.stringify(entry)}
153
+ `, "utf8");
154
+ }
155
+ };
156
+ var FileTransport = class {
157
+ name = "file";
158
+ filePath;
159
+ maxBytes;
160
+ compress;
161
+ ready;
162
+ rotating = Promise.resolve();
163
+ constructor(options) {
164
+ this.filePath = options.filePath;
165
+ this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;
166
+ this.compress = options.compress ?? true;
167
+ this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => void 0);
168
+ }
169
+ async write(entry) {
170
+ await this.ready;
171
+ await this.rotating;
172
+ await this.rotateIfNeeded();
173
+ const line = `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}${entry.context ? ` ${JSON.stringify(entry.context)}` : ""}
174
+ `;
175
+ await appendFile(this.filePath, line, "utf8");
176
+ }
177
+ async rotateIfNeeded() {
178
+ const size = await stat(this.filePath).then((s) => s.size).catch(() => 0);
179
+ if (size < this.maxBytes) return;
180
+ this.rotating = this.rotate();
181
+ await this.rotating;
182
+ }
183
+ async rotate() {
184
+ const rotatedPath = `${this.filePath}.${Date.now()}`;
185
+ await rename(this.filePath, rotatedPath);
186
+ if (this.compress) {
187
+ await pipeline(
188
+ createReadStream(rotatedPath),
189
+ createGzip(),
190
+ createWriteStream(`${rotatedPath}.gz`)
191
+ );
192
+ const { unlink } = await import('fs/promises');
193
+ await unlink(rotatedPath);
194
+ }
195
+ }
196
+ };
197
+
198
+ export { ConsoleTransport, FileTransport, JsonTransport, Logger };
199
+ //# sourceMappingURL=index.js.map
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/level.ts","../src/transports/console.ts","../src/logger.ts","../src/transports/json.ts","../src/transports/file.ts"],"names":["mkdir","dirname","appendFile"],"mappings":"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","file":"index.js","sourcesContent":["import type { LogLevel } from \"@appbuildersph/shared\";\n\n/** Severity order, low to high. \"audit\" and \"security\" are treated as high-priority. */\nconst LEVEL_WEIGHT: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warning: 2,\n error: 3,\n audit: 4,\n security: 4,\n critical: 5,\n};\n\nexport function meetsMinLevel(level: LogLevel, minLevel: LogLevel): boolean {\n return LEVEL_WEIGHT[level] >= LEVEL_WEIGHT[minLevel];\n}\n","import type { LogLevel } from \"@appbuildersph/shared\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nconst COLOR: Partial<Record<LogLevel, string>> = {\n debug: \"\\x1b[90m\",\n info: \"\\x1b[36m\",\n warning: \"\\x1b[33m\",\n error: \"\\x1b[31m\",\n critical: \"\\x1b[41m\",\n audit: \"\\x1b[35m\",\n security: \"\\x1b[35m\",\n};\nconst RESET = \"\\x1b[0m\";\n\nexport interface ConsoleTransportOptions {\n colors?: boolean;\n}\n\nexport class ConsoleTransport implements Transport {\n readonly name = \"console\";\n private readonly colors: boolean;\n\n constructor(options: ConsoleTransportOptions = {}) {\n this.colors = options.colors ?? true;\n }\n\n write(entry: LoggerEntry): void {\n const tag = `[${entry.level.toUpperCase()}]${entry.category ? ` (${entry.category})` : \"\"}`;\n const line = `${entry.timestamp} ${tag} ${entry.message}`;\n const output = this.colors ? `${COLOR[entry.level] ?? \"\"}${line}${RESET}` : line;\n\n const method =\n entry.level === \"critical\" || entry.level === \"error\"\n ? console.error\n : entry.level === \"warning\"\n ? console.warn\n : console.log;\n\n method(output, entry.context ?? \"\");\n }\n}\n","import type { LogLevel } from \"@appbuildersph/shared\";\nimport { nowIso } from \"@appbuildersph/shared\";\nimport { meetsMinLevel } from \"./level.js\";\nimport { ConsoleTransport } from \"./transports/console.js\";\nimport type { LogCategory, LoggerEntry, LoggerOptions, LogQuery, Transport } from \"./types.js\";\n\nexport class Logger {\n private readonly transports: Transport[];\n private readonly minLevel: LogLevel;\n private readonly bufferSize: number;\n private readonly category?: LogCategory;\n private buffer: LoggerEntry[] = [];\n\n constructor(options: LoggerOptions = {}) {\n this.transports = options.transports ?? [new ConsoleTransport()];\n this.minLevel = options.minLevel ?? \"debug\";\n this.bufferSize = options.bufferSize ?? 1000;\n this.category = options.category;\n }\n\n /** Returns a logger scoped to a category, sharing this logger's transports and buffer. */\n child(category: LogCategory): Logger {\n const scoped = new Logger({\n transports: this.transports,\n minLevel: this.minLevel,\n bufferSize: this.bufferSize,\n category,\n });\n scoped.buffer = this.buffer;\n return scoped;\n }\n\n log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\n if (!meetsMinLevel(level, this.minLevel)) return;\n\n const entry: LoggerEntry = {\n level,\n message,\n context,\n category: this.category,\n timestamp: nowIso(),\n };\n\n this.buffer.push(entry);\n if (this.buffer.length > this.bufferSize) this.buffer.shift();\n\n for (const transport of this.transports) {\n void transport.write(entry);\n }\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n this.log(\"debug\", message, context);\n }\n info(message: string, context?: Record<string, unknown>): void {\n this.log(\"info\", message, context);\n }\n warning(message: string, context?: Record<string, unknown>): void {\n this.log(\"warning\", message, context);\n }\n error(message: string, context?: Record<string, unknown>): void {\n this.log(\"error\", message, context);\n }\n critical(message: string, context?: Record<string, unknown>): void {\n this.log(\"critical\", message, context);\n }\n audit(message: string, context?: Record<string, unknown>): void {\n this.log(\"audit\", message, context);\n }\n security(message: string, context?: Record<string, unknown>): void {\n this.log(\"security\", message, context);\n }\n\n /** Search/filter over the in-memory buffer (level, category, free-text, time range). */\n query(query: LogQuery = {}): LoggerEntry[] {\n const from = query.from ? new Date(query.from) : undefined;\n const to = query.to ? new Date(query.to) : undefined;\n\n return this.buffer.filter((entry) => {\n if (query.level && entry.level !== query.level) return false;\n if (query.category && entry.category !== query.category) return false;\n if (query.search && !entry.message.toLowerCase().includes(query.search.toLowerCase())) {\n return false;\n }\n const entryTime = new Date(entry.timestamp);\n if (from && entryTime < from) return false;\n if (to && entryTime > to) return false;\n return true;\n });\n }\n\n /** Groups buffered entries by category (or \"uncategorized\"). */\n groupByCategory(): Record<string, LoggerEntry[]> {\n return this.buffer.reduce<Record<string, LoggerEntry[]>>((groups, entry) => {\n const key = entry.category ?? \"uncategorized\";\n (groups[key] ??= []).push(entry);\n return groups;\n }, {});\n }\n\n export(format: \"json\" | \"text\" = \"json\"): string {\n if (format === \"json\") return JSON.stringify(this.buffer, null, 2);\n return this.buffer\n .map((e) => `${e.timestamp} [${e.level.toUpperCase()}] ${e.message}`)\n .join(\"\\n\");\n }\n\n clear(): void {\n this.buffer = [];\n }\n}\n","import { appendFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nexport interface JsonTransportOptions {\n filePath: string;\n}\n\n/** Writes one JSON object per line (ndjson) — suited for log shipping/ingestion. */\nexport class JsonTransport implements Transport {\n readonly name = \"json\";\n private readonly filePath: string;\n private ready: Promise<void>;\n\n constructor(options: JsonTransportOptions) {\n this.filePath = options.filePath;\n this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => undefined);\n }\n\n async write(entry: LoggerEntry): Promise<void> {\n await this.ready;\n await appendFile(this.filePath, `${JSON.stringify(entry)}\\n`, \"utf8\");\n }\n}\n","import { appendFile, mkdir, rename, stat } from \"node:fs/promises\";\nimport { createReadStream, createWriteStream } from \"node:fs\";\nimport { createGzip } from \"node:zlib\";\nimport { pipeline } from \"node:stream/promises\";\nimport { dirname } from \"node:path\";\nimport type { LoggerEntry, Transport } from \"../types.js\";\n\nexport interface FileTransportOptions {\n filePath: string;\n /** Rotate once the file exceeds this many bytes. Default 10 MiB. */\n maxBytes?: number;\n /** Gzip the rotated file. Default true. */\n compress?: boolean;\n}\n\n/** Plain-text file transport with size-based rotation and optional gzip compression. */\nexport class FileTransport implements Transport {\n readonly name = \"file\";\n private readonly filePath: string;\n private readonly maxBytes: number;\n private readonly compress: boolean;\n private ready: Promise<void>;\n private rotating: Promise<void> = Promise.resolve();\n\n constructor(options: FileTransportOptions) {\n this.filePath = options.filePath;\n this.maxBytes = options.maxBytes ?? 10 * 1024 * 1024;\n this.compress = options.compress ?? true;\n this.ready = mkdir(dirname(this.filePath), { recursive: true }).then(() => undefined);\n }\n\n async write(entry: LoggerEntry): Promise<void> {\n await this.ready;\n await this.rotating;\n await this.rotateIfNeeded();\n\n const line = `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.message}${\n entry.context ? ` ${JSON.stringify(entry.context)}` : \"\"\n }\\n`;\n await appendFile(this.filePath, line, \"utf8\");\n }\n\n private async rotateIfNeeded(): Promise<void> {\n const size = await stat(this.filePath)\n .then((s) => s.size)\n .catch(() => 0);\n if (size < this.maxBytes) return;\n\n this.rotating = this.rotate();\n await this.rotating;\n }\n\n private async rotate(): Promise<void> {\n const rotatedPath = `${this.filePath}.${Date.now()}`;\n await rename(this.filePath, rotatedPath);\n\n if (this.compress) {\n await pipeline(\n createReadStream(rotatedPath),\n createGzip(),\n createWriteStream(`${rotatedPath}.gz`),\n );\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(rotatedPath);\n }\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@appbuildersph/logger",
3
+ "version": "0.0.1",
4
+ "description": "Enterprise logger for the App Builders PH SDK — levels, transports, rotation, and structured querying.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "dependencies": {
20
+ "@appbuildersph/shared": "0.0.1"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.3.5",
24
+ "typescript": "^5.6.3",
25
+ "vitest": "^2.1.4"
26
+ },
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
+ "typecheck": "tsc --noEmit",
31
+ "test": "vitest run"
32
+ }
33
+ }