Skip to content

Commit d3d58a9

Browse files
TheodoreSpeaksTheodore Liwaleedlatif1
authored
Feat/improved logging (#3833)
* feat(logs): add additional metadata for workflow execution logs * Revert "Feat(logs) upgrade mothership chat messages to error (#3772)" This reverts commit 9d1b976. * Fix lint, address greptile comments * improvement(sidebar): expand sidebar by hovering and clicking the edge (#3830) * improvement(sidebar): expand sidebar by hovering and clicking the edge * improvement(sidebar): add keyboard shortcuts for new workflow/task, center search modal, fix edge ARIA * improvement(sidebar): use Tooltip.Shortcut for inline shortcut display * fix(sidebar): change new workflow shortcut from Mod+Shift+W to Mod+Shift+P to avoid browser close-window conflict * fix(hotkeys): fall back to event.code for international keyboard layout compatibility * fix(sidebar): guard add-workflow shortcut with canEdit and isCreatingWorkflow checks * feat(ui): handle image paste (#3826) * feat(ui): handle image paste * Fix lint * Fix type error --------- Co-authored-by: Theodore Li <theo@sim.ai> * feat(files): interactive markdown checkbox toggling in preview (#3829) * feat(files): interactive markdown checkbox toggling in preview * fix(files): handle ordered-list checkboxes and fix index drift * lint * fix(files): remove counter offset that prevented checkbox toggling * fix(files): apply task-list styling to ordered lists too * fix(files): render single pass when interactive to avoid index drift * fix(files): move useMemo above conditional return to fix Rules of Hooks * fix(files): pass content directly to preview when not streaming to avoid stale frame * improvement(home): position @ mention popup at caret and fix icon consistency (#3831) * improvement(home): position @ mention popup at caret and fix icon consistency * fix(home): pin mirror div to document origin and guard button anchor * chore(auth): restore hybrid.ts to staging * improvement(ui): sidebar (#3832) * Fix logger tests * Add metadata to mothership logs --------- Co-authored-by: Theodore Li <theo@sim.ai> Co-authored-by: Waleed <walif6@gmail.com> Co-authored-by: Theodore Li <theo@sim.ai>
1 parent 1d59eca commit d3d58a9

File tree

31 files changed

+720
-972
lines changed

31 files changed

+720
-972
lines changed

apps/sim/app/api/copilot/chat/route.ts

Lines changed: 78 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
requestChatTitle,
1616
SSE_RESPONSE_HEADERS,
1717
} from '@/lib/copilot/chat-streaming'
18-
import { appendCopilotLogContext } from '@/lib/copilot/logging'
1918
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/models'
2019
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
2120
import { getStreamMeta, readStreamEvents } from '@/lib/copilot/orchestrator/stream/buffer'
@@ -184,36 +183,31 @@ export async function POST(req: NextRequest) {
184183
const wf = await getWorkflowById(workflowId)
185184
resolvedWorkspaceId = wf?.workspaceId ?? undefined
186185
} catch {
187-
logger.warn(
188-
appendCopilotLogContext('Failed to resolve workspaceId from workflow', {
189-
requestId: tracker.requestId,
190-
messageId: userMessageId,
191-
})
192-
)
186+
logger
187+
.withMetadata({ requestId: tracker.requestId, messageId: userMessageId })
188+
.warn('Failed to resolve workspaceId from workflow')
193189
}
194190

195191
const userMessageIdToUse = userMessageId || crypto.randomUUID()
192+
const reqLogger = logger.withMetadata({
193+
requestId: tracker.requestId,
194+
messageId: userMessageIdToUse,
195+
})
196196
try {
197-
logger.error(
198-
appendCopilotLogContext('Received chat POST', {
199-
requestId: tracker.requestId,
200-
messageId: userMessageIdToUse,
201-
}),
202-
{
203-
workflowId,
204-
hasContexts: Array.isArray(normalizedContexts),
205-
contextsCount: Array.isArray(normalizedContexts) ? normalizedContexts.length : 0,
206-
contextsPreview: Array.isArray(normalizedContexts)
207-
? normalizedContexts.map((c: any) => ({
208-
kind: c?.kind,
209-
chatId: c?.chatId,
210-
workflowId: c?.workflowId,
211-
executionId: (c as any)?.executionId,
212-
label: c?.label,
213-
}))
214-
: undefined,
215-
}
216-
)
197+
reqLogger.info('Received chat POST', {
198+
workflowId,
199+
hasContexts: Array.isArray(normalizedContexts),
200+
contextsCount: Array.isArray(normalizedContexts) ? normalizedContexts.length : 0,
201+
contextsPreview: Array.isArray(normalizedContexts)
202+
? normalizedContexts.map((c: any) => ({
203+
kind: c?.kind,
204+
chatId: c?.chatId,
205+
workflowId: c?.workflowId,
206+
executionId: (c as any)?.executionId,
207+
label: c?.label,
208+
}))
209+
: undefined,
210+
})
217211
} catch {}
218212

219213
let currentChat: any = null
@@ -251,40 +245,22 @@ export async function POST(req: NextRequest) {
251245
actualChatId
252246
)
253247
agentContexts = processed
254-
logger.error(
255-
appendCopilotLogContext('Contexts processed for request', {
256-
requestId: tracker.requestId,
257-
messageId: userMessageIdToUse,
258-
}),
259-
{
260-
processedCount: agentContexts.length,
261-
kinds: agentContexts.map((c) => c.type),
262-
lengthPreview: agentContexts.map((c) => c.content?.length ?? 0),
263-
}
264-
)
248+
reqLogger.info('Contexts processed for request', {
249+
processedCount: agentContexts.length,
250+
kinds: agentContexts.map((c) => c.type),
251+
lengthPreview: agentContexts.map((c) => c.content?.length ?? 0),
252+
})
265253
if (
266254
Array.isArray(normalizedContexts) &&
267255
normalizedContexts.length > 0 &&
268256
agentContexts.length === 0
269257
) {
270-
logger.warn(
271-
appendCopilotLogContext(
272-
'Contexts provided but none processed. Check executionId for logs contexts.',
273-
{
274-
requestId: tracker.requestId,
275-
messageId: userMessageIdToUse,
276-
}
277-
)
258+
reqLogger.warn(
259+
'Contexts provided but none processed. Check executionId for logs contexts.'
278260
)
279261
}
280262
} catch (e) {
281-
logger.error(
282-
appendCopilotLogContext('Failed to process contexts', {
283-
requestId: tracker.requestId,
284-
messageId: userMessageIdToUse,
285-
}),
286-
e
287-
)
263+
reqLogger.error('Failed to process contexts', e)
288264
}
289265
}
290266

