Compare commits
5 Commits
06b27ed2f2
...
22d7d982ac
Author | SHA1 | Date | |
---|---|---|---|
22d7d982ac | |||
45b1f8c2df | |||
33b1087671 | |||
477315dc72 | |||
2dd9ebb379 |
121
.eslintrc.json
121
.eslintrc.json
@ -1,31 +1,56 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended" ],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [ "@typescript-eslint" ],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"accessor-pairs": "warn",
|
||||
"array-callback-return": "warn",
|
||||
"array-bracket-newline": [ "warn", "consistent" ],
|
||||
"array-bracket-spacing": [ "warn", "always", { "objectsInArrays": false, "arraysInArrays": false }],
|
||||
"array-bracket-newline": [
|
||||
"warn",
|
||||
"consistent"
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
"warn",
|
||||
"always",
|
||||
{
|
||||
"objectsInArrays": false,
|
||||
"arraysInArrays": false
|
||||
}
|
||||
],
|
||||
"arrow-spacing": "warn",
|
||||
"block-scoped-var": "warn",
|
||||
"block-spacing": [ "warn", "always" ],
|
||||
"brace-style": [ "warn", "1tbs" ],
|
||||
"block-spacing": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"brace-style": [
|
||||
"warn",
|
||||
"allman"
|
||||
],
|
||||
"callback-return": "warn",
|
||||
"camelcase": "warn",
|
||||
"comma-dangle": [ "warn", "only-multiline" ],
|
||||
"comma-dangle": [
|
||||
"warn",
|
||||
"only-multiline"
|
||||
],
|
||||
"comma-spacing": [
|
||||
"warn",
|
||||
{
|
||||
@ -68,9 +93,25 @@
|
||||
"implicit-arrow-linebreak": "warn",
|
||||
"indent": "warn",
|
||||
"init-declarations": "warn",
|
||||
"jsx-quotes": [ "warn", "prefer-single" ],
|
||||
"key-spacing": [ "warn", { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [ "warn", { "after": true, "before": true }],
|
||||
"quotes": ["error" , "single"],
|
||||
"jsx-quotes": [
|
||||
"warn",
|
||||
"prefer-single"
|
||||
],
|
||||
"key-spacing": [
|
||||
"warn",
|
||||
{
|
||||
"beforeColon": false,
|
||||
"afterColon": true
|
||||
}
|
||||
],
|
||||
"keyword-spacing": [
|
||||
"warn",
|
||||
{
|
||||
"after": true,
|
||||
"before": true
|
||||
}
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
@ -79,6 +120,24 @@
|
||||
"lines-around-directive": "warn",
|
||||
"max-classes-per-file": "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",
|
||||
"no-alert": "warn",
|
||||
"no-array-constructor": "warn",
|
||||
@ -96,7 +155,6 @@
|
||||
"no-extend-native": "warn",
|
||||
"no-extra-bind": "warn",
|
||||
"no-extra-label": "warn",
|
||||
"no-extra-parens": "warn",
|
||||
"no-floating-decimal": "warn",
|
||||
"no-implicit-coercion": "warn",
|
||||
"no-implicit-globals": "warn",
|
||||
@ -147,7 +205,7 @@
|
||||
"no-unmodified-loop-condition": "warn",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unused-expressions": "warn",
|
||||
"no-use-before-define": "error",
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"no-useless-call": "warn",
|
||||
"no-useless-computed-key": "warn",
|
||||
"no-useless-concat": "warn",
|
||||
@ -155,20 +213,39 @@
|
||||
"no-useless-rename": "warn",
|
||||
"no-useless-return": "warn",
|
||||
"no-var": "warn",
|
||||
"no-void": "warn",
|
||||
// "no-void": "warn",
|
||||
"no-whitespace-before-property": "error",
|
||||
"nonblock-statement-body-position": [ "warn", "below" ],
|
||||
"nonblock-statement-body-position": [
|
||||
"warn",
|
||||
"below"
|
||||
],
|
||||
"object-curly-spacing": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"object-property-newline": [ "warn", { "allowAllPropertiesOnSameLine": true }],
|
||||
"object-property-newline": [
|
||||
"warn",
|
||||
{
|
||||
"allowAllPropertiesOnSameLine": true
|
||||
}
|
||||
],
|
||||
"object-shorthand": "warn",
|
||||
"one-var-declaration-per-line": "warn",
|
||||
"operator-assignment": "warn",
|
||||
"operator-linebreak": [ "warn", "before" ],
|
||||
"operator-linebreak": [
|
||||
"warn",
|
||||
"before"
|
||||
],
|
||||
"padding-line-between-statements": "warn",
|
||||
"padded-blocks": [ "warn", { "switches": "never" }, { "allowSingleLineBlocks": true }],
|
||||
"padded-blocks": [
|
||||
"warn",
|
||||
{
|
||||
"switches": "never"
|
||||
},
|
||||
{
|
||||
"allowSingleLineBlocks": true
|
||||
}
|
||||
],
|
||||
"prefer-arrow-callback": "warn",
|
||||
"prefer-const": "warn",
|
||||
"prefer-destructuring": "warn",
|
||||
@ -189,12 +266,18 @@
|
||||
"last"
|
||||
],
|
||||
"space-before-blocks": "warn",
|
||||
"space-before-function-paren": [ "error", "always" ],
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"space-in-parens": [
|
||||
"warn",
|
||||
"never"
|
||||
],
|
||||
"spaced-comment": [ "warn", "always" ],
|
||||
"spaced-comment": [
|
||||
"warn",
|
||||
"always"
|
||||
],
|
||||
"strict": "warn",
|
||||
"switch-colon-spacing": "warn",
|
||||
"symbol-description": "warn",
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
node_modules
|
||||
logs
|
||||
node_modules
|
||||
logs
|
||||
build
|
94
README.md
94
README.md
@ -1,48 +1,48 @@
|
||||
# Navy's logger
|
||||
|
||||
Simple logger I wrote to have a unified system for logging throughout my projects.
|
||||
|
||||
## 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 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).
|
||||
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.
|
||||
|
||||
## Logger Options
|
||||
```
|
||||
{
|
||||
//////// 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
|
||||
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
|
||||
logLevelMapping: {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
status: 2,
|
||||
warn: 3,
|
||||
error: 4
|
||||
}
|
||||
|
||||
/////// MASTER EXCLUSIVE
|
||||
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
|
||||
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
|
||||
directory: './logs', // Directory in which to write log files
|
||||
broadcastLevel: 4, // Level at which to broadcast to webhook if supplied
|
||||
webhook: {
|
||||
url: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Built-in log levels**
|
||||
0 - Debug
|
||||
1 - Info
|
||||
2 - Status
|
||||
3 - Warning
|
||||
# Navy's logger
|
||||
|
||||
Simple logger I wrote to have a unified system for logging throughout my projects.
|
||||
|
||||
## 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 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).
|
||||
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.
|
||||
|
||||
## Logger Options
|
||||
```
|
||||
{
|
||||
//////// 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
|
||||
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
|
||||
logLevelMapping: {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
status: 2,
|
||||
warn: 3,
|
||||
error: 4
|
||||
}
|
||||
|
||||
/////// MASTER EXCLUSIVE
|
||||
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
|
||||
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
|
||||
directory: './logs', // Directory in which to write log files
|
||||
broadcastLevel: 4, // Level at which to broadcast to webhook if supplied
|
||||
webhook: {
|
||||
url: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Built-in log levels**
|
||||
0 - Debug
|
||||
1 - Info
|
||||
2 - Status
|
||||
3 - Warning
|
||||
4 - Error
|
18
index.ts
18
index.ts
@ -1,8 +1,12 @@
|
||||
import MasterLogger from "./src/MasterLogger.js";
|
||||
import MasterLogger from './src/MasterLogger.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')
|
||||
throw new Error('Name must be a string');
|
||||
if (typeof level !== 'number')
|
||||
@ -12,9 +16,9 @@ const addLogLevel = (name: string, level: number) => {
|
||||
LogLevel[LogLevel[name] = level] = name;
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
MasterLogger,
|
||||
LoggerClient, Defaults, LogLevel, addLogLevel,
|
||||
};
|
||||
|
||||
export { LoggerClientOptions, LoggerMasterOptions } from './src/Defaults.js';
|
||||
LoggerClient,
|
||||
Defaults, LogLevel, addLogLevel,
|
||||
};
|
82
package.json
82
package.json
@ -1,41 +1,41 @@
|
||||
{
|
||||
"name": "@navy.gif/logger",
|
||||
"version": "2.3.0",
|
||||
"description": "Logging thing",
|
||||
"author": "Navy.gif",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"main": "build/cjs/index.js",
|
||||
"module": "build/esm/index.js",
|
||||
"types": "./build/esm/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/esm/index.js",
|
||||
"require": "./build/cjs/index.js",
|
||||
"default": "./build/cjs/index.js",
|
||||
"types": "./build/esm/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/node": "^18.15.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"eslint": "^8.26.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navy.gif/discord-webhook": "^1.0.0",
|
||||
"chalk": "^4.1.2",
|
||||
"moment": "^2.29.4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node test/test.js",
|
||||
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
|
||||
"release": "tsc && yarn publish"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "@navy.gif/logger",
|
||||
"version": "2.3.4",
|
||||
"description": "Logging thing",
|
||||
"author": "Navy.gif",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"main": "build/cjs/index.js",
|
||||
"module": "build/esm/index.js",
|
||||
"types": "./build/esm/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./build/esm/index.js",
|
||||
"require": "./build/cjs/index.js",
|
||||
"default": "./build/cjs/index.js",
|
||||
"types": "./build/esm/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"build/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/chalk": "^2.2.0",
|
||||
"@types/node": "^18.15.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"eslint": "^8.26.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@navy.gif/discord-webhook": "^1.0.0",
|
||||
"chalk": "^4.1.2",
|
||||
"moment": "^2.29.4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node test/test.js",
|
||||
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
|
||||
"release": "yarn build && yarn publish"
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
debug = 0,
|
||||
|
@ -1,9 +1,10 @@
|
||||
// const { inspect } = require('node:util');
|
||||
// const Defaults = require('./Defaults');
|
||||
import Defaults from "./Defaults.js";
|
||||
import { inspect } from "node:util";
|
||||
import { LogFunction, WriteOptions } from "./Types.js";
|
||||
import { Logger } from "./LoggerInterface.js";
|
||||
import Defaults from './Defaults.js';
|
||||
import { inspect } from 'node:util';
|
||||
import { LogFunction, WriteOptions } from './Types.js';
|
||||
import { Logger } from './LoggerInterface.js';
|
||||
import { makePlainError } from './Shared.js';
|
||||
|
||||
type ClientOptions = {
|
||||
name?: string,
|
||||
@ -19,7 +20,8 @@ type TransportOptions = {
|
||||
type: string
|
||||
}
|
||||
|
||||
class LoggerClient implements Logger {
|
||||
class LoggerClient implements Logger
|
||||
{
|
||||
|
||||
static MaxChars = 0;
|
||||
|
||||
@ -27,25 +29,27 @@ class LoggerClient implements Logger {
|
||||
|
||||
#_guard: string;
|
||||
#_logLevel: number;
|
||||
#_logLevelMapping: {[key: string]: number};
|
||||
#_logLevelMapping: { [key: string]: number };
|
||||
#_name: string;
|
||||
#types: string[];
|
||||
|
||||
constructor (opts: ClientOptions = Defaults.ClientOptions) {
|
||||
constructor (opts: ClientOptions = Defaults.ClientOptions)
|
||||
{
|
||||
|
||||
this.#_name = opts.name || opts.constructor.name;
|
||||
if (this.#_name === 'Object')
|
||||
if (this.#_name === 'Object')
|
||||
this.#_name = 'unknown';
|
||||
this.#_name = this.#_name.toUpperCase();
|
||||
|
||||
if (this.#_name.length > LoggerClient.MaxChars)
|
||||
if (this.#_name.length > LoggerClient.MaxChars)
|
||||
LoggerClient.MaxChars = this.#_name.length;
|
||||
|
||||
const { customTypes = [] } = opts;
|
||||
this.#types = [ ...customTypes, ...Defaults.Types ];
|
||||
for (const type of this.#types) {
|
||||
for (const type of this.#types)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
get logLevels () {
|
||||
get logLevels ()
|
||||
{
|
||||
return Object.keys(this.#_logLevelMapping);
|
||||
}
|
||||
|
||||
setLogLevel (level = 'info') {
|
||||
setLogLevel (level = 'info')
|
||||
{
|
||||
if (typeof level === 'number')
|
||||
this.#_logLevel = level;
|
||||
else if (typeof level === 'string')
|
||||
this.#_logLevel = this.#_logLevelMapping[level.toLowerCase()];
|
||||
else
|
||||
else
|
||||
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;
|
||||
|
||||
if (typeof message !== 'string')
|
||||
if (message instanceof Error)
|
||||
message = makePlainError(message);
|
||||
|
||||
if (typeof message !== 'string')
|
||||
message = inspect(message);
|
||||
const spacer = ' '.repeat(LoggerClient.MaxChars - this.#_name.length);
|
||||
const header = `${`[${this.#_name.substring(0, LoggerClient.MaxChars)}]${spacer}`} `;
|
||||
|
||||
|
||||
if (!process.send || !process.connected)
|
||||
throw new Error(`Missing connection to master proces`);
|
||||
else
|
||||
throw new Error('Missing connection to master proces');
|
||||
else
|
||||
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
|
||||
error (_str: string, _opts?: WriteOptions): void {
|
||||
error (_str: string | Error, _opts?: WriteOptions): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { WriteOptions } from "./Types";
|
||||
import { WriteOptions } from './Types';
|
||||
|
||||
export interface Logger {
|
||||
error(str: string, opts?: WriteOptions): void
|
||||
|
@ -13,6 +13,7 @@ import Defaults, { LogLevel } from './Defaults.js';
|
||||
import { IPCMessage, LogFunction, Shard, WriteOptions } from './Types.js';
|
||||
import { addLogLevel } from '../index.js';
|
||||
import { Logger } from './LoggerInterface.js';
|
||||
import { makePlainError } from './Shared.js';
|
||||
|
||||
const DAY = 1000 * 60 * 60 * 24;
|
||||
|
||||
@ -27,7 +28,8 @@ type WriteStreams = {
|
||||
[key:string]: fs.WriteStream
|
||||
}
|
||||
|
||||
class MasterLogger implements Logger {
|
||||
class MasterLogger implements Logger
|
||||
{
|
||||
|
||||
[key: string]: LogFunction | unknown;
|
||||
|
||||
@ -48,7 +50,8 @@ class MasterLogger implements Logger {
|
||||
#startDay: number;
|
||||
#rotateTO: NodeJS.Timeout;
|
||||
|
||||
constructor (config = Defaults.MasterOptions) {
|
||||
constructor (config = Defaults.MasterOptions)
|
||||
{
|
||||
|
||||
const {
|
||||
directory, customTypes = [], customStreams = [], customTypeMapping,
|
||||
@ -65,20 +68,23 @@ class MasterLogger implements Logger {
|
||||
this.#_logLevel = logLevel as number;
|
||||
this.#_logLevelMapping = { ...Defaults.MasterOptions.logLevelMapping, ...logLevelMapping };
|
||||
if (logLevelMapping)
|
||||
Object.entries(logLevelMapping).forEach(([ name, level ]) => {
|
||||
Object.entries(logLevelMapping).forEach(([ name, level ]) =>
|
||||
{
|
||||
addLogLevel(name, level);
|
||||
});
|
||||
this.#_guard = guard as string;
|
||||
|
||||
this.#types = [ ...customTypes, ...Defaults.Types ];
|
||||
for (const type of this.#types) {
|
||||
for (const type of this.#types)
|
||||
{
|
||||
Object.defineProperty(this, type, {
|
||||
value: (msg: string, opts: WriteOptions) => this.write(type, msg, opts)
|
||||
});
|
||||
}
|
||||
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')
|
||||
colour = `#${colour.toString(16)}`;
|
||||
else if (typeof colour !== 'string')
|
||||
@ -96,7 +102,8 @@ class MasterLogger implements Logger {
|
||||
|
||||
this.#streamTypes = [ ...customStreams, 'error', 'default' ];
|
||||
this.#streamTypeMapping = { ...Defaults.TypeStream, ...customTypeMapping };
|
||||
this.#writeStreams = this.#streamTypes.reduce((acc, type) => {
|
||||
this.#writeStreams = this.#streamTypes.reduce((acc, type) =>
|
||||
{
|
||||
acc[type] = this.loadFile(type);
|
||||
return acc;
|
||||
}, {} as WriteStreams);
|
||||
@ -106,41 +113,49 @@ class MasterLogger implements Logger {
|
||||
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) {
|
||||
if (webhook && webhook.url)
|
||||
{
|
||||
this.#webhook = new DiscordWebhook(webhook);
|
||||
this.#webhook.fetch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get guard () {
|
||||
get guard ()
|
||||
{
|
||||
return this.#_guard;
|
||||
}
|
||||
|
||||
get logLevel () {
|
||||
get logLevel ()
|
||||
{
|
||||
return this.#_logLevel;
|
||||
}
|
||||
|
||||
get logLevels () {
|
||||
get logLevels ()
|
||||
{
|
||||
return Object.keys(this.#_logLevelMapping);
|
||||
}
|
||||
|
||||
setLogLevel (level: number) {
|
||||
setLogLevel (level: number)
|
||||
{
|
||||
if (LogLevel[level] in this.#_logLevelMapping)
|
||||
this.#_logLevel = level;
|
||||
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)
|
||||
this.#_broadcastLevel = level;
|
||||
else
|
||||
throw new Error('Not a valid log level');
|
||||
}
|
||||
|
||||
attach (shard: Shard) {
|
||||
shard.on('message', (msg: IPCMessage) => {
|
||||
attach (shard: Shard)
|
||||
{
|
||||
shard.on('message', (msg: IPCMessage) =>
|
||||
{
|
||||
if (!msg[this.#_guard])
|
||||
return;
|
||||
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();
|
||||
let colour = this.#colourFuncs[type];
|
||||
if (!colour)
|
||||
colour = this.#colourFuncs.info;
|
||||
|
||||
if (text instanceof Error)
|
||||
text = makePlainError(text);
|
||||
|
||||
if (typeof text !== 'string')
|
||||
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
|
||||
|
||||
if ((broadcast || this.#_broadcastLevel <= this.#_logLevelMapping[type]) && this.#webhook)
|
||||
{
|
||||
const description = (subheader.length ? `**${subheader}**\n` : '') + `\`\`\`${text}\`\`\``;
|
||||
this.#webhook.send({
|
||||
embeds: [{
|
||||
title: `[__${type.toUpperCase()}__] ${this._shard(shard)}`,
|
||||
description: `**${subheader}**\n\`\`\`${text}\`\`\``,
|
||||
description,
|
||||
color: colour.int
|
||||
}]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const streamType = this.#streamTypeMapping[type] || 'default';
|
||||
if (this.#writeStreams[streamType])
|
||||
@ -185,9 +207,11 @@ class MasterLogger implements Logger {
|
||||
|
||||
}
|
||||
|
||||
rotateLogFiles () {
|
||||
rotateLogFiles ()
|
||||
{
|
||||
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].end();
|
||||
this.#writeStreams[type] = this.loadFile(type);
|
||||
@ -198,7 +222,8 @@ class MasterLogger implements Logger {
|
||||
this.#rotateTO = setTimeout(this.rotateLogFiles.bind(this), nextTime - Date.now());
|
||||
}
|
||||
|
||||
loadFile (type: string, date = Date.now()) {
|
||||
loadFile (type: string, date = Date.now())
|
||||
{
|
||||
if (!type)
|
||||
throw new Error('Missing file type');
|
||||
|
||||
@ -212,7 +237,8 @@ class MasterLogger implements Logger {
|
||||
|
||||
}
|
||||
|
||||
_shard (shard?: Shard) {
|
||||
_shard (shard?: Shard)
|
||||
{
|
||||
if (!shard)
|
||||
return 'controller';
|
||||
let id = '??';
|
||||
@ -221,29 +247,35 @@ class MasterLogger implements Logger {
|
||||
return `shard-${id}`;
|
||||
}
|
||||
|
||||
get date () {
|
||||
get date ()
|
||||
{
|
||||
return moment().format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
// These methods are dynamically implemented by the constructor
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
// 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.');
|
||||
}
|
||||
|
||||
|
8
src/Shared.ts
Normal file
8
src/Shared.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const makePlainError = (err: Error) =>
|
||||
{
|
||||
return {
|
||||
name: err.name,
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
};
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import EventEmitter from "node:events";
|
||||
import EventEmitter from 'node:events';
|
||||
|
||||
type Shard = {
|
||||
id: number
|
||||
@ -19,6 +19,6 @@ type IPCMessage = {
|
||||
broadcast: boolean
|
||||
}
|
||||
|
||||
type LogFunction = (str: string, opts: WriteOptions) => void
|
||||
type LogFunction = (str: string, opts?: WriteOptions) => void
|
||||
|
||||
export { WriteOptions, Shard, IPCMessage, LogFunction };
|
@ -1,5 +1,5 @@
|
||||
// const { LoggerClient } = require('../');
|
||||
import { LoggerClient } from "../build/esm/index.js";
|
||||
import { LoggerClient } from '../build/esm/index.js';
|
||||
|
||||
const logger = new LoggerClient({
|
||||
customTypes: [ 'access' ],
|
||||
@ -9,8 +9,10 @@ const logger = new LoggerClient({
|
||||
logLevelMapping: { access: 2 }
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const main = async () =>
|
||||
{
|
||||
for (let i = 0; i < 5; i++)
|
||||
{
|
||||
// if (i % 500 === 0)
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
logger.info('Info test');
|
||||
|
@ -28,8 +28,10 @@ console.log(logger.logLevel);
|
||||
// process.exit();
|
||||
logger.info('Test');
|
||||
|
||||
const spawn = (child) => {
|
||||
return new Promise((resolve) => {
|
||||
const spawn = (child) =>
|
||||
{
|
||||
return new Promise((resolve) =>
|
||||
{
|
||||
child.once('spawn', resolve);
|
||||
});
|
||||
|
||||
@ -37,12 +39,14 @@ const spawn = (child) => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ChildProcess = require('node:child_process');
|
||||
const main = async () => {
|
||||
const main = async () =>
|
||||
{
|
||||
const child = ChildProcess.fork('./test/otherProcess.js');
|
||||
logger.attach(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));
|
||||
logger.info(`Iteration ${i}`);
|
||||
}
|
||||
|
16
test/test.js
16
test/test.js
@ -1,17 +1,20 @@
|
||||
/* eslint-disable no-console */
|
||||
// const { MasterLogger } = require('../build');
|
||||
// const ChildProcess = require('node:child_process');
|
||||
import { MasterLogger } from "../build/index.js";
|
||||
import ChildProcess from "node:child_process";
|
||||
import { MasterLogger } from '../build/esm/index.js';
|
||||
import ChildProcess from 'node:child_process';
|
||||
|
||||
const spawn = (child) => {
|
||||
return new Promise((resolve) => {
|
||||
const spawn = (child) =>
|
||||
{
|
||||
return new Promise((resolve) =>
|
||||
{
|
||||
child.on('spawn', resolve);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const main = async () =>
|
||||
{
|
||||
const logger = new MasterLogger({
|
||||
debug: true,
|
||||
customTypes: [ 'access' ],
|
||||
@ -36,7 +39,8 @@ const main = async () => {
|
||||
const { types, colours, streamTypes, streamTypeMapping } = logger; // , 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));
|
||||
logger.info(`Iteration ${i}`);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user