refactor(logging): improve logging messages for clarity and consistency

- Updated logging statements across various modules to provide more structured and detailed information.
- Changed log levels from info to debug for less critical messages to reduce log clutter.
- Enhanced error logging to include relevant context such as agentId, sessionId, and model details.
- Standardized log messages to follow a consistent format, improving readability and maintainability.
This commit is contained in:
Vaayne 2025-09-24 21:57:02 +08:00
parent 3088887e57
commit 39fcc04d78
18 changed files with 245 additions and 184 deletions

View File

@ -26,7 +26,12 @@ app.use((req, res, next) => {
const start = Date.now()
res.on('finish', () => {
const duration = Date.now() - start
logger.info(`${req.method} ${req.path} - ${res.statusCode} - ${duration}ms`)
logger.info('API request completed', {
method: req.method,
path: req.path,
statusCode: res.statusCode,
durationMs: duration
})
})
next()
})

View File

@ -36,7 +36,7 @@ class ConfigManager {
}
return this._config
} catch (error: any) {
logger.warn('Failed to load config from Redux, using defaults:', error)
logger.warn('Failed to load config from Redux, using defaults', { error })
this._config = {
enabled: false,
port: defaultPort,

View File

@ -6,7 +6,7 @@ const logger = loggerService.withContext('ApiServerErrorHandler')
// oxlint-disable-next-line @typescript-eslint/no-unused-vars
export const errorHandler = (err: Error, _req: Request, res: Response, _next: NextFunction) => {
logger.error('API Server Error:', err)
logger.error('API server error', { error: err })
// Don't expose internal errors in production
const isDev = process.env.NODE_ENV === 'development'

View File

@ -197,10 +197,11 @@ export function setupOpenAPIDocumentation(app: Express) {
})
)
logger.info('OpenAPI documentation setup complete')
logger.info('Documentation available at /api-docs')
logger.info('OpenAPI spec available at /api-docs.json')
logger.info('OpenAPI documentation ready', {
docsPath: '/api-docs',
specPath: '/api-docs.json'
})
} catch (error) {
logger.error('Failed to setup OpenAPI documentation:', error as Error)
logger.error('Failed to setup OpenAPI documentation', { error })
}
}

View File

