Log function improvements
Allow arbitrary number of arguments to be passed to logging fuctions
This commit is contained in:
parent
92731815e1
commit
2ab62a6368
@ -1,13 +1,20 @@
|
||||
import Defaults, { LoggerClientOptions } from './Defaults.js';
|
||||
import { inspect } from 'node:util';
|
||||
import { LogFunction, WriteOptions } from './Types.js';
|
||||
import { LogFunction, Loggable, WriteOptions } from './Types.js';
|
||||
import { Logger } from './LoggerInterface.js';
|
||||
import { makePlainError } from './Shared.js';
|
||||
import { isWriteOptions, makePlainError } from './Shared.js';
|
||||
|
||||
type TransportOptions = {
|
||||
type: string,
|
||||
labels?: string[]
|
||||
}
|
||||
} & WriteOptions
|
||||
|
||||
const validKeys = [ 'labels' ];
|
||||
const isTransportOpts = (obj: object): obj is TransportOptions =>
|
||||
{
|
||||
const isWriteOption = isWriteOptions(obj);
|
||||
const keys = Object.keys(obj);
|
||||
return isWriteOption || keys.some(key => validKeys.includes(key));
|
||||
};
|
||||
|
||||
class LoggerClient implements Logger
|
||||
{
|
||||
@ -45,11 +52,11 @@ class LoggerClient implements Logger
|
||||
if (typeof this.#_logLevelMapping[type] === 'undefined')
|
||||
throw new Error(`Missing logLevelMapping for type ${type}`);
|
||||
Object.defineProperty(this, type, {
|
||||
value: (msg: string, o?: WriteOptions) =>
|
||||
{
|
||||
const { labels = [], ...writeOpts } = o ?? {};
|
||||
this.#transport(msg, { ...writeOpts, type, labels: [ ...this.#labels, ...labels ] });
|
||||
}
|
||||
value: (...rest: [...entries: Loggable[], options: TransportOptions]) => this.#transport(type, ...rest)
|
||||
// {
|
||||
// const { labels = [], ...writeOpts } = o ?? {};
|
||||
// this.#transport(msg, { ...writeOpts, type, labels: [ ...this.#labels, ...labels ] });
|
||||
// }
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -74,50 +81,64 @@ class LoggerClient implements Logger
|
||||
throw new Error(`Invalid log level type, expected string or number, got ${typeof level}`);
|
||||
}
|
||||
|
||||
#transport (message: string | object | Error, opts: TransportOptions)
|
||||
#transport (type = 'info', ...args: [...entries: Loggable[], options: TransportOptions])
|
||||
{
|
||||
|
||||
if (this.#_logLevelMapping[opts.type] < this.#_logLevel)
|
||||
if (this.#_logLevelMapping[type] < this.#_logLevel)
|
||||
return;
|
||||
|
||||
if (message instanceof Error)
|
||||
message = makePlainError(message);
|
||||
|
||||
if (typeof message !== 'string')
|
||||
message = inspect(message);
|
||||
const last = args[args.length - 1];
|
||||
let opts: TransportOptions = {};
|
||||
if (typeof last === 'object' && isTransportOpts(last))
|
||||
{
|
||||
opts = last;
|
||||
args.pop();
|
||||
}
|
||||
opts.labels = opts.labels ? [ ...opts.labels, ...this.#labels ] : this.#labels;
|
||||
|
||||
let message = '';
|
||||
for (const entry of args as Loggable[])
|
||||
{
|
||||
if (entry instanceof Error)
|
||||
message += inspect(makePlainError(entry)) + ' ';
|
||||
else if (typeof entry === 'string' || typeof entry === 'number')
|
||||
message += entry + ' ';
|
||||
else
|
||||
message += inspect(entry) + ' ';
|
||||
}
|
||||
|
||||
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
|
||||
process.send({ [this.#_guard]: true, header, message, ...opts });
|
||||
process.send({ [this.#_guard]: true, type, header, message, ...opts });
|
||||
|
||||
}
|
||||
|
||||
// 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 | object | Error, _opts?: WriteOptions): void
|
||||
error (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
warn (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
warn (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
status (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
status (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
info (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
info (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
debug (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
debug (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { WriteOptions } from './Types';
|
||||
import { Loggable, WriteOptions } from './Types';
|
||||
|
||||
export interface Logger {
|
||||
error(str: string, opts?: WriteOptions): void
|
||||
warn(str: string, opts?: WriteOptions): void
|
||||
status(str: string, opts?: WriteOptions): void
|
||||
info(str: string, opts?: WriteOptions): void
|
||||
debug(str: string, opts?: WriteOptions): void
|
||||
error(...args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
warn(...args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
status(...args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
info(...args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
debug(...args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
}
|
@ -10,10 +10,10 @@ import { inspect } from 'node:util';
|
||||
// Own
|
||||
import DiscordWebhook from '@navy.gif/discord-webhook';
|
||||
import Defaults, { LogLevel } from './Defaults.js';
|
||||
import { IPCMessage, LogFunction, Shard, WriteOptions } from './Types.js';
|
||||
import { IPCMessage, LogFunction, Loggable, Shard, WriteOptions } from './Types.js';
|
||||
import { addLogLevel } from '../index.js';
|
||||
import { Logger } from './LoggerInterface.js';
|
||||
import { makePlainError } from './Shared.js';
|
||||
import { isWriteOptions, makePlainError } from './Shared.js';
|
||||
|
||||
const DAY = 1000 * 60 * 60 * 24;
|
||||
|
||||
@ -85,7 +85,8 @@ class MasterLogger implements Logger
|
||||
if (typeof this.#_logLevelMapping[type] === 'undefined')
|
||||
throw new Error(`Missing logLevelMapping for type ${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)
|
||||
value: (...rest: [...entries: Loggable[], options: WriteOptions]) => this.write(type, ...rest)
|
||||
});
|
||||
}
|
||||
this.#colours = { ...Defaults.Colours, ...customColours };
|
||||
@ -207,17 +208,31 @@ class MasterLogger implements Logger
|
||||
});
|
||||
}
|
||||
|
||||
write (type = 'info', text: string | object | Error, { subheader = '', shard, broadcast = false, labels = [] }: WriteOptions = {})
|
||||
write (type = 'info', ...args: [...entries: Loggable[], options: WriteOptions])
|
||||
{
|
||||
const last = args[args.length - 1];
|
||||
let { subheader = '', shard, broadcast = false, labels = [] }: WriteOptions = {};
|
||||
if (typeof last === 'object' && isWriteOptions(last))
|
||||
{
|
||||
({ subheader = '', shard, broadcast = false, labels =[] } = last);
|
||||
args.pop();
|
||||
}
|
||||
|
||||
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);
|
||||
let text = '';
|
||||
for (const entry of args as Loggable[])
|
||||
{
|
||||
if (entry instanceof Error)
|
||||
text += inspect(makePlainError(entry)) + ' ';
|
||||
else if (typeof entry === 'string' || typeof entry === 'number')
|
||||
text += entry + ' ';
|
||||
else
|
||||
text += inspect(text) + ' ';
|
||||
}
|
||||
text = text.trim();
|
||||
|
||||
const header = `[${this.date}] [${this._shard(shard)}]`;
|
||||
const maxChars = Math.max(...this.#types.map(t => t.length));
|
||||
@ -233,7 +248,7 @@ class MasterLogger implements Logger
|
||||
}
|
||||
if ((broadcast || (this.#_broadcastLevel <= this.#_logLevelMapping[type])) && this.#webhook)
|
||||
{
|
||||
const description = (subheader.length ? `**${subheader}**: ${process.env.NODE_ENV ?? 'production'}\n` : '') + `\`\`\`${text}\`\`\``;
|
||||
const description = (subheader.length ? `**${subheader.trim()}**: ${process.env.NODE_ENV ?? 'production'}\n` : '') + `\`\`\`${text}\`\`\``;
|
||||
this.#webhook.send({
|
||||
embeds: [{
|
||||
title: `[__${type.toUpperCase()}__] ${this._shard(shard)}`,
|
||||
@ -303,27 +318,27 @@ class MasterLogger implements Logger
|
||||
|
||||
// These methods are dynamically implemented by the constructor
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
error (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
error (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
warn (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
warn (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
status (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
status (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
info (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
info (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
debug (_str: string | object | Error, _opts?: WriteOptions): void
|
||||
debug (..._args: [...entries: Loggable[], options: WriteOptions]): void
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { WriteOptions } from './Types';
|
||||
|
||||
export const makePlainError = (err: Error) =>
|
||||
{
|
||||
return {
|
||||
@ -5,4 +7,16 @@ export const makePlainError = (err: Error) =>
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
};
|
||||
};
|
||||
|
||||
const validKeys = [ 'subheader', 'shard', 'broadcast', 'labels' ];
|
||||
export const isWriteOptions = (obj: object, extended = false): obj is WriteOptions =>
|
||||
{
|
||||
const keys = Object.keys(obj);
|
||||
// Check for invalid keys, in some cases an arbitrary object might share keys
|
||||
// while still allowing for an option to be extended
|
||||
if (!extended && keys.some(key => !validKeys.includes(key)))
|
||||
return false;
|
||||
// Make sure it's not an empty object
|
||||
return keys.some(key => validKeys.includes(key));
|
||||
};
|
@ -23,4 +23,6 @@ type IPCMessage = {
|
||||
|
||||
type LogFunction = (str: string, opts?: WriteOptions) => void
|
||||
|
||||
export { WriteOptions, Shard, IPCMessage, LogFunction };
|
||||
type Loggable = string | number | object | Error
|
||||
|
||||
export { WriteOptions, Shard, IPCMessage, LogFunction, Loggable };
|
Loading…
Reference in New Issue
Block a user