@@ -313,13 +289,7 @@ export async function POST(req: NextRequest) {
313289
if (result.status === 'fulfilled' && result.value) {
314290
agentContexts.push(result.value)
315291
} else if (result.status === 'rejected') {
316-
logger.error(
317-
appendCopilotLogContext('Failed to resolve resource attachment', {
318-
requestId: tracker.requestId,
319-
messageId: userMessageIdToUse,
320-
}),
321-
result.reason
322-
)
292+
reqLogger.error('Failed to resolve resource attachment', result.reason)
323293
}
324294
}
325295
}
@@ -358,26 +328,20 @@ export async function POST(req: NextRequest) {
358328
)
359329

360330
try {
361-
logger.error(
362-
appendCopilotLogContext('About to call Sim Agent', {
363-
requestId: tracker.requestId,
364-
messageId: userMessageIdToUse,
365-
}),
366-
{
367-
hasContext: agentContexts.length > 0,
368-
contextCount: agentContexts.length,
369-
hasFileAttachments: Array.isArray(requestPayload.fileAttachments),
370-
messageLength: message.length,
371-
mode: effectiveMode,
372-
hasTools: Array.isArray(requestPayload.tools),
373-
toolCount: Array.isArray(requestPayload.tools) ? requestPayload.tools.length : 0,
374-
hasBaseTools: Array.isArray(requestPayload.baseTools),
375-
baseToolCount: Array.isArray(requestPayload.baseTools)
376-
? requestPayload.baseTools.length
377-
: 0,
378-
hasCredentials: !!requestPayload.credentials,
379-
}
380-
)
331+
reqLogger.info('About to call Sim Agent', {
332+
hasContext: agentContexts.length > 0,
333+
contextCount: agentContexts.length,
334+
hasFileAttachments: Array.isArray(requestPayload.fileAttachments),
335+
messageLength: message.length,
336+
mode: effectiveMode,
337+
hasTools: Array.isArray(requestPayload.tools),
338+
toolCount: Array.isArray(requestPayload.tools) ? requestPayload.tools.length : 0,
339+
hasBaseTools: Array.isArray(requestPayload.baseTools),
340+
baseToolCount: Array.isArray(requestPayload.baseTools)
341+
? requestPayload.baseTools.length
342+
: 0,
343+
hasCredentials: !!requestPayload.credentials,
344+
})
381345
} catch {}
382346

