Compare commits

...

5 Commits

Author SHA1 Message Date
22d7d982ac
v2.3.4 2023-06-15 13:13:46 +03:00
45b1f8c2df
v2.3.3 2023-05-03 23:16:02 +03:00
33b1087671
v2.3.2 2023-04-16 16:55:02 +03:00
477315dc72
v2.3.1 2023-04-16 16:53:50 +03:00
2dd9ebb379
export types 2023-04-16 16:53:37 +03:00
14 changed files with 342 additions and 189 deletions

View File

@ -1,31 +1,56 @@
{ {
"plugins": [
"@typescript-eslint"
],
"env": { "env": {
"es6": true, "es6": true,
"node": true "node": true
}, },
"extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ], "extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"globals": { "globals": {
"Atomics": "readonly", "Atomics": "readonly",
"SharedArrayBuffer": "readonly" "SharedArrayBuffer": "readonly"
}, },
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"plugins": [ "@typescript-eslint" ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 2022, "ecmaVersion": 2022,
"sourceType": "module" "sourceType": "module"
}, },
"rules": { "rules": {
"@typescript-eslint/no-unused-vars": "off",
"accessor-pairs": "warn", "accessor-pairs": "warn",
"array-callback-return": "warn", "array-callback-return": "warn",
"array-bracket-newline": [ "warn", "consistent" ], "array-bracket-newline": [
"array-bracket-spacing": [ "warn", "always", { "objectsInArrays": false, "arraysInArrays": false }], "warn",
"consistent"
],
"array-bracket-spacing": [
"warn",
"always",
{
"objectsInArrays": false,
"arraysInArrays": false
}
],
"arrow-spacing": "warn", "arrow-spacing": "warn",
"block-scoped-var": "warn", "block-scoped-var": "warn",
"block-spacing": [ "warn", "always" ], "block-spacing": [
"brace-style": [ "warn", "1tbs" ], "warn",
"always"
],
"brace-style": [
"warn",
"allman"
],
"callback-return": "warn", "callback-return": "warn",
"camelcase": "warn", "camelcase": "warn",
"comma-dangle": [ "warn", "only-multiline" ], "comma-dangle": [
"warn",
"only-multiline"
],
"comma-spacing": [ "comma-spacing": [
"warn", "warn",
{ {
@ -68,9 +93,25 @@
"implicit-arrow-linebreak": "warn", "implicit-arrow-linebreak": "warn",
"indent": "warn", "indent": "warn",
"init-declarations": "warn", "init-declarations": "warn",
"jsx-quotes": [ "warn", "prefer-single" ], "quotes": ["error" , "single"],
"key-spacing": [ "warn", { "beforeColon": false, "afterColon": true }], "jsx-quotes": [
"keyword-spacing": [ "warn", { "after": true, "before": true }], "warn",
"prefer-single"
],
"key-spacing": [
"warn",
{
"beforeColon": false,
"afterColon": true
}
],
"keyword-spacing": [
"warn",
{
"after": true,
"before": true
}
],
"linebreak-style": [ "linebreak-style": [
"error", "error",
"unix" "unix"
@ -79,6 +120,24 @@
"lines-around-directive": "warn", "lines-around-directive": "warn",
"max-classes-per-file": "warn", "max-classes-per-file": "warn",
"max-nested-callbacks": "warn", "max-nested-callbacks": "warn",
"max-len": [
"warn",
{
"code": 140,
"ignoreComments": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true,
"ignoreRegExpLiterals": true
}
],
"max-lines-per-function": [
"warn",
100
],
"max-depth": [
"warn",
3
],
"new-parens": "warn", "new-parens": "warn",
"no-alert": "warn", "no-alert": "warn",
"no-array-constructor": "warn", "no-array-constructor": "warn",
@ -96,7 +155,6 @@
"no-extend-native": "warn", "no-extend-native": "warn",
"no-extra-bind": "warn", "no-extra-bind": "warn",
"no-extra-label": "warn", "no-extra-label": "warn",
"no-extra-parens": "warn",
"no-floating-decimal": "warn", "no-floating-decimal": "warn",
"no-implicit-coercion": "warn", "no-implicit-coercion": "warn",
"no-implicit-globals": "warn", "no-implicit-globals": "warn",
@ -147,7 +205,7 @@
"no-unmodified-loop-condition": "warn", "no-unmodified-loop-condition": "warn",
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
"no-unused-expressions": "warn", "no-unused-expressions": "warn",
"no-use-before-define": "error", "@typescript-eslint/no-use-before-define": "error",
"no-useless-call": "warn", "no-useless-call": "warn",
"no-useless-computed-key": "warn", "no-useless-computed-key": "warn",
"no-useless-concat": "warn", "no-useless-concat": "warn",
@ -155,20 +213,39 @@
"no-useless-rename": "warn", "no-useless-rename": "warn",
"no-useless-return": "warn", "no-useless-return": "warn",
"no-var": "warn", "no-var": "warn",
"no-void": "warn", // "no-void": "warn",
"no-whitespace-before-property": "error", "no-whitespace-before-property": "error",
"nonblock-statement-body-position": [ "warn", "below" ], "nonblock-statement-body-position": [
"warn",
"below"
],
"object-curly-spacing": [ "object-curly-spacing": [
"warn", "warn",
"always" "always"
], ],
"object-property-newline": [ "warn", { "allowAllPropertiesOnSameLine": true }], "object-property-newline": [
"warn",
{
"allowAllPropertiesOnSameLine": true
}
],
"object-shorthand": "warn", "object-shorthand": "warn",
"one-var-declaration-per-line": "warn", "one-var-declaration-per-line": "warn",
"operator-assignment": "warn", "operator-assignment": "warn",
"operator-linebreak": [ "warn", "before" ], "operator-linebreak": [
"warn",
"before"
],
"padding-line-between-statements": "warn", "padding-line-between-statements": "warn",
"padded-blocks": [ "warn", { "switches": "never" }, { "allowSingleLineBlocks": true }], "padded-blocks": [
"warn",
{
"switches": "never"
},
{
"allowSingleLineBlocks": true
}
],
"prefer-arrow-callback": "warn", "prefer-arrow-callback": "warn",
"prefer-const": "warn", "prefer-const": "warn",
"prefer-destructuring": "warn", "prefer-destructuring": "warn",
@ -189,12 +266,18 @@
"last" "last"
], ],
"space-before-blocks": "warn", "space-before-blocks": "warn",
"space-before-function-paren": [ "error", "always" ], "space-before-function-paren": [
"error",
"always"
],
"space-in-parens": [ "space-in-parens": [
"warn", "warn",
"never" "never"
], ],
"spaced-comment": [ "warn", "always" ], "spaced-comment": [
"warn",
"always"
],
"strict": "warn", "strict": "warn",
"switch-colon-spacing": "warn", "switch-colon-spacing": "warn",
"symbol-description": "warn", "symbol-description": "warn",

4
.gitignore vendored
View File

@ -1,3 +1,3 @@
node_modules node_modules
logs logs
build build

View File

@ -1,48 +1,48 @@
# Navy's logger # Navy's logger
Simple logger I wrote to have a unified system for logging throughout my projects. Simple logger I wrote to have a unified system for logging throughout my projects.
## Features ## Features
Split into Master and Client for logging between processes, where master resides on the master process and the clients on the spawned processes. 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 or some other IPC protocol. Should be fairly trivial to modify it to work across nodes with websockets or some other IPC protocol.
**Note** **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). 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).
Notably the logger will work with just a raw child process object though, it will lack the identifier. Notably the logger will work with just a raw child process object though, it will lack the identifier.
The child processes are expected to be attached with the attach() method found in the master logger. This will attatch a listener for the 'message' event. The child processes are expected to be attached with the attach() method found in the master logger. This will attatch a listener for the 'message' event.
## Logger Options ## Logger Options
``` ```
{ {
//////// SHARED BETWEEN CLIENT & MASTER //////// SHARED BETWEEN CLIENT & MASTER
guard: '_logger', // Message guard, e.g this property has to be true in the IPC message for the logger to read it guard: '_logger', // Message guard, e.g this property has to be true in the IPC message for the logger to read it
loglevel: false, // On the master logger this determines whether the output is written to console (i.e. will always be written to file), on the client this determines whether it is sent to the master at all loglevel: false, // On the master logger this determines whether the output is written to console (i.e. will always be written to file), on the client this determines whether it is sent to the master at all
customTypes: [], // Log types, defaults are 'error', 'warn', 'info', 'debug', 'status'. Each one of these has an associated shorthand function, the custom ones will receive one too, e.g. adding 'access' to the custom types will add a logger.access() function customTypes: [], // Log types, defaults are 'error', 'warn', 'info', 'debug', 'status'. Each one of these has an associated shorthand function, the custom ones will receive one too, e.g. adding 'access' to the custom types will add a logger.access() function
logLevelMapping: { logLevelMapping: {
debug: 0, debug: 0,
info: 1, info: 1,
status: 2, status: 2,
warn: 3, warn: 3,
error: 4 error: 4
} }
/////// MASTER EXCLUSIVE /////// MASTER EXCLUSIVE
customStreams: [], // File streams, by default there are streams for error and default customStreams: [], // File streams, by default there are streams for error and default
customTypeMapping: {}, // This maps a type to a stream, e.g. adding "warn": "error" will pipe any warnings to the error log file customTypeMapping: {}, // This maps a type to a stream, e.g. adding "warn": "error" will pipe any warnings to the error log file
customColours: {}, // Supports any colours chalk.js supports, e.g. "warn": "green" will turn warning outputs green customColours: {}, // 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 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 directory: './logs', // Directory in which to write log files
broadcastLevel: 4, // Level at which to broadcast to webhook if supplied broadcastLevel: 4, // Level at which to broadcast to webhook if supplied
webhook: { webhook: {
url: string url: string
} }
} }
``` ```
**Built-in log levels** **Built-in log levels**
0 - Debug 0 - Debug
1 - Info 1 - Info
2 - Status 2 - Status
3 - Warning 3 - Warning
4 - Error 4 - Error

View File

@ -1,8 +1,12 @@
import MasterLogger from "./src/MasterLogger.js"; import MasterLogger from './src/MasterLogger.js';
import LoggerClient from './src/LoggerClient.js'; import LoggerClient from './src/LoggerClient.js';
import Defaults, { LogLevel } from "./src/Defaults.js"; import Defaults, { LogLevel } from './src/Defaults.js';
const addLogLevel = (name: string, level: number) => { export { WriteOptions, LogFunction } from './src/Types.js';
export { LoggerClientOptions, LoggerMasterOptions } from './src/Defaults.js';
const addLogLevel = (name: string, level: number) =>
{
if (typeof name !== 'string') if (typeof name !== 'string')
throw new Error('Name must be a string'); throw new Error('Name must be a string');
if (typeof level !== 'number') if (typeof level !== 'number')
@ -12,9 +16,9 @@ const addLogLevel = (name: string, level: number) => {
LogLevel[LogLevel[name] = level] = name; LogLevel[LogLevel[name] = level] = name;
}; };
export { export {
MasterLogger, MasterLogger,
LoggerClient, Defaults, LogLevel, addLogLevel, LoggerClient,
}; Defaults, LogLevel, addLogLevel,
};
export { LoggerClientOptions, LoggerMasterOptions } from './src/Defaults.js';

View File

@ -1,41 +1,41 @@
{ {
"name": "@navy.gif/logger", "name": "@navy.gif/logger",
"version": "2.3.0", "version": "2.3.4",
"description": "Logging thing", "description": "Logging thing",
"author": "Navy.gif", "author": "Navy.gif",
"license": "MIT", "license": "MIT",
"private": false, "private": false,
"type": "module", "type": "module",
"main": "build/cjs/index.js", "main": "build/cjs/index.js",
"module": "build/esm/index.js", "module": "build/esm/index.js",
"types": "./build/esm/index.d.ts", "types": "./build/esm/index.d.ts",
"exports": { "exports": {
".": { ".": {
"import": "./build/esm/index.js", "import": "./build/esm/index.js",
"require": "./build/cjs/index.js", "require": "./build/cjs/index.js",
"default": "./build/cjs/index.js", "default": "./build/cjs/index.js",
"types": "./build/esm/index.d.ts" "types": "./build/esm/index.d.ts"
} }
}, },
"files": [ "files": [
"build/**/*" "build/**/*"
], ],
"devDependencies": { "devDependencies": {
"@types/chalk": "^2.2.0", "@types/chalk": "^2.2.0",
"@types/node": "^18.15.11", "@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0", "@typescript-eslint/parser": "^5.58.0",
"eslint": "^8.26.0", "eslint": "^8.26.0",
"typescript": "^5.0.4" "typescript": "^5.0.4"
}, },
"dependencies": { "dependencies": {
"@navy.gif/discord-webhook": "^1.0.0", "@navy.gif/discord-webhook": "^1.0.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"moment": "^2.29.4" "moment": "^2.29.4"
}, },
"scripts": { "scripts": {
"test": "node test/test.js", "test": "node test/test.js",
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js", "build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
"release": "tsc && yarn publish" "release": "yarn build && yarn publish"
} }
} }

View File

@ -1,4 +1,4 @@
import { WebhookClientOptions } from "@navy.gif/discord-webhook/build/esm/src/types/Webhook"; import { WebhookClientOptions } from '@navy.gif/discord-webhook/build/esm/src/types/Webhook';
enum LogLevel { enum LogLevel {
debug = 0, debug = 0,

View File

@ -1,9 +1,10 @@
// const { inspect } = require('node:util'); // const { inspect } = require('node:util');
// const Defaults = require('./Defaults'); // const Defaults = require('./Defaults');
import Defaults from "./Defaults.js"; import Defaults from './Defaults.js';
import { inspect } from "node:util"; import { inspect } from 'node:util';
import { LogFunction, WriteOptions } from "./Types.js"; import { LogFunction, WriteOptions } from './Types.js';
import { Logger } from "./LoggerInterface.js"; import { Logger } from './LoggerInterface.js';
import { makePlainError } from './Shared.js';
type ClientOptions = { type ClientOptions = {
name?: string, name?: string,
@ -19,7 +20,8 @@ type TransportOptions = {
type: string type: string
} }
class LoggerClient implements Logger { class LoggerClient implements Logger
{
static MaxChars = 0; static MaxChars = 0;
@ -27,25 +29,27 @@ class LoggerClient implements Logger {
#_guard: string; #_guard: string;
#_logLevel: number; #_logLevel: number;
#_logLevelMapping: {[key: string]: number}; #_logLevelMapping: { [key: string]: number };
#_name: string; #_name: string;
#types: string[]; #types: string[];
constructor (opts: ClientOptions = Defaults.ClientOptions) { constructor (opts: ClientOptions = Defaults.ClientOptions)
{
this.#_name = opts.name || opts.constructor.name; this.#_name = opts.name || opts.constructor.name;
if (this.#_name === 'Object') if (this.#_name === 'Object')
this.#_name = 'unknown'; this.#_name = 'unknown';
this.#_name = this.#_name.toUpperCase(); this.#_name = this.#_name.toUpperCase();
if (this.#_name.length > LoggerClient.MaxChars) if (this.#_name.length > LoggerClient.MaxChars)
LoggerClient.MaxChars = this.#_name.length; LoggerClient.MaxChars = this.#_name.length;
const { customTypes = [] } = opts; const { customTypes = [] } = opts;
this.#types = [ ...customTypes, ...Defaults.Types ]; this.#types = [ ...customTypes, ...Defaults.Types ];
for (const type of this.#types) { for (const type of this.#types)
{
Object.defineProperty(this, type, { Object.defineProperty(this, type, {
value: (msg: string, o: WriteOptions) => this.transport(msg, { ...o, type }) value: (msg: string, o: WriteOptions) => this.transport(msg, { ...o, type })
}); });
} }
@ -55,59 +59,71 @@ class LoggerClient implements Logger {
} }
get logLevel () { get logLevel ()
{
return this.#_logLevel; return this.#_logLevel;
} }
get logLevels () { get logLevels ()
{
return Object.keys(this.#_logLevelMapping); return Object.keys(this.#_logLevelMapping);
} }
setLogLevel (level = 'info') { setLogLevel (level = 'info')
{
if (typeof level === 'number') if (typeof level === 'number')
this.#_logLevel = level; this.#_logLevel = level;
else if (typeof level === 'string') else if (typeof level === 'string')
this.#_logLevel = this.#_logLevelMapping[level.toLowerCase()]; this.#_logLevel = this.#_logLevelMapping[level.toLowerCase()];
else else
throw new Error(`Invalid log level type, expected string or number, got ${typeof level}`); throw new Error(`Invalid log level type, expected string or number, got ${typeof level}`);
} }
transport (message: string, opts: TransportOptions) { transport (message: string | Error, opts: TransportOptions)
{
if (this.#_logLevelMapping[opts.type] < this.#_logLevel) if (this.#_logLevelMapping[opts.type] < this.#_logLevel)
return; return;
if (typeof message !== 'string') if (message instanceof Error)
message = makePlainError(message);
if (typeof message !== 'string')
message = inspect(message); message = inspect(message);
const spacer = ' '.repeat(LoggerClient.MaxChars - this.#_name.length); const spacer = ' '.repeat(LoggerClient.MaxChars - this.#_name.length);
const header = `${`[${this.#_name.substring(0, LoggerClient.MaxChars)}]${spacer}`} `; const header = `${`[${this.#_name.substring(0, LoggerClient.MaxChars)}]${spacer}`} `;
if (!process.send || !process.connected) if (!process.send || !process.connected)
throw new Error(`Missing connection to master proces`); throw new Error('Missing connection to master proces');
else else
process.send({ [this.#_guard]: true, header, message, ...opts }); process.send({ [this.#_guard]: true, header, message, ...opts });
} }
// These methods are dynamically implemented by the constructor // These methods are dynamically implemented by the constructor, simply here to provide IDE hints
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
error (_str: string, _opts?: WriteOptions): void { error (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
warn (_str: string, _opts?: WriteOptions): void { warn (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
status (_str: string, _opts?: WriteOptions): void { status (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
info (_str: string, _opts?: WriteOptions): void { info (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
debug (_str: string, _opts?: WriteOptions): void { debug (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@ -1,4 +1,4 @@
import { WriteOptions } from "./Types"; import { WriteOptions } from './Types';
export interface Logger { export interface Logger {
error(str: string, opts?: WriteOptions): void error(str: string, opts?: WriteOptions): void

View File

@ -13,6 +13,7 @@ import Defaults, { LogLevel } from './Defaults.js';
import { IPCMessage, LogFunction, Shard, WriteOptions } from './Types.js'; import { IPCMessage, LogFunction, Shard, WriteOptions } from './Types.js';
import { addLogLevel } from '../index.js'; import { addLogLevel } from '../index.js';
import { Logger } from './LoggerInterface.js'; import { Logger } from './LoggerInterface.js';
import { makePlainError } from './Shared.js';
const DAY = 1000 * 60 * 60 * 24; const DAY = 1000 * 60 * 60 * 24;
@ -27,7 +28,8 @@ type WriteStreams = {
[key:string]: fs.WriteStream [key:string]: fs.WriteStream
} }
class MasterLogger implements Logger { class MasterLogger implements Logger
{
[key: string]: LogFunction | unknown; [key: string]: LogFunction | unknown;
@ -48,7 +50,8 @@ class MasterLogger implements Logger {
#startDay: number; #startDay: number;
#rotateTO: NodeJS.Timeout; #rotateTO: NodeJS.Timeout;
constructor (config = Defaults.MasterOptions) { constructor (config = Defaults.MasterOptions)
{
const { const {
directory, customTypes = [], customStreams = [], customTypeMapping, directory, customTypes = [], customStreams = [], customTypeMapping,
@ -65,20 +68,23 @@ class MasterLogger implements Logger {
this.#_logLevel = logLevel as number; this.#_logLevel = logLevel as number;
this.#_logLevelMapping = { ...Defaults.MasterOptions.logLevelMapping, ...logLevelMapping }; this.#_logLevelMapping = { ...Defaults.MasterOptions.logLevelMapping, ...logLevelMapping };
if (logLevelMapping) if (logLevelMapping)
Object.entries(logLevelMapping).forEach(([ name, level ]) => { Object.entries(logLevelMapping).forEach(([ name, level ]) =>
{
addLogLevel(name, level); addLogLevel(name, level);
}); });
this.#_guard = guard as string; this.#_guard = guard as string;
this.#types = [ ...customTypes, ...Defaults.Types ]; this.#types = [ ...customTypes, ...Defaults.Types ];
for (const type of this.#types) { for (const type of this.#types)
{
Object.defineProperty(this, type, { Object.defineProperty(this, type, {
value: (msg: string, opts: WriteOptions) => this.write(type, msg, opts) value: (msg: string, opts: WriteOptions) => this.write(type, msg, opts)
}); });
} }
this.#colours = { ...Defaults.Colours, ...customColours }; this.#colours = { ...Defaults.Colours, ...customColours };
this.#colourFuncs = Object.entries(this.#colours).reduce((prev: FuncsType, [ type, colour ]: (string | number)[]) => { this.#colourFuncs = Object.entries(this.#colours).reduce((prev: FuncsType, [ type, colour ]: (string | number)[]) =>
{
if (typeof colour === 'number') if (typeof colour === 'number')
colour = `#${colour.toString(16)}`; colour = `#${colour.toString(16)}`;
else if (typeof colour !== 'string') else if (typeof colour !== 'string')
@ -96,7 +102,8 @@ class MasterLogger implements Logger {
this.#streamTypes = [ ...customStreams, 'error', 'default' ]; this.#streamTypes = [ ...customStreams, 'error', 'default' ];
this.#streamTypeMapping = { ...Defaults.TypeStream, ...customTypeMapping }; this.#streamTypeMapping = { ...Defaults.TypeStream, ...customTypeMapping };
this.#writeStreams = this.#streamTypes.reduce((acc, type) => { this.#writeStreams = this.#streamTypes.reduce((acc, type) =>
{
acc[type] = this.loadFile(type); acc[type] = this.loadFile(type);
return acc; return acc;
}, {} as WriteStreams); }, {} as WriteStreams);
@ -106,41 +113,49 @@ class MasterLogger implements Logger {
this.#startDay = Math.floor(Date.now() / this.#rotationFreq) * this.#rotationFreq; this.#startDay = Math.floor(Date.now() / this.#rotationFreq) * this.#rotationFreq;
this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), this.#startDay + this.#rotationFreq - Date.now()); this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), this.#startDay + this.#rotationFreq - Date.now());
if (webhook && webhook.url) { if (webhook && webhook.url)
{
this.#webhook = new DiscordWebhook(webhook); this.#webhook = new DiscordWebhook(webhook);
this.#webhook.fetch(); this.#webhook.fetch();
} }
} }
get guard () { get guard ()
{
return this.#_guard; return this.#_guard;
} }
get logLevel () { get logLevel ()
{
return this.#_logLevel; return this.#_logLevel;
} }
get logLevels () { get logLevels ()
{
return Object.keys(this.#_logLevelMapping); return Object.keys(this.#_logLevelMapping);
} }
setLogLevel (level: number) { setLogLevel (level: number)
{
if (LogLevel[level] in this.#_logLevelMapping) if (LogLevel[level] in this.#_logLevelMapping)
this.#_logLevel = level; this.#_logLevel = level;
else else
throw new Error(`Not a valid log level`); throw new Error('Not a valid log level');
} }
setBroadcastLevel (level: number) { setBroadcastLevel (level: number)
{
if (LogLevel[level] in this.#_logLevelMapping) if (LogLevel[level] in this.#_logLevelMapping)
this.#_broadcastLevel = level; this.#_broadcastLevel = level;
else else
throw new Error('Not a valid log level'); throw new Error('Not a valid log level');
} }
attach (shard: Shard) { attach (shard: Shard)
shard.on('message', (msg: IPCMessage) => { {
shard.on('message', (msg: IPCMessage) =>
{
if (!msg[this.#_guard]) if (!msg[this.#_guard])
return; return;
const { message, type, header, broadcast } = msg; const { message, type, header, broadcast } = msg;
@ -151,13 +166,17 @@ class MasterLogger implements Logger {
}); });
} }
write (type = 'info', text: string, { subheader = '', shard, broadcast = false }: WriteOptions = {}) { write (type = 'info', text: string | Error, { subheader = '', shard, broadcast = false }: WriteOptions = {})
{
type = type.toLowerCase(); type = type.toLowerCase();
let colour = this.#colourFuncs[type]; let colour = this.#colourFuncs[type];
if (!colour) if (!colour)
colour = this.#colourFuncs.info; colour = this.#colourFuncs.info;
if (text instanceof Error)
text = makePlainError(text);
if (typeof text !== 'string') if (typeof text !== 'string')
text = inspect(text); text = inspect(text);
@ -169,13 +188,16 @@ class MasterLogger implements Logger {
console.log(`${colour.func(type)}${spacer} ${colour.func(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 || this.#_broadcastLevel <= this.#_logLevelMapping[type]) && this.#webhook) if ((broadcast || this.#_broadcastLevel <= this.#_logLevelMapping[type]) && this.#webhook)
{
const description = (subheader.length ? `**${subheader}**\n` : '') + `\`\`\`${text}\`\`\``;
this.#webhook.send({ this.#webhook.send({
embeds: [{ embeds: [{
title: `[__${type.toUpperCase()}__] ${this._shard(shard)}`, title: `[__${type.toUpperCase()}__] ${this._shard(shard)}`,
description: `**${subheader}**\n\`\`\`${text}\`\`\``, description,
color: colour.int color: colour.int
}] }]
}); });
}
const streamType = this.#streamTypeMapping[type] || 'default'; const streamType = this.#streamTypeMapping[type] || 'default';
if (this.#writeStreams[streamType]) if (this.#writeStreams[streamType])
@ -185,9 +207,11 @@ class MasterLogger implements Logger {
} }
rotateLogFiles () { rotateLogFiles ()
{
const streams = Object.keys(this.#writeStreams); const streams = Object.keys(this.#writeStreams);
for (const type of streams) { for (const type of streams)
{
this.#writeStreams[type].write('\nRotating log file'); this.#writeStreams[type].write('\nRotating log file');
this.#writeStreams[type].end(); this.#writeStreams[type].end();
this.#writeStreams[type] = this.loadFile(type); this.#writeStreams[type] = this.loadFile(type);
@ -198,7 +222,8 @@ class MasterLogger implements Logger {
this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), nextTime - Date.now()); this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), nextTime - Date.now());
} }
loadFile (type: string, date = Date.now()) { loadFile (type: string, date = Date.now())
{
if (!type) if (!type)
throw new Error('Missing file type'); throw new Error('Missing file type');
@ -212,7 +237,8 @@ class MasterLogger implements Logger {
} }
_shard (shard?: Shard) { _shard (shard?: Shard)
{
if (!shard) if (!shard)
return 'controller'; return 'controller';
let id = '??'; let id = '??';
@ -221,29 +247,35 @@ class MasterLogger implements Logger {
return `shard-${id}`; return `shard-${id}`;
} }
get date () { get date ()
{
return moment().format('YYYY-MM-DD HH:mm:ss'); return moment().format('YYYY-MM-DD HH:mm:ss');
} }
// These methods are dynamically implemented by the constructor // These methods are dynamically implemented by the constructor
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
error (_str: string, _opts?: WriteOptions): void { error (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
warn (_str: string, _opts?: WriteOptions): void { warn (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
status (_str: string, _opts?: WriteOptions): void { status (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
info (_str: string, _opts?: WriteOptions): void { info (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
debug (_str: string, _opts?: WriteOptions): void { debug (_str: string | Error, _opts?: WriteOptions): void
{
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

8
src/Shared.ts Normal file
View File

@ -0,0 +1,8 @@
export const makePlainError = (err: Error) =>
{
return {
name: err.name,
message: err.message,
stack: err.stack
};
};

View File

@ -1,4 +1,4 @@
import EventEmitter from "node:events"; import EventEmitter from 'node:events';
type Shard = { type Shard = {
id: number id: number
@ -19,6 +19,6 @@ type IPCMessage = {
broadcast: boolean broadcast: boolean
} }
type LogFunction = (str: string, opts: WriteOptions) => void type LogFunction = (str: string, opts?: WriteOptions) => void
export { WriteOptions, Shard, IPCMessage, LogFunction }; export { WriteOptions, Shard, IPCMessage, LogFunction };

View File

@ -1,5 +1,5 @@
// const { LoggerClient } = require('../'); // const { LoggerClient } = require('../');
import { LoggerClient } from "../build/esm/index.js"; import { LoggerClient } from '../build/esm/index.js';
const logger = new LoggerClient({ const logger = new LoggerClient({
customTypes: [ 'access' ], customTypes: [ 'access' ],
@ -9,8 +9,10 @@ const logger = new LoggerClient({
logLevelMapping: { access: 2 } logLevelMapping: { access: 2 }
}); });
const main = async () => { const main = async () =>
for (let i = 0; i < 5; i++) { {
for (let i = 0; i < 5; i++)
{
// if (i % 500 === 0) // if (i % 500 === 0)
await new Promise(resolve => setTimeout(resolve, 1000)); await new Promise(resolve => setTimeout(resolve, 1000));
logger.info('Info test'); logger.info('Info test');

View File

@ -28,8 +28,10 @@ console.log(logger.logLevel);
// process.exit(); // process.exit();
logger.info('Test'); logger.info('Test');
const spawn = (child) => { const spawn = (child) =>
return new Promise((resolve) => { {
return new Promise((resolve) =>
{
child.once('spawn', resolve); child.once('spawn', resolve);
}); });
@ -37,12 +39,14 @@ const spawn = (child) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const ChildProcess = require('node:child_process'); const ChildProcess = require('node:child_process');
const main = async () => { const main = async () =>
{
const child = ChildProcess.fork('./test/otherProcess.js'); const child = ChildProcess.fork('./test/otherProcess.js');
logger.attach(child); logger.attach(child);
await spawn(child); await spawn(child);
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++)
{
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
logger.info(`Iteration ${i}`); logger.info(`Iteration ${i}`);
} }

View File

@ -1,17 +1,20 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
// const { MasterLogger } = require('../build'); // const { MasterLogger } = require('../build');
// const ChildProcess = require('node:child_process'); // const ChildProcess = require('node:child_process');
import { MasterLogger } from "../build/index.js"; import { MasterLogger } from '../build/esm/index.js';
import ChildProcess from "node:child_process"; import ChildProcess from 'node:child_process';
const spawn = (child) => { const spawn = (child) =>
return new Promise((resolve) => { {
return new Promise((resolve) =>
{
child.on('spawn', resolve); child.on('spawn', resolve);
}); });
}; };
const main = async () => { const main = async () =>
{
const logger = new MasterLogger({ const logger = new MasterLogger({
debug: true, debug: true,
customTypes: [ 'access' ], customTypes: [ 'access' ],
@ -36,7 +39,8 @@ const main = async () => {
const { types, colours, streamTypes, streamTypeMapping } = logger; // , writeStreams const { types, colours, streamTypes, streamTypeMapping } = logger; // , writeStreams
console.log(types, colours, streamTypes, streamTypeMapping); // , writeStreams console.log(types, colours, streamTypes, streamTypeMapping); // , writeStreams
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++)
{
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
logger.info(`Iteration ${i}`); logger.info(`Iteration ${i}`);
} }