Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(err): show a list of symbol sets and associated frames #26300

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ import {
} from '~/types'

import { AlertType, AlertTypeWrite } from './components/Alerts/types'
import { ErrorTrackingStackFrameRecord, ErrorTrackingSymbolSet } from './components/Errors/types'
import {
ACTIVITY_PAGE_SIZE,
DashboardPrivilegeLevel,
Expand Down Expand Up @@ -723,6 +724,14 @@ class ApiRequest {
return this.errorTracking().addPathComponent('stack_frames').withQueryString({ ids })
}

public symbolSets(): ApiRequest {
return this.errorTracking().withAction('symbol_sets')
}

public symbolSetStackFrames(symbolSetId: string): ApiRequest {
return this.symbolSets().withAction(symbolSetId).withAction('stack_frames')
}

// # Warehouse
public dataWarehouseTables(teamId?: TeamType['id']): ApiRequest {
return this.projectsDetail(teamId).addPathComponent('warehouse_tables')
Expand Down Expand Up @@ -1865,6 +1874,18 @@ const api = {
async fetchStackFrames(ids: string[]): Promise<{ content: string }> {
return await new ApiRequest().errorTrackingStackFrames(ids).get()
},

async fetchSymbolSets(): Promise<ErrorTrackingSymbolSet[]> {
return await api.loadPaginatedResults(new ApiRequest().symbolSets().assembleFullUrl())
},

async fetchSymbolSetStackFrames(symbolSetId: string): Promise<ErrorTrackingStackFrameRecord[]> {
return await new ApiRequest().symbolSetStackFrames(symbolSetId).get()
},

async fetchSymbolSet(symbolSetId: string): Promise<ErrorTrackingSymbolSet> {
return await new ApiRequest().symbolSets().withAction(symbolSetId).get()
},
},

recordings: {
Expand Down
23 changes: 15 additions & 8 deletions frontend/src/lib/components/Errors/ErrorDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { useState } from 'react'

import { EventType } from '~/types'

import { StackFrame } from './stackFrameLogic'
import { ErrorTrackingStackFrame } from './types'

interface RawStackTrace {
type: 'raw'
frames: StackFrame[]
frames: ErrorTrackingStackFrame[]
}
interface ResolvedStackTrace {
type: 'resolved'
frames: StackFrame[]
frames: ErrorTrackingStackFrame[]
}

interface Exception {
Expand All @@ -29,26 +29,33 @@ interface Exception {
value: string
}

function StackTrace({ frames, showAllFrames }: { frames: StackFrame[]; showAllFrames: boolean }): JSX.Element | null {
function StackTrace({
frames,
showAllFrames,
}: {
frames: ErrorTrackingStackFrame[]
showAllFrames: boolean
}): JSX.Element | null {
const displayFrames = showAllFrames ? frames : frames.filter((f) => f.in_app)

const panels = displayFrames.map(({ filename, lineno, colno, function: functionName }, index) => {
// TODO - this doesn't account for nulls
const panels = displayFrames.map(({ source, line, column, resolved_name: functionName }, index) => {
return {
key: index,
header: (
<div className="flex flex-wrap space-x-0.5">
<span>{filename}</span>
<span>{source}</span>
{functionName ? (
<div className="flex space-x-0.5">
<span className="text-muted">in</span>
<span>{functionName}</span>
</div>
) : null}
{lineno && colno ? (
{line && column ? (
<div className="flex space-x-0.5">
<span className="text-muted">at line</span>
<span>
{lineno}:{colno}
{line}:{column}
</span>
</div>
) : null}
Expand Down
116 changes: 116 additions & 0 deletions frontend/src/lib/components/Errors/SymbolSetDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { LemonCollapse, Spinner } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { useEffect, useState } from 'react'

import { symbolSetLogic } from './symbolSetLogic'
import { ErrorTrackingStackFrameRecord, ErrorTrackingSymbolSet } from './types'

function StackFrameDisplay({ frame }: { frame: ErrorTrackingStackFrameRecord }): JSX.Element {
const { contents } = frame

return (
<LemonCollapse
panels={[
{
key: frame.raw_id,
header: (
<div className="flex flex-wrap space-x-0.5">
<span>{contents.source || 'Unknown source'}</span>
{contents.resolved_name && (
<div className="flex space-x-0.5">
<span className="text-muted">in</span>
<span>{contents.resolved_name}</span>
</div>
)}
{contents.line && (
<div className="flex space-x-0.5">
<span className="text-muted">at line</span>
<span>
{contents.line}:{contents.column || 0}
</span>
</div>
)}
</div>
),
content: frame.context && <div className="font-mono text-xs whitespace-pre">{frame.context}</div>, // TODO - this needs to account for structure context later
},
]}
/>
)
}

function SymbolSetDisplay({ symbolSet }: { symbolSet: ErrorTrackingSymbolSet }): JSX.Element {
const [expanded, setExpanded] = useState(false)
const { symbolSetStackFrames, symbolSetStackFramesLoading } = useValues(symbolSetLogic)
const { loadStackFrames } = useActions(symbolSetLogic)

useEffect(() => {
if (expanded && !symbolSetStackFrames[symbolSet.id]) {
loadStackFrames({ symbolSetId: symbolSet.id })
}
}, [expanded, symbolSet.id, loadStackFrames, symbolSetStackFrames])

return (
<LemonCollapse
panels={[
{
key: symbolSet.id,
header: (
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<h3 className="mb-0">{symbolSet.ref}</h3>
{symbolSet.failure_reason && (
<div className="text-danger">Failed: {symbolSet.failure_reason}</div>
)}
</div>
<div className="text-muted">Storage: {symbolSet.storage_ptr || 'Not stored'}</div>
</div>
),
content: (
<div className="flex flex-col gap-4 mt-4">
{symbolSetStackFramesLoading ? (
<div className="flex justify-center">
<Spinner className="text-xl" />
</div>
) : (
symbolSetStackFrames[symbolSet.id]?.map((frame: ErrorTrackingStackFrameRecord) => (
<StackFrameDisplay key={frame.raw_id} frame={frame} />
))
)}
</div>
),
},
]}
onChange={(key) => setExpanded(!!key)}
/>
)
}

export function SymbolSetsDisplay(): JSX.Element {
const { symbolSets, symbolSetsLoading } = useValues(symbolSetLogic)
const { loadSymbolSets } = useActions(symbolSetLogic)

useEffect(() => {
loadSymbolSets()
}, [loadSymbolSets])

if (symbolSetsLoading) {
return (
<div className="flex justify-center">
<Spinner className="text-xl" />
</div>
)
}

if (!symbolSets?.length) {
return <div className="text-muted">No symbol sets found</div>
}

return (
<div className="flex flex-col space-y-4">
{symbolSets.map((symbolSet) => (
<SymbolSetDisplay key={symbolSet.id} symbolSet={symbolSet} />
))}
</div>
)
}
11 changes: 2 additions & 9 deletions frontend/src/lib/components/Errors/stackFrameLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@ import { loaders } from 'kea-loaders'
import api from 'lib/api'

import type { stackFrameLogicType } from './stackFrameLogicType'

export interface StackFrame {
filename: string
lineno: number
colno: number
function: string
in_app?: boolean
}
import { ErrorTrackingStackFrame } from './types'

export const stackFrameLogic = kea<stackFrameLogicType>([
path(['components', 'Errors', 'stackFrameLogic']),
loaders(({ values }) => ({
stackFrames: [
{} as Record<string, StackFrame>,
{} as Record<string, ErrorTrackingStackFrame>,
{
loadFrames: async ({ frameIds }: { frameIds: string[] }) => {
const loadedFrameIds = Object.keys(values.stackFrames)
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/lib/components/Errors/symbolSetLogic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { kea, path } from 'kea'
import { loaders } from 'kea-loaders'
import api from 'lib/api'

import type { symbolSetLogicType } from './symbolSetLogicType'
import { ErrorTrackingStackFrameRecord, ErrorTrackingSymbolSet } from './types'

export const symbolSetLogic = kea<symbolSetLogicType>([
path(['components', 'Errors', 'symbolSetLogic']),
loaders(({ values }) => ({
symbolSets: [
[] as ErrorTrackingSymbolSet[],
{
loadSymbolSets: async () => {
return await api.errorTracking.fetchSymbolSets()
},
},
],
symbolSetStackFrames: [
{} as Record<string, ErrorTrackingStackFrameRecord[]>,
{
loadStackFrames: async ({ symbolSetId }: { symbolSetId: string }) => {
const frames = await api.errorTracking.fetchSymbolSetStackFrames(symbolSetId)
return {
...values.symbolSetStackFrames,
[symbolSetId]: frames,
}
},
},
],
})),
])
31 changes: 31 additions & 0 deletions frontend/src/lib/components/Errors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export interface ErrorTrackingStackFrameRecord {
id: string
raw_id: string
created_at: string
symbol_set: string
resolved: boolean
context: string | null // TODO - switch this to the structure we've discussed once the migration is merged
contents: ErrorTrackingStackFrame // For now, while we're not 100% on content structure
}

export interface ErrorTrackingStackFrame {
raw_id: string
mangled_name: string
line: number | null
column: number | null
source: string | null
in_app: boolean
resolved_name: string | null
lang: string
resolved: boolean
resolve_failure: string | null
}

export interface ErrorTrackingSymbolSet {
id: string
ref: string
team_id: number
created_at: string
storage_ptr: string | null
failure_reason: string | null
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export class EventPipelineRunner {
event.team_id
)

if (event.event === '$exception' && event.team_id == 2) {
if (event.event === '$exception') {
const [exceptionAck] = await this.runStep(
produceExceptionSymbolificationEventStep,
[this, rawEvent],
Expand Down
13 changes: 7 additions & 6 deletions posthog/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
dead_letter_queue,
debug_ch_queries,
early_access_feature,
error_tracking,
event_definition,
exports,
feature_flag,
Expand Down Expand Up @@ -499,12 +500,12 @@ def register_grandfathered_environment_nested_viewset(
["project_id"],
)

# projects_router.register(
# r"error_tracking",
# error_tracking.ErrorTrackingGroupViewSet,
# "project_error_tracking",
# ["team_id"],
# )
projects_router.register(
r"error_tracking/symbol_sets",
error_tracking.ErrorTrackingSymbolSetViewSet,
"project_error_tracking_symbol_sets",
["project_id"],
)

projects_router.register(
r"comments",
Expand Down
Loading
Loading