383347
if (stream && actualChatId) {
@@ -521,16 +485,10 @@ export async function POST(req: NextRequest) {
521485
.where(eq(copilotChats.id, actualChatId))
522486
}
523487
} catch (error) {
524-
logger.error(
525-
appendCopilotLogContext('Failed to persist chat messages', {
526-
requestId: tracker.requestId,
527-
messageId: userMessageIdToUse,
528-
}),
529-
{
530-
chatId: actualChatId,
531-
error: error instanceof Error ? error.message : 'Unknown error',
532-
}
533-
)
488+
reqLogger.error('Failed to persist chat messages', {
489+
chatId: actualChatId,
490+
error: error instanceof Error ? error.message : 'Unknown error',
491+
})
534492
}
535493
},
536494
},
@@ -572,19 +530,13 @@ export async function POST(req: NextRequest) {
572530
provider: typeof requestPayload?.provider === 'string' ? requestPayload.provider : undefined,
573531
}
574532

575-
logger.error(
576-
appendCopilotLogContext('Non-streaming response from orchestrator', {
577-
requestId: tracker.requestId,
578-
messageId: userMessageIdToUse,
579-
}),
580-
{
581-
hasContent: !!responseData.content,
582-
contentLength: responseData.content?.length || 0,
583-
model: responseData.model,
584-
provider: responseData.provider,
585-
toolCallsCount: responseData.toolCalls?.length || 0,
586-
}
587-
)
533+
reqLogger.info('Non-streaming response from orchestrator', {
534+
hasContent: !!responseData.content,
535+
contentLength: responseData.content?.length || 0,
536+
model: responseData.model,
537+
provider: responseData.provider,
538+
toolCallsCount: responseData.toolCalls?.length || 0,
539+
})
588540

