@fonoster/logger 0.27.0 → 0.30.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.
@@ -0,0 +1,17 @@
1
+ type LoggerOptions = {
2
+ level?: string;
3
+ format?: "json" | "text";
4
+ transport?: "console" | "fluent";
5
+ fluent?: {
6
+ host?: string;
7
+ port?: number;
8
+ tagPrefix?: string;
9
+ };
10
+ };
11
+ /**
12
+ * Configures logging from a service's config file. Loggers created earlier
13
+ * (including at import time) switch to the new level and transport.
14
+ * Before this is called, settings come from environment variables.
15
+ */
16
+ declare function configureLogger(options: LoggerOptions): void;
17
+ export { configureLogger, LoggerOptions };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configureLogger = configureLogger;
7
+ /**
8
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
9
+ * http://github.com/fonoster/fonoster
10
+ *
11
+ * This file is part of Fonoster
12
+ *
13
+ * Licensed under the MIT License (the "License");
14
+ * you may not use this file except in compliance with
15
+ * the License. You may obtain a copy of the License at
16
+ *
17
+ * https://opensource.org/licenses/MIT
18
+ *
19
+ * Unless required by applicable law or agreed to in writing, software
20
+ * distributed under the License is distributed on an "AS IS" BASIS,
21
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
+ * See the License for the specific language governing permissions and
23
+ * limitations under the License.
24
+ */
25
+ const envs_1 = require("./envs");
26
+ const logger_1 = __importDefault(require("./logger"));
27
+ const registry_1 = require("./registry");
28
+ /**
29
+ * Configures logging from a service's config file. Loggers created earlier
30
+ * (including at import time) switch to the new level and transport.
31
+ * Before this is called, settings come from environment variables.
32
+ */
33
+ function configureLogger(options) {
34
+ var _a, _b, _c;
35
+ const current = (0, envs_1.getSettings)();
36
+ (0, envs_1.applySettings)({
37
+ level: (_a = options.level) !== null && _a !== void 0 ? _a : current.level,
38
+ format: (_b = options.format) !== null && _b !== void 0 ? _b : current.format,
39
+ transport: (_c = options.transport) !== null && _c !== void 0 ? _c : current.transport,
40
+ fluent: Object.assign(Object.assign({}, current.fluent), options.fluent)
41
+ });
42
+ const { level } = (0, envs_1.getSettings)();
43
+ for (const logger of registry_1.registry) {
44
+ logger.clear();
45
+ (0, envs_1.getTransports)().forEach((transport) => logger.add(transport));
46
+ logger.level = level;
47
+ }
48
+ // Per-service loggers keep their own human-readable format; only the
49
+ // default logger follows logs.format, as it does with LOGS_FORMAT.
50
+ logger_1.default.format = (0, envs_1.buildFormat)();
51
+ }
package/dist/envs.d.ts CHANGED
@@ -1,6 +1,20 @@
1
1
  import winston from "winston";
