diff --git a/server/package.json b/server/package.json index 55b329d..603ccbf 100644 --- a/server/package.json +++ b/server/package.json @@ -22,6 +22,7 @@ "html-entities": "^2.5.2", "ip-address": "^9.0.5", "mysql2": "^3.10.2", + "pino": "^9.3.2", "sharp": "^0.33.4", "toml": "^3.0.0", "ws": "^8.17.1" diff --git a/server/src/index.ts b/server/src/index.ts index 36cbe87..ab77534 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,4 +1,4 @@ -import Fastify from 'fastify'; +import Fastify, { FastifyInstance } from 'fastify'; import FastifyWS from '@fastify/websocket'; import FastifyStatic from '@fastify/static'; import FastifyCors from '@fastify/cors'; @@ -14,6 +14,11 @@ import { Database } from './database.js'; import { MSAgentErrorMessage, MSAgentProtocolMessageType } from '@msagent-chat/protocol'; import { DiscordLogger } from './discord.js'; import { ImageUploader } from './imageuploader.js'; +import pino from 'pino'; + +let rootLogger = pino({ + name: 'MSAgentChat' +}); let config: IConfig; let configPath: string; @@ -21,7 +26,7 @@ if (process.argv.length < 3) configPath = './config.toml'; else configPath = process.argv[2]; if (!fs.existsSync(configPath)) { - console.error(`${configPath} not found. Please copy config.example.toml and fill out fields.`); + rootLogger.error(`${configPath} not found. Please copy config.example.toml and fill out fields.`); process.exit(1); } @@ -29,7 +34,7 @@ try { let configRaw = fs.readFileSync(configPath, 'utf-8'); config = toml.parse(configRaw); } catch (e) { - console.error(`Failed to read or parse ${configPath}: ${(e as Error).message}`); + rootLogger.error(`Failed to read or parse ${configPath}: ${(e as Error).message}`); process.exit(1); } @@ -37,9 +42,9 @@ let db = new Database(config.mysql); await db.init(); const app = Fastify({ - logger: true, - bodyLimit: 20971520 -}); + logger: rootLogger.child({ module: 'HTTP' }), + bodyLimit: 20971520, +}) as unknown as FastifyInstance; // fastify please fix your shit app.register(FastifyCors, { origin: config.http.origins, @@ -51,7 +56,7 @@ app.register(FastifyWS); let tts = null; if (config.tts.enabled) { - tts = new TTSClient(config.tts); + tts = new TTSClient(config.tts, rootLogger.child({module: "TTS"})); app.register(FastifyStatic, { root: config.tts.tempDir, prefix: '/api/tts/', @@ -61,13 +66,13 @@ if (config.tts.enabled) { if (!config.chat.agentsDir.endsWith('/')) config.chat.agentsDir += '/'; if (!fs.existsSync(config.chat.agentsDir)) { - console.error(`Directory ${config.chat.agentsDir} does not exist.`); + rootLogger.error(`Directory ${config.chat.agentsDir} does not exist.`); process.exit(1); } for (let agent of config.agents) { if (!fs.existsSync(path.join(config.chat.agentsDir, agent.filename))) { - console.error(`${agent.filename} does not exist.`); + rootLogger.error(`${agent.filename} does not exist.`); process.exit(1); } } @@ -103,7 +108,7 @@ if (config.discord.enabled) { // Image upload let img = new ImageUploader(app, config.images); -let room = new MSAgentChatRoom(config.chat, config.agents, db, img, tts, discord); +let room = new MSAgentChatRoom(config.chat, rootLogger.child({module: "Room#Default"}), config.agents, db, img, tts, discord); app.register(async (app) => { app.get('/api/socket', { websocket: true }, async (socket, req) => { @@ -111,7 +116,7 @@ app.register(async (app) => { let ip: string; if (config.http.proxied) { if (req.headers['x-forwarded-for'] === undefined) { - console.error(`Warning: X-Forwarded-For not set! This is likely a misconfiguration of your reverse proxy.`); + rootLogger.child({module: "HTTP"}).error(`Warning: X-Forwarded-For not set! This is likely a misconfiguration of your reverse proxy.`); socket.close(); return; } @@ -119,7 +124,7 @@ app.register(async (app) => { if (xff instanceof Array) ip = xff[0]; else ip = xff; if (!isIP(ip)) { - console.error(`Warning: X-Forwarded-For malformed! This is likely a misconfiguration of your reverse proxy.`); + rootLogger.child({module: "HTTP"}).error(`Warning: X-Forwarded-For malformed! This is likely a misconfiguration of your reverse proxy.`); socket.close(); return; } diff --git a/server/src/room.ts b/server/src/room.ts index 32f8476..a01c2f8 100644 --- a/server/src/room.ts +++ b/server/src/room.ts @@ -15,6 +15,7 @@ import * as htmlentities from 'html-entities'; import { Database } from './database.js'; import { DiscordLogger } from './discord.js'; import { ImageUploader } from './imageuploader.js'; +import { Logger } from 'pino'; export class MSAgentChatRoom { agents: AgentConfig[]; @@ -25,8 +26,9 @@ export class MSAgentChatRoom { db: Database; img: ImageUploader; discord: DiscordLogger | null; + private logger: Logger; - constructor(config: ChatConfig, agents: AgentConfig[], db: Database, img: ImageUploader, tts: TTSClient | null, discord: DiscordLogger | null) { + constructor(config: ChatConfig, logger: Logger, agents: AgentConfig[], db: Database, img: ImageUploader, tts: TTSClient | null, discord: DiscordLogger | null) { this.agents = agents; this.clients = []; this.config = config; @@ -34,11 +36,18 @@ export class MSAgentChatRoom { this.db = db; this.img = img; this.discord = discord; + this.logger = logger; } addClient(client: Client) { this.clients.push(client); + this.logger.info(`New connection from ${client.ip}`); client.on('close', () => { + if (client.username === null) { + this.logger.info(`${client.ip} disconnected`); + } else { + this.logger.info(`${client.ip} disconnected (${client.username})`); + } this.clients.splice(this.clients.indexOf(client), 1); if (client.username === null) return; let msg: MSAgentRemoveUserMessage = { @@ -52,6 +61,7 @@ export class MSAgentChatRoom { } }); client.on('join', () => { + this.logger.info(`${client.ip} joined as ${client.username}`); let agent = this.agents.find((a) => a.filename === client.agent)!; let initmsg: MSAgentInitMessage = { op: MSAgentProtocolMessageType.Init, @@ -99,7 +109,7 @@ export class MSAgentChatRoom { let filename = await this.tts.synthesizeToFile(message, (++this.msgId).toString(10)); msg.data.audio = '/api/tts/' + filename; } catch (e) { - console.error(`Error synthesizing TTS: ${(e as Error).message}`); + this.logger.error(`Error synthesizing TTS: ${(e as Error).message}`); } } for (const _client of this.getActiveClients()) { diff --git a/server/src/tts.ts b/server/src/tts.ts index 7605b3c..49861a5 100644 --- a/server/src/tts.ts +++ b/server/src/tts.ts @@ -5,15 +5,18 @@ import { Readable } from 'node:stream'; import { ReadableStream } from 'node:stream/web'; import { finished } from 'node:stream/promises'; import ffmpeg from 'fluent-ffmpeg'; +import { Logger } from 'pino'; export class TTSClient { private config: TTSConfig; private deleteOps: Map; + private logger: Logger; - constructor(config: TTSConfig) { + constructor(config: TTSConfig, logger: Logger) { this.config = config; if (!this.config.tempDir.endsWith('/')) this.config.tempDir += '/'; this.deleteOps = new Map(); + this.logger = logger; } async ensureDirectoryExists() { @@ -24,7 +27,7 @@ export class TTSClient { let error = e as NodeJS.ErrnoException; switch (error.code) { case 'ENOTDIR': { - console.warn('File exists at TTS temp directory path. Unlinking...'); + this.logger.warn('File exists at TTS temp directory path. Unlinking...'); await fs.unlink(this.config.tempDir.substring(0, this.config.tempDir.length - 1)); // intentional fall-through } @@ -33,7 +36,7 @@ export class TTSClient { break; } default: { - console.error(`Cannot access TTS Temp dir: ${error.message}`); + this.logger.error(`Cannot access TTS Temp dir: ${error.message}`); process.exit(1); break; } diff --git a/yarn.lock b/yarn.lock index f363c4a..0a0a771 100644 --- a/yarn.lock +++ b/yarn.lock @@ -513,6 +513,7 @@ __metadata: html-entities: "npm:^2.5.2" ip-address: "npm:^9.0.5" mysql2: "npm:^3.10.2" + pino: "npm:^9.3.2" sharp: "npm:^0.33.4" toml: "npm:^3.0.0" typescript: "npm:5.4.5" @@ -3896,6 +3897,27 @@ __metadata: languageName: node linkType: hard +"pino@npm:^9.3.2": + version: 9.3.2 + resolution: "pino@npm:9.3.2" + dependencies: + atomic-sleep: "npm:^1.0.0" + fast-redact: "npm:^3.1.1" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^1.2.0" + pino-std-serializers: "npm:^7.0.0" + process-warning: "npm:^4.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.2.0" + safe-stable-stringify: "npm:^2.3.1" + sonic-boom: "npm:^4.0.1" + thread-stream: "npm:^3.0.0" + bin: + pino: bin.js + checksum: 10c0/698eb2ebfcc4252da9d035fcf9c999bf27615b66ebc47f9b3d7e942750e50ebe38429e6457abcf8014d70125964ddf114e696cb8225b480d9930271708e3fb52 + languageName: node + linkType: hard + "postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" @@ -3985,6 +4007,13 @@ __metadata: languageName: node linkType: hard +"process-warning@npm:^4.0.0": + version: 4.0.0 + resolution: "process-warning@npm:4.0.0" + checksum: 10c0/5312a72b69d37a1b82ad03f3dfa0090dab3804a8fd995d06c28e3c002852bd82f5584217d9f4a3f197892bb2afc22d57e2c662c7e906b5abb48c0380c7b0880d + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10"