589541
// Save messages if we have a chat
590542
if (currentChat && responseData.content) {
@@ -617,12 +569,7 @@ export async function POST(req: NextRequest) {
617569

618570
// Start title generation in parallel if this is first message (non-streaming)
619571
if (actualChatId && !currentChat.title && conversationHistory.length === 0) {
620-
logger.error(
621-
appendCopilotLogContext('Starting title generation for non-streaming response', {
622-
requestId: tracker.requestId,
623-
messageId: userMessageIdToUse,
624-
})
625-
)
572+
reqLogger.info('Starting title generation for non-streaming response')
626573
requestChatTitle({ message, model: selectedModel, provider, messageId: userMessageIdToUse })
627574
.then(async (title) => {
628575
if (title) {
@@ -633,22 +580,11 @@ export async function POST(req: NextRequest) {
633580
updatedAt: new Date(),
634581
})
635582
.where(eq(copilotChats.id, actualChatId!))
636-
logger.error(
637-
appendCopilotLogContext(`Generated and saved title: ${title}`, {
638-
requestId: tracker.requestId,
639-
messageId: userMessageIdToUse,
640-
})
641-
)
583+
reqLogger.info(`Generated and saved title: ${title}`)
642584
}
643585
})
644586
.catch((error) => {
645-
logger.error(
646-
appendCopilotLogContext('Title generation failed', {
647-
requestId: tracker.requestId,
648-
messageId: userMessageIdToUse,
649-
}),
650-
error
651-
)
587+
reqLogger.error('Title generation failed', error)
652588
})
653589
}
654590

@@ -662,17 +598,11 @@ export async function POST(req: NextRequest) {
662598
.where(eq(copilotChats.id, actualChatId!))
663599
}
664600

665-
logger.error(
666-
appendCopilotLogContext('Returning non-streaming response', {
667-
requestId: tracker.requestId,
668-
messageId: userMessageIdToUse,
669-
}),
670-
{
671-
duration: tracker.getDuration(),
672-
chatId: actualChatId,
673-
responseLength: responseData.content?.length || 0,
674-
}
675-
)
601+
reqLogger.info('Returning non-streaming response', {
602+
duration: tracker.getDuration(),
603+
chatId: actualChatId,
604+
responseLength: responseData.content?.length || 0,
605+
})
676606

677607
return NextResponse.json({
678608
success: true,
@@ -696,33 +626,25 @@ export async function POST(req: NextRequest) {
696626
const duration = tracker.getDuration()
697627

698628
if (error instanceof z.ZodError) {
699-
logger.error(
700-
appendCopilotLogContext('Validation error', {
701-
requestId: tracker.requestId,
702-
messageId: pendingChatStreamID ?? undefined,
703-
}),
704-
{
629+
logger
630+
.withMetadata({ requestId: tracker.requestId, messageId: pendingChatStreamID ?? undefined })
631+
.error('Validation error', {
705632
duration,
706633
errors: error.errors,
707-
}
708-
)
634+
})
709635
return NextResponse.json(
710636
{ error: 'Invalid request data', details: error.errors },
711637
{ status: 400 }
712638
)
713639
}
714640

715-
logger.error(
716-
appendCopilotLogContext('Error handling copilot chat', {
717-
requestId: tracker.requestId,
718-
messageId: pendingChatStreamID ?? undefined,
719-
}),
720-
{
641+
logger
642+
.withMetadata({ requestId: tracker.requestId, messageId: pendingChatStreamID ?? undefined })
643+
.error('Error handling copilot chat', {
721644
duration,
722645
error: error instanceof Error ? error.message : 'Unknown error',
723646
stack: error instanceof Error ? error.stack : undefined,
724-
}
725-
)
647+
})
726648

727649
return NextResponse.json(
728650
{ error: error instanceof Error ? error.message : 'Internal server error' },
@@ -767,16 +689,13 @@ export async function GET(req: NextRequest) {
767689
status: meta?.status || 'unknown',
768690
}
769691
} catch (err) {
770-
logger.warn(
771-
appendCopilotLogContext('Failed to read stream snapshot for chat', {
772-
messageId: chat.conversationId || undefined,
773-
}),
774-
{
692+
logger
693+
.withMetadata({ messageId: chat.conversationId || undefined })
694+
.warn('Failed to read stream snapshot for chat', {
775695
chatId,
776696
conversationId: chat.conversationId,
777697
error: err instanceof Error ? err.message : String(err),
778-
}
779-
)
698+
})
780699
}
781700
}
782701

@@ -795,11 +714,9 @@ export async function GET(req: NextRequest) {
795714
...(streamSnapshot ? { streamSnapshot } : {}),
796715
}
797716

798-
logger.error(
799-
appendCopilotLogContext(`Retrieved chat ${chatId}`, {
800-
messageId: chat.conversationId || undefined,
801-
})
802-
)
717+
logger
718+
.withMetadata({ messageId: chat.conversationId || undefined })
719+
.info(`Retrieved chat ${chatId}`)
803720
return NextResponse.json({ success: true, chat: transformedChat })
804721
}
805722

0 commit comments

Comments
 (0)