2
- declare const LOGS_LEVEL = "info";
3
- declare const fluent: any;
4
- declare const format: winston.Logform.Format;
5
- declare const transports: any[];
6
- export { fluent, format, LOGS_LEVEL as level, transports };
2
+ type LoggerSettings = {
3
+ level: string;
4
+ format: "json" | "text";
5
+ transport: "console" | "fluent";
6
+ fluent: {
7
+ host?: string;
8
+ port: number;
9
+ tagPrefix: string;
10
+ };
11
+ };
12
+ /** Settings from environment variables: the default until `configureLogger` runs. */
13
+ declare function settingsFromEnv(): LoggerSettings;
14
+ declare function buildFormat(): winston.Logform.Format;
15
+ declare const getSettings: () => LoggerSettings;
16
+ declare const getTransports: () => winston.transport[];
17
+ /** Ends the Fluent sender, if one is in use. */
18
+ declare function endFluent(): void;
19
+ declare function applySettings(next: LoggerSettings): void;
20
+ export { applySettings, buildFormat, endFluent, getSettings, getTransports, LoggerSettings, settingsFromEnv };
package/dist/envs.js CHANGED
@@ -3,7 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.transports = exports.level = exports.format = exports.fluent = void 0;
6
+ exports.getTransports = exports.getSettings = void 0;
7
+ exports.applySettings = applySettings;
8
+ exports.buildFormat = buildFormat;
9
+ exports.endFluent = endFluent;
10
+ exports.settingsFromEnv = settingsFromEnv;
7
11
  /**
8
12
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
9
13
  * http://github.com/fonoster/fonoster
@@ -25,25 +29,57 @@ exports.transports = exports.level = exports.format = exports.fluent = void 0;
25
29
  const fluent_logger_1 = __importDefault(require("fluent-logger"));
26
30
  const winston_1 = __importDefault(require("winston"));
27
31
  const getEnv_1 = require("./getEnv");
32
+ const redact_1 = require("./redact");
28
33
  const FluentTransport = fluent_logger_1.default.support.winstonTransport();
29
- const LOGS_DRIVER_HOST = (0, getEnv_1.getEnv)("LOGS_DRIVER_HOST");
30
- // Note: if LOGS_DRIVER_PORT is provided as a string in the environment, it will be used as-is.
31
- const LOGS_DRIVER_PORT = (0, getEnv_1.getEnv)("LOGS_DRIVER_PORT", 24224);
32
- const LOGS_OPT_TAG_PREFIX = (0, getEnv_1.getEnv)("LOGS_OPT_TAG_PREFIX", "fonoster-logs");
33
- const LOGS_FORMAT = (0, getEnv_1.getEnv)("LOGS_FORMAT", "json");
34
- const LOGS_LEVEL = (0, getEnv_1.getEnv)("LOGS_LEVEL", "info");
35
- exports.level = LOGS_LEVEL;
36
- const LOGS_TRANSPORT = (0, getEnv_1.getEnv)("LOGS_TRANSPORT", "console");
37
- const fluent = new FluentTransport(`${LOGS_OPT_TAG_PREFIX}`, {
38
- host: LOGS_DRIVER_HOST,
39
- port: LOGS_DRIVER_PORT,
40
- timeout: 3.0,
41
- requireAckResponse: false
42
- });
43
- exports.fluent = fluent;
44
- const format = LOGS_FORMAT === "json"
45
- ? winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.json())
46
- : winston_1.default.format.combine(winston_1.default.format.colorize(), winston_1.default.format.simple());
47
- exports.format = format;
48
- const transports = LOGS_TRANSPORT === "fluent" ? [fluent] : [new winston_1.default.transports.Console()];
49
- exports.transports = transports;
34
+ /** Settings from environment variables: the default until `configureLogger` runs. */
35
+ function settingsFromEnv() {
36
+ return {
37
+ level: (0, getEnv_1.getEnv)("LOGS_LEVEL", "info"),
38
+ format: (0, getEnv_1.getEnv)("LOGS_FORMAT", "json") === "json" ? "json" : "text",
39
+ transport: (0, getEnv_1.getEnv)("LOGS_TRANSPORT", "console"),
40
+ fluent: {
41
+ host: (0, getEnv_1.getEnv)("LOGS_DRIVER_HOST"),
42
+ // If LOGS_DRIVER_PORT is a string in the environment, it is used as-is.
43
+ port: (0, getEnv_1.getEnv)("LOGS_DRIVER_PORT", 24224),
44
+ tagPrefix: (0, getEnv_1.getEnv)("LOGS_OPT_TAG_PREFIX", "fonoster-logs")
45
+ }
46
+ };
47
+ }
48
+ let settings = settingsFromEnv();
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ let fluent;
51
+ let transports = [];
52
+ function buildTransports() {
53
+ if (settings.transport === "fluent") {
54
+ fluent = new FluentTransport(settings.fluent.tagPrefix, {
55
+ host: settings.fluent.host,
56
+ port: settings.fluent.port,
57
+ timeout: 3.0,
58
+ requireAckResponse: false
59
+ });
60
+ transports = [fluent];
61
+ }
62
+ else {
63
+ fluent = undefined;
64
+ transports = [new winston_1.default.transports.Console()];
65
+ }
66
+ }
67
+ buildTransports();
68
+ function buildFormat() {
69
+ return settings.format === "json"
70
+ ? winston_1.default.format.combine((0, redact_1.redactFormat)(), winston_1.default.format.timestamp(), winston_1.default.format.json())
71
+ : winston_1.default.format.combine((0, redact_1.redactFormat)(), winston_1.default.format.colorize(), winston_1.default.format.simple());
72
+ }
73
+ const getSettings = () => settings;
74
+ exports.getSettings = getSettings;
75
+ const getTransports = () => transports;
76
+ exports.getTransports = getTransports;
77
+ /** Ends the Fluent sender, if one is in use. */
78
+ function endFluent() {
79
+ var _a;
80
+ (_a = fluent === null || fluent === void 0 ? void 0 : fluent.sender) === null || _a === void 0 ? void 0 : _a.end("end", {}, () => { });
81
+ }
82
+ function applySettings(next) {
83
+ settings = next;
84
+ buildTransports();
85
+ }
package/dist/getLogger.js CHANGED
@@ -35,27 +35,27 @@ exports.getLogger = getLogger;
35
35
  */
