barehttp 2.1.0 → 2.2.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 CHANGED
@@ -16,7 +16,7 @@ Full-featured slim webserver for microservices with extremely low overhead and r
16
16
  ## Requirements
17
17
 
18
18
  ```bash
19
- Node.js >= 14.8
19
+ Node.js >= 22
20
20
  ```
21
21
 
22
22
  ## Quick start
@@ -147,6 +147,23 @@ Default `false`
147
147
 
148
148
  Enable request/response logging, format varies from `production` or `development` environments, though to change use e.g. `NODE_ENV=production`
149
149
 
150
+ #### `logger?` (Object)
151
+
152
+ Configure log outputs for app logs and http request logs.
153
+
154
+ ```typescript
155
+ const app = new BareHttp({
156
+ logging: true,
157
+ logger: {
158
+ app: { file: './logs/app.log' },
159
+ http: { file: './logs/http.log' },
160
+ level: 'debug',
161
+ },
162
+ });
163
+ ```
164
+
165
+ `app.file` and `http.file` accept a path string or `{ path, sync }`, and `http.file` is used only when `logging: true`. `console` (default `true`) keeps stdout logging, and `pretty` (default `true` outside production) controls colored console output. `level` sets the base level and can be overridden per target with `app.level` or `http.level`. Use `sourceMaps: true` to map log locations back to `.ts` files (requires source maps in your build).
166
+
150
167
  #### `ws?` (Boolean)
151
168
 
152
169
  Default `false`
package/lib/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export { BareHttp } from './server.js';
2
2
  export type { BareHttpType } from './server.js';
3
3
  export type { BareRequest } from './request.js';
4
4
  export { context } from './context/index.js';
5
- export { logMe } from './logger/index.js';
5
+ export { configureLogger, logMe } from './logger/index.js';
6
+ export type { LoggerConfig } from './logger/index.js';
package/lib/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { BareHttp } from './server.js';
2
2
  export { context } from './context/index.js';
3
- export { logMe } from './logger/index.js';
3
+ export { configureLogger, logMe } from './logger/index.js';
@@ -1,4 +1,22 @@
1
+ import { type LevelWithSilent } from 'pino';
1
2
  import { serializeHttp } from './serializers.js';
3
+ type LoggerFileConfig = {
4
+ path: string;
5
+ sync?: boolean;
6
+ };
7
+ type LoggerTargetConfig = {
8
+ file?: string | LoggerFileConfig;
9
+ level?: LevelWithSilent;
10
+ };
11
+ export type LoggerConfig = {
12
+ console?: boolean;
13
+ pretty?: boolean;
14
+ level?: LevelWithSilent;
15
+ app?: LoggerTargetConfig;
16
+ http?: LoggerTargetConfig;
17
+ sourceMaps?: boolean;
18
+ };
19
+ export declare const configureLogger: (config?: LoggerConfig) => void;
2
20
  interface LogMeFn {
3
21
  (obj: unknown, ...args: []): void;
4
22
  (msg: string, ...args: any[]): void;
@@ -1,26 +1,149 @@
1
- import pino, { destination } from 'pino';
1
+ import pino, { destination, } from 'pino';
2
+ import { mkdirSync } from 'fs';
3
+ import { dirname } from 'path';
2
4
  import { serializeLog, serializeHttp } from './serializers.js';
3
5
  import { envs } from '../env.js';
4
- const asyncDest = envs.isProd ? [destination({ sync: false })] : [];
6
+ const defaultPretty = !envs.isProd;
7
+ const defaultFileSync = envs.isProd ? false : true;
5
8
  const pinoCommonOptions = {
6
9
  timestamp: () => `,"time":"${new Date()[envs.isProd ? 'toISOString' : 'toLocaleTimeString']()}"`,
7
10
  formatters: {
8
11
  level: (label) => ({ level: label }),
9
12
  },
10
13
  messageKey: 'message',
11
- transport: envs.isProd ? undefined : { target: 'pino-pretty', options: { colorize: true } },
12
14
  };
13
- const logger = pino(pinoCommonOptions, asyncDest[0]);
15
+ const noop = () => { };
16
+ const noopLogger = {
17
+ trace: noop,
18
+ debug: noop,
19
+ info: noop,
20
+ warn: noop,
21
+ error: noop,
22
+ fatal: noop,
23
+ silent: noop,
24
+ };
25
+ let sourceMapsRegistered = false;
26
+ const enableSourceMaps = (enabled) => {
27
+ if (!enabled || sourceMapsRegistered)
28
+ return;
29
+ sourceMapsRegistered = true;
30
+ void import('source-map-support/register').catch(() => {
31
+ sourceMapsRegistered = false;
32
+ });
33
+ };
34
+ const normalizeFileConfig = (file) => {
35
+ if (!file)
36
+ return undefined;
37
+ if (typeof file === 'string')
38
+ return { path: file, sync: defaultFileSync };
39
+ return { path: file.path, sync: file.sync ?? defaultFileSync };
40
+ };
41
+ const ensureLogDir = (filePath) => {
42
+ const dir = dirname(filePath);
43
+ if (dir && dir !== '.') {
44
+ mkdirSync(dir, { recursive: true });
45
+ }
46
+ };
47
+ const toLoggerLike = (logger) => {
48
+ const call = (method) => (...args) => logger[method](...args);
49
+ return {
50
+ trace: call('trace'),
51
+ debug: call('debug'),
52
+ info: call('info'),
53
+ warn: call('warn'),
54
+ error: call('error'),
55
+ fatal: call('fatal'),
56
+ silent: noop,
57
+ };
58
+ };
59
+ const buildConsoleLogger = (options, prettyEnabled) => {
60
+ if (prettyEnabled) {
61
+ try {
62
+ const transport = pino.transport({
63
+ targets: [{ target: 'pino-pretty', options: { colorize: true } }],
64
+ });
65
+ return toLoggerLike(pino(options, transport));
66
+ }
67
+ catch {
68
+ return toLoggerLike(pino(options, destination({ dest: 1, sync: false })));
69
+ }
70
+ }
71
+ return toLoggerLike(pino(options, destination({ dest: 1, sync: false })));
72
+ };
73
+ const buildFileLogger = (options, fileConfig) => {
74
+ ensureLogDir(fileConfig.path);
75
+ return toLoggerLike(pino(options, destination({ dest: fileConfig.path, sync: fileConfig.sync ?? defaultFileSync })));
76
+ };
77
+ const combineLoggers = (primary, secondary) => {
78
+ if (!primary && !secondary)
79
+ return noopLogger;
80
+ const first = primary ?? noopLogger;
81
+ const second = secondary ?? noopLogger;
82
+ return {
83
+ trace: (...args) => {
84
+ first.trace(...args);
85
+ second.trace(...args);
86
+ },
87
+ debug: (...args) => {
88
+ first.debug(...args);
89
+ second.debug(...args);
90
+ },
91
+ info: (...args) => {
92
+ first.info(...args);
93
+ second.info(...args);
94
+ },
95
+ warn: (...args) => {
96
+ first.warn(...args);
97
+ second.warn(...args);
98
+ },
99
+ error: (...args) => {
100
+ first.error(...args);
101
+ second.error(...args);
102
+ },
103
+ fatal: (...args) => {
104
+ first.fatal(...args);
105
+ second.fatal(...args);
106
+ },
107
+ silent: noop,
108
+ };
109
+ };
110
+ const buildLogger = (config, target) => {
111
+ const consoleEnabled = config.console ?? true;
112
+ const prettyEnabled = config.pretty ?? defaultPretty;
113
+ const fileConfig = normalizeFileConfig(target?.file);
114
+ const level = target?.level ?? config.level;
115
+ const options = { ...pinoCommonOptions };
116
+ if (level)
117
+ options.level = level;
118
+ const consoleLogger = consoleEnabled ? buildConsoleLogger(options, prettyEnabled) : undefined;
119
+ const fileLogger = fileConfig ? buildFileLogger(options, fileConfig) : undefined;
120
+ return combineLoggers(consoleLogger, fileLogger);
121
+ };
122
+ let appLogger;
123
+ let httpLogger;
124
+ export const configureLogger = (config = {}) => {
125
+ enableSourceMaps(config.sourceMaps);
126
+ if (config.app || config.http) {
127
+ appLogger = buildLogger(config, config.app);
128
+ httpLogger = buildLogger(config, config.http);
129
+ }
130
+ else {
131
+ const shared = buildLogger(config);
132
+ appLogger = shared;
133
+ httpLogger = shared;
134
+ }
135
+ };
136
+ configureLogger();
14
137
  export const logHttp = (...params) => {
15
138
  const { level, logObject } = serializeHttp(...params);
16
- logger[level](logObject);
139
+ httpLogger[level](logObject);
17
140
  };
18
141
  // TODO: remove the test condition
19
142
  export const logMe = {
20
- debug: (...args) => !envs.isTest && logger.debug(serializeLog(...args)),
21
- info: (...args) => !envs.isTest && logger.info(serializeLog(...args)),
22
- warn: (...args) => !envs.isTest && logger.warn(serializeLog(...args)),
23
- error: (...args) => !envs.isTest && logger.error(serializeLog(...args)),
24
- fatal: (...args) => !envs.isTest && logger.fatal(serializeLog(...args)),
25
- trace: (...args) => !envs.isTest && logger.trace(serializeLog(...args)),
143
+ debug: (...args) => !envs.isTest && appLogger.debug(serializeLog(...args)),
144
+ info: (...args) => !envs.isTest && appLogger.info(serializeLog(...args)),
145
+ warn: (...args) => !envs.isTest && appLogger.warn(serializeLog(...args)),
146
+ error: (...args) => !envs.isTest && appLogger.error(serializeLog(...args)),
147
+ fatal: (...args) => !envs.isTest && appLogger.fatal(serializeLog(...args)),
148
+ trace: (...args) => !envs.isTest && appLogger.trace(serializeLog(...args)),
26
149
  };
package/lib/server.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ServerOptions } from 'ws';
2
2
  import { Ajv } from 'ajv';