@ -51,18 +51,18 @@ const modelValidationErrorBody = (error: AgentModelValidationError) => ({
*/
export const createAgent = async (req: Request, res: Response): Promise<Response> => {
try {
logger.info('Creating new agent')
logger.debug('Agent data:', req.body)
logger.debug('Creating agent')
logger.debug('Agent payload', { body: req.body })
const agent = await agentService.createAgent(req.body)
try {
logger.info(`Agent created successfully: ${agent.id}`)
logger.info(`Creating default session for new agent: ${agent.id}`)
logger.info('Agent created', { agentId: agent.id })
logger.debug('Creating default session for agent', { agentId: agent.id })
await sessionService.createSession(agent.id, {})
logger.info(`Default session created for agent: ${agent.id}`)
logger.info('Default session created for agent', { agentId: agent.id })
return res.status(201).json(agent)
} catch (sessionError: any) {
logger.error('Failed to create default session for new agent, rolling back agent creation', {
@ -89,7 +89,7 @@ export const createAgent = async (req: Request, res: Response): Promise<Response
}
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Agent model validation error during create:', {
logger.warn('Agent model validation error during create', {
agentType: error.context.agentType,
field: error.context.field,
model: error.context.model,
@ -98,7 +98,7 @@ export const createAgent = async (req: Request, res: Response): Promise<Response
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error creating agent:', error)
logger.error('Error creating agent', { error })
return res.status(500).json({
error: {
message: `Failed to create agent: ${error.message}`,
@ -171,11 +171,16 @@ export const listAgents = async (req: Request, res: Response): Promise<Response>
const limit = req.query.limit ? parseInt(req.query.limit as string) : 20
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0
logger.info(`Listing agents with limit=${limit}, offset=${offset}`)
logger.debug('Listing agents', { limit, offset })
const result = await agentService.listAgents({ limit, offset })
logger.info(`Retrieved ${result.agents.length} agents (total: ${result.total})`)
logger.info('Agents listed', {
returned: result.agents.length,
total: result.total,
limit,
offset
})
return res.json({
data: result.agents,
total: result.total,
@ -183,7 +188,7 @@ export const listAgents = async (req: Request, res: Response): Promise<Response>
offset
} satisfies ListAgentsResponse)
} catch (error: any) {
logger.error('Error listing agents:', error)
logger.error('Error listing agents', { error })
return res.status(500).json({
error: {
message: 'Failed to list agents',
@ -231,12 +236,12 @@ export const listAgents = async (req: Request, res: Response): Promise<Response>
export const getAgent = async (req: Request, res: Response): Promise<Response> => {
try {
const { agentId } = req.params
logger.info(`Getting agent: ${agentId}`)
logger.debug('Getting agent', { agentId })
const agent = await agentService.getAgent(agentId)
if (!agent) {
logger.warn(`Agent not found: ${agentId}`)
logger.warn('Agent not found', { agentId })
return res.status(404).json({
error: {
message: 'Agent not found',
@ -246,10 +251,10 @@ export const getAgent = async (req: Request, res: Response): Promise<Response> =
})
}
logger.info(`Agent retrieved successfully: ${agentId}`)
logger.info('Agent retrieved', { agentId })
return res.json(agent)
} catch (error: any) {
logger.error('Error getting agent:', error)
logger.error('Error getting agent', { error, agentId: req.params.agentId })
return res.status(500).json({
error: {
message: 'Failed to get agent',
@ -309,8 +314,8 @@ export const getAgent = async (req: Request, res: Response): Promise<Response> =
export const updateAgent = async (req: Request, res: Response): Promise<Response> => {
const { agentId } = req.params
try {
logger.info(`Updating agent: ${agentId}`)
logger.debug('Update data:', req.body)
logger.debug('Updating agent', { agentId })
logger.debug('Replace payload', { body: req.body })
const { validatedBody } = req as ValidationRequest
const replacePayload = (validatedBody ?? {}) as ReplaceAgentRequest
@ -318,7 +323,7 @@ export const updateAgent = async (req: Request, res: Response): Promise<Response
const agent = await agentService.updateAgent(agentId, replacePayload, { replace: true })
if (!agent) {
logger.warn(`Agent not found for update: ${agentId}`)
logger.warn('Agent not found for update', { agentId })
return res.status(404).json({
error: {
message: 'Agent not found',
@ -328,11 +333,11 @@ export const updateAgent = async (req: Request, res: Response): Promise<Response
})
}
logger.info(`Agent updated successfully: ${agentId}`)
logger.info('Agent updated', { agentId })
return res.json(agent)
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Agent model validation error during update:', {
logger.warn('Agent model validation error during update', {
agentId,
agentType: error.context.agentType,
field: error.context.field,
@ -342,7 +347,7 @@ export const updateAgent = async (req: Request, res: Response): Promise<Response
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error updating agent:', error)
logger.error('Error updating agent', { error, agentId })
return res.status(500).json({
error: {
message: 'Failed to update agent: ' + error.message,
@ -455,8 +460,8 @@ export const updateAgent = async (req: Request, res: Response): Promise<Response
export const patchAgent = async (req: Request, res: Response): Promise<Response> => {
const { agentId } = req.params
try {
logger.info(`Partially updating agent: ${agentId}`)
logger.debug('Partial update data:', req.body)
logger.debug('Partially updating agent', { agentId })
logger.debug('Patch payload', { body: req.body })
const { validatedBody } = req as ValidationRequest
const updatePayload = (validatedBody ?? {}) as UpdateAgentRequest
@ -464,7 +469,7 @@ export const patchAgent = async (req: Request, res: Response): Promise<Response>
const agent = await agentService.updateAgent(agentId, updatePayload)
if (!agent) {
logger.warn(`Agent not found for partial update: ${agentId}`)
logger.warn('Agent not found for partial update', { agentId })
return res.status(404).json({
error: {
message: 'Agent not found',
@ -474,11 +479,11 @@ export const patchAgent = async (req: Request, res: Response): Promise<Response>
})
}
logger.info(`Agent partially updated successfully: ${agentId}`)
logger.info('Agent patched', { agentId })
return res.json(agent)
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Agent model validation error during partial update:', {
logger.warn('Agent model validation error during partial update', {
agentId,
agentType: error.context.agentType,
field: error.context.field,
@ -488,7 +493,7 @@ export const patchAgent = async (req: Request, res: Response): Promise<Response>
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error partially updating agent:', error)
logger.error('Error partially updating agent', { error, agentId })
return res.status(500).json({
error: {
message: `Failed to partially update agent: ${error.message}`,
@ -532,12 +537,12 @@ export const patchAgent = async (req: Request, res: Response): Promise<Response>
export const deleteAgent = async (req: Request, res: Response): Promise<Response> => {
try {
const { agentId } = req.params
logger.info(`Deleting agent: ${agentId}`)
logger.debug('Deleting agent', { agentId })
const deleted = await agentService.deleteAgent(agentId)
if (!deleted) {
logger.warn(`Agent not found for deletion: ${agentId}`)
logger.warn('Agent not found for deletion', { agentId })
return res.status(404).json({
error: {
message: 'Agent not found',
@ -547,10 +552,10 @@ export const deleteAgent = async (req: Request, res: Response): Promise<Response
})
}
logger.info(`Agent deleted successfully: ${agentId}`)
logger.info('Agent deleted', { agentId })
return res.status(204).send()
} catch (error: any) {
logger.error('Error deleting agent:', error)
logger.error('Error deleting agent', { error, agentId: req.params.agentId })
return res.status(500).json({
error: {
message: 'Failed to delete agent',

View File

@ -32,8 +32,8 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
const messageData = req.body
logger.info(`Creating streaming message for session: ${sessionId}`)
logger.debug('Streaming message data:', messageData)
logger.info('Creating streaming message', { agentId, sessionId })
logger.debug('Streaming message payload', { messageData })
// Set SSE headers
res.setHeader('Content-Type', 'text/event-stream')
@ -68,7 +68,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
// res.write('data: {"type":"finish"}\n\n')
res.write('data: [DONE]\n\n')
} catch (writeError) {
logger.error('Error writing final sentinel to SSE stream:', { error: writeError as Error })
logger.error('Error writing final sentinel to SSE stream', { error: writeError as Error })
}
res.end()
}
@ -94,7 +94,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
*/
const handleDisconnect = () => {
if (responseEnded) return
logger.info(`Client disconnected from streaming message for session: ${sessionId}`)
logger.info('Streaming client disconnected', { agentId, sessionId })
responseEnded = true
abortController.abort('Client disconnected')
reader.cancel('Client disconnected').catch(() => {})
@ -119,7 +119,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
finalizeResponse()
} catch (error) {
if (responseEnded) return
logger.error('Error reading agent stream:', { error })
logger.error('Error reading agent stream', { error })
try {
res.write(
`data: ${JSON.stringify({
@ -132,7 +132,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
})}\n\n`
)
} catch (writeError) {
logger.error('Error writing stream error to SSE:', { error: writeError })
logger.error('Error writing stream error to SSE', { error: writeError })
}
responseEnded = true
res.end()
@ -140,7 +140,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
}
pumpStream().catch((error) => {
logger.error('Pump stream failure:', { error })
logger.error('Pump stream failure', { error })
})
completion
@ -150,7 +150,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
})
.catch((error) => {
if (responseEnded) return
logger.error(`Streaming message error for session: ${sessionId}:`, error)
logger.error('Streaming message error', { agentId, sessionId, error })
try {
res.write(
`data: ${JSON.stringify({
@ -163,7 +163,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
})}\n\n`
)
} catch (writeError) {
logger.error('Error writing completion error to SSE stream:', { error: writeError })
logger.error('Error writing completion error to SSE stream', { error: writeError })
}
responseEnded = true
res.end()
@ -173,7 +173,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
const timeout = setTimeout(
() => {
if (!responseEnded) {
logger.error(`Streaming message timeout for session: ${sessionId}`)
logger.error('Streaming message timeout', { agentId, sessionId })
try {
res.write(
`data: ${JSON.stringify({
@ -186,7 +186,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
})}\n\n`
)
} catch (writeError) {
logger.error('Error writing timeout to SSE stream:', { error: writeError })
logger.error('Error writing timeout to SSE stream', { error: writeError })
}
abortController.abort('stream timeout')
reader.cancel('stream timeout').catch(() => {})
@ -201,7 +201,11 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
res.on('close', () => clearTimeout(timeout))
res.on('finish', () => clearTimeout(timeout))
} catch (error: any) {
logger.error('Error in streaming message handler:', error)
logger.error('Error in streaming message handler', {
error,
agentId: req.params.agentId,
sessionId: req.params.sessionId
})
// Send error as SSE if possible
if (!res.headersSent) {
@ -222,7 +226,7 @@ export const createMessage = async (req: Request, res: Response): Promise<void>
res.write(`data: ${JSON.stringify(errorResponse)}\n\n`)
} catch (writeError) {
logger.error('Error writing initial error to SSE stream:', { error: writeError })
logger.error('Error writing initial error to SSE stream', { error: writeError })
}
res.end()
@ -239,7 +243,7 @@ export const deleteMessage = async (req: Request, res: Response): Promise<Respon
const deleted = await sessionMessageService.deleteSessionMessage(sessionId, messageId)
if (!deleted) {
logger.warn(`Message ${messageId} not found for session ${sessionId}`)
logger.warn('Session message not found', { agentId, sessionId, messageId })
return res.status(404).json({
error: {
message: 'Message not found for this session',
@ -249,7 +253,7 @@ export const deleteMessage = async (req: Request, res: Response): Promise<Respon
})
}
logger.info(`Message ${messageId} deleted successfully for session ${sessionId}`)
logger.info('Session message deleted', { agentId, sessionId, messageId })
return res.status(204).send()
} catch (error: any) {
if (error?.status === 404) {
@ -268,7 +272,12 @@ export const deleteMessage = async (req: Request, res: Response): Promise<Respon
})
}
logger.error('Error deleting session message:', error)
logger.error('Error deleting session message', {
error,
agentId: req.params.agentId,
sessionId: req.params.sessionId,
messageId: Number(req.params.messageId)
})
return res.status(500).json({
error: {
message: 'Failed to delete session message',

View File

@ -20,16 +20,16 @@ export const createSession = async (req: Request, res: Response): Promise<Respon
try {
const sessionData = req.body
logger.info(`Creating new session for agent: ${agentId}`)
logger.debug('Session data:', sessionData)
logger.debug('Creating new session', { agentId })
logger.debug('Session payload', { sessionData })
const session = await sessionService.createSession(agentId, sessionData)
logger.info(`Session created successfully: ${session?.id}`)
logger.info('Session created', { agentId, sessionId: session?.id })
return res.status(201).json(session)
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Session model validation error during create:', {
logger.warn('Session model validation error during create', {
agentId,
agentType: error.context.agentType,
field: error.context.field,
@ -39,7 +39,7 @@ export const createSession = async (req: Request, res: Response): Promise<Respon
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error creating session:', error)
logger.error('Error creating session', { error, agentId })
return res.status(500).json({
error: {
message: `Failed to create session: ${error.message}`,
@ -51,17 +51,23 @@ export const createSession = async (req: Request, res: Response): Promise<Respon
}
export const listSessions = async (req: Request, res: Response): Promise<Response> => {
const { agentId } = req.params
try {
const { agentId } = req.params
const limit = req.query.limit ? parseInt(req.query.limit as string) : 20
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0
const status = req.query.status as any
logger.info(`Listing sessions for agent: ${agentId} with limit=${limit}, offset=${offset}, status=${status}`)
logger.debug('Listing agent sessions', { agentId, limit, offset, status })
const result = await sessionService.listSessions(agentId, { limit, offset })
logger.info(`Retrieved ${result.sessions.length} sessions (total: ${result.total}) for agent: ${agentId}`)
logger.info('Agent sessions listed', {
agentId,
returned: result.sessions.length,
total: result.total,
limit,
offset
})
return res.json({
data: result.sessions,
total: result.total,
@ -69,7 +75,7 @@ export const listSessions = async (req: Request, res: Response): Promise<Respons
offset
})
} catch (error: any) {
logger.error('Error listing sessions:', error)
logger.error('Error listing sessions', { error, agentId })
return res.status(500).json({
error: {
message: 'Failed to list sessions',
@ -83,12 +89,12 @@ export const listSessions = async (req: Request, res: Response): Promise<Respons
export const getSession = async (req: Request, res: Response): Promise<Response> => {
try {
const { agentId, sessionId } = req.params
logger.info(`Getting session: ${sessionId} for agent: ${agentId}`)
logger.debug('Getting session', { agentId, sessionId })
const session = await sessionService.getSession(agentId, sessionId)
if (!session) {
logger.warn(`Session not found: ${sessionId}`)
logger.warn('Session not found', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found',
@ -110,7 +116,7 @@ export const getSession = async (req: Request, res: Response): Promise<Response>
// }
// Fetch session messages
logger.info(`Fetching messages for session: ${sessionId}`)
logger.debug('Fetching session messages', { sessionId })
const { messages } = await sessionMessageService.listSessionMessages(sessionId)
// Add messages to session
@ -119,10 +125,10 @@ export const getSession = async (req: Request, res: Response): Promise<Response>
messages: messages
}
logger.info(`Session retrieved successfully: ${sessionId} with ${messages.length} messages`)
logger.info('Session retrieved', { agentId, sessionId, messageCount: messages.length })
return res.json(sessionWithMessages)
} catch (error: any) {
logger.error('Error getting session:', error)
logger.error('Error getting session', { error, agentId: req.params.agentId, sessionId: req.params.sessionId })
return res.status(500).json({
error: {
message: 'Failed to get session',
@ -136,13 +142,13 @@ export const getSession = async (req: Request, res: Response): Promise<Response>
export const updateSession = async (req: Request, res: Response): Promise<Response> => {
const { agentId, sessionId } = req.params
try {
logger.info(`Updating session: ${sessionId} for agent: ${agentId}`)
logger.debug('Update data:', req.body)
logger.debug('Updating session', { agentId, sessionId })
logger.debug('Replace payload', { body: req.body })
// First check if session exists and belongs to agent
const existingSession = await sessionService.getSession(agentId, sessionId)
if (!existingSession || existingSession.agent_id !== agentId) {
logger.warn(`Session ${sessionId} not found for agent ${agentId}`)
logger.warn('Session not found for update', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found for this agent',
@ -158,7 +164,7 @@ export const updateSession = async (req: Request, res: Response): Promise<Respon
const session = await sessionService.updateSession(agentId, sessionId, replacePayload)
if (!session) {
logger.warn(`Session not found for update: ${sessionId}`)
logger.warn('Session missing during update', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found',
@ -168,11 +174,11 @@ export const updateSession = async (req: Request, res: Response): Promise<Respon
})
}
logger.info(`Session updated successfully: ${sessionId}`)
logger.info('Session updated', { agentId, sessionId })
return res.json(session satisfies UpdateSessionResponse)
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Session model validation error during update:', {
logger.warn('Session model validation error during update', {
agentId,
sessionId,
agentType: error.context.agentType,
@ -183,7 +189,7 @@ export const updateSession = async (req: Request, res: Response): Promise<Respon
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error updating session:', error)
logger.error('Error updating session', { error, agentId, sessionId })
return res.status(500).json({
error: {
message: `Failed to update session: ${error.message}`,
@ -197,13 +203,13 @@ export const updateSession = async (req: Request, res: Response): Promise<Respon
export const patchSession = async (req: Request, res: Response): Promise<Response> => {
const { agentId, sessionId } = req.params
try {
logger.info(`Patching session: ${sessionId} for agent: ${agentId}`)
logger.debug('Patch data:', req.body)
logger.debug('Patching session', { agentId, sessionId })
logger.debug('Patch payload', { body: req.body })
// First check if session exists and belongs to agent
const existingSession = await sessionService.getSession(agentId, sessionId)
if (!existingSession || existingSession.agent_id !== agentId) {
logger.warn(`Session ${sessionId} not found for agent ${agentId}`)
logger.warn('Session not found for patch', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found for this agent',
@ -217,7 +223,7 @@ export const patchSession = async (req: Request, res: Response): Promise<Respons
const session = await sessionService.updateSession(agentId, sessionId, updateSession)
if (!session) {
logger.warn(`Session not found for patch: ${sessionId}`)
logger.warn('Session missing while patching', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found',
@ -227,11 +233,11 @@ export const patchSession = async (req: Request, res: Response): Promise<Respons
})
}
logger.info(`Session patched successfully: ${sessionId}`)
logger.info('Session patched', { agentId, sessionId })
return res.json(session)
} catch (error: any) {
if (error instanceof AgentModelValidationError) {
logger.warn('Session model validation error during patch:', {
logger.warn('Session model validation error during patch', {
agentId,
sessionId,
agentType: error.context.agentType,
@ -242,7 +248,7 @@ export const patchSession = async (req: Request, res: Response): Promise<Respons
return res.status(400).json(modelValidationErrorBody(error))
}
logger.error('Error patching session:', error)
logger.error('Error patching session', { error, agentId, sessionId })
return res.status(500).json({
error: {
message: `Failed to patch session, ${error.message}`,
@ -256,12 +262,12 @@ export const patchSession = async (req: Request, res: Response): Promise<Respons
export const deleteSession = async (req: Request, res: Response): Promise<Response> => {
try {
const { agentId, sessionId } = req.params
logger.info(`Deleting session: ${sessionId} for agent: ${agentId}`)
logger.debug('Deleting session', { agentId, sessionId })
// First check if session exists and belongs to agent
const existingSession = await sessionService.getSession(agentId, sessionId)
if (!existingSession || existingSession.agent_id !== agentId) {
logger.warn(`Session ${sessionId} not found for agent ${agentId}`)
logger.warn('Session not found for deletion', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found for this agent',
@ -274,7 +280,7 @@ export const deleteSession = async (req: Request, res: Response): Promise<Respon
const deleted = await sessionService.deleteSession(agentId, sessionId)
if (!deleted) {
logger.warn(`Session not found for deletion: ${sessionId}`)
logger.warn('Session missing during delete', { agentId, sessionId })
return res.status(404).json({
error: {
message: 'Session not found',
@ -284,15 +290,15 @@ export const deleteSession = async (req: Request, res: Response): Promise<Respon
})
}
logger.info(`Session deleted successfully: ${sessionId}`)
logger.info('Session deleted', { agentId, sessionId })
const { total } = await sessionService.listSessions(agentId, { limit: 1 })
if (total === 0) {
logger.info(`No remaining sessions for agent ${agentId}, creating default session`)
logger.info('No remaining sessions, creating default', { agentId })
try {
const fallbackSession = await sessionService.createSession(agentId, {})
logger.info('Default session created after deleting last session', {
logger.info('Default session created after delete', {
agentId,
sessionId: fallbackSession?.id
})
@ -313,7 +319,7 @@ export const deleteSession = async (req: Request, res: Response): Promise<Respon
return res.status(204).send()
} catch (error: any) {
logger.error('Error deleting session:', error)
logger.error('Error deleting session', { error, agentId: req.params.agentId, sessionId: req.params.sessionId })
return res.status(500).json({
error: {
message: 'Failed to delete session',
@ -331,11 +337,16 @@ export const listAllSessions = async (req: Request, res: Response): Promise<Resp
const offset = req.query.offset ? parseInt(req.query.offset as string) : 0
const status = req.query.status as any
logger.info(`Listing all sessions with limit=${limit}, offset=${offset}, status=${status}`)
logger.debug('Listing all sessions', { limit, offset, status })
const result = await sessionService.listSessions(undefined, { limit, offset })
logger.info(`Retrieved ${result.sessions.length} sessions (total: ${result.total})`)
logger.info('Sessions listed', {
returned: result.sessions.length,
total: result.total,
limit,
offset
})
return res.json({
data: result.sessions,
total: result.total,
@ -343,7 +354,7 @@ export const listAllSessions = async (req: Request, res: Response): Promise<Resp
offset
} satisfies ListAgentSessionsResponse)
} catch (error: any) {
logger.error('Error listing all sessions:', error)
logger.error('Error listing all sessions', { error })
return res.status(500).json({
error: {
message: 'Failed to list sessions',

View File

@ -29,7 +29,10 @@ export const checkAgentExists = async (req: Request, res: Response, next: any):
next()
} catch (error) {
logger.error('Error checking agent existence:', error as Error)
logger.error('Error checking agent existence', {
error: error as Error,
agentId: req.params.agentId
})
res.status(500).json({
error: {
message: 'Failed to validate agent',

View File

@ -22,7 +22,7 @@ interface ErrorResponseBody {
const mapChatCompletionError = (error: unknown): { status: number; body: ErrorResponseBody } => {
if (error instanceof ChatCompletionValidationError) {
logger.warn('Chat completion validation error:', {
logger.warn('Chat completion validation error', {
errors: error.errors
})
@ -39,7 +39,7 @@ const mapChatCompletionError = (error: unknown): { status: number; body: ErrorRe
}
if (error instanceof ChatCompletionModelError) {
logger.warn('Chat completion model error:', error.error)
logger.warn('Chat completion model error', error.error)
return {
status: 400,
@ -72,7 +72,7 @@ const mapChatCompletionError = (error: unknown): { status: number; body: ErrorRe
errorCode = 'upstream_error'
}
logger.error('Chat completion error:', { error })
logger.error('Chat completion error', { error })
return {
status: statusCode,
@ -86,7 +86,7 @@ const mapChatCompletionError = (error: unknown): { status: number; body: ErrorRe
}
}
logger.error('Chat completion unknown error:', { error })
logger.error('Chat completion unknown error', { error })
return {
status: 500,
@ -193,7 +193,7 @@ router.post('/completions', async (req: Request, res: Response) => {
})
}
logger.info('Chat completion request:', {
logger.debug('Chat completion request', {
model: request.model,
messageCount: request.messages?.length || 0,
stream: request.stream,
@ -217,7 +217,7 @@ router.post('/completions', async (req: Request, res: Response) => {
}
res.write('data: [DONE]\n\n')
} catch (streamError: any) {
logger.error('Stream error:', streamError)
logger.error('Stream error', { error: streamError })
res.write(
`data: ${JSON.stringify({
error: {

View File

@ -43,14 +43,14 @@ const router = express.Router()
*/
router.get('/', async (req: Request, res: Response) => {
try {
logger.info('Get all MCP servers request received')
logger.debug('Listing MCP servers')
const servers = await mcpApiService.getAllServers(req)
return res.json({
success: true,
data: servers
})
} catch (error: any) {
logger.error('Error fetching MCP servers:', error)
logger.error('Error fetching MCP servers', { error })
return res.status(503).json({
success: false,
error: {
@ -103,10 +103,12 @@ router.get('/', async (req: Request, res: Response) => {
*/
router.get('/:server_id', async (req: Request, res: Response) => {
try {
logger.info('Get MCP server info request received')
logger.debug('Get MCP server info request received', {
serverId: req.params.server_id
})
const server = await mcpApiService.getServerInfo(req.params.server_id)
if (!server) {
logger.warn('MCP server not found')
logger.warn('MCP server not found', { serverId: req.params.server_id })
return res.status(404).json({
success: false,
error: {
@ -121,7 +123,7 @@ router.get('/:server_id', async (req: Request, res: Response) => {
data: server
})
} catch (error: any) {
logger.error('Error fetching MCP server info:', error)
logger.error('Error fetching MCP server info', { error, serverId: req.params.server_id })
return res.status(503).json({
success: false,
error: {
@ -137,7 +139,7 @@ router.get('/:server_id', async (req: Request, res: Response) => {
router.all('/:server_id/mcp', async (req: Request, res: Response) => {
const server = await mcpApiService.getServerById(req.params.server_id)
if (!server) {
logger.warn('MCP server not found')
logger.warn('MCP server not found', { serverId: req.params.server_id })
return res.status(404).json({
success: false,
error: {

View File

@ -12,7 +12,9 @@ const providerRouter = express.Router({ mergeParams: true })
// Helper functions for shared logic
async function validateRequestBody(req: Request): Promise<{ valid: boolean; error?: any }> {
logger.info('Validating request body', { body: req.body })
logger.debug('Validating message request body', {
hasBody: Boolean(req.body)
})
const request: MessageCreateParams = req.body
if (!request) {
@ -50,7 +52,7 @@ async function handleStreamingResponse(
}
res.write('data: [DONE]\n\n')
} catch (streamError: any) {
logger.error('Stream error:', streamError)
logger.error('Stream error', { error: streamError })
res.write(
`data: ${JSON.stringify({
type: 'error',
@ -66,7 +68,7 @@ async function handleStreamingResponse(
}
function handleErrorResponse(res: Response, error: any, logger: any): Response {
logger.error('Message processing error:', error)
logger.error('Message processing error', { error })
let statusCode = 500
let errorType = 'api_error'
@ -303,7 +305,10 @@ router.post('/', async (req: Request, res: Response) => {
const modelValidation = await validateModelId(request.model)
if (!modelValidation.valid) {
const error = modelValidation.error!
logger.warn(`Model validation failed for '${request.model}':`, error)
logger.warn('Model validation failed', {
model: request.model,
error
})
return res.status(400).json({
type: 'error',
error: {

View File

@ -75,13 +75,13 @@ const router = express
*/
.get('/', async (req: Request, res: Response) => {
try {
logger.info('Models list request received', { query: req.query })
logger.debug('Models list request received', { query: req.query })
// Validate query parameters using Zod schema
const filterResult = ApiModelsFilterSchema.safeParse(req.query)
if (!filterResult.success) {
logger.warn('Invalid query parameters:', filterResult.error.issues)
logger.warn('Invalid model query parameters', { issues: filterResult.error.issues })
return res.status(400).json({
error: {
message: 'Invalid query parameters',
@ -99,24 +99,20 @@ const router = express
const response = await modelsService.getModels(filter)
if (response.data.length === 0) {
logger.warn(
'No models available from providers. This may be because no OpenAI/Anthropic providers are configured or enabled.',
{ filter }
)
logger.warn('No models available from providers', { filter })
}
logger.info(`Returning ${response.data.length} models`, {
logger.info('Models response ready', {
filter,
total: response.total
})
logger.debug(
'Model IDs:',
response.data.map((m) => m.id)
)
logger.debug('Model IDs returned', {
modelIds: response.data.map((m) => m.id)
})
return res.json(response satisfies ApiModelsResponse)
} catch (error: any) {
logger.error('Error fetching models:', error)
logger.error('Error fetching models', { error })
return res.status(503).json({
error: {
message: 'Failed to retrieve models from available providers',

View File

@ -17,12 +17,12 @@ export class ApiServer {
}
// Load config
const { port, host, apiKey } = await config.load()
const { port, host } = await config.load()
// Initialize AgentService
logger.info('Initializing AgentService...')
logger.info('Initializing AgentService')
await agentService.initialize()
logger.info('AgentService initialized successfully')
logger.info('AgentService initialized')
// Create server with Express app
this.server = createServer(app)
@ -30,8 +30,7 @@ export class ApiServer {
// Start server
return new Promise((resolve, reject) => {
this.server!.listen(port, host, () => {
logger.info(`API Server started at http://${host}:${port}`)
logger.info(`API Key: ${apiKey}`)
logger.info('API server started', { host, port })
resolve()
})
@ -44,7 +43,7 @@ export class ApiServer {
return new Promise((resolve) => {
this.server!.close(() => {
logger.info('API Server stopped')
logger.info('API server stopped')
this.server = null
resolve()
})
@ -62,7 +61,7 @@ export class ApiServer {
const isListening = this.server?.listening || false
const result = hasServer && isListening
logger.debug('isRunning check:', { hasServer, isListening, result })
logger.debug('isRunning check', { hasServer, isListening, result })
return result
}

View File

@ -98,7 +98,7 @@ export class ChatCompletionService {
const { provider, modelId, client } = providerContext
logger.info('Model validation successful:', {
logger.debug('Model validation successful', {
provider: provider.id,
providerType: provider.type,
modelId,
@ -160,7 +160,7 @@ export class ChatCompletionService {
response: OpenAI.Chat.Completions.ChatCompletion
}> {
try {
logger.info('Processing chat completion request:', {
logger.debug('Processing chat completion request', {
model: request.model,
messageCount: request.messages.length,
stream: request.stream
@ -177,7 +177,7 @@ export class ChatCompletionService {
const { provider, modelId, client, providerRequest } = preparation
logger.debug('Sending request to provider:', {
logger.debug('Sending request to provider', {
provider: provider.id,
model: modelId,
apiHost: provider.apiHost
@ -185,14 +185,20 @@ export class ChatCompletionService {
const response = (await client.chat.completions.create(providerRequest)) as OpenAI.Chat.Completions.ChatCompletion
logger.info('Successfully processed chat completion')
logger.info('Chat completion processed', {
modelId,
provider: provider.id
})
return {
provider,
modelId,
response
}
} catch (error: any) {
logger.error('Error processing chat completion:', error)
logger.error('Error processing chat completion', {
error,
model: request.model
})
throw error
}
}
@ -203,7 +209,7 @@ export class ChatCompletionService {
stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>
}> {
try {
logger.info('Processing streaming chat completion request:', {
logger.debug('Processing streaming chat completion request', {
model: request.model,
messageCount: request.messages.length
})
@ -219,7 +225,7 @@ export class ChatCompletionService {
const { provider, modelId, client, providerRequest } = preparation
logger.debug('Sending streaming request to provider:', {
logger.debug('Sending streaming request to provider', {
provider: provider.id,
model: modelId,
apiHost: provider.apiHost
@ -230,14 +236,20 @@ export class ChatCompletionService {
streamRequest
)) as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>
logger.info('Successfully started streaming chat completion')
logger.info('Streaming chat completion started', {
modelId,
provider: provider.id
})
return {
provider,
modelId,
stream
}
} catch (error: any) {
logger.error('Error processing streaming chat completion:', error)
logger.error('Error processing streaming chat completion', {
error,
model: request.model
})
throw error
}
}

View File

@ -49,7 +49,7 @@ class MCPApiService extends EventEmitter {
constructor() {
super()
this.initMcpServer()
logger.silly('MCPApiService initialized')
logger.debug('MCPApiService initialized')
}
private initMcpServer() {
@ -60,7 +60,7 @@ class MCPApiService extends EventEmitter {
async getAllServers(req: Request): Promise<McpServersResp> {
try {
const servers = await getMCPServersFromRedux()
logger.silly(`Returning ${servers.length} servers`)
logger.debug('Returning servers from Redux', { count: servers.length })
const resp: McpServersResp = {
servers: {}
}
@ -77,7 +77,7 @@ class MCPApiService extends EventEmitter {
}
return resp
} catch (error: any) {
logger.error('Failed to get all servers:', error)
logger.error('Failed to get all servers', { error })
throw new Error('Failed to retrieve servers')
}
}
@ -85,17 +85,17 @@ class MCPApiService extends EventEmitter {
// get server by id
async getServerById(id: string): Promise<MCPServer | null> {
try {
logger.silly(`getServerById called with id: ${id}`)
logger.debug('getServerById called', { id })
const servers = await getMCPServersFromRedux()
const server = servers.find((s) => s.id === id)
if (!server) {
logger.warn(`Server with id ${id} not found`)
logger.warn('Server not found', { id })
return null
}
logger.silly(`Returning server with id ${id}`)
logger.debug('Returning server', { id })
return server
} catch (error: any) {
logger.error(`Failed to get server with id ${id}:`, error)
logger.error('Failed to get server', { id, error })
throw new Error('Failed to retrieve server')
}
}
@ -104,7 +104,7 @@ class MCPApiService extends EventEmitter {
try {
const server = await this.getServerById(id)
if (!server) {
logger.warn(`Server with id ${id} not found`)
logger.warn('Server not found while fetching info', { id })
return null
}
@ -118,14 +118,14 @@ class MCPApiService extends EventEmitter {
tools: tools.tools
}
} catch (error: any) {
logger.error(`Failed to get server info with id ${id}:`, error)
logger.error('Failed to get server info', { id, error })
throw new Error('Failed to retrieve server info')
}
}
async handleRequest(req: Request, res: Response, server: MCPServer) {
const sessionId = req.headers['mcp-session-id'] as string | undefined
logger.silly(`Handling request for server with sessionId ${sessionId}`)
logger.debug('Handling MCP request', { sessionId, serverId: server.id })
let transport: StreamableHTTPServerTransport
if (sessionId && transports[sessionId]) {
transport = transports[sessionId]
@ -138,7 +138,7 @@ class MCPApiService extends EventEmitter {
})
transport.onclose = () => {
logger.info(`Transport for sessionId ${sessionId} closed`)
logger.info('Transport closed', { sessionId })
if (transport.sessionId) {
delete transports[transport.sessionId]
}
@ -173,12 +173,15 @@ class MCPApiService extends EventEmitter {
}
}
logger.info(`Request body`, { rawBody: req.body, messages: JSON.stringify(messages) })
logger.debug('Dispatching MCP request', {
sessionId: transport.sessionId ?? sessionId,
messageCount: messages.length
})
await transport.handleRequest(req as IncomingMessage, res as ServerResponse, messages)
}
private onMessage(message: JSONRPCMessage, extra?: MessageExtraInfo) {
logger.info(`Received message: ${JSON.stringify(message)}`, extra)
logger.debug('Received MCP message', { message, extra })
// Handle message here
}
}

View File

@ -58,7 +58,11 @@ export class ModelsService {
logger.debug(`Applied offset: offset=${offset}, showing ${modelData.length} of ${total} models`)
}
logger.info(`Successfully retrieved ${modelData.length} models from ${models.length} total models`)
logger.info('Models retrieved', {
returned: modelData.length,
discovered: models.length,
filter
})
if (models.length > total) {
logger.debug(`Filtered out ${models.length - total} models after deduplication and filtering`)
@ -80,7 +84,7 @@ export class ModelsService {
return response
} catch (error: any) {
logger.error('Error getting models:', error)
logger.error('Error getting models', { error, filter })
return {
object: 'list',
data: []

View File

@ -14,14 +14,16 @@ export async function getAvailableProviders(): Promise<Provider[]> {
// Try to get from cache first (faster)
const cachedSupportedProviders = CacheService.get<Provider[]>(PROVIDERS_CACHE_KEY)
if (cachedSupportedProviders) {
logger.debug(`Found ${cachedSupportedProviders.length} supported providers (from cache)`)
logger.debug('Providers resolved from cache', {
count: cachedSupportedProviders.length
})
return cachedSupportedProviders
}
// If cache is not available, get fresh data from Redux
const providers = await reduxService.select('state.llm.providers')
if (!providers || !Array.isArray(providers)) {
logger.warn('No providers found in Redux store, returning empty array')
logger.warn('No providers found in Redux store')
return []
}
@ -33,11 +35,14 @@ export async function getAvailableProviders(): Promise<Provider[]> {
// Cache the filtered results
CacheService.set(PROVIDERS_CACHE_KEY, supportedProviders, PROVIDERS_CACHE_TTL)
logger.info(`Filtered to ${supportedProviders.length} supported providers from ${providers.length} total providers`)
logger.info('Providers filtered', {
supported: supportedProviders.length,
total: providers.length
})
return supportedProviders
} catch (error: any) {
logger.error('Failed to get providers from Redux store:', error)
logger.error('Failed to get providers from Redux store', { error })
return []
}
}
@ -47,7 +52,7 @@ export async function listAllAvailableModels(): Promise<Model[]> {
const providers = await getAvailableProviders()
return providers.map((p: Provider) => p.models || []).flat()
} catch (error: any) {
logger.error('Failed to list available models:', error)
logger.error('Failed to list available models', { error })
return []
}
}
@ -55,15 +60,13 @@ export async function listAllAvailableModels(): Promise<Model[]> {
export async function getProviderByModel(model: string): Promise<Provider | undefined> {
try {
if (!model || typeof model !== 'string') {
logger.warn(`Invalid model parameter: ${model}`)
logger.warn('Invalid model parameter', { model })
return undefined
}
// Validate model format first
if (!model.includes(':')) {
logger.warn(
`Invalid model format, must contain ':' separator. Expected format "provider:model_id", got: ${model}`
)
logger.warn('Invalid model format missing separator', { model })
return undefined
}
@ -71,7 +74,7 @@ export async function getProviderByModel(model: string): Promise<Provider | unde
const modelInfo = model.split(':')
if (modelInfo.length < 2 || modelInfo[0].length === 0 || modelInfo[1].length === 0) {
logger.warn(`Invalid model format, expected "provider:model_id" with non-empty parts, got: ${model}`)
logger.warn('Invalid model format with empty parts', { model })
return undefined
}
@ -79,16 +82,17 @@ export async function getProviderByModel(model: string): Promise<Provider | unde
const provider = providers.find((p: Provider) => p.id === providerId)
if (!provider) {
logger.warn(
`Provider '${providerId}' not found or not enabled. Available providers: ${providers.map((p) => p.id).join(', ')}`
)
logger.warn('Provider not found for model', {
providerId,
available: providers.map((p) => p.id)
})
return undefined
}
logger.debug(`Found provider '${providerId}' for model: ${model}`)
logger.debug('Provider resolved for model', { providerId, model })
return provider
} catch (error: any) {
logger.error('Failed to get provider by model:', error)
logger.error('Failed to get provider by model', { error, model })
return undefined
}
}
@ -176,7 +180,7 @@ export async function validateModelId(
modelId
}
} catch (error: any) {
logger.error('Error validating model ID:', error)
logger.error('Error validating model ID', { error, model })
return {
valid: false,
error: {
@ -207,7 +211,7 @@ export function transformModelToOpenAI(model: Model, providers: Provider[]): Api
export async function getProviderById(providerId: string): Promise<Provider | undefined> {
try {
if (!providerId || typeof providerId !== 'string') {
logger.warn(`Invalid provider ID parameter: ${providerId}`)
logger.warn('Invalid provider ID parameter', { providerId })
return undefined
}
@ -215,16 +219,17 @@ export async function getProviderById(providerId: string): Promise<Provider | un
const provider = providers.find((p: Provider) => p.id === providerId)
if (!provider) {
logger.warn(
`Provider '${providerId}' not found or not enabled. Available providers: ${providers.map((p) => p.id).join(', ')}`
)
logger.warn('Provider not found by ID', {
providerId,
available: providers.map((p) => p.id)
})
return undefined
}
logger.debug(`Found provider '${providerId}'`)
logger.debug('Provider found by ID', { providerId })
return provider
} catch (error: any) {
logger.error('Failed to get provider by ID:', error)
logger.error('Failed to get provider by ID', { error, providerId })
return undefined
}
}
@ -237,7 +242,7 @@ export function validateProvider(provider: Provider): boolean {
// Check required fields
if (!provider.id || !provider.type || !provider.apiKey || !provider.apiHost) {
logger.warn('Provider missing required fields:', {
logger.warn('Provider missing required fields', {
id: !!provider.id,
type: !!provider.type,
apiKey: !!provider.apiKey,
@ -248,21 +253,22 @@ export function validateProvider(provider: Provider): boolean {
// Check if provider is enabled
if (!provider.enabled) {
logger.debug(`Provider is disabled: ${provider.id}`)
logger.debug('Provider is disabled', { providerId: provider.id })
return false
}
// Support OpenAI and Anthropic type providers
if (provider.type !== 'openai' && provider.type !== 'anthropic') {
logger.debug(
`Provider type '${provider.type}' not supported, only 'openai' and 'anthropic' types are currently supported: ${provider.id}`
)
logger.debug('Provider type not supported', {
providerId: provider.id,
providerType: provider.type
})
return false
}
return true
} catch (error: any) {
logger.error('Error validating provider:', error)
logger.error('Error validating provider', { error, providerId: provider?.id })
return false
}
}

View File

@ -47,12 +47,12 @@ async function getMcpServerConfigById(id: string): Promise<MCPServer | undefined
*/
export async function getMCPServersFromRedux(): Promise<MCPServer[]> {
try {
logger.silly('Getting servers from Redux store')
logger.debug('Getting servers from Redux store')
// Try to get from cache first (faster)
const cachedServers = CacheService.get<MCPServer[]>(MCP_SERVERS_CACHE_KEY)
if (cachedServers) {
logger.silly(`Found ${cachedServers.length} servers (from cache)`)
logger.debug('MCP servers resolved from cache', { count: cachedServers.length })
return cachedServers
}
@ -63,10 +63,10 @@ export async function getMCPServersFromRedux(): Promise<MCPServer[]> {
// Cache the results
CacheService.set(MCP_SERVERS_CACHE_KEY, serverList, MCP_SERVERS_CACHE_TTL)
logger.silly(`Fetched ${serverList.length} servers from Redux store`)
logger.debug('Fetched servers from Redux store', { count: serverList.length })
return serverList
} catch (error: any) {
logger.error('Failed to get servers from Redux:', error)
logger.error('Failed to get servers from Redux', { error })
return []
}
}
@ -91,6 +91,6 @@ export async function getMcpServerById(id: string): Promise<Server> {
cachedServers[id] = newServer
return newServer
}
logger.silly('getMcpServer ', { server: server })
logger.debug('Returning cached MCP server', { id, hasHandlers: Boolean(server) })
return server
}