add webhook support, better colour resolving

This commit is contained in:
Erik 2023-04-12 18:45:44 +03:00
parent 2016bcede0
commit 5d7c9bb198
Signed by: Navy.gif
GPG Key ID: 2532FBBB61C65A68
7 changed files with 128 additions and 39 deletions

View File

@ -17,7 +17,6 @@
"array-callback-return": "warn",
"array-bracket-newline": ["warn", "consistent"],
"array-bracket-spacing": ["warn", "always", { "objectsInArrays": false, "arraysInArrays": false }],
// "arrow-parens": "warn",
"arrow-spacing": "warn",
"block-scoped-var": "warn",
"block-spacing": ["warn", "always"],
@ -76,10 +75,6 @@
],
"lines-around-comment": "warn",
"lines-around-directive": "warn",
// "lines-between-class-members": [
// "warn",
// "always"
// ],
"max-classes-per-file": "warn",
"max-nested-callbacks": "warn",
"new-parens": "warn",
@ -108,7 +103,6 @@
"no-invalid-this": "warn",
"no-iterator": "warn",
"no-label-var": "warn",
// "no-labels": "warn",
"no-lone-blocks": "warn",
"no-lonely-if": "warn",
"no-loop-func": "warn",

View File

@ -2,12 +2,9 @@
Simple logger I wrote to have a unified system for logging throughout my projects.
## TODO
- Discord webhook, need to write a separate package for that, don't want to add the entirety of d.js as a dep just for a webhook
## Features
Split into Master and Client for logging between processes, where master resides on the master process and the clients on the spawned processes.
Should be fairly trivial to modify it to work across nodes with websockets.
Should be fairly trivial to modify it to work across nodes with websockets or some other IPC protocol.
**Note**
When logging from a child process, the master logger expects the child process to be be in a wrapper containing at the very least an ID property to work properly (used for identifying which child the message came from).
@ -28,6 +25,17 @@ The child processes are expected to be attached with the attach() method found i
customColors: {}, // Supports any colours chalk.js supports, e.g. "warn": "green" will turn warning outputs green
fileRotationFreq: 1, // How frequently to roate files in days - will not work with anything less than 1 day currently
directory: './logs', // Directory in which to write log files
logLevelMapping: {
debug: 0,
info: 1,
status: 2,
warn: 3,
error: 4
}
broadcastLevel: 4, // Level at which to broadcast to webhook if supplied
webhook: {
url: string
}
}
```

View File

@ -13,6 +13,7 @@
"eslint": "^8.26.0"
},
"dependencies": {
"@navy.gif/discord-webhook": "^1.0.0",
"chalk": "^4.1.2",
"moment": "^2.29.4"
},

View File

@ -1,3 +1,11 @@
const ColourCodes = {
error: 0xe88388,
warn: 0xf9d472,
info: 0x76a9d8,
debug: 0xd8abd7,
status: 0x72d4d7
};
module.exports = {
Types: [
'error',
@ -16,13 +24,7 @@ module.exports = {
debug: 'magenta',
status: 'cyanBright'
},
ColourCodes: {
error: 0xe88388,
warn: 0xf9d472,
info: 0x76a9d8,
debug: 0xd8abd7,
status: 0x72d4d7
},
ColourCodes,
MasterOptions: {
fileRotationFreq: 1,
directory: './logs',
@ -31,6 +33,7 @@ module.exports = {
customTypeMapping: {},
customColors: {},
guard: '_logger',
broadcastLevel: 4,
logLevel: 1,
logLevelMapping: {
debug: 0,
@ -38,7 +41,10 @@ module.exports = {
status: 2,
warn: 3,
error: 4
}
},
webhook: {
url: null
},
},
ClientOptions: {
guard: '_logger',

View File

@ -6,28 +6,31 @@ const path = require('node:path');
const fs = require('node:fs');
const { inspect } = require('node:util');
// const DiscordWebhook = require('@navy.gif/discord-webhook');
const Defaults = require('./Defaults');
const DAY = 1000 * 60 * 60 * 24;
// TODO:
// - Discord webhook, need to write a separate package for that, don't want to add the entirety of d.js as a dep just for a webhook
class MasterLogger {
#_guard = null;
#_logLevel = null;
#_logLevelMapping = null;
#_broadcastLevel = null;
#webhook = null;
constructor (config = Defaults.MasterOptions) {
const {
directory, customTypes, customStreams, customTypeMapping,
customColors, guard, fileRotationFreq, logLevel, logLevelMapping
customColors, guard, fileRotationFreq, logLevel, logLevelMapping,
webhook, broadcastLevel
} = { ...Defaults.MasterOptions, ...config };
this.directory = path.resolve(directory);
if (!fs.existsSync(this.directory))
fs.mkdirSync(this.directory, { recursive: true });
this.#_broadcastLevel = broadcastLevel;
this.#_logLevel = logLevel;
this.#_logLevelMapping = { ...Defaults.MasterOptions.logLevelMapping, ...logLevelMapping };
this.#_guard = guard;
@ -40,6 +43,20 @@ class MasterLogger {
}
this.colours = { ...Defaults.Colours, ...customColors };
this.colourFuncs = Object.entries(this.colours).reduce((prev, [ type, colour ]) => {
if (typeof colour === 'number')
colour = `#${colour.toString(16)}`;
else if (typeof colour !== 'string')
throw new Error('Expecting colours to be either an integer, a hex string or one of the chalk compatible colour names');
if ((/#[A-Fa-f0-9]+/u).test(colour))
prev[type] = { func: chalk.hex(colour), int: parseInt(colour.replace('#', ''), 16) };
else
prev[type] = { func: chalk[colour], int: Defaults.ColourCodes[type] || Defaults.ColourCodes.info };
return prev;
}, {});
this.streamTypes = [ ...customStreams, 'error', 'default' ];
this.streamTypeMapping = { ...Defaults.TypeStream, ...customTypeMapping };
this.writeStreams = this.streamTypes.reduce((acc, type) => {
@ -52,6 +69,12 @@ class MasterLogger {
this.startDay = Math.floor(Date.now() / this.rotationFreq) * this.rotationFreq;
this.rotateTO = setTimeout(this.rotateLogFiles.bind(this), this.startDay + this.rotationFreq - Date.now());
if (webhook && webhook.url)
import('@navy.gif/discord-webhook').then(({ default: DiscordWebhook }) => {
this.#webhook = new DiscordWebhook({ url: webhook.url });
this.#webhook.fetch();
});
}
get logLevel () {
@ -83,9 +106,9 @@ class MasterLogger {
write (type = 'info', text, { subheader = '', shard = null, broadcast = false } = {}) {
type = type.toLowerCase();
let colour = this.colours[type];
let colour = this.colourFuncs[type];
if (!colour)
colour = this.colours.info;
colour = this.colourCodes.info;
if (typeof text !== 'string')
text = inspect(text);
@ -94,13 +117,17 @@ class MasterLogger {
const maxChars = Math.max(...this.types.map(t => t.length));
const spacer = ' '.repeat(maxChars - type.length);
// if (type === 'debug' && this._debug || type !== 'debug')
if (this.#_logLevelMapping[type] >= this.#_logLevel)
console.log(`${chalk[colour](type)}${spacer} ${chalk[colour](header)}: ${chalk.bold(subheader)}${text}`); // eslint-disable-line no-console
console.log(`${colour.func(type)}${spacer} ${colour.func(header)}: ${chalk.bold(subheader)}${text}`); // eslint-disable-line no-console
if (broadcast) {
// Send to webhook - TODO
}
if (broadcast || this.#_broadcastLevel <= this.#_logLevelMapping[type] && this.#webhook)
this.#webhook.send({
embeds: [{
title: `[__${type.toUpperCase()}__] ${this._shard(shard)}`,
description: `**${subheader}**\n${text}`,
color: colour.int
}]
});
const streamType = this.streamTypeMapping[type] || 'default';
if (this.writeStreams[streamType])

View File

@ -15,9 +15,16 @@ const main = async () => {
customTypes: [ 'access' ],
customStreams: [ 'access' ],
customTypeMapping: { access: 'access', warn: 'error' },
customColors: { access: 'green' },
customColors: {
access: 'green',
// error: '#FF0000'
},
fileRotationFreq: 0.0001,
logLevelMapping: { access: 2 }
logLevelMapping: { access: 2 },
broadcastLevel: 3,
webhook: {
url: 'https://discord.com/api/webhooks/1093874668886294548/uDMRD6g1lmq_2EZynsbKytzWoMM-0N4te0m61r_cv1BsSnDKDxG3fvI6sxSoG5t5b_xn'
}
});
const child = ChildProcess.fork('./test/otherProcess.js');
logger.attach(child);

View File

@ -36,6 +36,13 @@
resolved "https://registry.corgi.wtf/@humanwhocodes%2fobject-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@navy.gif/discord-webhook@^1.0.0":
version "1.1.0"
resolved "https://registry.corgi.wtf/@navy.gif/discord-webhook/-/discord-webhook-1.1.0.tgz#b3f980bc42663ff1617c3756ce6c9184bc2a48f3"
integrity sha512-+aJOfZD5kRoxEWU+sDAjX4qSRZ7vTPDBuFd6MsoaWC7ZSjSFuDqryyumBCs+w0EKU94xdxoH2qQ8ebFZ//ZugQ==
dependencies:
node-fetch "^3.3.1"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.corgi.wtf/@nodelib%2ffs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@ -146,6 +153,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.corgi.wtf/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
debug@^4.1.1, debug@^4.3.2:
version "4.3.4"
resolved "https://registry.corgi.wtf/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -295,6 +307,14 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.corgi.wtf/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.corgi.wtf/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@ -323,6 +343,13 @@ flatted@^3.1.0:
resolved "https://registry.corgi.wtf/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.corgi.wtf/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.corgi.wtf/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -481,6 +508,20 @@ natural-compare@^1.4.0:
resolved "https://registry.corgi.wtf/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.corgi.wtf/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^3.3.1:
version "3.3.1"
resolved "https://registry.corgi.wtf/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e"
integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.corgi.wtf/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -635,6 +676,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
web-streams-polyfill@^3.0.3:
version "3.2.1"
resolved "https://registry.corgi.wtf/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
which@^2.0.1:
version "2.0.2"
resolved "https://registry.corgi.wtf/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"