3
3
  import { BareRequest, CacheOpts } from './request.js';
4
+ import type { LoggerConfig } from './logger/index.js';
4
5
  import { CookiesManagerOptions } from './middlewares/cookies/cookie-manager.js';
5
6
  import { HttpMethodsUnion, StatusCodesUnion } from './utils/index.js';
6
7
  import { CorsOptions } from './middlewares/cors/cors.js';
@@ -62,6 +63,10 @@ type BareOptions<A extends IP> = {
62
63
  * Default `false`
63
64
  */
64
65
  logging?: boolean;
66
+ /**
67
+ * Logger configuration. Controls outputs for app and http logs.
68
+ */
69
+ logger?: LoggerConfig;
65
70
  errorHandlerMiddleware?: ErrorHandler;
66
71
  /**
67
72
  * Request time format in `seconds` or `milliseconds`
package/lib/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import Router from 'find-my-way';
2
2
  import { Ajv } from 'ajv';
3
3
  import { BareRequest } from './request.js';
4
- import { logMe } from './logger/index.js';
4
+ import { configureLogger, logMe } from './logger/index.js';
5
5
  import { context, enableContext, newContext } from './context/index.js';
6
6
  import { HttpMethods, } from './utils/index.js';
7
7
  import { Cors } from './middlewares/cors/cors.js';
@@ -75,6 +75,8 @@ export class BareServer {
75
75
  };
76
76
  applyLaunchOptions = () => {
77
77
  const { bareOptions: bo } = this;
78
+ if (bo.logger)
79
+ configureLogger(bo.logger);
78
80
  if (bo.setRandomPort) {
79
81
  this.#port = undefined;
80
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barehttp",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Lightweight and fast Node.js web server",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -53,6 +53,7 @@
53
53
  "lodash": "^4.17.21",
54
54
  "pino": "^10.1.0",
55
55
  "pino-pretty": "^13.1.3",
56
+ "source-map-support": "^0.5.21",
56
57
  "ts-morph": "^27.0.2",
57
58
  "ws": "^8.18.3"
58
59
  },