Skip to content

Commit a9e9ecf

Browse files
waleedlatif1claude
andauthored
feat(posthog): correlate task events with copilot logs via request_id (#4453)
* feat(posthog): correlate task events with Go logs via request_id Auto-injects server request_id into all PostHog server events from AsyncLocalStorage context. Adds task_request_started client event fired when SSE traceparent response header arrives, carrying the trace ID used by Go for log correlation. task_generation_aborted now includes the in-flight request_id when available. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * improvement(posthog): tighten trace ID parse and verbose comments * fix(posthog): use traceparent header as canonical request_id source --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 6bc34c5 commit a9e9ecf

5 files changed

Lines changed: 58 additions & 4 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/home.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,21 @@ export function Home({ chatId }: HomeProps = {}) {
143143
editQueuedMessage,
144144
previewSession,
145145
genericResourceData,
146+
getCurrentRequestId,
146147
} = useChat(
147148
workspaceId,
148149
chatId,
149150
getMothershipUseChatOptions({
150151
onResourceEvent: handleResourceEvent,
151152
initialActiveResourceId: initialResourceId,
153+
onRequestStarted: ({ requestId, userMessageId }) => {
154+
captureEvent(posthogRef.current, 'task_request_started', {
155+
workspace_id: workspaceId,
156+
view: 'mothership',
157+
request_id: requestId,
158+
user_message_id: userMessageId,
159+
})
160+
},
152161
})
153162
)
154163

@@ -198,6 +207,7 @@ export function Home({ chatId }: HomeProps = {}) {
198207
captureEvent(posthogRef.current, 'task_generation_aborted', {
199208
workspace_id: workspaceId,
200209
view: 'mothership',
210+
request_id: getCurrentRequestId(),
201211
})
202212
void stopGeneration().catch(() => {})
203213
}

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export interface UseChatReturn {
167167
editQueuedMessage: (id: string) => QueuedMessage | undefined
168168
previewSession: FilePreviewSession | null
169169
genericResourceData: GenericResourceData | null
170+
getCurrentRequestId: () => string | undefined
170171
}
171172

172173
const DEPLOY_TOOL_NAMES: Set<string> = new Set([
@@ -1278,6 +1279,8 @@ export interface UseChatOptions {
12781279
onTitleUpdate?: () => void
12791280
onStreamEnd?: (chatId: string, messages: ChatMessage[]) => void
12801281
initialActiveResourceId?: string | null
1282+
/** Fired when the server's `traceparent` response header arrives, before any stream content. */
1283+
onRequestStarted?: (info: { requestId: string; userMessageId: string }) => void
12811284
}
12821285

12831286
interface ActiveStreamRecovery {
@@ -1293,7 +1296,10 @@ interface StopGenerationOptions {
12931296
}
12941297

12951298
export function getMothershipUseChatOptions(
1296-
options: Pick<UseChatOptions, 'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId'> = {}
1299+
options: Pick<
1300+
UseChatOptions,
1301+
'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId' | 'onRequestStarted'
1302+
> = {}
12971303
): UseChatOptions {
12981304
return {
12991305
apiPath: MOTHERSHIP_CHAT_API_PATH,
@@ -1305,7 +1311,7 @@ export function getMothershipUseChatOptions(
13051311
export function getWorkflowCopilotUseChatOptions(
13061312
options: Pick<
13071313
UseChatOptions,
1308-
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd'
1314+
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd' | 'onRequestStarted'
13091315
> = {}
13101316
): UseChatOptions {
13111317
return {
@@ -1351,6 +1357,13 @@ export function useChat(
13511357
onTitleUpdateRef.current = options?.onTitleUpdate
13521358
const onStreamEndRef = useRef(options?.onStreamEnd)
13531359
onStreamEndRef.current = options?.onStreamEnd
1360+
const onRequestStartedRef = useRef(options?.onRequestStarted)
1361+
onRequestStartedRef.current = options?.onRequestStarted
1362+
1363+
const getCurrentRequestId = useCallback(() => {
1364+
const traceId = streamTraceparentRef.current?.split('-')[1] ?? ''
1365+
return /^[0-9a-f]{32}$/.test(traceId) ? traceId : undefined
1366+
}, [])
13541367

13551368
const clearQueueDispatchState = useCallback(() => {
13561369
queueDispatchEpochRef.current++
@@ -4209,6 +4222,14 @@ export function useChat(
42094222
if (traceparent) {
42104223
streamTraceparentRef.current = traceparent
42114224
setCurrentChatTraceparent(traceparent)
4225+
const traceId = traceparent.split('-')[1] ?? ''
4226+
if (/^[0-9a-f]{32}$/.test(traceId)) {
4227+
try {
4228+
onRequestStartedRef.current?.({ requestId: traceId, userMessageId })
4229+
} catch (callbackError) {
4230+
logger.warn('onRequestStarted callback threw', { error: callbackError })
4231+
}
4232+
}
42124233
}
42134234

42144235
if (!response.ok) {
@@ -5103,5 +5124,6 @@ export function useChat(
51035124
editQueuedMessage,
51045125
previewSession,
51055126
genericResourceData,
5127+
getCurrentRequestId,
51065128
}
51075129
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,13 +346,22 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
346346
removeFromQueue: copilotRemoveFromQueue,
347347
sendNow: copilotSendNow,
348348
editQueuedMessage: copilotEditQueuedMessage,
349+
getCurrentRequestId: getCopilotCurrentRequestId,
349350
} = useChat(
350351
workspaceId,
351352
copilotChatId,
352353
getWorkflowCopilotUseChatOptions({
353354
workflowId: activeWorkflowId || undefined,
354355
onTitleUpdate: loadCopilotChats,
355356
onToolResult: handleCopilotToolResult,
357+
onRequestStarted: ({ requestId, userMessageId }) => {
358+
captureEvent(posthogRef.current, 'task_request_started', {
359+
workspace_id: workspaceId,
360+
view: 'copilot',
361+
request_id: requestId,
362+
user_message_id: userMessageId,
363+
})
364+
},
356365
})
357366
)
358367

@@ -413,9 +422,10 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
413422
captureEvent(posthogRef.current, 'task_generation_aborted', {
414423
workspace_id: workspaceId,
415424
view: 'copilot',
425+
request_id: getCopilotCurrentRequestId(),
416426
})
417427
copilotStopGeneration()
418-
}, [copilotStopGeneration, workspaceId])
428+
}, [copilotStopGeneration, getCopilotCurrentRequestId, workspaceId])
419429

420430
const handleCopilotSubmit = useCallback(
421431
(text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => {

apps/sim/lib/posthog/events.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ export interface PostHogEventMap {
418418
task_generation_aborted: {
419419
workspace_id: string
420420
view: 'mothership' | 'copilot'
421+
request_id?: string
421422
}
422423

423424
task_message_sent: {
@@ -427,6 +428,14 @@ export interface PostHogEventMap {
427428
is_new_task: boolean
428429
}
429430

431+
/** Pairs with `task_message_sent` via `request_id` for correlation with server-side logs. */
432+
task_request_started: {
433+
workspace_id: string
434+
view: 'mothership' | 'copilot'
435+
request_id: string
436+
user_message_id: string
437+
}
438+
430439
tour_started: {
431440
tour_type: 'nav' | 'workflow'
432441
}

apps/sim/lib/posthog/server.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createLogger } from '@sim/logger'
1+
import { createLogger, getRequestContext } from '@sim/logger'
22
import type { PostHog } from 'posthog-node'
33
import type { PostHogEventMap, PostHogEventName } from '@/lib/posthog/events'
44

@@ -71,11 +71,14 @@ export function captureServerEvent<E extends PostHogEventName>(
7171
const client = getClient()
7272
if (!client) return
7373

74+
const contextRequestId = getRequestContext()?.requestId
75+
const props = properties as Record<string, unknown>
7476
client.capture({
7577
distinctId,
7678
event,
7779
properties: {
7880
...properties,
81+
...(contextRequestId && !('request_id' in props) ? { request_id: contextRequestId } : {}),
7982
...(options?.groups ? { $groups: options.groups } : {}),
8083
...(options?.set ? { $set: options.set } : {}),
8184
...(options?.setOnce ? { $set_once: options.setOnce } : {}),

0 commit comments

Comments
 (0)