Added winston logging
- use logger.xxx instead of console.xxx - added express middleware logging (using jsonl) - added LOG_PATH as environment variable - more configs postponed for later iteration
This commit is contained in:
parent
13ad030ea0
commit
a9e269b52c
|
|
@ -8,6 +8,7 @@
|
||||||
**/yarn.lock
|
**/yarn.lock
|
||||||
|
|
||||||
## logs
|
## logs
|
||||||
|
logs/**/*
|
||||||
**/*.log
|
**/*.log
|
||||||
|
|
||||||
## build
|
## build
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@ PORT=3000
|
||||||
# DEBUG=true
|
# DEBUG=true
|
||||||
# DATABASE_PATH=/your_database_path/.flowise
|
# DATABASE_PATH=/your_database_path/.flowise
|
||||||
# APIKEY_PATH=/your_api_key_path/.flowise
|
# APIKEY_PATH=/your_api_key_path/.flowise
|
||||||
|
# LOG_PATH=/your_api_key_path/logs
|
||||||
# EXECUTION_MODE=child or main
|
# EXECUTION_MODE=child or main
|
||||||
|
|
@ -4,4 +4,5 @@ PORT=3000
|
||||||
# DEBUG=true
|
# DEBUG=true
|
||||||
# DATABASE_PATH=/your_database_path/.flowise
|
# DATABASE_PATH=/your_database_path/.flowise
|
||||||
# APIKEY_PATH=/your_api_key_path/.flowise
|
# APIKEY_PATH=/your_api_key_path/.flowise
|
||||||
|
# LOG_PATH=./logs
|
||||||
# EXECUTION_MODE=child or main
|
# EXECUTION_MODE=child or main
|
||||||
|
|
@ -58,7 +58,8 @@
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"socket.io": "^4.6.1",
|
"socket.io": "^4.6.1",
|
||||||
"sqlite3": "^5.1.6",
|
"sqlite3": "^5.1.6",
|
||||||
"typeorm": "^0.3.6"
|
"typeorm": "^0.3.6",
|
||||||
|
"winston": "^3.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import path from 'path'
|
||||||
import * as Server from '../index'
|
import * as Server from '../index'
|
||||||
import * as DataSource from '../DataSource'
|
import * as DataSource from '../DataSource'
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
|
import logger from '../utils/logger'
|
||||||
|
|
||||||
dotenv.config({ path: path.join(__dirname, '..', '..', '.env'), override: true })
|
dotenv.config({ path: path.join(__dirname, '..', '..', '.env'), override: true })
|
||||||
|
|
||||||
|
|
@ -25,11 +26,11 @@ export default class Start extends Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopProcess() {
|
async stopProcess() {
|
||||||
console.info('Shutting down Flowise...')
|
logger.info('Shutting down Flowise...')
|
||||||
try {
|
try {
|
||||||
// Shut down the app after timeout if it ever stuck removing pools
|
// Shut down the app after timeout if it ever stuck removing pools
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.info('Flowise was forced to shut down after 30 secs')
|
logger.info('Flowise was forced to shut down after 30 secs')
|
||||||
process.exit(processExitCode)
|
process.exit(processExitCode)
|
||||||
}, 30000)
|
}, 30000)
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ export default class Start extends Command {
|
||||||
const serverApp = Server.getInstance()
|
const serverApp = Server.getInstance()
|
||||||
if (serverApp) await serverApp.stopApp()
|
if (serverApp) await serverApp.stopApp()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('There was an error shutting down Flowise...', error)
|
logger.error('There was an error shutting down Flowise...', error)
|
||||||
}
|
}
|
||||||
process.exit(processExitCode)
|
process.exit(processExitCode)
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +50,7 @@ export default class Start extends Command {
|
||||||
// Prevent throw new Error from crashing the app
|
// Prevent throw new Error from crashing the app
|
||||||
// TODO: Get rid of this and send proper error message to ui
|
// TODO: Get rid of this and send proper error message to ui
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
console.error('uncaughtException: ', err)
|
logger.error('uncaughtException: ', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
const { flags } = await this.parse(Start)
|
const { flags } = await this.parse(Start)
|
||||||
|
|
@ -63,11 +64,11 @@ export default class Start extends Command {
|
||||||
|
|
||||||
await (async () => {
|
await (async () => {
|
||||||
try {
|
try {
|
||||||
this.log('Starting Flowise...')
|
logger.info('Starting Flowise...')
|
||||||
await DataSource.init()
|
await DataSource.init()
|
||||||
await Server.start()
|
await Server.start()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('There was an error starting Flowise...', error)
|
logger.error('There was an error starting Flowise...', error)
|
||||||
processExitCode = EXIT_CODE.FAILED
|
processExitCode = EXIT_CODE.FAILED
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
process.emit('SIGINT')
|
process.emit('SIGINT')
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import http from 'http'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import basicAuth from 'express-basic-auth'
|
import basicAuth from 'express-basic-auth'
|
||||||
import { Server } from 'socket.io'
|
import { Server } from 'socket.io'
|
||||||
|
import logger from './utils/logger'
|
||||||
|
import { expressRequestLogger } from './utils/logger'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IChatFlow,
|
IChatFlow,
|
||||||
|
|
@ -57,13 +59,16 @@ export class App {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.app = express()
|
this.app = express()
|
||||||
|
|
||||||
|
// Add the expressRequestLogger middleware to log all requests
|
||||||
|
this.app.use(expressRequestLogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
async initDatabase() {
|
async initDatabase() {
|
||||||
// Initialize database
|
// Initialize database
|
||||||
this.AppDataSource.initialize()
|
this.AppDataSource.initialize()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
console.info('📦[server]: Data Source has been initialized!')
|
logger.info('📦 [server]: Data Source has been initialized!')
|
||||||
|
|
||||||
// Initialize pools
|
// Initialize pools
|
||||||
this.nodesPool = new NodesPool()
|
this.nodesPool = new NodesPool()
|
||||||
|
|
@ -75,7 +80,7 @@ export class App {
|
||||||
await getAPIKeys()
|
await getAPIKeys()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('❌[server]: Error during Data Source initialization:', err)
|
logger.error('❌ [server]: Error during Data Source initialization:', err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,7 +619,7 @@ export class App {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -792,7 +797,7 @@ export class App {
|
||||||
const removePromises: any[] = []
|
const removePromises: any[] = []
|
||||||
await Promise.all(removePromises)
|
await Promise.all(removePromises)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`❌[server]: Flowise Server shut down error: ${e}`)
|
logger.error(`❌[server]: Flowise Server shut down error: ${e}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -832,7 +837,7 @@ export async function start(): Promise<void> {
|
||||||
await serverApp.config(io)
|
await serverApp.config(io)
|
||||||
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.info(`⚡️[server]: Flowise Server is listening at ${port}`)
|
logger.info(`⚡️ [server]: Flowise Server is listening at ${port}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// BEWARE: This file is an intereem solution until we have a proper config strategy
|
||||||
|
|
||||||
|
import path from 'path'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
|
dotenv.config({ path: path.join(__dirname, '..', '..', '.env'), override: true })
|
||||||
|
|
||||||
|
// default config
|
||||||
|
const loggingConfig = {
|
||||||
|
dir: process.env.LOG_PATH ?? './logs',
|
||||||
|
server: {
|
||||||
|
level: 'info',
|
||||||
|
filename: 'server.log',
|
||||||
|
errorFilename: 'server-error.log'
|
||||||
|
},
|
||||||
|
express: {
|
||||||
|
level: 'info',
|
||||||
|
format: 'jsonl', // can't be changed currently
|
||||||
|
filename: 'server-requests.log.jsonl' // should end with .jsonl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
logging: loggingConfig
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import logger from './logger'
|
||||||
import {
|
import {
|
||||||
IComponentNodes,
|
IComponentNodes,
|
||||||
IDepthQueue,
|
IDepthQueue,
|
||||||
|
|
@ -227,7 +228,7 @@ export const buildLangchain = async (
|
||||||
databaseEntities
|
databaseEntities
|
||||||
})
|
})
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
throw new Error(e)
|
throw new Error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,7 +596,7 @@ export const replaceAllAPIKeys = async (content: ICommonObject[]): Promise<void>
|
||||||
try {
|
try {
|
||||||
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')
|
await fs.promises.writeFile(getAPIKeyPath(), JSON.stringify(content), 'utf8')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
logger.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
import * as path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import config from './config' // should be replaced by node-config or similar
|
||||||
|
import { createLogger, transports, format } from 'winston'
|
||||||
|
import { NextFunction, Request, Response } from 'express'
|
||||||
|
|
||||||
|
const { combine, timestamp, printf } = format
|
||||||
|
|
||||||
|
// expect the log dir be relative to the projects root
|
||||||
|
const logDir = path.join(__dirname, '../../../..', config.logging.dir ?? './logs')
|
||||||
|
|
||||||
|
// Create the log directory if it doesn't exist
|
||||||
|
if (!fs.existsSync(logDir)) {
|
||||||
|
fs.mkdirSync(logDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = createLogger({
|
||||||
|
format: combine(
|
||||||
|
timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||||
|
format.json(),
|
||||||
|
printf(({ level, message, timestamp }) => {
|
||||||
|
return `${timestamp} [${level.toUpperCase()}]: ${message}`
|
||||||
|
})
|
||||||
|
),
|
||||||
|
defaultMeta: {
|
||||||
|
package: 'server'
|
||||||
|
},
|
||||||
|
transports: [
|
||||||
|
new transports.Console(),
|
||||||
|
new transports.File({
|
||||||
|
filename: path.join(logDir, config.logging.server.filename ?? 'server.log'),
|
||||||
|
level: config.logging.server.level ?? 'info'
|
||||||
|
}),
|
||||||
|
new transports.File({
|
||||||
|
filename: path.join(logDir, config.logging.server.errorFilename ?? 'server-error.log'),
|
||||||
|
level: 'error' // Log only errors to this file
|
||||||
|
})
|
||||||
|
],
|
||||||
|
exceptionHandlers: [
|
||||||
|
new transports.File({
|
||||||
|
filename: path.join(logDir, config.logging.server.errorFilename ?? 'server-error.log')
|
||||||
|
})
|
||||||
|
],
|
||||||
|
rejectionHandlers: [
|
||||||
|
new transports.File({
|
||||||
|
filename: path.join(logDir, config.logging.server.errorFilename ?? 'server-error.log')
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used by express as a middleware.
|
||||||
|
* @example
|
||||||
|
* this.app = express()
|
||||||
|
* this.app.use(expressRequestLogger)
|
||||||
|
*/
|
||||||
|
export function expressRequestLogger(req: Request, res: Response, next: NextFunction): void {
|
||||||
|
const fileLogger = createLogger({
|
||||||
|
format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), format.json()),
|
||||||
|
defaultMeta: {
|
||||||
|
package: 'server',
|
||||||
|
request: {
|
||||||
|
method: req.method,
|
||||||
|
url: req.url,
|
||||||
|
body: req.body,
|
||||||
|
query: req.query,
|
||||||
|
params: req.params,
|
||||||
|
headers: req.headers
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transports: [
|
||||||
|
new transports.File({
|
||||||
|
filename: path.join(logDir, config.logging.express.filename ?? 'server-requests.log.jsonl'),
|
||||||
|
level: 'debug'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const getRequestEmoji = (method: string) => {
|
||||||
|
const requetsEmojis: Record<string, string> = {
|
||||||
|
GET: '⬇️',
|
||||||
|
POST: '⬆️',
|
||||||
|
PUT: '🖊',
|
||||||
|
DELETE: '❌'
|
||||||
|
}
|
||||||
|
|
||||||
|
return requetsEmojis[method] || '?'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
fileLogger.info(`${getRequestEmoji(req.method)} ${req.method} ${req.url}`)
|
||||||
|
logger.info(`${getRequestEmoji(req.method)} ${req.method} ${req.url}`)
|
||||||
|
} else {
|
||||||
|
fileLogger.http(`${getRequestEmoji(req.method)} ${req.method} ${req.url}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default logger
|
||||||
Loading…
Reference in New Issue