Skip to content

Commit 7de6520

Browse files
waleedlatif1claude
andcommitted
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>
1 parent 6bc34c5 commit 7de6520

5 files changed

Lines changed: 66 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: 27 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,13 @@ export interface UseChatOptions {
12781279
onTitleUpdate?: () => void
12791280
onStreamEnd?: (chatId: string, messages: ChatMessage[]) => void
12801281
initialActiveResourceId?: string | null
1282+
/**
1283+
* Fired once per chat send as soon as the server's `traceparent`
1284+
* response header arrives (i.e. before any stream content). Used by
1285+
* callers to emit a follow-up PostHog event carrying the request ID
1286+
* for correlation with Go-side logs.
1287+
*/
1288+
onRequestStarted?: (info: { requestId: string; userMessageId: string }) => void
12811289
}
12821290

12831291
interface ActiveStreamRecovery {
@@ -1293,7 +1301,10 @@ interface StopGenerationOptions {
12931301
}
12941302

12951303
export function getMothershipUseChatOptions(
1296-
options: Pick<UseChatOptions, 'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId'> = {}
1304+
options: Pick<
1305+
UseChatOptions,
1306+
'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId' | 'onRequestStarted'
1307+
> = {}
12971308
): UseChatOptions {
12981309
return {
12991310
apiPath: MOTHERSHIP_CHAT_API_PATH,
@@ -1305,7 +1316,7 @@ export function getMothershipUseChatOptions(
13051316
export function getWorkflowCopilotUseChatOptions(
13061317
options: Pick<
13071318
UseChatOptions,
1308-
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd'
1319+
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd' | 'onRequestStarted'
13091320
> = {}
13101321
): UseChatOptions {
13111322
return {
@@ -1351,6 +1362,10 @@ export function useChat(
13511362
onTitleUpdateRef.current = options?.onTitleUpdate
13521363
const onStreamEndRef = useRef(options?.onStreamEnd)
13531364
onStreamEndRef.current = options?.onStreamEnd
1365+
const onRequestStartedRef = useRef(options?.onRequestStarted)
1366+
onRequestStartedRef.current = options?.onRequestStarted
1367+
1368+
const getCurrentRequestId = useCallback(() => streamRequestIdRef.current, [])
13541369

13551370
const clearQueueDispatchState = useCallback(() => {
13561371
queueDispatchEpochRef.current++
@@ -4209,6 +4224,15 @@ export function useChat(
42094224
if (traceparent) {
42104225
streamTraceparentRef.current = traceparent
42114226
setCurrentChatTraceparent(traceparent)
4227+
const parts = traceparent.split('-')
4228+
const traceId = parts.length === 4 ? parts[1] : ''
4229+
if (/^[0-9a-f]{32}$/.test(traceId) && traceId !== '0'.repeat(32)) {
4230+
try {
4231+
onRequestStartedRef.current?.({ requestId: traceId, userMessageId })
4232+
} catch (callbackError) {
4233+
logger.warn('onRequestStarted callback threw', { error: callbackError })
4234+
}
4235+
}
42124236
}
42134237

42144238
if (!response.ok) {
@@ -5103,5 +5127,6 @@ export function useChat(
51035127
editQueuedMessage,
51045128
previewSession,
51055129
genericResourceData,
5130+
getCurrentRequestId,
51065131
}
51075132
}

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: 15 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,20 @@ export interface PostHogEventMap {
427428
is_new_task: boolean
428429
}
429430

431+
/**
432+
* Fired when the server has assigned a request ID for a chat send (i.e.
433+
* the SSE response headers have arrived with a `traceparent`). Pairs
434+
* one-to-one with `task_message_sent` (or with a queued/replay dispatch)
435+
* via timestamp proximity, and carries the `request_id` used by Go for
436+
* log correlation.
437+
*/
438+
task_request_started: {
439+
workspace_id: string
440+
view: 'mothership' | 'copilot'
441+
request_id: string
442+
user_message_id: string
443+
}
444+
430445
tour_started: {
431446
tour_type: 'nav' | 'workflow'
432447
}

apps/sim/lib/posthog/server.ts

Lines changed: 3 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,10 +71,12 @@ export function captureServerEvent<E extends PostHogEventName>(
7171
const client = getClient()
7272
if (!client) return
7373

74+
const requestId = getRequestContext()?.requestId
7475
client.capture({
7576
distinctId,
7677
event,
7778
properties: {
79+
...(requestId ? { request_id: requestId } : {}),
7880
...properties,
7981
...(options?.groups ? { $groups: options.groups } : {}),
8082
...(options?.set ? { $set: options.set } : {}),

0 commit comments

Comments
 (0)