Skip to content

Commit

Permalink
feat: Updated v8 sentry integration (#1224)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Jun 10, 2024
1 parent 02a8e17 commit 1928a58
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@rrweb/types": "2.0.0-alpha.13",
"@sentry/types": "7.37.2",
"@sentry/types": "8.7.0",
"@testing-library/dom": "^9.3.0",
"@types/eslint": "^8.44.6",
"@types/jest": "^29.5.1",
Expand Down
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

149 changes: 96 additions & 53 deletions src/extensions/sentry-integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { PostHog } from '../posthog-core'
// Hub as _SentryHub,
// Integration as _SentryIntegration,
// SeverityLevel as _SeverityLevel,
// IntegrationClass as _SentryIntegrationClass,
// } from '@sentry/types'

// Uncomment the above and comment the below to get type checking for development
Expand All @@ -35,11 +36,16 @@ type _SentryEvent = any
type _SentryEventProcessor = any
type _SentryHub = any

interface _SentryIntegration {
interface _SentryIntegrationClass {
name: string
setupOnce(addGlobalEventProcessor: (callback: _SentryEventProcessor) => void, getCurrentHub: () => _SentryHub): void
}

interface _SentryIntegration {
name: string
processEvent(event: _SentryEvent): _SentryEvent
}

// levels copied from Sentry to avoid relying on a frequently changing @sentry/types dependency
// but provided as an array of literal types, so we can constrain the level below
const severityLevels = ['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const
Expand All @@ -54,7 +60,90 @@ interface SentryExceptionProperties {
$sentry_url?: string
}

export class SentryIntegration implements _SentryIntegration {
export type SentryIntegrationOptions = {
organization?: string
projectId?: number
prefix?: string
/**
* By default, only errors are sent to PostHog. You can set this to '*' to send all events.
* Or to an error of SeverityLevel to only send events matching the provided levels.
* e.g. ['error', 'fatal'] to send only errors and fatals
* e.g. ['error'] to send only errors -- the default when omitted
* e.g. '*' to send all events
*/
severityAllowList?: _SeverityLevel[] | '*'
}

const NAME = 'posthog-js'

export function createEventProcessor(
_posthog: PostHog,
{ organization, projectId, prefix, severityAllowList = ['error'] }: SentryIntegrationOptions = {}
): (event: _SentryEvent) => _SentryEvent {
return (event) => {
const shouldProcessLevel =
severityAllowList === '*' || severityAllowList.includes(event.level as _SeverityLevel)
if (!shouldProcessLevel || !_posthog.__loaded) return event
if (!event.tags) event.tags = {}

const personUrl = _posthog.requestRouter.endpointFor(
'ui',
`/project/${_posthog.config.token}/person/${_posthog.get_distinct_id()}`
)
event.tags['PostHog Person URL'] = personUrl
if (_posthog.sessionRecordingStarted()) {
event.tags['PostHog Recording URL'] = _posthog.get_session_replay_url({ withTimestamp: true })
}

const exceptions = event.exception?.values || []

const data: SentryExceptionProperties & {
// two properties added to match any exception auto-capture
// added manually to avoid any dependency on the lazily loaded content
$exception_message: any
$exception_type: any
$exception_personURL: string
$level: _SeverityLevel
} = {
// PostHog Exception Properties,
$exception_message: exceptions[0]?.value || event.message,
$exception_type: exceptions[0]?.type,
$exception_personURL: personUrl,
// Sentry Exception Properties
$sentry_event_id: event.event_id,
$sentry_exception: event.exception,
$sentry_exception_message: exceptions[0]?.value || event.message,
$sentry_exception_type: exceptions[0]?.type,
$sentry_tags: event.tags,
$level: event.level,
}

if (organization && projectId) {
data['$sentry_url'] =
(prefix || 'https://sentry.io/organizations/') +
organization +
'/issues/?project=' +
projectId +
'&query=' +
event.event_id
}
_posthog.capture('$exception', data)
return event
}
}

// V8 integration - function based
export function sentryIntegration(_posthog: PostHog, options?: SentryIntegrationOptions): _SentryIntegration {
const processor = createEventProcessor(_posthog, options)
return {
name: NAME,
processEvent(event) {
return processor(event)
},
}
}
// V7 integration - class based
export class SentryIntegration implements _SentryIntegrationClass {
name: string

setupOnce: (
Expand All @@ -74,60 +163,14 @@ export class SentryIntegration implements _SentryIntegration {
* e.g. ['error'] to send only errors -- the default when omitted
* e.g. '*' to send all events
*/
severityAllowList: _SeverityLevel[] | '*' = ['error']
severityAllowList?: _SeverityLevel[] | '*'
) {
// setupOnce gets called by Sentry when it intializes the plugin
this.name = 'posthog-js'
this.name = NAME
this.setupOnce = function (addGlobalEventProcessor: (callback: _SentryEventProcessor) => void) {
addGlobalEventProcessor((event: _SentryEvent) => {
const shouldProcessLevel =
severityAllowList === '*' || severityAllowList.includes(event.level as _SeverityLevel)
if (!shouldProcessLevel || !_posthog.__loaded) return event
if (!event.tags) event.tags = {}

const personUrl = _posthog.requestRouter.endpointFor(
'ui',
`/project/${_posthog.config.token}/person/${_posthog.get_distinct_id()}`
)
event.tags['PostHog Person URL'] = personUrl
if (_posthog.sessionRecordingStarted()) {
event.tags['PostHog Recording URL'] = _posthog.get_session_replay_url({ withTimestamp: true })
}

const exceptions = event.exception?.values || []

const data: SentryExceptionProperties & {
// two properties added to match any exception auto-capture
// added manually to avoid any dependency on the lazily loaded content
$exception_message: any
$exception_type: any
$exception_personURL: string
$level: _SeverityLevel
} = {
// PostHog Exception Properties,
$exception_message: exceptions[0]?.value || event.message,
$exception_type: exceptions[0]?.type,
$exception_personURL: personUrl,
// Sentry Exception Properties
$sentry_event_id: event.event_id,
$sentry_exception: event.exception,
$sentry_exception_message: exceptions[0]?.value || event.message,
$sentry_exception_type: exceptions[0]?.type,
$sentry_tags: event.tags,
$level: event.level,
}

if (organization && projectId)
data['$sentry_url'] =
(prefix || 'https://sentry.io/organizations/') +
organization +
'/issues/?project=' +
projectId +
'&query=' +
event.event_id
_posthog.capture('$exception', data)
return event
})
addGlobalEventProcessor(
createEventProcessor(_posthog, { organization, projectId, prefix, severityAllowList })
)
}
}
}
4 changes: 3 additions & 1 deletion src/posthog-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
SnippetArrayItem,
ToolbarParams,
} from './types'
import { SentryIntegration } from './extensions/sentry-integration'
import { SentryIntegration, SentryIntegrationOptions, sentryIntegration } from './extensions/sentry-integration'
import { setupSegmentIntegration } from './extensions/segment-integration'
import { PageViewManager } from './page-view'
import { PostHogSurveys } from './posthog-surveys'
Expand Down Expand Up @@ -262,6 +262,7 @@ export class PostHog {
analyticsDefaultEndpoint: string

SentryIntegration: typeof SentryIntegration
sentryIntegration: (options?: SentryIntegrationOptions) => ReturnType<typeof sentryIntegration>

private _debugEventEmitter = new SimpleEventEmitter()

Expand All @@ -276,6 +277,7 @@ export class PostHog {

this.decideEndpointWasHit = false
this.SentryIntegration = SentryIntegration
this.sentryIntegration = (options?: SentryIntegrationOptions) => sentryIntegration(this, options)
this.__request_queue = []
this.__loaded = false
this.analyticsDefaultEndpoint = '/e/'
Expand Down

0 comments on commit 1928a58

Please sign in to comment.