mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
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>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
6bc34c5a68
commit
a9e9ecf767
@@ -143,12 +143,21 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
editQueuedMessage,
|
||||
previewSession,
|
||||
genericResourceData,
|
||||
getCurrentRequestId,
|
||||
} = useChat(
|
||||
workspaceId,
|
||||
chatId,
|
||||
getMothershipUseChatOptions({
|
||||
onResourceEvent: handleResourceEvent,
|
||||
initialActiveResourceId: initialResourceId,
|
||||
onRequestStarted: ({ requestId, userMessageId }) => {
|
||||
captureEvent(posthogRef.current, 'task_request_started', {
|
||||
workspace_id: workspaceId,
|
||||
view: 'mothership',
|
||||
request_id: requestId,
|
||||
user_message_id: userMessageId,
|
||||
})
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
@@ -198,6 +207,7 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
captureEvent(posthogRef.current, 'task_generation_aborted', {
|
||||
workspace_id: workspaceId,
|
||||
view: 'mothership',
|
||||
request_id: getCurrentRequestId(),
|
||||
})
|
||||
void stopGeneration().catch(() => {})
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ export interface UseChatReturn {
|
||||
editQueuedMessage: (id: string) => QueuedMessage | undefined
|
||||
previewSession: FilePreviewSession | null
|
||||
genericResourceData: GenericResourceData | null
|
||||
getCurrentRequestId: () => string | undefined
|
||||
}
|
||||
|
||||
const DEPLOY_TOOL_NAMES: Set<string> = new Set([
|
||||
@@ -1278,6 +1279,8 @@ export interface UseChatOptions {
|
||||
onTitleUpdate?: () => void
|
||||
onStreamEnd?: (chatId: string, messages: ChatMessage[]) => void
|
||||
initialActiveResourceId?: string | null
|
||||
/** Fired when the server's `traceparent` response header arrives, before any stream content. */
|
||||
onRequestStarted?: (info: { requestId: string; userMessageId: string }) => void
|
||||
}
|
||||
|
||||
interface ActiveStreamRecovery {
|
||||
@@ -1293,7 +1296,10 @@ interface StopGenerationOptions {
|
||||
}
|
||||
|
||||
export function getMothershipUseChatOptions(
|
||||
options: Pick<UseChatOptions, 'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId'> = {}
|
||||
options: Pick<
|
||||
UseChatOptions,
|
||||
'onResourceEvent' | 'onStreamEnd' | 'initialActiveResourceId' | 'onRequestStarted'
|
||||
> = {}
|
||||
): UseChatOptions {
|
||||
return {
|
||||
apiPath: MOTHERSHIP_CHAT_API_PATH,
|
||||
@@ -1305,7 +1311,7 @@ export function getMothershipUseChatOptions(
|
||||
export function getWorkflowCopilotUseChatOptions(
|
||||
options: Pick<
|
||||
UseChatOptions,
|
||||
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd'
|
||||
'workflowId' | 'onToolResult' | 'onTitleUpdate' | 'onStreamEnd' | 'onRequestStarted'
|
||||
> = {}
|
||||
): UseChatOptions {
|
||||
return {
|
||||
@@ -1351,6 +1357,13 @@ export function useChat(
|
||||
onTitleUpdateRef.current = options?.onTitleUpdate
|
||||
const onStreamEndRef = useRef(options?.onStreamEnd)
|
||||
onStreamEndRef.current = options?.onStreamEnd
|
||||
const onRequestStartedRef = useRef(options?.onRequestStarted)
|
||||
onRequestStartedRef.current = options?.onRequestStarted
|
||||
|
||||
const getCurrentRequestId = useCallback(() => {
|
||||
const traceId = streamTraceparentRef.current?.split('-')[1] ?? ''
|
||||
return /^[0-9a-f]{32}$/.test(traceId) ? traceId : undefined
|
||||
}, [])
|
||||
|
||||
const clearQueueDispatchState = useCallback(() => {
|
||||
queueDispatchEpochRef.current++
|
||||
@@ -4209,6 +4222,14 @@ export function useChat(
|
||||
if (traceparent) {
|
||||
streamTraceparentRef.current = traceparent
|
||||
setCurrentChatTraceparent(traceparent)
|
||||
const traceId = traceparent.split('-')[1] ?? ''
|
||||
if (/^[0-9a-f]{32}$/.test(traceId)) {
|
||||
try {
|
||||
onRequestStartedRef.current?.({ requestId: traceId, userMessageId })
|
||||
} catch (callbackError) {
|
||||
logger.warn('onRequestStarted callback threw', { error: callbackError })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -5103,5 +5124,6 @@ export function useChat(
|
||||
editQueuedMessage,
|
||||
previewSession,
|
||||
genericResourceData,
|
||||
getCurrentRequestId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,6 +346,7 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
|
||||
removeFromQueue: copilotRemoveFromQueue,
|
||||
sendNow: copilotSendNow,
|
||||
editQueuedMessage: copilotEditQueuedMessage,
|
||||
getCurrentRequestId: getCopilotCurrentRequestId,
|
||||
} = useChat(
|
||||
workspaceId,
|
||||
copilotChatId,
|
||||
@@ -353,6 +354,14 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
onTitleUpdate: loadCopilotChats,
|
||||
onToolResult: handleCopilotToolResult,
|
||||
onRequestStarted: ({ requestId, userMessageId }) => {
|
||||
captureEvent(posthogRef.current, 'task_request_started', {
|
||||
workspace_id: workspaceId,
|
||||
view: 'copilot',
|
||||
request_id: requestId,
|
||||
user_message_id: userMessageId,
|
||||
})
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
@@ -413,9 +422,10 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel
|
||||
captureEvent(posthogRef.current, 'task_generation_aborted', {
|
||||
workspace_id: workspaceId,
|
||||
view: 'copilot',
|
||||
request_id: getCopilotCurrentRequestId(),
|
||||
})
|
||||
copilotStopGeneration()
|
||||
}, [copilotStopGeneration, workspaceId])
|
||||
}, [copilotStopGeneration, getCopilotCurrentRequestId, workspaceId])
|
||||
|
||||
const handleCopilotSubmit = useCallback(
|
||||
(text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => {
|
||||
|
||||
@@ -418,6 +418,7 @@ export interface PostHogEventMap {
|
||||
task_generation_aborted: {
|
||||
workspace_id: string
|
||||
view: 'mothership' | 'copilot'
|
||||
request_id?: string
|
||||
}
|
||||
|
||||
task_message_sent: {
|
||||
@@ -427,6 +428,14 @@ export interface PostHogEventMap {
|
||||
is_new_task: boolean
|
||||
}
|
||||
|
||||
/** Pairs with `task_message_sent` via `request_id` for correlation with server-side logs. */
|
||||
task_request_started: {
|
||||
workspace_id: string
|
||||
view: 'mothership' | 'copilot'
|
||||
request_id: string
|
||||
user_message_id: string
|
||||
}
|
||||
|
||||
tour_started: {
|
||||
tour_type: 'nav' | 'workflow'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { createLogger, getRequestContext } from '@sim/logger'
|
||||
import type { PostHog } from 'posthog-node'
|
||||
import type { PostHogEventMap, PostHogEventName } from '@/lib/posthog/events'
|
||||
|
||||
@@ -71,11 +71,14 @@ export function captureServerEvent<E extends PostHogEventName>(
|
||||
const client = getClient()
|
||||
if (!client) return
|
||||
|
||||
const contextRequestId = getRequestContext()?.requestId
|
||||
const props = properties as Record<string, unknown>
|
||||
client.capture({
|
||||
distinctId,
|
||||
event,
|
||||
properties: {
|
||||
...properties,
|
||||
...(contextRequestId && !('request_id' in props) ? { request_id: contextRequestId } : {}),
|
||||
...(options?.groups ? { $groups: options.groups } : {}),
|
||||
...(options?.set ? { $set: options.set } : {}),
|
||||
...(options?.setOnce ? { $set_once: options.setOnce } : {}),
|
||||
|
||||
Reference in New Issue
Block a user