36
36
  const winston_1 = __importDefault(require("winston"));
37
37
  const envs_1 = require("./envs");
38
+ const redact_1 = require("./redact");
39
+ const registry_1 = require("./registry");
38
40
  const loggers = new Map();
39
41
  function getLogger(config) {
40
42
  const key = config.service || "default";
41
43
  if (loggers.has(key)) {
42
44
  return loggers.get(key);
43
45
  }
44
- const humanFormat = winston_1.default.format.combine(winston_1.default.format.timestamp({
45
- format: "YYYY-MM-dd HH:mm:ss.SSS"
46
+ const humanFormat = winston_1.default.format.combine((0, redact_1.redactFormat)(), winston_1.default.format.timestamp({
47
+ format: "YYYY-MM-DD HH:mm:ss.SSS"
46
48
  }), winston_1.default.format.printf((_a) => {
47
49
  var { level, message, timestamp } = _a, metadata = __rest(_a, ["level", "message", "timestamp"]);
48
50
  return `${timestamp} [${level}]: ${config.service ? `(${config.service})` : ""} ${message} ${JSON.stringify(metadata)}`;
49
51
  }));
50
- const newLogger = winston_1.default.createLogger({
52
+ const newLogger = (0, registry_1.register)(winston_1.default.createLogger({
51
53
  levels: winston_1.default.config.npm.levels,
52
54
  format: humanFormat,
53
- transports: envs_1.transports,
54
- level: envs_1.level
55
- });
56
- newLogger.on("finish", () => {
57
- envs_1.fluent.sender.end("end", {}, () => { });
58
- });
55
+ transports: (0, envs_1.getTransports)(),
56
+ level: (0, envs_1.getSettings)().level
57
+ }));
58
+ newLogger.on("finish", envs_1.endFluent);
59
59
  loggers.set(key, newLogger);
60
60
  return newLogger;
61
61
  }
package/dist/index.d.ts CHANGED
@@ -16,8 +16,10 @@
16
16
  * See the License for the specific language governing permissions and
17
17
  * limitations under the License.
18
18
  */
19
+ import { configureLogger, LoggerOptions } from "./configureLogger";
19
20
  import { getLogger } from "./getLogger";
20
21
  import logger, { mute, unmute } from "./logger";
22
+ import { redactSecrets } from "./redact";
21
23
  import { ULogType } from "./types";
22
24
  import ulogger from "./userLogger";
23
- export { ULogType, logger as default, getLogger, mute, ulogger, unmute };
25
+ export { ULogType, logger as default, configureLogger, getLogger, LoggerOptions, mute, redactSecrets, ulogger, unmute };
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.unmute = exports.ulogger = exports.mute = exports.getLogger = exports.default = exports.ULogType = void 0;
39
+ exports.unmute = exports.ulogger = exports.redactSecrets = exports.mute = exports.getLogger = exports.configureLogger = exports.default = exports.ULogType = void 0;
40
40
  /**
41
41
  * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
42
42
  * http://github.com/fonoster/fonoster
@@ -55,12 +55,16 @@ exports.unmute = exports.ulogger = exports.mute = exports.getLogger = exports.de
55
55
  * See the License for the specific language governing permissions and
56
56
  * limitations under the License.
57
57
  */
58
+ const configureLogger_1 = require("./configureLogger");
59
+ Object.defineProperty(exports, "configureLogger", { enumerable: true, get: function () { return configureLogger_1.configureLogger; } });
58
60
  const getLogger_1 = require("./getLogger");
59
61
  Object.defineProperty(exports, "getLogger", { enumerable: true, get: function () { return getLogger_1.getLogger; } });
60
62
  const logger_1 = __importStar(require("./logger"));
61
63
  exports.default = logger_1.default;
62
64
  Object.defineProperty(exports, "mute", { enumerable: true, get: function () { return logger_1.mute; } });
63
65
  Object.defineProperty(exports, "unmute", { enumerable: true, get: function () { return logger_1.unmute; } });
66
+ const redact_1 = require("./redact");
67
+ Object.defineProperty(exports, "redactSecrets", { enumerable: true, get: function () { return redact_1.redactSecrets; } });
64
68
  const types_1 = require("./types");
65
69
  Object.defineProperty(exports, "ULogType", { enumerable: true, get: function () { return types_1.ULogType; } });
66
70
  const userLogger_1 = __importDefault(require("./userLogger"));
package/dist/logger.js CHANGED
@@ -24,16 +24,15 @@ exports.unmute = exports.mute = exports.default = void 0;
24
24
  */
25
25
  const winston_1 = __importDefault(require("winston"));
26
26
  const envs_1 = require("./envs");
27
- const logger = winston_1.default.createLogger({
27
+ const registry_1 = require("./registry");
28
+ const logger = (0, registry_1.register)(winston_1.default.createLogger({
28
29
  levels: winston_1.default.config.npm.levels,
29
- format: envs_1.format,
30
- transports: envs_1.transports,
31
- level: envs_1.level
32
- });
30
+ format: (0, envs_1.buildFormat)(),
31
+ transports: (0, envs_1.getTransports)(),
32
+ level: (0, envs_1.getSettings)().level
33
+ }));
33
34
  exports.default = logger;
34
- logger.on("finish", () => {
35
- envs_1.fluent.sender.end("end", {}, () => { });
36
- });
35
+ logger.on("finish", envs_1.endFluent);
37
36
  const mute = () => logger.transports.forEach((t) => (t.silent = true));
38
37
  exports.mute = mute;
39
38
  const unmute = () => logger.transports.forEach((t) => (t.silent = false));
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
+ * http://github.com/fonoster/fonoster
4
+ *
5
+ * This file is part of Fonoster
6
+ *
7
+ * Licensed under the MIT License (the "License");
8
+ * you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ *
11
+ * https://opensource.org/licenses/MIT
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import winston from "winston";
20
+ declare const REDACTED = "[redacted]";
21
+ /** True for field names that hold secrets: passwords, tokens, keys, secrets. */
22
+ declare function isSecretName(key: string): boolean;
23
+ /**
24
+ * A copy of `value` with secret fields replaced by "[redacted]", at any depth
25
+ * in plain objects and arrays. The value passed in is never changed.
26
+ */
27
+ declare function redactSecrets(value: unknown, seen?: WeakSet<object>): unknown;
28
+ /**
29
+ * Winston format that redacts secret fields in a log entry's metadata, so a
30
+ * request logged at verbose level never carries passwords or tokens.
31
+ */
32
+ declare const redactFormat: winston.Logform.FormatWrap;
33
+ export { isSecretName, REDACTED, redactFormat, redactSecrets };
package/dist/redact.js ADDED
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.redactFormat = exports.REDACTED = void 0;
7
+ exports.isSecretName = isSecretName;
8
+ exports.redactSecrets = redactSecrets;
9
+ /**
10
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
11
+ * http://github.com/fonoster/fonoster
12
+ *
13
+ * This file is part of Fonoster
14
+ *
15
+ * Licensed under the MIT License (the "License");
16
+ * you may not use this file except in compliance with
17
+ * the License. You may obtain a copy of the License at
18
+ *
19
+ * https://opensource.org/licenses/MIT
20
+ *
21
+ * Unless required by applicable law or agreed to in writing, software
22
+ * distributed under the License is distributed on an "AS IS" BASIS,
23
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
+ * See the License for the specific language governing permissions and
25
+ * limitations under the License.
26
+ */
27
+ const winston_1 = __importDefault(require("winston"));
28
+ const REDACTED = "[redacted]";
29
+ exports.REDACTED = REDACTED;
30
+ /** Field names whose values are never logged (compared lowercased, without `-` and `_`). */
31
+ const SECRET_NAMES = new Set([
32
+ "password",
33
+ "passwd",
34
+ "currentpassword",
35
+ "newpassword",
36
+ "secret",
37
+ "token",
38
+ "accesstoken",
39
+ "refreshtoken",
40
+ "idtoken",
41
+ "sessiontoken",
42
+ "authtoken",
43
+ "apikey",
44
+ "apisecret",
45
+ "accesskeysecret",
46
+ "authorization",
47
+ "clientsecret",
48
+ "signingsecret",
49
+ "privatekey",
50
+ "encryptionkey",
51
+ "verificationcode",
52
+ "twofactorcode"
53
+ ]);
54
+ const normalize = (key) => key.toLowerCase().replace(/[-_]/g, "");
55
+ /** True for field names that hold secrets: passwords, tokens, keys, secrets. */
56
+ function isSecretName(key) {
57
+ const name = normalize(key);
58
+ return (SECRET_NAMES.has(name) ||
59
+ name.endsWith("password") ||
60
+ name.endsWith("secret") ||
61
+ (name.endsWith("token") && name !== "tokenuse"));
62
+ }
63
+ const isPlainObject = (value) => typeof value === "object" &&
64
+ value !== null &&
65
+ (Object.getPrototypeOf(value) === Object.prototype ||
66
+ Object.getPrototypeOf(value) === null);
67
+ const isSet = (value) => value !== undefined && value !== null && value !== "";
68
+ function redactField(key, value, seen) {
69
+ return isSecretName(key) && isSet(value)
70
+ ? REDACTED
71
+ : redactSecrets(value, seen);
72
+ }
73
+ /**
74
+ * A copy of `value` with secret fields replaced by "[redacted]", at any depth
75
+ * in plain objects and arrays. The value passed in is never changed.
76
+ */
77
+ function redactSecrets(value, seen = new WeakSet()) {
78
+ if (Array.isArray(value)) {
79
+ if (seen.has(value))
80
+ return value;
81
+ seen.add(value);
82
+ return value.map((item) => redactSecrets(item, seen));
83
+ }
84
+ if (!isPlainObject(value))
85
+ return value;
86
+ if (seen.has(value))
87
+ return value;
88
+ seen.add(value);
89
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
90
+ key,
91
+ redactField(key, item, seen)
92
+ ]));
93
+ }
94
+ /**
95
+ * Winston format that redacts secret fields in a log entry's metadata, so a
96
+ * request logged at verbose level never carries passwords or tokens.
97
+ */
98
+ const redactFormat = winston_1.default.format((info) => {
99
+ const seen = new WeakSet();
100
+ Object.keys(info)
101
+ .filter((key) => key !== "level" && key !== "message")
102
+ .forEach((key) => {
103
+ info[key] = redactField(key, info[key], seen);
104
+ });
105
+ return info;
106
+ });
107
+ exports.redactFormat = redactFormat;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
+ * http://github.com/fonoster/fonoster
4
+ *
5
+ * This file is part of Fonoster
6
+ *
7
+ * Licensed under the MIT License (the "License");
8
+ * you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ *
11
+ * https://opensource.org/licenses/MIT
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import winston from "winston";
20
+ /** Every logger created so far, so `configureLogger` can update them in place. */
21
+ declare const registry: Set<winston.Logger>;
22
+ declare const register: (logger: winston.Logger) => winston.Logger;
23
+ export { register, registry };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registry = exports.register = void 0;
4
+ /** Every logger created so far, so `configureLogger` can update them in place. */
5
+ const registry = new Set();
6
+ exports.registry = registry;
7
+ const register = (logger) => {
8
+ registry.add(logger);
9
+ return logger;
10
+ };
11
+ exports.register = register;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonoster/logger",
3
- "version": "0.27.0",
3
+ "version": "0.30.0",
4
4
  "description": "Logger module",
5
5
  "author": "Pedro Sanders <psanders@fonoster.com>",
6
6
  "homepage": "https://github.com/fonoster/fonoster#readme",
@@ -32,5 +32,5 @@
32
32
  "fluent-logger": "^3.4.1",
33
33
  "winston": "^3.13.0"
34
34
  },
35
- "gitHead": "1191b34677b51a36000177887ff82303ef2693e1"
35
+ "gitHead": "08c9cfd7932675b6b16111daa4d03f14b4e287d1"
36
36
  }