Skip to content

Commit

Permalink
feat: Error tracking (simple start) (#22814)
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin authored Jun 11, 2024
1 parent 0ec897a commit fda2156
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 102 deletions.
9 changes: 9 additions & 0 deletions frontend/src/layout/navigation-3000/navigationLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
IconServer,
IconTestTube,
IconToggle,
IconWarning,
} from '@posthog/icons'
import { lemonToast, Spinner } from '@posthog/lemon-ui'
import { captureException } from '@sentry/react'
Expand Down Expand Up @@ -450,6 +451,14 @@ export const navigation3000Logic = kea<navigation3000LogicType>([
icon: <IconRewindPlay />,
to: urls.replay(),
},
featureFlags[FEATURE_FLAGS.ERROR_TRACKING]
? {
identifier: Scene.ErrorTracking,
label: 'Error tracking',
icon: <IconWarning />,
to: urls.errorTracking(),
}
: null,
featureFlags[FEATURE_FLAGS.HEATMAPS_UI]
? {
identifier: Scene.Heatmaps,
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
IconTrends,
IconUnlock,
IconUserPaths,
IconWarning,
IconX,
} from '@posthog/icons'
import { Parser } from 'expr-eval'
Expand Down Expand Up @@ -581,6 +582,17 @@ export const commandPaletteLogic = kea<commandPaletteLogicType>([
},
]
: []),
...(values.featureFlags[FEATURE_FLAGS.ERROR_TRACKING]
? [
{
icon: IconWarning,
display: 'Go to Error tracking',
executor: () => {
push(urls.errorTracking())
},
},
]
: []),
{
display: 'Go to Session replay',
icon: IconRewindPlay,
Expand Down
177 changes: 84 additions & 93 deletions frontend/src/lib/components/Errors/ErrorDisplay.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +1,103 @@
import { Meta } from '@storybook/react'
import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay'

import { EventType, RecordingEventType } from '~/types'
import { EventType } from '~/types'

const meta: Meta<typeof ErrorDisplay> = {
title: 'Components/Errors/Error Display',
component: ErrorDisplay,
}
export default meta

function errorEvent(properties: Record<string, any>): EventType | RecordingEventType {
function errorProperties(properties: Record<string, any>): EventType['properties'] {
return {
id: '12345',
elements: [],
uuid: '018880b6-b781-0008-a2e5-629b2624fd2f',
event: '$exception',
properties: {
$os: 'Windows',
$os_version: '10.0',
$browser: 'Chrome',
$device_type: 'Desktop',
$current_url: 'https://app.posthog.com/home',
$host: 'app.posthog.com',
$pathname: '/home',
$browser_version: 113,
$browser_language: 'es-ES',
$screen_height: 1080,
$screen_width: 1920,
$viewport_height: 929,
$viewport_width: 1920,
$lib: 'web',
$lib_version: '1.63.3',
distinct_id: 'iOizUPH4RH65nZjvGVBz5zZUmwdHvq2mxzNySQqqYkG',
$device_id: '186144e7357245-0cfe8bf1b5b877-26021051-1fa400-186144e7358d3',
$active_feature_flags: ['are-the-flags', 'important-for-the-error'],
$feature_flag_payloads: {
'are-the-flags': '{\n "flag": "payload"\n}',
},
$user_id: 'iOizUPH4RH65nZjvGVBz5zZUmwdHvq2mxzNySQqqYkG',
$groups: {
project: '00000000-0000-0000-1847-88f0ffa23444',
organization: '00000000-0000-0000-a050-5d4557279956',
customer: 'the-customer',
instance: 'https://app.posthog.com',
},
$exception_message: 'ResizeObserver loop limit exceeded',
$exception_type: 'Error',
$exception_personURL: 'https://app.posthog.com/person/the-person-id',
$sentry_event_id: 'id-from-the-sentry-integration',
$sentry_exception: {
values: [
{
value: 'ResizeObserver loop limit exceeded',
type: 'Error',
mechanism: {
type: 'onerror',
handled: false,
synthetic: true,
},
stacktrace: {
frames: [
{
colno: 0,
filename: 'https://app.posthog.com/home',
function: '?',
in_app: true,
lineno: 0,
},
],
},
$os: 'Windows',
$os_version: '10.0',
$browser: 'Chrome',
$device_type: 'Desktop',
$current_url: 'https://app.posthog.com/home',
$host: 'app.posthog.com',
$pathname: '/home',
$browser_version: 113,
$browser_language: 'es-ES',
$screen_height: 1080,
$screen_width: 1920,
$viewport_height: 929,
$viewport_width: 1920,
$lib: 'web',
$lib_version: '1.63.3',
distinct_id: 'iOizUPH4RH65nZjvGVBz5zZUmwdHvq2mxzNySQqqYkG',
$device_id: '186144e7357245-0cfe8bf1b5b877-26021051-1fa400-186144e7358d3',
$active_feature_flags: ['are-the-flags', 'important-for-the-error'],
$feature_flag_payloads: {
'are-the-flags': '{\n "flag": "payload"\n}',
},
$user_id: 'iOizUPH4RH65nZjvGVBz5zZUmwdHvq2mxzNySQqqYkG',
$groups: {
project: '00000000-0000-0000-1847-88f0ffa23444',
organization: '00000000-0000-0000-a050-5d4557279956',
customer: 'the-customer',
instance: 'https://app.posthog.com',
},
$exception_message: 'ResizeObserver loop limit exceeded',
$exception_type: 'Error',
$exception_personURL: 'https://app.posthog.com/person/the-person-id',
$sentry_event_id: 'id-from-the-sentry-integration',
$sentry_exception: {
values: [
{
value: 'ResizeObserver loop limit exceeded',
type: 'Error',
mechanism: {
type: 'onerror',
handled: false,
synthetic: true,
},
],
},
$sentry_exception_message: 'ResizeObserver loop limit exceeded',
$sentry_exception_type: 'Error',
$sentry_tags: {
'PostHog Person URL': 'https://app.posthog.com/person/the-person-id',
'PostHog Recording URL': 'https://app.posthog.com/replay/the-session-id?t=866',
},
$sentry_url:
'https://sentry.io/organizations/posthog/issues/?project=the-sentry-project-id&query=the-sentry-id',
$session_id: 'the-session-id',
$window_id: 'the-window-id',
$pageview_id: 'the-pageview-id',
$sent_at: '2023-06-03T10:03:57.787000+00:00',
$geoip_city_name: 'Whoville',
$geoip_country_name: 'Wholand',
$geoip_country_code: 'WH',
$geoip_continent_name: 'Mystery',
$geoip_continent_code: 'MY',
$geoip_latitude: -30.5023,
$geoip_longitude: -71.1545,
$geoip_time_zone: 'UTC',
$lib_version__major: 1,
$lib_version__minor: 63,
$lib_version__patch: 3,
...properties,
stacktrace: {
frames: [
{
colno: 0,
filename: 'https://app.posthog.com/home',
function: '?',
in_app: true,
lineno: 0,
},
],
},
},
],
},
$sentry_exception_message: 'ResizeObserver loop limit exceeded',
$sentry_exception_type: 'Error',
$sentry_tags: {
'PostHog Person URL': 'https://app.posthog.com/person/the-person-id',
'PostHog Recording URL': 'https://app.posthog.com/replay/the-session-id?t=866',
},
timestamp: '2023-06-03T03:03:57.316-07:00',
distinct_id: 'the-distinct-id',
elements_chain: '',
$sentry_url:
'https://sentry.io/organizations/posthog/issues/?project=the-sentry-project-id&query=the-sentry-id',
$session_id: 'the-session-id',
$window_id: 'the-window-id',
$pageview_id: 'the-pageview-id',
$sent_at: '2023-06-03T10:03:57.787000+00:00',
$geoip_city_name: 'Whoville',
$geoip_country_name: 'Wholand',
$geoip_country_code: 'WH',
$geoip_continent_name: 'Mystery',
$geoip_continent_code: 'MY',
$geoip_latitude: -30.5023,
$geoip_longitude: -71.1545,
$geoip_time_zone: 'UTC',
$lib_version__major: 1,
$lib_version__minor: 63,
$lib_version__patch: 3,
...properties,
}
}

export function ResizeObserverLoopLimitExceeded(): JSX.Element {
return (
<ErrorDisplay
event={errorEvent({
eventProperties={errorProperties({
$exception_message: 'ResizeObserver loop limit exceeded',
$exception_type: 'Error',
$exception_personURL: 'https://app.posthog.com/person/the-person-id',
Expand All @@ -118,7 +109,7 @@ export function ResizeObserverLoopLimitExceeded(): JSX.Element {
export function SafariScriptError(): JSX.Element {
return (
<ErrorDisplay
event={errorEvent({
eventProperties={errorProperties({
$exception_type: 'Error',
$exception_message: 'Script error.',
$exception_is_synthetic: true,
Expand All @@ -130,7 +121,7 @@ export function SafariScriptError(): JSX.Element {
export function ImportingModule(): JSX.Element {
return (
<ErrorDisplay
event={errorEvent({
eventProperties={errorProperties({
$exception_type: 'UnhandledRejection',
$exception_message: "Importing module '/static/chunk-PIJHGO7Q.js' is not found.",
$exception_stack_trace_raw: '[]',
Expand All @@ -143,7 +134,7 @@ export function ImportingModule(): JSX.Element {
export function AnonymousErrorWithStackTrace(): JSX.Element {
return (
<ErrorDisplay
event={errorEvent({
eventProperties={errorProperties({
$exception_type: 'Error',
$exception_message: 'wat',
$exception_stack_trace_raw:
Expand Down
10 changes: 3 additions & 7 deletions frontend/src/lib/components/Errors/ErrorDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { Link } from 'lib/lemon-ui/Link'
import posthog from 'posthog-js'

import { EventType, RecordingEventType } from '~/types'
import { EventType } from '~/types'

interface StackFrame {
filename: string
Expand Down Expand Up @@ -156,11 +156,7 @@ export function getExceptionPropertiesFrom(eventProperties: Record<string, any>)
}
}

export function ErrorDisplay({ event }: { event: EventType | RecordingEventType }): JSX.Element {
if (event.event !== '$exception') {
return <>Unknown type of error</>
}

export function ErrorDisplay({ eventProperties }: { eventProperties: EventType['properties'] }): JSX.Element {
const {
$exception_type,
$exception_message,
Expand All @@ -175,7 +171,7 @@ export function ErrorDisplay({ event }: { event: EventType | RecordingEventType
$sentry_url,
$exception_stack_trace_raw,
$level,
} = getExceptionPropertiesFrom(event.properties)
} = getExceptionPropertiesFrom(eventProperties)

return (
<div className="flex flex-col space-y-2 pr-4 pb-2">
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ export const FEATURE_FLAGS = {
PERSONLESS_EVENTS_NOT_SUPPORTED: 'personless-events-not-supported', // owner: @raquelmsmith
SESSION_REPLAY_UNIVERSAL_FILTERS: 'session-replay-universal-filters', // owner: #team-replay
ALERTS: 'alerts', // owner: github.com/nikitaevg
ERROR_TRACKING: 'error-tracking', // owner: #team-replay
SETTINGS_BOUNCE_RATE_PAGE_VIEW_MODE: 'settings-bounce-rate-page-view-mode', // owner: @robbie-c
SURVEYS_BRANCHING_LOGIC: 'surveys-branching-logic', // owner: @jurajmajerik #team-feature-success
} as const
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/activity/explore/EventDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function EventDetails({ event, tableProps }: EventDetailsProps): JSX.Elem
label: 'Exception',
content: (
<div className="ml-10 my-2">
<ErrorDisplay event={event} />
<ErrorDisplay eventProperties={event.properties} />
</div>
),
})
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/appScenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const appScenes: Record<Scene, () => any> = {
[Scene.FeatureFlag]: () => import('./feature-flags/FeatureFlag'),
[Scene.EarlyAccessFeatures]: () => import('./early-access-features/EarlyAccessFeatures'),
[Scene.EarlyAccessFeature]: () => import('./early-access-features/EarlyAccessFeature'),
[Scene.ErrorTracking]: () => import('./error-tracking/ErrorTrackingScene'),
[Scene.Surveys]: () => import('./surveys/Surveys'),
[Scene.Survey]: () => import('./surveys/Survey'),
[Scene.SurveyTemplates]: () => import('./surveys/SurveyTemplates'),
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/scenes/error-tracking/ErrorTrackingScene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { LemonTable } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay'
import { SceneExport } from 'scenes/sceneTypes'

import { ErrorTrackingGroup } from '~/types'

import { errorTrackingSceneLogic } from './errorTrackingSceneLogic'

export const scene: SceneExport = {
component: ErrorTrackingScene,
logic: errorTrackingSceneLogic,
}

export function ErrorTrackingScene(): JSX.Element {
const { errorGroups, errorGroupsLoading } = useValues(errorTrackingSceneLogic)

return (
<LemonTable
columns={[
{
dataIndex: 'title',
width: '50%',
},
{
title: 'Occurrences',
dataIndex: 'occurrences',
sorter: (a, b) => a.occurrences - b.occurrences,
},
{
title: 'Sessions',
dataIndex: 'uniqueSessions',
sorter: (a, b) => a.uniqueSessions - b.uniqueSessions,
},
]}
loading={errorGroupsLoading}
dataSource={errorGroups}
expandable={{
expandedRowRender: function renderExpand(group: ErrorTrackingGroup) {
return <ErrorDisplay eventProperties={group.sampleEventProperties} />
},
noIndent: true,
}}
/>
)
}
Loading

0 comments on commit fda2156

Please sign in to comment.