mynth-logger 1.2.0 → 2.0.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.
@@ -17,7 +17,7 @@ jobs:
17
17
  - name: Set up Node.js
18
18
  uses: actions/setup-node@v3
19
19
  with:
20
- node-version: 18
20
+ node-version: 18.18
21
21
  check-latest: true
22
22
 
23
23
  - name: Update npm
@@ -29,28 +29,6 @@ jobs:
29
29
  - name: Run tests
30
30
  run: npm run test
31
31
 
32
- test-pretty:
33
- runs-on: ubuntu-latest
34
- timeout-minutes: 10
35
- steps:
36
- - name: Checkout repository
37
- uses: actions/checkout@v3
38
-
39
- - name: Set up Node.js
40
- uses: actions/setup-node@v3
41
- with:
42
- node-version: 18
43
- check-latest: true
44
-
45
- - name: Update npm
46
- run: npm install -g npm@latest
47
-
48
- - name: Install dependencies
49
- run: npm ci --include dev
50
-
51
- - name: Run tests
52
- run: npm run test:pretty
53
-
54
32
  test-run:
55
33
  runs-on: ubuntu-latest
56
34
  timeout-minutes: 10
@@ -61,7 +39,7 @@ jobs:
61
39
  - name: Set up Node.js
62
40
  uses: actions/setup-node@v3
63
41
  with:
64
- node-version: 18
42
+ node-version: 18.18
65
43
  check-latest: true
66
44
 
67
45
  - name: Update npm
@@ -71,7 +49,7 @@ jobs:
71
49
  run: npm ci --include dev
72
50
 
73
51
  - name: Run tests
74
- run: npm run logs
52
+ run: npx tsx tests/app.ts
75
53
 
76
54
  lint:
77
55
  runs-on: ubuntu-latest
@@ -84,7 +62,7 @@ jobs:
84
62
  - name: Set up Node.js
85
63
  uses: actions/setup-node@v3
86
64
  with:
87
- node-version: 18
65
+ node-version: 18.18
88
66
  check-latest: true
89
67
 
90
68
  - name: Update npm
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Set up Node.js
26
26
  uses: actions/setup-node@v3
27
27
  with:
28
- node-version: 18
28
+ node-version: 18.18
29
29
  check-latest: true
30
30
 
31
31
  - name: Update npm
package/README.md CHANGED
@@ -34,13 +34,6 @@ analysis, and alerting.
34
34
  npm install mynth-logger
35
35
  ```
36
36
 
37
- **For local development**, install `pino-pretty` to enable colorful log
38
- output:
39
-
40
- ``` bash
41
- npm install --save-dev pino-pretty
42
- ```
43
-
44
37
  ### Usage
45
38
 
46
39
  **Import and set up logging** in your entrypoint script:
@@ -59,12 +52,29 @@ formatting the logs appropriately based on the environment:
59
52
  console.log('This is a log message');
60
53
  ```
61
54
 
62
- In **development**, you’ll see colorful logs in the terminal.
55
+ In **development** (when NODE\_ENV isn’t set to `production`), you’ll
56
+ see colorful logs in the terminal.
63
57
 
64
- In **production**, logs will be output in JSON format to stdout.
58
+ In **production** (when NODE\_ENV is set to `production`), logs will be
59
+ output in JSON format to stdout.
65
60
 
66
61
  ## Configuration
67
62
 
68
63
  No additional configuration is required to start using Mynth Logger.
