Winston log provider

This commit is contained in:
Ben Allfree 2024-09-25 07:27:00 +00:00
parent a11e819d5e
commit 46aeaaa1c2
20 changed files with 266 additions and 220 deletions

View File

@ -34,7 +34,7 @@ module.exports = {
},
{
name: `health-compact`,
restart_delay: 60 * 1000, // 1 minute
restart_delay: 60 * 60 * 1000 * 24, // 1 day
script: 'pnpm prod:cli health compact',
},
],

View File

@ -65,7 +65,8 @@
"url-pattern": "^1.0.3",
"vhost": "^3.0.2",
"winston": "^3.11.0",
"winston-syslog": "^2.7.0"
"winston-syslog": "^2.7.0",
"winston-transport": "^4.7.1"
},
"devDependencies": {
"@types/cors": "^2.8.17",

View File

@ -140,7 +140,6 @@ export class PhFs implements FileSystem {
? normalize(resolvedVirtualPath)
: join('/', this.cwd, resolvedVirtualPath)
console.log(`***finalVirtualPath`, { finalVirtualPath })
// Create local filesystem path using the platform separator
const [empty, subdomain, ...restOfVirtualPath] = finalVirtualPath.split('/')
dbg({
@ -154,7 +153,7 @@ export class PhFs implements FileSystem {
// Check if the instance is valid
const instance = await (async () => {
console.log(`***checking validity`, { subdomain })
dbg(`checking validity`, { subdomain })
if (!subdomain) return
const instance = await this.client
.collection(`instances`)
@ -225,8 +224,7 @@ export class PhFs implements FileSystem {
async list(path = '.') {
const { dbg, error } = this.log
.create(`list`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(path)
.breadcrumb({ cwd: this.cwd, path })
const { fsPath, instance } = await this._resolvePath(path)
@ -277,9 +275,7 @@ export class PhFs implements FileSystem {
const { dbg, error } = this.log
.create(`get`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(fileName)
.breadcrumb(fsPath)
.breadcrumb({ cwd: this.cwd, fileName, fsPath })
dbg(`get`)
/*
@ -325,8 +321,7 @@ export class PhFs implements FileSystem {
) {
const { dbg, error } = this.log
.create(`write`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(fileName)
.breadcrumb({ cwd: this.cwd, fileName })
dbg(`write`)
const { fsPath, clientPath, instance } = await this._resolvePath(fileName)
@ -362,8 +357,7 @@ export class PhFs implements FileSystem {
): Promise<any> {
const { dbg, error } = this.log
.create(`read`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(fileName)
.breadcrumb({ cwd: this.cwd, fileName })
dbg(`read`)
const { fsPath, clientPath } = await this._resolvePath(fileName)
@ -387,8 +381,7 @@ export class PhFs implements FileSystem {
async delete(path: string) {
const { dbg, error } = this.log
.create(`delete`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(path)
.breadcrumb({ cwd: this.cwd, path })
dbg(`delete`)
const { fsPath, instance } = await this._resolvePath(path)
@ -405,8 +398,7 @@ export class PhFs implements FileSystem {
async mkdir(path: string) {
const { dbg, error } = this.log
.create(`mkdir`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(path)
.breadcrumb({ cwd: this.cwd, path })
dbg(`mkdir`)
const { fsPath } = await this._resolvePath(path)
@ -417,9 +409,7 @@ export class PhFs implements FileSystem {
async rename(from: string, to: string) {
const { dbg, error } = this.log
.create(`rename`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(from)
.breadcrumb(to)
.breadcrumb({ cwd: this.cwd, from, to })
dbg(`rename`)
const { fsPath: fromPath, instance } = await this._resolvePath(from)
@ -434,9 +424,7 @@ export class PhFs implements FileSystem {
async chmod(path: string, mode: Mode) {
const { dbg, error } = this.log
.create(`chmod`)
.breadcrumb(`cwd:${this.cwd}`)
.breadcrumb(path)
.breadcrumb(mode.toString())
.breadcrumb({ cwd: this.cwd, path, mode })
dbg(`chmod`)
const { fsPath } = await this._resolvePath(path)

View File

@ -1,20 +1,22 @@
import { Request } from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
import vhost from 'vhost'
import { logger } from '../../../../../core/ioc'
export function createVhostProxyMiddleware(
host: string,
target: string,
ws = false,
) {
console.log(`Creating ${host}->${target}`)
const { dbg } = logger()
dbg(`Creating ${host}->${target}`)
const handler = createProxyMiddleware({ target, ws, changeOrigin: ws })
return vhost(host, (_req, res, next) => {
const req = _req as unknown as Request
const method = req.method
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
console.log(`${method} ${fullUrl} -> ${target}`)
dbg(`${method} ${fullUrl} -> ${target}`)
// @ts-ignore
return handler(req, res, next)
})

View File

@ -7,7 +7,6 @@ import fs from 'fs'
import http from 'http'
import { createProxyMiddleware } from 'http-proxy-middleware'
import https from 'https'
import { LoggerService } from '../../../../../common'
import {
APEX_DOMAIN,
APP_NAME,
@ -18,13 +17,13 @@ import {
MOTHERSHIP_PORT,
SSL_CERT,
SSL_KEY,
discordAlert,
} from '../../../../../core'
import { logger } from '../../../../../core/ioc'
import { createIpWhitelistMiddleware } from './cidr'
import { createVhostProxyMiddleware } from './createVhostProxyMiddleware'
export const firewall = async () => {
const { debug } = LoggerService().create(`proxy`)
const { dbg, error } = logger()
const PROD_ROUTES = {
[`${MOTHERSHIP_NAME()}.${APEX_DOMAIN()}`]: `http://localhost:${MOTHERSHIP_PORT()}`,
@ -49,10 +48,12 @@ export const firewall = async () => {
app.use(createIpWhitelistMiddleware(IPCIDR_LIST()))
forEach(hostnameRoutes, (target, host) => {
dbg(`Creating ${host}->${target}`)
app.use(createVhostProxyMiddleware(host, target, IS_DEV()))
})
app.get(`/_api/health`, (req, res, next) => {
dbg(`Health check`)
res.json({ status: 'ok' })
res.end()
})
@ -65,19 +66,19 @@ export const firewall = async () => {
const method = req.method
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
debug(`${method} ${fullUrl} -> ${`http://localhost:${DAEMON_PORT()}`}`)
dbg(`${method} ${fullUrl} -> ${`http://localhost:${DAEMON_PORT()}`}`)
handler(req, res, next)
})
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
discordAlert(err.toString())
error(err)
res.status(500).send(err.toString())
}
app.use(errorHandler)
http.createServer(app).listen(80, () => {
console.log('SSL redirect server listening on 80')
dbg('SSL redirect server listening on 80')
})
// HTTPS server options
@ -88,6 +89,6 @@ export const firewall = async () => {
// Create HTTPS server
https.createServer(httpsOptions, app).listen(443, () => {
console.log('HTTPS server running on port 443')
dbg('HTTPS server running on port 443')
})
}

View File

@ -1,15 +1,18 @@
import { exec } from 'child_process'
import { globSync } from 'glob'
import { DATA_ROOT } from '../../../../core'
import { logger } from '../../../core/ioc'
export const compact = async () => {
const { info } = logger()
const files = [`data`, `logs`].flatMap((db) =>
globSync(`${DATA_ROOT()}/*/pb_data/${db}.db{-shm,-wal}`),
)
files.map(async (file) => {
console.log(`Compacting ${file}`)
info(`Compacting ${file}`)
exec(`sqlite3 ${file} ".tables"`)
})
console.log(`Compaction complete`)
info(`Compaction complete`)
}

View File

@ -25,7 +25,7 @@ export const SqliteService = mkSingleton((config: SqliteServiceConfig) => {
filename: string,
): Promise<SqliteServiceApi> => {
const _dbLogger = LoggerService().create(`SqliteService`)
_dbLogger.breadcrumb(filename)
_dbLogger.breadcrumb({ filename })
const { dbg, error, abort } = _dbLogger
trace(`Fetching database`, connections)

View File

@ -3,16 +3,12 @@
import { program } from 'commander'
import EventSource from 'eventsource'
import {
DEBUG,
DefaultSettingsService,
LogLevelName,
LoggerService,
PH_PLUGINS,
SETTINGS,
loadPlugins,
} from '../../core'
import { version } from '../../package.json'
import { GobotService } from '../services/GobotService'
import { EdgeCommand } from './commands/EdgeCommand'
import { FirewallCommand } from './commands/FirewallCommand'
import { HealthCommand } from './commands/HealthCommand'
@ -20,17 +16,13 @@ import { MothershipCommand } from './commands/MothershipCommand'
import { PocketBaseCommand } from './commands/PocketBaseCommand'
import { SendMailCommand } from './commands/SendMailCommand'
import { ServeCommand } from './commands/ServeCommand'
import './ioc'
export type GlobalOptions = {
logLevel?: LogLevelName
debug: boolean
}
DefaultSettingsService(SETTINGS)
LoggerService({ level: DEBUG() ? LogLevelName.Debug : LogLevelName.Info })
GobotService({})
//@ts-ignore
global.EventSource = EventSource

View File

@ -0,0 +1,11 @@
import { LoggerService } from '../common'
import { RegisterEnvSettingsService } from '../constants'
import { ioc } from '../core/ioc'
import { WinstonLoggerService } from '../core/winston'
import { GobotService } from '../services/GobotService'
ioc.register('logger', WinstonLoggerService({}))
RegisterEnvSettingsService()
LoggerService({})
GobotService({})

View File

@ -1,16 +1,13 @@
/// <require "node">
import chalk from 'chalk'
import stringify from 'json-stringify-safe'
import { action, mergeConfig, mkSingleton } from '.'
import { mkSingleton } from '.'
import { logger } from '../core/ioc'
export type LoggerConfig = {
level: LogLevelName
pfx: string[]
}
export type Logger = ReturnType<typeof createLogger>
export const isLevelLte = (a: LogLevelName, b: LogLevelName) => {
return LogLevels[a] <= LogLevels[b]
}
@ -53,118 +50,23 @@ export const LogLevels = {
[LogLevelName.Abort]: 6,
} as const
export const createLogger = (config: Partial<LoggerConfig>) => {
const _config = mergeConfig<LoggerConfig>(
{
level: LogLevelName.Debug,
pfx: [''],
},
config,
)
const { pfx } = _config
const setLevel = (level: LogLevelName) => {
_config.level = level
}
const _pfx = (s: string) =>
[new Date().toISOString(), s, ...pfx]
.filter((v) => !!v)
.map((p) => `[${p}]`)
.join(' ')
const _log = (levelIn: LogLevelName, ...args: any[]) => {
action(`log`, _config.level, levelIn, args)
}
const raw = (...args: any[]) => {
_log(LogLevelName.Raw, _pfx('RAW'), ...args)
}
const trace = (...args: any[]) => {
_log(LogLevelName.Trace, _pfx(`TRACE`), ...args)
}
const dbg = (...args: any[]) => {
_log(LogLevelName.Debug, _pfx(chalk.blueBright('DBG')), ...args)
}
const info = (...args: any[]) => {
_log(
LogLevelName.Info,
_pfx(
isLevelGt(LogLevelName.Info, _config.level) ? chalk.gray(`INFO`) : '',
),
...args,
)
}
const warn = (...args: any[]) => {
_log(
LogLevelName.Warn,
_pfx(chalk.yellow(chalk.cyanBright('WARN'))),
...args,
)
}
const error = (...args: any[]) => {
_log(LogLevelName.Error, ...[_pfx(chalk.bgRed(`ERROR`)), ...args])
}
const criticalError = (...args: any[]) => {
_log(LogLevelName.Error, ...[_pfx(chalk.bgRed(`ERROR`)), ...args])
new Error().stack?.split(/\n/).forEach((line) => {
_log(LogLevelName.Debug, _pfx(chalk.bgRed(`ERROR`)), line)
})
}
const abort = (...args: any[]): never => {
_log(LogLevelName.Abort, true, ...[_pfx(chalk.bgRed(`ABORT`)), ...args])
throw new Error(`Fatal error: ${stringify(args)}`)
}
const create = (name: string, configOverride?: Partial<LoggerConfig>) =>
createLogger({
..._config,
...configOverride,
pfx: [..._config.pfx, name],
})
const breadcrumb = (s: string | object) => {
if (typeof s === 'string') {
pfx.push(s)
} else {
Object.entries(s).forEach(([k, v]) => pfx.push(`${k}: ${v}`))
}
return api
}
// Compatibility func
const child = (name: string) => create(name)
const api = {
raw,
dbg,
warn,
info,
error,
criticalError,
create,
child,
trace,
debug: dbg,
breadcrumb,
abort,
shutdown() {
dbg(`Logger shutting down`)
},
setLevel,
}
return api
export type Logger = {
raw: (...args: any[]) => void
dbg: (...args: any[]) => void
warn: (...args: any[]) => void
info: (...args: any[]) => void
error: (...args: any[]) => void
criticalError: (...args: any[]) => void
create: (name: string, configOverride?: Partial<LoggerConfig>) => Logger
child: (name: string) => Logger
trace: (...args: any[]) => void
debug: (...args: any[]) => void
breadcrumb: (s: object) => Logger
abort: (...args: any[]) => never
shutdown: () => void
setLevel: (level: LogLevelName) => void
}
export type LoggerServiceApi = ReturnType<typeof createLogger>
export const LoggerService = mkSingleton((config: Partial<LoggerConfig> = {}) =>
createLogger(config),
logger(),
)

View File

@ -6,15 +6,11 @@ import { default as env } from 'env-var'
import { mkdirSync, writeFileSync } from 'fs'
import { dirname, join } from 'path'
import { fileURLToPath } from 'url'
import { LogEntry } from 'winston'
import {
InstanceFields,
InstanceId,
IoCManager,
SettingsHandlerFactory,
SettingsService,
UserFields,
mkSingleton,
} from '../core'
import {
mkBoolean,
@ -23,6 +19,7 @@ import {
mkPath,
mkString,
} from './core/Settings'
import { ioc, settings } from './core/ioc'
const __dirname = dirname(fileURLToPath(import.meta.url))
@ -147,55 +144,23 @@ export const SETTINGS = {
PH_GOBOT_ROOT: mkPath(join(_PH_HOME, 'gobot'), { create: true }),
}
export type Settings = ReturnType<typeof DefaultSettingsService>
export type Settings = ReturnType<typeof RegisterEnvSettingsService>
export type SettingsDefinition = {
[_ in keyof Settings]: SettingsHandlerFactory<Settings[_]>
}
export const DefaultSettingsService = mkSingleton(
(settings: typeof SETTINGS) => {
const _settings = SettingsService(settings)
export const RegisterEnvSettingsService = () => {
const _settings = SettingsService(SETTINGS)
ioc.register('settings', _settings)
ioc.register('settings', _settings)
if (DEBUG()) {
logConstants()
}
if (DEBUG()) {
logConstants()
}
return _settings
},
)
export type MothershipProvider = {
getAllInstances(): Promise<InstanceFields[]>
getInstanceById(id: InstanceId): Promise<[InstanceFields, UserFields] | []>
getInstanceBySubdomain(
subdomain: InstanceFields['subdomain'],
): Promise<[InstanceFields, UserFields] | []>
updateInstance(id: InstanceId, fields: Partial<InstanceFields>): Promise<void>
return _settings
}
type UnsubFunc = () => void
export type InstanceLogProvider = (
instanceId: InstanceId,
target: string,
) => {
info(msg: string): void
error(msg: string): void
tail(linesBack: number, data: (line: LogEntry) => void): UnsubFunc
}
export const ioc = new IoCManager<{
settings: Settings
mothership: MothershipProvider
instanceLogger: InstanceLogProvider
}>()
export const settings = () => ioc.service('settings')
export const mothership = () => ioc.service('mothership')
export const instanceLogger = () => ioc.service('instanceLogger')
/** Accessors */
export const PH_PLUGINS = () => settings().PH_PLUGINS

View File

@ -0,0 +1,19 @@
import TransportStream from 'winston-transport'
import { discordAlert } from '..'
export type DiscordTransportType = {
webhookUrl: string
} & TransportStream.TransportStreamOptions
export class DiscordTransport extends TransportStream {
private url: string
constructor(opts: DiscordTransportType) {
super(opts)
this.url = opts.webhookUrl
}
log(info: any, callback: any) {
discordAlert(info)
callback()
}
}

View File

@ -20,8 +20,7 @@ export function SyslogLogger(instanceId: string, target: string) {
const { error, warn } = LoggerService()
.create('SyslogLogger')
.breadcrumb(instanceId)
.breadcrumb(target)
.breadcrumb({ instanceId, target })
const api = {
info: (msg: string) => {

View File

@ -0,0 +1,35 @@
import { LogEntry } from 'winston'
import { InstanceFields, InstanceId, Logger, UserFields } from '../common'
import { IoCManager } from '../common/ioc'
import { Settings } from '../constants'
export type MothershipProvider = {
getAllInstances(): Promise<InstanceFields[]>
getInstanceById(id: InstanceId): Promise<[InstanceFields, UserFields] | []>
getInstanceBySubdomain(
subdomain: InstanceFields['subdomain'],
): Promise<[InstanceFields, UserFields] | []>
updateInstance(id: InstanceId, fields: Partial<InstanceFields>): Promise<void>
}
type UnsubFunc = () => void
export type InstanceLogProvider = (
instanceId: InstanceId,
target: string,
) => {
info(msg: string): void
error(msg: string): void
tail(linesBack: number, data: (line: LogEntry) => void): UnsubFunc
}
export const ioc = new IoCManager<{
settings: Settings
mothership: MothershipProvider
instanceLogger: InstanceLogProvider
logger: Logger
}>()
export const settings = () => ioc.service('settings')
export const mothership = () => ioc.service('mothership')
export const instanceLogger = () => ioc.service('instanceLogger')
export const logger = () => ioc.service('logger')

View File

@ -33,7 +33,7 @@ export const tryFetch = async (
timeoutMs: TRYFETCH_TIMEOUT_MS,
...config,
}
const logger = LoggerService().create(`tryFetch`).breadcrumb(url)
const logger = LoggerService().create(`tryFetch`).breadcrumb({ url })
const { dbg } = logger
return new Promise<Response>((resolve, reject) => {
const again = () => setTimeout(_real_tryFetch, retryMs)

View File

@ -0,0 +1,111 @@
import { inspect } from 'node:util'
import winston from 'winston'
import { Logger, mkSingleton } from '../common'
import { DEBUG, DISCORD_ALERT_CHANNEL_URL } from '../constants'
import { DiscordTransport } from './DiscordTransport'
const format = winston.format.combine(
winston.format.colorize(),
winston.format.printf(({ level, message, timestamp, ...meta }) => {
const final: string[] = []
message.forEach((m: string) => {
if (typeof m === 'string' && !!m.match(/\n/)) {
final.push(...m.split(/\n/))
} else if (typeof m === 'object') {
final.push(inspect(m, { depth: null }))
} else {
final.push(m)
}
})
return `${level}: ${final.join(' ')}`
}),
)
export const WinstonLoggerService = mkSingleton<{}, Logger>(() => {
const logger = winston.createLogger({
format: winston.format.json(),
transports: [
new winston.transports.Console({
level: DEBUG() ? 'debug' : 'info',
format,
}),
new winston.transports.File({
filename: 'error.log',
level: 'error',
maxsize: 100 * 1024 * 1024,
maxFiles: 10,
tailable: true,
}),
new winston.transports.File({
filename: 'debug.log',
level: 'debug',
maxsize: 100 * 1024 * 1024,
maxFiles: 10,
tailable: true,
}),
],
rejectionHandlers: [
new winston.transports.Console({
level: 'error',
format,
}),
new winston.transports.File({
filename: 'rejections.log',
maxsize: 100 * 1024 * 1024,
maxFiles: 10,
tailable: true,
}),
],
exceptionHandlers: [
new winston.transports.Console({
level: 'error',
format,
}),
new winston.transports.File({
filename: 'exceptions.log',
maxsize: 100 * 1024 * 1024,
maxFiles: 10,
tailable: true,
}),
],
defaultMeta: {},
})
logger.exitOnError = true
{
const url = DISCORD_ALERT_CHANNEL_URL()
if (url) {
logger.add(new DiscordTransport({ level: 'error', webhookUrl: url }))
}
}
const createApi = (logger: winston.Logger): Logger => {
const api: Logger = {
create: (name: string) => {
return createApi(logger.child({ ...logger.defaultMeta, name }))
},
raw: (...args: any[]) => logger.silly(args),
dbg: (...args: any[]) => logger.debug(args),
warn: (...args: any[]) => logger.warn(args),
info: (...args: any[]) => logger.info(args),
error: (...args: any[]) => logger.error(args),
criticalError: (...args: any[]) => logger.error(args),
setLevel: (level) => {},
trace: (...args: any[]) => logger.silly(args),
debug: (...args: any[]) => logger.debug(args),
breadcrumb: (s) => {
Object.assign(logger.defaultMeta, s)
return api
},
shutdown: () => {},
child: (name) => createApi(logger.child({ name })),
abort: (...args) => {
logger.error(args)
process.exit(1)
},
}
return api
}
return createApi(logger)
})

View File

@ -33,7 +33,9 @@ export function InstanceLogger(
target: string,
options: Partial<InstanceLoggerOptions> = {},
) {
const { dbg, info } = LoggerService().create(instanceId).breadcrumb(target)
const { dbg, info } = LoggerService()
.create(instanceId)
.breadcrumb({ target })
const { ttl } = mergeConfig<InstanceLoggerOptions>({ ttl: 0 }, options)
dbg({ ttl })
@ -85,8 +87,7 @@ export function InstanceLogger(
const { error, warn } = LoggerService()
.create('InstanceLogger')
.breadcrumb(instanceId)
.breadcrumb(target)
.breadcrumb({ instanceId, target })
const resetTtl = (() => {
let tid: ReturnType<typeof setTimeout>

View File

@ -72,7 +72,7 @@ export const instanceService = mkSingleton(
const getInstanceApi = (instance: InstanceFields): Promise<InstanceApi> => {
const _logger = instanceServiceLogger.create(`getInstanceApi`)
const { id, subdomain, version } = instance
_logger.breadcrumb(`${subdomain}:${id}:${version}`)
_logger.breadcrumb({ subdomain, id, version })
const { dbg, trace } = _logger
return new Promise<InstanceApi>((resolve, reject) => {
let maxTries = instanceApiTimeoutMs / instanceApiCheckIntervalMs
@ -256,7 +256,7 @@ export const instanceService = mkSingleton(
dbg(`shut down: releasing port`)
releasePort()
}, CLEANUP_PRIORITY_LAST)
systemInstanceLogger.breadcrumb(`port:${newPort}`)
systemInstanceLogger.breadcrumb({ port: newPort })
dbg(`Found port`)
/*

View File

@ -94,7 +94,7 @@ export const createPocketbaseService = async (
dev,
} = _cfg
logger.breadcrumb(subdomain).breadcrumb(instanceId)
logger.breadcrumb({ subdomain, instanceId })
const iLogger = SyslogLogger(instanceId, 'exec')
cm.add(async () => {
dbg(`Shutting down iLogger`)
@ -259,7 +259,7 @@ export const createPocketbaseService = async (
cm.shutdown().catch(error)
})
const url = mkInternalUrl(port)
logger.breadcrumb(url)
logger.breadcrumb({ url })
dbg(`Making exit hook for ${url}`)
const unsub = asyncExitHook(async () => {
await api.kill()

28
pnpm-lock.yaml generated
View File

@ -479,6 +479,9 @@ importers:
winston-syslog:
specifier: ^2.7.0
version: 2.7.0(winston@3.11.0)
winston-transport:
specifier: ^4.7.1
version: 4.7.1
devDependencies:
'@types/cors':
specifier: ^2.8.17
@ -3894,6 +3897,10 @@ packages:
resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==}
engines: {node: '>= 12.0.0'}
logform@2.6.1:
resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==}
engines: {node: '>= 12.0.0'}
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
@ -6012,8 +6019,8 @@ packages:
peerDependencies:
winston: ^3.8.2
winston-transport@4.6.0:
resolution: {integrity: sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==}
winston-transport@4.7.1:
resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==}
engines: {node: '>= 12.0.0'}
winston@3.11.0:
@ -7159,7 +7166,7 @@ snapshots:
'@types/glossy': 0.1.3
'@types/node': 20.8.10
winston: 3.11.0
winston-transport: 4.6.0
winston-transport: 4.7.1
JSONStream@1.3.5:
dependencies:
@ -9832,6 +9839,15 @@ snapshots:
safe-stable-stringify: 2.4.3
triple-beam: 1.4.1
logform@2.6.1:
dependencies:
'@colors/colors': 1.6.0
'@types/triple-beam': 1.3.4
fecha: 4.2.3
ms: 2.1.3
safe-stable-stringify: 2.4.3
triple-beam: 1.4.1
lower-case@2.0.2:
dependencies:
tslib: 2.6.2
@ -12167,9 +12183,9 @@ snapshots:
optionalDependencies:
unix-dgram: 2.0.6
winston-transport@4.6.0:
winston-transport@4.7.1:
dependencies:
logform: 2.6.0
logform: 2.6.1
readable-stream: 3.6.2
triple-beam: 1.4.1
@ -12185,7 +12201,7 @@ snapshots:
safe-stable-stringify: 2.4.3
stack-trace: 0.0.10
triple-beam: 1.4.1
winston-transport: 4.6.0
winston-transport: 4.7.1
with@7.0.2:
dependencies: