vibe-gx 4.1.1 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/core/logger.js +24 -5
- package/utils/core/server.js +9 -1
- package/vibe.d.ts +8 -0
- package/vibe.js +25 -1
package/package.json
CHANGED
package/utils/core/logger.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os from "os";
|
|
2
|
+
import fs from "fs";
|
|
2
3
|
import { color } from "../helpers/colors.js";
|
|
3
4
|
|
|
4
5
|
const LOG_LEVELS = {
|
|
@@ -25,11 +26,20 @@ const LEVEL_NAMES = {
|
|
|
25
26
|
export class Logger {
|
|
26
27
|
constructor(options = {}) {
|
|
27
28
|
this.level = LOG_LEVELS[options.level || "info"] || 30;
|
|
28
|
-
this.
|
|
29
|
+
this.colors = options.colors !== undefined ? options.colors : true;
|
|
30
|
+
this.prettyPrint =
|
|
31
|
+
options.prettyPrint !== undefined ? options.prettyPrint : this.colors;
|
|
29
32
|
this.lifecycle = options.lifecycle || false;
|
|
30
33
|
this.stream = options.stream || process.stdout;
|
|
34
|
+
this.dest = options.dest || "console"; // "console", "file", "both"
|
|
35
|
+
this.logFile = options.logFile;
|
|
31
36
|
this.bindings = options.bindings || {};
|
|
32
37
|
|
|
38
|
+
// Initialize file stream if needed
|
|
39
|
+
if (this.logFile && (this.dest === "file" || this.dest === "both")) {
|
|
40
|
+
this.fileStream = fs.createWriteStream(this.logFile, { flags: "a" });
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
if (!this.bindings.pid) this.bindings.pid = process.pid;
|
|
34
44
|
if (!this.bindings.hostname) this.bindings.hostname = os.hostname();
|
|
35
45
|
}
|
|
@@ -42,9 +52,12 @@ export class Logger {
|
|
|
42
52
|
level: Object.keys(LOG_LEVELS).find(
|
|
43
53
|
(key) => LOG_LEVELS[key] === this.level,
|
|
44
54
|
),
|
|
55
|
+
colors: this.colors,
|
|
45
56
|
prettyPrint: this.prettyPrint,
|
|
46
57
|
lifecycle: this.lifecycle,
|
|
47
58
|
stream: this.stream,
|
|
59
|
+
dest: this.dest,
|
|
60
|
+
logFile: this.logFile,
|
|
48
61
|
bindings: { ...this.bindings, ...bindings },
|
|
49
62
|
});
|
|
50
63
|
}
|
|
@@ -107,10 +120,16 @@ export class Logger {
|
|
|
107
120
|
|
|
108
121
|
const finalLog = { ...base, ...logData };
|
|
109
122
|
|
|
110
|
-
if (this.
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
if (this.dest === "console" || this.dest === "both") {
|
|
124
|
+
if (this.prettyPrint) {
|
|
125
|
+
this._printPretty(finalLog);
|
|
126
|
+
} else {
|
|
127
|
+
this.stream.write(JSON.stringify(finalLog) + "\n");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if ((this.dest === "file" || this.dest === "both") && this.fileStream) {
|
|
132
|
+
this.fileStream.write(JSON.stringify(finalLog) + "\n");
|
|
114
133
|
}
|
|
115
134
|
}
|
|
116
135
|
|
package/utils/core/server.js
CHANGED
|
@@ -95,7 +95,15 @@ async function server(options, port, host, callback) {
|
|
|
95
95
|
|
|
96
96
|
if (options.loggerConfig && options.loggerConfig.lifecycle) {
|
|
97
97
|
req.startTime = Date.now();
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
// Determine sender IP early for logging
|
|
100
|
+
const sender =
|
|
101
|
+
req.socket.remoteAddress || req.headers["x-forwarded-for"] || "unknown";
|
|
102
|
+
|
|
103
|
+
req.log.info(
|
|
104
|
+
{ type: "req", url: req.url, method: req.method, sender },
|
|
105
|
+
"Incoming request",
|
|
106
|
+
);
|
|
99
107
|
|
|
100
108
|
res.on("finish", () => {
|
|
101
109
|
req.log.info(
|
package/vibe.d.ts
CHANGED
|
@@ -181,6 +181,12 @@ export interface LoggerConfig {
|
|
|
181
181
|
lifecycle?: boolean;
|
|
182
182
|
/** If true, formats JSON output into human-readable Vibe-styled terminal lines (like pino-pretty) */
|
|
183
183
|
prettyPrint?: boolean;
|
|
184
|
+
/** If true, applies ANSI color formatting to terminal logs. Default: true (unless overridden) */
|
|
185
|
+
colors?: boolean;
|
|
186
|
+
/** Destination for the logs. Accepts "console", "file", or "both". Default: "console" */
|
|
187
|
+
dest?: "console" | "file" | "both";
|
|
188
|
+
/** Absolute or relative path to the target log file. Active when dest is "file" or "both" */
|
|
189
|
+
logFile?: string;
|
|
184
190
|
/** Custom writable stream to output logs to (defaults to process.stdout) */
|
|
185
191
|
stream?: NodeJS.WritableStream;
|
|
186
192
|
}
|
|
@@ -213,6 +219,8 @@ export interface LoggerAPI {
|
|
|
213
219
|
export interface VibeConfig {
|
|
214
220
|
/** Configuration for the native Vibe terminal logger */
|
|
215
221
|
logger?: LoggerConfig | boolean;
|
|
222
|
+
/** Enable automatic process restarting on crash. Spawns an internal cluster manager. Default: false */
|
|
223
|
+
autoRestart?: boolean;
|
|
216
224
|
}
|
|
217
225
|
|
|
218
226
|
// ==========================================
|
package/vibe.js
CHANGED
|
@@ -6,6 +6,7 @@ import { PathToRegex } from "./utils/core/handler.js";
|
|
|
6
6
|
import { compileSerializer } from "./utils/core/compile-serializer.js";
|
|
7
7
|
import { createLogger, Logger } from "./utils/core/logger.js";
|
|
8
8
|
import { handleError } from "./utils/core/handler.js";
|
|
9
|
+
import { clusterize } from "./utils/scaling/cluster.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Helper to generate regex for a path
|
|
@@ -133,6 +134,7 @@ function pathToRegex(path) {
|
|
|
133
134
|
* Initializes a Vibe application instance.
|
|
134
135
|
* @param {Object} [config={}]
|
|
135
136
|
* @param {Object|boolean} [config.logger] - Logger configuration
|
|
137
|
+
* @param {boolean} [config.autoRestart] - Restart server automatically on crash
|
|
136
138
|
* @returns {VibeApp}
|
|
137
139
|
*/
|
|
138
140
|
const vibe = (config = {}) => {
|
|
@@ -173,6 +175,22 @@ const vibe = (config = {}) => {
|
|
|
173
175
|
errorHandler: handleError,
|
|
174
176
|
};
|
|
175
177
|
|
|
178
|
+
// Add global uncaught exception handler to prevent silent deaths and log cleanly
|
|
179
|
+
process.on("uncaughtException", (err) => {
|
|
180
|
+
appLogger.fatal(err, "Uncaught Exception crashed the server");
|
|
181
|
+
// Only exit here if we're not exiting gracefully anyway,
|
|
182
|
+
// cluster manager will restart it.
|
|
183
|
+
setTimeout(() => process.exit(1), 100);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
187
|
+
appLogger.fatal(
|
|
188
|
+
{ err: reason },
|
|
189
|
+
"Unhandled Promise Rejection crashed the server",
|
|
190
|
+
);
|
|
191
|
+
setTimeout(() => process.exit(1), 100);
|
|
192
|
+
});
|
|
193
|
+
|
|
176
194
|
// Register default landing route
|
|
177
195
|
const defaultRoute = {
|
|
178
196
|
method: "GET",
|
|
@@ -377,7 +395,13 @@ const vibe = (config = {}) => {
|
|
|
377
395
|
host = undefined;
|
|
378
396
|
}
|
|
379
397
|
|
|
380
|
-
server(options, Number(port), host, callback);
|
|
398
|
+
const startServer = () => server(options, Number(port), host, callback);
|
|
399
|
+
|
|
400
|
+
if (config.autoRestart) {
|
|
401
|
+
clusterize(startServer, { workers: 1, restart: true });
|
|
402
|
+
} else {
|
|
403
|
+
startServer();
|
|
404
|
+
}
|
|
381
405
|
}
|
|
382
406
|
|
|
383
407
|
/**
|