69
- However, you can customize the behavior of the logging by passing
70
- options to `setupLogging()` as needed.
64
+
65
+ ## Discord
66
+
67
+ To send a message to Discord:
68
+
69
+ ``` typescript
70
+ console.info("Sending this message to discord", {
71
+ discord: true,
72
+ color: "2404635",
73
+ title: "This is a test",
74
+ webhookUrl,
75
+ });
76
+ ```
77
+
78
+ A valid Discord webhook URL needs to be passed in as a parameter. This
79
+ will be used to send the message to Discord, but it won’t be outputted
80
+ to the logs.
@@ -0,0 +1,2 @@
1
+ declare const format: (items: unknown[]) => string;
2
+ export { format };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.format = void 0;
4
+ const json_1 = require("@ungap/structured-clone/json");
5
+ const formatItem = (item) => {
6
+ if (typeof item === "undefined") {
7
+ return "undefined";
8
+ }
9
+ // Remove colors from strings
10
+ if (typeof item === "string") {
11
+ // eslint-disable-next-line no-control-regex
12
+ return item.replace(/\x1b\[[0-9;]*m/g, "");
13
+ }
14
+ // Check if this is an Error
15
+ if (item &&
16
+ typeof item === "object" &&
17
+ "message" in item &&
18
+ typeof item.message === "string")
19
+ return item.message;
20
+ // Check if this is a string
21
+ if (typeof item === "string")
22
+ return item;
23
+ const stringified = (() => {
24
+ try {
25
+ return (0, json_1.stringify)(item);
26
+ }
27
+ catch {
28
+ return String(item);
29
+ }
30
+ })();
31
+ return stringified.replace(/^'|'$/g, "");
32
+ };
33
+ const format = (items) => {
34
+ return Array.from(items)
35
+ .map((item) => formatItem(item))
36
+ .join(" ");
37
+ };
38
+ exports.format = format;
@@ -1,20 +1,2 @@
1
- import { Logger, LoggerOptions } from "pino";
2
- type AwaitableLogger = Logger & {
3
- untilFinished: Promise<void>;
4
- };
5
- declare enum KnownTransports {
6
- Discord = "./transports/discord.js",
7
- Stdout = "./transports/datadog-stdout.js"
8
- }
9
- type Target = {
10
- target: KnownTransports;
11
- level: string;
12
- options: object;
13
- };
14
- type Params = {
15
- options?: LoggerOptions;
16
- pretty?: boolean;
17
- targets?: Target[];
18
- };
19
- declare const setupLogging: (args?: Params) => AwaitableLogger;
20
- export { AwaitableLogger, KnownTransports, setupLogging };
1
+ declare const setupLogging: () => import("consola").ConsolaInstance;
2
+ export { setupLogging };
@@ -3,81 +3,16 @@ 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.setupLogging = exports.KnownTransports = void 0;
7
- const pino_1 = __importDefault(require("pino"));
8
- const json_1 = require("@ungap/structured-clone/json");
9
- var KnownTransports;
10
- (function (KnownTransports) {
11
- KnownTransports["Discord"] = "./transports/discord.js";
12
- KnownTransports["Stdout"] = "./transports/datadog-stdout.js";
13
- })(KnownTransports || (exports.KnownTransports = KnownTransports = {}));
14
- const formatItem = (item) => {
15
- if (typeof item === "undefined") {
16
- return "undefined";
17
- }
18
- // Remove colors from strings
19
- if (typeof item === "string") {
20
- // eslint-disable-next-line no-control-regex
21
- return item.replace(/\x1b\[[0-9;]*m/g, "");
22
- }
23
- // Check if this is an Error
24
- if (item &&
25
- typeof item === "object" &&
26
- "message" in item &&
27
- typeof item.message === "string")
28
- return item.message;
29
- // Check if this is a string
30
- if (typeof item === "string")
31
- return item;
32
- const stringified = (() => {
33
- try {
34
- return (0, json_1.stringify)(item);
35
- }
36
- catch {
37
- return String(item);
38
- }
39
- })();
40
- return stringified.replace(/^'|'$/g, "");
41
- };
42
- const format = (items) => {
43
- return Array.from(items)
44
- .map((item) => formatItem(item))
45
- .join(" ");
46
- };
47
- const overrideConsole = (logger) => {
48
- console.log = function (...args) {
49
- logger.info(format(Array.from(args)));
50
- };
51
- console.info = function (...args) {
52
- logger.info(format(Array.from(args)));
53
- };
54
- console.warn = function (...args) {
55
- logger.warn(format(Array.from(args)));
56
- };
57
- console.error = function (...args) {
58
- logger.error(format(Array.from(args)));
59
- };
60
- console.debug = function (...args) {
61
- logger.debug(format(Array.from(args)));
62
- };
63
- };
64
- const setupLogging = (args = {}) => {
65
- const transportTargets = [
66
- {
67
- target: KnownTransports.Stdout,
68
- level: "debug",
69
- options: { pretty: args.pretty != false },
70
- },
71
- ...(args.targets || []),
72
- ];
73
- const transport = pino_1.default.transport({
74
- targets: transportTargets,
75
- });
76
- const logger = (0, pino_1.default)(args.options || { level: "debug" }, transport);
77
- logger.untilFinished = new Promise((resolve) => {
78
- transport.on("ready", resolve);
79
- });
80
- typeof window === "undefined" && overrideConsole(logger);
81
- return logger;
6
+ exports.setupLogging = void 0;
7
+ const consola_1 = require("consola");
8
+ const datadog_js_1 = __importDefault(require("./reporters/datadog.js"));
9
+ const discord_js_1 = __importDefault(require("./reporters/discord.js"));
10
+ const setupLogging = () => {
11
+ const consola = (0, consola_1.createConsola)({ fancy: true, level: 5 });
12
+ if (process.env.NODE_ENV === "production")
13
+ consola.setReporters([datadog_js_1.default]);
14
+ consola.setReporters([discord_js_1.default, ...consola.options.reporters]);
15
+ consola.wrapConsole();
16
+ return consola;
82
17
  };
83
18
  exports.setupLogging = setupLogging;
@@ -0,0 +1,5 @@
1
+ import { LogObject } from "consola";
2
+ declare const Reporter: {
3
+ log: (logObj: LogObject) => void;
4
+ };
5
+ export default Reporter;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const format_js_1 = require("../format.js");
4
+ const levelMap = {
5
+ 0: "error",
6
+ 1: "warn",
7
+ 2: "info",
8
+ 3: "info",
9
+ 4: "debug",
10
+ 5: "debug",
11
+ };
12
+ const Reporter = {
13
+ log: (logObj) => {
14
+ const message = JSON.stringify({
15
+ level: levelMap[logObj.level] || "debug",
16
+ message: (0, format_js_1.format)(logObj.args),
17
+ });
18
+ process.stdout.write(`${message}\n`);
19
+ },
20
+ };
21
+ exports.default = Reporter;
@@ -0,0 +1,5 @@
1
+ import { LogObject } from "consola";
2
+ declare const Reporter: {
3
+ log: (logObj: LogObject) => void;
4
+ };
5
+ export default Reporter;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const format_js_1 = require("../format.js");
30
+ const arktype_1 = require("arktype");
31
+ const axios_1 = __importDefault(require("axios"));
32
+ const axios_retry_1 = __importStar(require("axios-retry"));
33
+ const Discord = (0, arktype_1.type)({
34
+ discord: (0, arktype_1.type)("boolean").narrow((v) => v),
35
+ color: "string",
36
+ title: "string",
37
+ webhookUrl: "string",
38
+ });
39
+ const NullDiscord = { discord: false };
40
+ const Reporter = {
41
+ log: (logObj) => {
42
+ const [discord, args] = getDiscord(logObj.args);
43
+ if (!discord.discord)
44
+ return;
45
+ sendToDiscord((0, format_js_1.format)(args), discord);
46
+ // Filter Discord data before the other reporters
47
+ // process the message
48
+ logObj.args = args;
49
+ },
50
+ };
51
+ const getDiscord = (args) => {
52
+ for (let i = 0; i < args.length; i++) {
53
+ const discord = Discord(args[i]);
54
+ if (discord instanceof arktype_1.type.errors)
55
+ continue;
56
+ return [discord, args.filter((_, j) => j !== i)];
57
+ }
58
+ return [NullDiscord, args];
59
+ };
60
+ const sendToDiscord = async (description, options) => {
61
+ const axiosInstance = axios_1.default.create();
62
+ (0, axios_retry_1.default)(axiosInstance, {
63
+ retries: 3,
64
+ retryDelay: axios_retry_1.exponentialDelay,
65
+ });
66
+ try {
67
+ await axiosInstance.post(options.webhookUrl, {
68
+ embeds: [
69
+ {
70
+ title: options.title,
71
+ description,
72
+ color: options.color,
73
+ },
74
+ ],
75
+ });
76
+ }
77
+ catch (error) {
78
+ console.error("Unable to send message to Discord", error);
79
+ }
80
+ };
81
+ exports.default = Reporter;
package/dist/tests/app.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const index_1 = require("../src/index");
3
+ const mynth_logger_1 = require("mynth-logger");
4
4
  const run = async () => {
5
- (0, index_1.setupLogging)();
5
+ (0, mynth_logger_1.setupLogging)();
6
6
  console.log("Hello, this is a log");
7
7
  console.info("Hello, this is an info log");
8
8
  console.debug("Hello, this is a debug log");
@@ -57,5 +57,11 @@ const run = async () => {
57
57
  }
58
58
  console.log("This is concatenating an object", {});
59
59
  console.log("This is an object with bigint", { name: "bigint", value: 100n });
60
+ console.info("Sending this message to discord", {
61
+ discord: true,
62
+ color: "2404635",
63
+ title: "This is a test",
64
+ webhookUrl: process.env["DISCORD_TEST_WEB_HOOK"],
65
+ });
60
66
  };
61
67
  run();
@@ -4,17 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const ava_1 = __importDefault(require("ava"));
7
- const index_1 = require("../src/index");
8
- let logger;
9
- const sleep = (ms) => {
10
- return new Promise((resolve) => setTimeout(resolve, ms));
11
- };
7
+ const mynth_logger_1 = require("mynth-logger");
12
8
  ava_1.default.before(() => {
13
- logger = (0, index_1.setupLogging)({ pretty: "TEST_PRETTY" in process.env });
14
- });
15
- ava_1.default.afterEach(async () => {
16
- await sleep(100);
17
- logger && (await logger.untilFinished);
9
+ (0, mynth_logger_1.setupLogging)();
18
10
  });
19
11
  ava_1.default.serial("logs display to terminal", (t) => {
20
12
  console.debug("Hello ava");
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "mynth-logger",
3
- "version": "1.2.0",
3
+ "version": "2.0.2",
4
4
  "description": "Package to format logs for mynth microservices.",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
7
7
  "scripts": {
8
- "build": "npx tsc",
9
- "test": "npx tsc && npx ava",
10
- "test:pretty": "npx tsc && TEST_PRETTY=true npx ava",
8
+ "build": "rm -rf dist && npx tsc",
9
+ "test": "npx ava",
11
10
  "logs": "npx tsc && node dist/src/run.js",
12
11
  "prettier": "npx prettier -w '**/*.{js,jsx,ts,tsx,json,yml.j2,yml,yaml,.*}'",
13
12
  "lint": "concurrently \"npx prettier --check '**/*.{js,jsx,ts,tsx,json,yml.j2,yml,yaml,.*}'\" \"npx eslint . --max-warnings=0\""
@@ -23,27 +22,35 @@
23
22
  },
24
23
  "dependencies": {
25
24
  "@ungap/structured-clone": "^1.2.0",
26
- "axios": "^1.6.8",
27
- "pino": "^8.19.0"
25
+ "arktype": "^2.0.0-dev.16",
26
+ "axios": "^1.7.2",
27
+ "axios-retry": "^4.3.0",
28
+ "consola": "^3.2.3"
28
29
  },
29
30
  "devDependencies": {
30
- "@types/node": "^20.5.0",
31
+ "@types/node": "^20.12.12",
31
32
  "@types/ungap__structured-clone": "^1.2.0",
32
- "@typescript-eslint/eslint-plugin": "^6.8.0",
33
- "ava": "^5.3.1",
34
- "concurrently": "^8.2.0",
33
+ "@typescript-eslint/eslint-plugin": "^7.11.0",
34
+ "ava": "^6.1.3",
35
+ "concurrently": "^8.2.2",
35
36
  "eslint": "^8.47.0",
36
37
  "eslint-plugin-ava": "^14.0.0",
37
- "eslint-plugin-file-extension-in-import-ts": "^1.0.2",
38
- "eslint-plugin-import": "^2.28.1",
39
- "pino-pretty": "^10.2.0",
40
- "prettier": "^3.0.2",
41
- "tsx": "^3.12.7",
42
- "typescript": "^5.1.6"
38
+ "eslint-plugin-file-extension-in-import-ts": "^2.1.0",
39
+ "eslint-plugin-import": "^2.29.1",
40
+ "prettier": "^3.2.5",
41
+ "tsx": "^4.11.0",
42
+ "typescript": "^5.4.5"
43
43
  },
44
44
  "ava": {
45
45
  "files": [
46
- "dist/tests/**/*.test.js"
46
+ "**/*.test.ts"
47
+ ],
48
+ "extensions": {
49
+ "ts": "module"
50
+ },
51
+ "nodeArguments": [
52
+ "--loader=tsx",
53
+ "--no-warnings"
47
54
  ]
48
55
  }
49
56
  }
package/src/format.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { stringify } from "@ungap/structured-clone/json";
2
+
3
+ const formatItem = (item: unknown): string => {
4
+ if (typeof item === "undefined") {
5
+ return "undefined";
6
+ }
7
+
8
+ // Remove colors from strings
9
+ if (typeof item === "string") {
10
+ // eslint-disable-next-line no-control-regex
11
+ return item.replace(/\x1b\[[0-9;]*m/g, "");
12
+ }
13
+
14
+ // Check if this is an Error
15
+ if (
16
+ item &&
17
+ typeof item === "object" &&
18
+ "message" in item &&
19
+ typeof item.message === "string"
20
+ )
21
+ return item.message;
22
+
23
+ // Check if this is a string
24
+ if (typeof item === "string") return item;
25
+
26
+ const stringified = (() => {
27
+ try {
28
+ return stringify(item);
29
+ } catch {
30
+ return String(item);
31
+ }
32
+ })();
33
+
34
+ return stringified.replace(/^'|'$/g, "");
35
+ };
36
+
37
+ const format = (items: unknown[]): string => {
38
+ return Array.from(items)
39
+ .map((item) => formatItem(item))
40
+ .join(" ");
41
+ };
42
+
43
+ export { format };
package/src/logging.ts CHANGED
@@ -1,113 +1,16 @@
1
- import pino, { Logger, LoggerOptions } from "pino";
2
- import { stringify } from "@ungap/structured-clone/json";
1
+ import { createConsola } from "consola";
2
+ import DatadogReporter from "./reporters/datadog.js";
3
+ import DiscordReporter from "./reporters/discord.js";
3
4
 
4
- type AwaitableLogger = Logger & {
5
- untilFinished: Promise<void>;
6
- };
7
-
8
- enum KnownTransports {
9
- Discord = "./transports/discord.js",
10
- Stdout = "./transports/datadog-stdout.js",
11
- }
12
-
13
- type Target = {
14
- target: KnownTransports;
15
- level: string;
16
- options: object;
17
- };
18
-
19
- type Params = {
20
- options?: LoggerOptions;
21
- pretty?: boolean;
22
- targets?: Target[];
23
- };
24
-
25
- const formatItem = (item: unknown): string => {
26
- if (typeof item === "undefined") {
27
- return "undefined";
28
- }
29
-
30
- // Remove colors from strings
31
- if (typeof item === "string") {
32
- // eslint-disable-next-line no-control-regex
33
- return item.replace(/\x1b\[[0-9;]*m/g, "");
34
- }
35
-
36
- // Check if this is an Error
37
- if (
38
- item &&
39
- typeof item === "object" &&
40
- "message" in item &&
41
- typeof item.message === "string"
42
- )
43
- return item.message;
44
-
45
- // Check if this is a string
46
- if (typeof item === "string") return item;
47
-
48
- const stringified = (() => {
49
- try {
50
- return stringify(item);
51
- } catch {
52
- return String(item);
53
- }
54
- })();
55
-
56
- return stringified.replace(/^'|'$/g, "");
57
- };
58
-
59
- const format = (items: unknown[]): string => {
60
- return Array.from(items)
61
- .map((item) => formatItem(item))
62
- .join(" ");
63
- };
64
-
65
- const overrideConsole = (logger: Logger) => {
66
- console.log = function (...args) {
67
- logger.info(format(Array.from(args)));
68
- };
69
-
70
- console.info = function (...args) {
71
- logger.info(format(Array.from(args)));
72
- };
73
-
74
- console.warn = function (...args) {
75
- logger.warn(format(Array.from(args)));
76
- };
77
-
78
- console.error = function (...args) {
79
- logger.error(format(Array.from(args)));
80
- };
81
-
82
- console.debug = function (...args) {
83
- logger.debug(format(Array.from(args)));
84
- };
85
- };
86
-
87
- const setupLogging = (args: Params = {}): AwaitableLogger => {
88
- const transportTargets = [
89
- {
90
- target: KnownTransports.Stdout,
91
- level: "debug",
92
- options: { pretty: args.pretty != false },
93
- },
94
- ...(args.targets || []),
95
- ];
96
-
97
- const transport = pino.transport({
98
- targets: transportTargets,
99
- });
5
+ const setupLogging = () => {
6
+ const consola = createConsola({ fancy: true, level: 5 });
100
7
 
101
- const logger = pino(
102
- args.options || { level: "debug" },
103
- transport
104
- ) as AwaitableLogger;
105
- logger.untilFinished = new Promise<void>((resolve) => {
106
- transport.on("ready", resolve);
107
- });
8
+ if (process.env.NODE_ENV === "production")
9
+ consola.setReporters([DatadogReporter]);
10
+ consola.setReporters([DiscordReporter, ...consola.options.reporters]);
108
11
 
109
- typeof window === "undefined" && overrideConsole(logger);
110
- return logger;
12
+ consola.wrapConsole();
13
+ return consola;
111
14
  };
112
15
 
113
- export { AwaitableLogger, KnownTransports, setupLogging };
16
+ export { setupLogging };
@@ -0,0 +1,23 @@
1
+ import { LogObject, LogLevel } from "consola";
2
+ import { format } from "../format.js";
3
+
4
+ const levelMap: Record<LogLevel, string> = {
5
+ 0: "error",
6
+ 1: "warn",
7
+ 2: "info",
8
+ 3: "info",
9
+ 4: "debug",
10
+ 5: "debug",
11
+ };
12
+
13
+ const Reporter = {
14
+ log: (logObj: LogObject) => {
15
+ const message = JSON.stringify({
16
+ level: levelMap[logObj.level] || "debug",
17
+ message: format(logObj.args),
18
+ });
19
+ process.stdout.write(`${message}\n`);
20
+ },
21
+ };
22
+
23
+ export default Reporter;
@@ -0,0 +1,66 @@
1
+ import { LogObject } from "consola";
2
+ import { format } from "../format.js";
3
+ import { type } from "arktype";
4
+ import axios from "axios";
5
+ import axiosRetry, { exponentialDelay } from "axios-retry";
6
+
7
+ const Discord = type({
8
+ discord: type("boolean").narrow((v) => v),
9
+ color: "string",
10
+ title: "string",
11
+ webhookUrl: "string",
12
+ });
13
+
14
+ type Discord = typeof Discord.infer;
15
+
16
+ const NullDiscord = { discord: false } as const;
17
+
18
+ type NullDiscord = typeof NullDiscord;
19
+
20
+ const Reporter = {
21
+ log: (logObj: LogObject) => {
22
+ const [discord, args] = getDiscord(logObj.args);
23
+ if (!discord.discord) return;
24
+
25
+ sendToDiscord(format(args), discord);
26
+
27
+ // Filter Discord data before the other reporters
28
+ // process the message
29
+ logObj.args = args;
30
+ },
31
+ };
32
+
33
+ const getDiscord = (args: unknown[]): [Discord | NullDiscord, unknown[]] => {
34
+ for (let i = 0; i < args.length; i++) {
35
+ const discord = Discord(args[i]);
36
+ if (discord instanceof type.errors) continue;
37
+
38
+ return [discord, args.filter((_, j) => j !== i)];
39
+ }
40
+
41
+ return [NullDiscord, args];
42
+ };
43
+
44
+ const sendToDiscord = async (description: string, options: Discord) => {
45
+ const axiosInstance = axios.create();
46
+ axiosRetry(axiosInstance, {
47
+ retries: 3,
48
+ retryDelay: exponentialDelay,
49
+ });
50
+
51
+ try {
52
+ await axiosInstance.post(options.webhookUrl, {
53
+ embeds: [
54
+ {
55
+ title: options.title,
56
+ description,
57
+ color: options.color,
58
+ },
59
+ ],
60
+ });
61
+ } catch (error) {
62
+ console.error("Unable to send message to Discord", error);
63
+ }
64
+ };
65
+
66
+ export default Reporter;
package/tests/app.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { setupLogging } from "../src/index";
1
+ import { setupLogging } from "mynth-logger";
2
2
 
3
3
  const run = async () => {
4
4
  setupLogging();
@@ -59,6 +59,13 @@ const run = async () => {
59
59
  console.log("This is concatenating an object", {});
60
60
 
61
61
  console.log("This is an object with bigint", { name: "bigint", value: 100n });
62
+
63
+ console.info("Sending this message to discord", {
64
+ discord: true,
65
+ color: "2404635",
66
+ title: "This is a test",
67
+ webhookUrl: process.env["DISCORD_TEST_WEB_HOOK"],
68
+ });
62
69
  };
63
70
 
64
71
  run();
@@ -1,19 +1,8 @@
1
1
  import test from "ava";
2
- import { AwaitableLogger, setupLogging } from "../src/index";
3
-
4
- let logger: AwaitableLogger | undefined;
5
-
6
- const sleep = (ms: number) => {
7
- return new Promise((resolve) => setTimeout(resolve, ms));
8
- };
2
+ import { setupLogging } from "mynth-logger";
9
3
 
10
4
  test.before(() => {
11
- logger = setupLogging({ pretty: "TEST_PRETTY" in process.env });
12
- });
13
-
14
- test.afterEach(async () => {
15
- await sleep(100);
16
- logger && (await logger.untilFinished);
5
+ setupLogging();
17
6
  });
18
7
 
19
8
  test.serial("logs display to terminal", (t) => {
package/tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "baseUrl": "./",
3
+ "baseUrl": "./src",
4
4
  "target": "es2022",
5
5
  "module": "CommonJS",
6
6
  "moduleResolution": "Node",
@@ -10,6 +10,11 @@
10
10
  "forceConsistentCasingInFileNames": true,
11
11
  "outDir": "./dist",
12
12
  "declaration": true,
13
- "declarationDir": "dist"
14
- }
13
+ "declarationDir": "dist",
14
+ "paths": {
15
+ "mynth-logger": ["index"]
16
+ }
17
+ },
18
+ "include": ["src/**/*", "tests/**/*"],
19
+ "exclude": ["node_modules"]
15
20
  }
package/dist/src/run.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/src/run.js DELETED
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const index_js_1 = require("./index.js");
4
- const run = (pretty = true) => {
5
- (0, index_js_1.setupLogging)({ pretty });
6
- console.debug("hello world");
7
- };
8
- run();
9
- // Use `run(false)` to disable pino-pretty
10
- // pino-pretty will also be disabled if it's not installed
@@ -1,3 +0,0 @@
1
- import { DestinationStream } from "pino";
2
- declare const transport: (opts: unknown) => Promise<DestinationStream>;
3
- export default transport;
@@ -1,55 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- const pino_abstract_transport_1 = __importDefault(require("pino-abstract-transport"));
30
- const loadPretty = async () => {
31
- try {
32
- return await Promise.resolve().then(() => __importStar(require("pino-pretty")));
33
- }
34
- catch {
35
- return null;
36
- }
37
- };
38
- const isPrettyDisabled = (opts) => {
39
- return opts && typeof opts === "object" && "pretty" in opts && !opts.pretty;
40
- };
41
- const transport = async (opts) => {
42
- if (!isPrettyDisabled(opts)) {
43
- const pretty = await loadPretty();
44
- if (pretty)
45
- return pretty.default({ colorize: true, ignore: "pid,time,hostname" });
46
- }
47
- return (0, pino_abstract_transport_1.default)(async (source) => {
48
- for await (const log of source) {
49
- const level = "level" in log ? String(log.level) : "error";
50
- const msg = "msg" in log ? String(log.msg) : String(log);
51
- process.stdout.write(`${JSON.stringify({ level, msg })}\n`);
52
- }
53
- });
54
- };
55
- exports.default = transport;
@@ -1,3 +0,0 @@
1
- import { DestinationStream } from "pino";
2
- declare const transport: (opts: unknown) => Promise<DestinationStream>;
3
- export default transport;
@@ -1,45 +0,0 @@
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
- const pino_abstract_transport_1 = __importDefault(require("pino-abstract-transport"));
7
- const axios_1 = __importDefault(require("axios"));
8
- const invariant = (condition, message) => {
9
- if (condition)
10
- return;
11
- throw new Error(message);
12
- };
13
- const cast = (opts) => {
14
- invariant(opts && typeof opts === "object", "Discord transport options must be specified");
15
- invariant("color" in opts && typeof opts.color === "string", "Color must be provided");
16
- invariant("title" in opts && typeof opts.title === "string", "Title must be provided");
17
- invariant("webhookUrl" in opts && typeof opts.webhookUrl === "string", "Webhook URL must be provided");
18
- return {
19
- color: opts.color,
20
- title: opts.title,
21
- webhookUrl: opts.webhookUrl,
22
- filter: "filter" in opts ? String(opts.filter) : "",
23
- };
24
- };
25
- const transport = async (opts) => {
26
- const options = cast(opts);
27
- return (0, pino_abstract_transport_1.default)(async (source) => {
28
- for await (const log of source) {
29
- const description = "msg" in log ? String(log.msg) : String(log);
30
- if (!options.filter ||
31
- (options.filter in log && log[options.filter])) {
32
- await axios_1.default.post(options.webhookUrl, {
33
- embeds: [
34
- {
35
- title: options.title,
36
- description,
37
- color: options.color,
38
- },
39
- ],
40
- });
41
- }
42
- }
43
- });
44
- };
45
- exports.default = transport;
package/src/run.ts DELETED
@@ -1,10 +0,0 @@
1
- import { setupLogging } from "./index.js";
2
-
3
- const run = (pretty: boolean = true) => {
4
- setupLogging({ pretty });
5
- console.debug("hello world");
6
- };
7
-
8
- run();
9
- // Use `run(false)` to disable pino-pretty
10
- // pino-pretty will also be disabled if it's not installed
@@ -1,33 +0,0 @@
1
- import build from "pino-abstract-transport";
2
- import { DestinationStream } from "pino";
3
-
4
- const loadPretty = async () => {
5
- try {
6
- return await import("pino-pretty");
7
- } catch {
8
- return null;
9
- }
10
- };
11
-
12
- const isPrettyDisabled = (opts: unknown) => {
13
- return opts && typeof opts === "object" && "pretty" in opts && !opts.pretty;
14
- };
15
-
16
- const transport = async (opts: unknown): Promise<DestinationStream> => {
17
- if (!isPrettyDisabled(opts)) {
18
- const pretty = await loadPretty();
19
- if (pretty)
20
- return pretty.default({ colorize: true, ignore: "pid,time,hostname" });
21
- }
22
-
23
- return build(async (source: AsyncIterable<object>) => {
24
- for await (const log of source) {
25
- const level = "level" in log ? String(log.level) : "error";
26
- const msg = "msg" in log ? String(log.msg) : String(log);
27
-
28
- process.stdout.write(`${JSON.stringify({ level, msg })}\n`);
29
- }
30
- });
31
- };
32
-
33
- export default transport;
@@ -1,65 +0,0 @@
1
- import build from "pino-abstract-transport";
2
- import axios from "axios";
3
- import { DestinationStream } from "pino";
4
-
5
- const invariant: (
6
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
- condition: any,
8
- message: string
9
- ) => asserts condition = (condition, message) => {
10
- if (condition) return;
11
- throw new Error(message);
12
- };
13
-
14
- const cast = (opts: unknown) => {
15
- invariant(
16
- opts && typeof opts === "object",
17
- "Discord transport options must be specified"
18
- );
19
- invariant(
20
- "color" in opts && typeof opts.color === "string",
21
- "Color must be provided"
22
- );
23
- invariant(
24
- "title" in opts && typeof opts.title === "string",
25
- "Title must be provided"
26
- );
27
- invariant(
28
- "webhookUrl" in opts && typeof opts.webhookUrl === "string",
29
- "Webhook URL must be provided"
30
- );
31
-
32
- return {
33
- color: opts.color,
34
- title: opts.title,
35
- webhookUrl: opts.webhookUrl,
36
- filter: "filter" in opts ? String(opts.filter) : "",
37
- };
38
- };
39
-
40
- const transport = async (opts: unknown): Promise<DestinationStream> => {
41
- const options = cast(opts);
42
-
43
- return build(async (source: AsyncIterable<object>) => {
44
- for await (const log of source) {
45
- const description = "msg" in log ? String(log.msg) : String(log);
46
-
47
- if (
48
- !options.filter ||
49
- (options.filter in log && log[options.filter as keyof typeof log])
50
- ) {
51
- await axios.post(options.webhookUrl, {
52
- embeds: [
53
- {
54
- title: options.title,
55
- description,
56
- color: options.color,
57
- },
58
- ],
59
- });
60
- }
61
- }
62
- });
63
- };
64
-
65
- export default transport;