modernirc
Version:
IRC library for creating modern IRC bots
185 lines (172 loc) • 5.11 kB
JavaScript
/**
* Internal Logger
*
* Console, file and HTTP capability
*/
import http from 'node:http'
import https from 'node:https'
import qs from 'node:querystring'
import { join } from 'node:path'
import { createWriteStream } from 'node:fs'
import process from 'node:process'
function defaultPrettyFormat({ name, date, level, text }) {
return `[${date}] (${level.toUpperCase()}) ${name}:${text}\n`
}
function createConsoleTransporter(name, options = {}) {
const { pretty = false, formatter = null } = options
return {
output(_level, _raw) {
if (pretty) {
process.stdout.write(
// (formatter || defaultPrettyFormat)(`[${new Date().toISOString()}] (${_level.toUpperCase()}) ${name}:${_raw}\n`)
(formatter || defaultPrettyFormat)({
name,
date: new Date().toISOString(),
level: _level,
text: _raw,
})
)
} else {
process.stdout.write(`${JSON.stringify({
date: new Date().toISOString(),
level: _level.toUpperCase(),
name,
message: _raw,
})}\n`)
}
},
}
}
function createFileTransporter(name, options = {}) {
const { pretty = false, path = '/var/log', formatter = null } = options
const fout = createWriteStream(join(path, `${name}.log`))
return {
output(_level, _raw) {
if (pretty) {
fout.write(
(formatter || defaultPrettyFormat)({
name,
date: new Date().toISOString(),
level: _level,
text: _raw,
})
)
} else {
fout.write(`${JSON.stringify({
date: new Date().toISOString(),
level: _level.toUpperCase(),
name,
message: _raw,
})}\n`)
}
},
}
}
function createHttpTransporter(name, options = {}) {
const {
target,
method = 'post',
formatter = null,
contentType = 'application/json',
headers = {},
} = options
return {
output(_level, _raw) {
let body = {
date: new Date().toISOString(),
name,
level: _level.toUpperCase(),
text: _raw,
}
if (typeof formatter === 'function') {
body = formatter(body)
}
if (contentType === 'application/json') {
body = JSON.stringify(body)
} else if (contentType === 'application/x-www-form-urlencoded') {
body = qs.stringify(body)
} else {
throw new Error('invalid contentType')
}
const intf = target.startsWith('https://') ? https : http
return new Promise((resolve, reject) => {
const req = intf.request(target, {
method,
headers: {
'Content-Type': contentType,
...headers,
},
}, (res) => {
if (res.statusCode < 300) {
resolve()
} else {
reject(new Error(res.statusMessage))
}
})
req.on('error', reject)
req.end(body)
})
},
}
}
/**
* Creates a new logger
* @param options {object}
* @returns {object}
*/
export function createLogger(options = {}) {
const {
level = 'info',
levels = ['error', 'warning', 'info', 'log', 'debug'],
transports = [{ type: 'console', level: 'debug', opts: { pretty: true } }],
name = 'app',
} = options
const indexOfCurrentLevel = levels.indexOf(level)
// const _transporters = {
// console: createConsoleTransporter(name, transports.find((t) => t.type === 'console')?.opts ?? {}),
// file: createFileTransporter(name, transports.find((t) => t.type === 'file')?.opts ?? {}),
// }
const _transporters = transports.map((meta) => {
if (meta.type === 'console') {
return createConsoleTransporter(name, meta.opts)
}
if (meta.type === 'file') {
return createFileTransporter(name, meta.opts)
}
if (meta.type === 'http') {
return createHttpTransporter(name, meta.opts)
}
if (!meta.type || typeof meta.builder === 'function') {
return meta.builder(name, meta.opts)
}
throw new Error(`unknown transport type: ${meta.type}`)
})
return new Proxy({ _options: options }, {
get(target, p) {
if (levels.includes(p.toLowerCase())) {
return (str) => {
const indexOfP = levels.indexOf(p)
for (let i = 0; i < transports.length; i++) {
let _transporterLevel = levels.indexOf(transports[i].level?.toLowerCase())
if (_transporterLevel === -1) {
_transporterLevel = indexOfCurrentLevel
}
if (indexOfP <= _transporterLevel) {
const _transport = _transporters[i]
_transport.output(p, str)
}
}
}
} else if (p.toLowerCase() === 'child') {
// Create a child logger with a specific name
return (childName) => createLogger({
...options,
name: `${name}/${childName}`,
})
} else if (p.toLowerCase() === 'options') {
return target._options
}
return undefined
},
})
}