From 5ed8c0f8f0da13eee11e2697fc02ba9ee6253227 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 31 Oct 2023 16:21:33 +0100 Subject: [PATCH] move components and utils to integration --- src/components/Overview.tsx | 76 +++---------------- src/components/Trigger.tsx | 1 + src/index.tsx | 38 +--------- .../sentry}/components/DateTime.tsx | 0 .../sentry}/components/EventBreadcrumbs.tsx | 2 +- .../sentry}/components/EventContexts.tsx | 2 +- .../sentry}/components/EventDetails.tsx | 4 +- .../sentry}/components/EventList.tsx | 4 +- .../sentry}/components/Events/Error.tsx | 2 +- .../sentry}/components/Events/Error/Frame.tsx | 0 .../sentry}/components/PlatformIcon.tsx | 0 .../sentry}/components/SdkList.tsx | 0 .../sentry}/components/SpanDetails.tsx | 4 +- .../sentry}/components/SpanTree.tsx | 2 +- .../sentry}/components/Time.tsx | 0 .../sentry}/components/TimeSince.tsx | 0 .../sentry}/components/TraceDetails.tsx | 4 +- .../sentry}/components/TraceList.tsx | 2 +- src/integrations/sentry/index.ts | 63 ++++++++++++++- .../sentry/utils}/duration.ts | 0 src/lib/math.ts | 3 - website/src/spotlight/overlay/index.ts | 2 +- website/src/spotlight/snippets.ts | 6 +- 23 files changed, 94 insertions(+), 121 deletions(-) rename src/{ => integrations/sentry}/components/DateTime.tsx (100%) rename src/{ => integrations/sentry}/components/EventBreadcrumbs.tsx (96%) rename src/{ => integrations/sentry}/components/EventContexts.tsx (97%) rename src/{ => integrations/sentry}/components/EventDetails.tsx (97%) rename src/{ => integrations/sentry}/components/EventList.tsx (91%) rename src/{ => integrations/sentry}/components/Events/Error.tsx (97%) rename src/{ => integrations/sentry}/components/Events/Error/Frame.tsx (100%) rename src/{ => integrations/sentry}/components/PlatformIcon.tsx (100%) rename src/{ => integrations/sentry}/components/SdkList.tsx (100%) rename src/{ => integrations/sentry}/components/SpanDetails.tsx (98%) rename src/{ => integrations/sentry}/components/SpanTree.tsx (97%) rename src/{ => integrations/sentry}/components/Time.tsx (100%) rename src/{ => integrations/sentry}/components/TimeSince.tsx (100%) rename src/{ => integrations/sentry}/components/TraceDetails.tsx (96%) rename src/{ => integrations/sentry}/components/TraceList.tsx (97%) rename src/{lib => integrations/sentry/utils}/duration.ts (100%) delete mode 100644 src/lib/math.ts diff --git a/src/components/Overview.tsx b/src/components/Overview.tsx index 7e21dac9..f9bfbb5a 100644 --- a/src/components/Overview.tsx +++ b/src/components/Overview.tsx @@ -1,25 +1,16 @@ import { useState } from 'react'; -import { useSentryEvents } from '../lib/useSentryEvents'; import Tabs from './Tabs'; -import TraceList from './TraceList'; -import EventList from './EventList'; import useKeyPress from '~/lib/useKeyPress'; -import EventDetails from './EventDetails'; -import TraceDetails from './TraceDetails'; import dataCache from '~/lib/dataCache'; import { useNavigation } from '~/lib/useNavigation'; -import SdkList from './SdkList'; -import { useSentryTraces } from '~/lib/useSentryTraces'; -import { IntegrationTab } from '~/integrations/integration'; +import EventDetails from '~/integrations/sentry/components/EventDetails'; +import TraceDetails from '~/integrations/sentry/components/TraceDetails'; const DEFAULT_TAB = 'errors'; export default function Overview({ integrationData }: { integrationData: Record> }) { const [activeTab, setActiveTab] = useState(DEFAULT_TAB); - const events = useSentryEvents(); - const traces = useSentryTraces(); - const { integrations, traceId, setTraceId, eventId, setEventId, setSpanId } = useNavigation(); useKeyPress('Escape', () => { @@ -28,46 +19,10 @@ export default function Overview({ integrationData }: { integrationData: Record< setSpanId(null); }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const tabs: IntegrationTab[] = [ - { - id: 'errors', - title: 'Errors', - count: events.filter(e => 'exception' in e).length, - active: activeTab === 'errors' && (!traceId || !!eventId), - onSelect: () => { - setEventId(null); - setTraceId(null); - setActiveTab('errors'); - }, - }, - { - id: 'traces', - title: 'Traces', - count: traces.length, - active: activeTab === 'traces' && (!eventId || !!traceId), - onSelect: () => { - setEventId(null); - setTraceId(null); - setActiveTab('traces'); - }, - }, - { - id: 'sdks', - title: 'SDKs', - active: activeTab === 'sdks' && !eventId && !traceId, - onSelect: () => { - setEventId(null); - setTraceId(null); - setActiveTab('sdks'); - }, - }, - ]; - - integrations.forEach(integration => { - if (integration.tabs) { - integration.tabs.forEach(tab => { - tabs.push({ + const tabs = integrations + .map(integration => { + if (integration.tabs) { + return integration.tabs.map(tab => ({ ...tab, active: activeTab === tab.id, onSelect: () => { @@ -75,10 +30,11 @@ export default function Overview({ integrationData }: { integrationData: Record< setTraceId(null); setActiveTab(tab.id); }, - }); - }); - } - }); + })); + } + return []; + }) + .flat(); if (eventId) { const activeEvent = dataCache.getEventById(eventId); @@ -110,15 +66,7 @@ export default function Overview({ integrationData }: { integrationData: Record< return ( <> - {activeTab === 'traces' ? ( - - ) : activeTab === 'errors' ? ( - - ) : activeTab === 'sdks' ? ( - - ) : ( - - )} + ); } diff --git a/src/components/Trigger.tsx b/src/components/Trigger.tsx index 01136538..377a4ac3 100644 --- a/src/components/Trigger.tsx +++ b/src/components/Trigger.tsx @@ -2,6 +2,7 @@ import { useSentryTraces } from '~/lib/useSentryTraces'; import { useSentryEvents } from '../lib/useSentryEvents'; export default function Trigger({ isOpen, setOpen }: { isOpen: boolean; setOpen: (value: boolean) => void }) { + // TODO: replace w/ generic counter const events = useSentryEvents(); const traces = useSentryTraces(); diff --git a/src/index.tsx b/src/index.tsx index ce84d2a9..17d28b63 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,13 +4,12 @@ import ReactDOM from 'react-dom/client'; import fontStyles from '@fontsource/raleway/index.css?inline'; import App from './App.tsx'; -import { SentryEvent } from './types.ts'; import type { Integration } from './integrations/integration'; import { initIntegrations } from './integrations/integration'; import globalStyles from './index.css?inline'; -import dataCache from './lib/dataCache.ts'; -import type { Envelope } from '@sentry/types'; +// TODO: get rid of this here +import dataCache from './lib/dataCache.ts'; export { default as sentry } from './integrations/sentry'; export { default as console } from './integrations/console'; @@ -85,14 +84,6 @@ export async function init({ }); } -export function pushEvent(event: SentryEvent) { - dataCache.pushEvent(event); -} - -export function pushEnvelope(envelope: Envelope) { - dataCache.pushEnvelope(envelope); -} - export function connectToSidecar( sidecarUrl: string, contentTypeToIntegrations: Map[]>, @@ -101,31 +92,9 @@ export function connectToSidecar( console.log('[Spotlight] Connecting to sidecar at', sidecarUrl); const source = new EventSource(sidecarUrl); - source.addEventListener('application/x-sentry-envelope', event => { - console.log('[spotlight] Received new envelope'); - const [rawHeader, ...rawEntries] = event.data.split('\n'); - const header = JSON.parse(rawHeader) as Envelope[0]; - console.log(`[Spotlight] Received new envelope from SDK ${header.sdk?.name || '(unknown)'}`); - - const items: Envelope[1][] = []; - for (let i = 0; i < rawEntries.length; i += 2) { - items.push([JSON.parse(rawEntries[i]), JSON.parse(rawEntries[i + 1])]); - } - - const envelope = [header, items] as Envelope; - console.log('[Spotlight]', envelope); - - dataCache.pushEnvelope(envelope); - }); - const contentTypeListeners: [contentType: string, listener: (event: MessageEvent) => void][] = []; for (const [contentType, integrations] of contentTypeToIntegrations.entries()) { - // TODO: remove this, for now this isolates the sentry stuff from the new integrations API - if (contentType === 'application/x-sentry-envelope') { - continue; - } - const listener = (event: MessageEvent): void => { console.log(`[spotlight] Received new ${contentType} event`); integrations.forEach(integration => { @@ -161,12 +130,13 @@ export function connectToSidecar( source.addEventListener('open', () => { console.log('[Spotlight] open'); + // TODO: remove from datacache and useEffect instead dataCache.setOnline(true); }); source.addEventListener('error', err => { dataCache.setOnline(false); - + // TODO: remove from datacache and useEffect instead console.error('EventSource failed:', err); }); diff --git a/src/components/DateTime.tsx b/src/integrations/sentry/components/DateTime.tsx similarity index 100% rename from src/components/DateTime.tsx rename to src/integrations/sentry/components/DateTime.tsx diff --git a/src/components/EventBreadcrumbs.tsx b/src/integrations/sentry/components/EventBreadcrumbs.tsx similarity index 96% rename from src/components/EventBreadcrumbs.tsx rename to src/integrations/sentry/components/EventBreadcrumbs.tsx index 6eb21ed3..dfaf9927 100644 --- a/src/components/EventBreadcrumbs.tsx +++ b/src/integrations/sentry/components/EventBreadcrumbs.tsx @@ -1,4 +1,4 @@ -import { SentryEvent } from '../types'; +import { SentryEvent } from '~/types'; import Time from './Time'; const EXAMPLE_BREADCRUMB = `Sentry.addBreadcrumb({ diff --git a/src/components/EventContexts.tsx b/src/integrations/sentry/components/EventContexts.tsx similarity index 97% rename from src/components/EventContexts.tsx rename to src/integrations/sentry/components/EventContexts.tsx index a061aa29..5b6df665 100644 --- a/src/components/EventContexts.tsx +++ b/src/integrations/sentry/components/EventContexts.tsx @@ -1,4 +1,4 @@ -import { SentryEvent } from '../types'; +import { SentryEvent } from '~/types'; const EXAMPLE_CONTEXT = `Sentry.setContext("character", { name: "Mighty Fighter", diff --git a/src/components/EventDetails.tsx b/src/integrations/sentry/components/EventDetails.tsx similarity index 97% rename from src/components/EventDetails.tsx rename to src/integrations/sentry/components/EventDetails.tsx index 4c54491e..5daf2b79 100644 --- a/src/components/EventDetails.tsx +++ b/src/integrations/sentry/components/EventDetails.tsx @@ -1,12 +1,12 @@ import { useState } from 'react'; -import { SentryEvent } from '../types'; import Error, { ErrorTitle } from './Events/Error'; -import Tabs from './Tabs'; import EventContexts from './EventContexts'; import useKeyPress from '~/lib/useKeyPress'; import PlatformIcon from './PlatformIcon'; import { useNavigation } from '~/lib/useNavigation'; import EventBreadcrumbs from './EventBreadcrumbs'; +import { SentryEvent } from '~/types'; +import Tabs from '~/components/Tabs'; function renderEvent(event: SentryEvent) { if ('exception' in event) return ; diff --git a/src/components/EventList.tsx b/src/integrations/sentry/components/EventList.tsx similarity index 91% rename from src/components/EventList.tsx rename to src/integrations/sentry/components/EventList.tsx index e4421730..65154daf 100644 --- a/src/components/EventList.tsx +++ b/src/integrations/sentry/components/EventList.tsx @@ -3,14 +3,16 @@ import { ErrorSummary } from './Events/Error'; import TimeSince from './TimeSince'; import PlatformIcon from './PlatformIcon'; import { useNavigation } from '~/lib/useNavigation'; +import { useSentryEvents } from '~/lib/useSentryEvents'; function renderEvent(event: SentryEvent) { if ('exception' in event) return ; return null; } -export default function EventList({ events }: { events: SentryEvent[] }) { +export default function EventList() { const { setEventId } = useNavigation(); + const events = useSentryEvents(); const matchingEvents = events.filter(e => e.type !== 'transaction'); diff --git a/src/components/Events/Error.tsx b/src/integrations/sentry/components/Events/Error.tsx similarity index 97% rename from src/components/Events/Error.tsx rename to src/integrations/sentry/components/Events/Error.tsx index 3ea7c9d9..2b8c1383 100644 --- a/src/components/Events/Error.tsx +++ b/src/integrations/sentry/components/Events/Error.tsx @@ -1,4 +1,4 @@ -import { SentryErrorEvent } from '../../types'; +import { SentryErrorEvent } from '~/types'; import Frame from './Error/Frame'; export function ErrorTitle({ event }: { event: SentryErrorEvent }) { diff --git a/src/components/Events/Error/Frame.tsx b/src/integrations/sentry/components/Events/Error/Frame.tsx similarity index 100% rename from src/components/Events/Error/Frame.tsx rename to src/integrations/sentry/components/Events/Error/Frame.tsx diff --git a/src/components/PlatformIcon.tsx b/src/integrations/sentry/components/PlatformIcon.tsx similarity index 100% rename from src/components/PlatformIcon.tsx rename to src/integrations/sentry/components/PlatformIcon.tsx diff --git a/src/components/SdkList.tsx b/src/integrations/sentry/components/SdkList.tsx similarity index 100% rename from src/components/SdkList.tsx rename to src/integrations/sentry/components/SdkList.tsx diff --git a/src/components/SpanDetails.tsx b/src/integrations/sentry/components/SpanDetails.tsx similarity index 98% rename from src/components/SpanDetails.tsx rename to src/integrations/sentry/components/SpanDetails.tsx index ba84e7da..1589a43e 100644 --- a/src/components/SpanDetails.tsx +++ b/src/integrations/sentry/components/SpanDetails.tsx @@ -1,13 +1,13 @@ -import DateTime from '~/components/DateTime'; import useKeyPress from '~/lib/useKeyPress'; import { format as formatSQL } from 'sql-formatter'; -import { getDuration } from '~/lib/duration'; import SpanTree from './SpanTree'; import { SentryErrorEvent, Span, TraceContext } from '~/types'; import { ErrorTitle } from './Events/Error'; import dataCache from '~/lib/dataCache'; import { useNavigation } from '~/lib/useNavigation'; +import DateTime from './DateTime'; +import { getDuration } from '../utils/duration'; function formatSpanDescription(desc: string) { if (desc.match(/^(SELECT|INSERT|UPDATE|DELETE|TRUNCATE|ALTER) /i)) { diff --git a/src/components/SpanTree.tsx b/src/integrations/sentry/components/SpanTree.tsx similarity index 97% rename from src/components/SpanTree.tsx rename to src/integrations/sentry/components/SpanTree.tsx index 7c22177a..b235f560 100644 --- a/src/components/SpanTree.tsx +++ b/src/integrations/sentry/components/SpanTree.tsx @@ -1,8 +1,8 @@ import classNames from '~/lib/classNames'; import { Span, TraceContext } from '~/types'; import PlatformIcon from './PlatformIcon'; -import { getDuration, getSpanDurationClassName } from '~/lib/duration'; import { useNavigation } from '~/lib/useNavigation'; +import { getDuration, getSpanDurationClassName } from '../utils/duration'; export default function SpanTree({ traceContext, diff --git a/src/components/Time.tsx b/src/integrations/sentry/components/Time.tsx similarity index 100% rename from src/components/Time.tsx rename to src/integrations/sentry/components/Time.tsx diff --git a/src/components/TimeSince.tsx b/src/integrations/sentry/components/TimeSince.tsx similarity index 100% rename from src/components/TimeSince.tsx rename to src/integrations/sentry/components/TimeSince.tsx diff --git a/src/components/TraceDetails.tsx b/src/integrations/sentry/components/TraceDetails.tsx similarity index 96% rename from src/components/TraceDetails.tsx rename to src/integrations/sentry/components/TraceDetails.tsx index fbed6b2c..972b0c43 100644 --- a/src/components/TraceDetails.tsx +++ b/src/integrations/sentry/components/TraceDetails.tsx @@ -1,12 +1,12 @@ -import { Trace } from '../types'; import useKeyPress from '~/lib/useKeyPress'; import SpanDetails from './SpanDetails'; import SpanTree from './SpanTree'; -import { getDuration } from '~/lib/duration'; import DateTime from './DateTime'; import PlatformIcon from './PlatformIcon'; import { useNavigation } from '~/lib/useNavigation'; import dataCache from '~/lib/dataCache'; +import { Trace } from '~/types'; +import { getDuration } from '../utils/duration'; export default function TraceDetails({ trace }: { trace: Trace }) { useKeyPress('Escape', () => { diff --git a/src/components/TraceList.tsx b/src/integrations/sentry/components/TraceList.tsx similarity index 97% rename from src/components/TraceList.tsx rename to src/integrations/sentry/components/TraceList.tsx index e36b2b04..da32de73 100644 --- a/src/components/TraceList.tsx +++ b/src/integrations/sentry/components/TraceList.tsx @@ -1,9 +1,9 @@ -import { getDuration } from '~/lib/duration'; import classNames from '~/lib/classNames'; import TimeSince from './TimeSince'; import PlatformIcon from './PlatformIcon'; import { useSentryTraces } from '~/lib/useSentryTraces'; import { useNavigation } from '~/lib/useNavigation'; +import { getDuration } from '../utils/duration'; export default function TraceList() { const traceList = useSentryTraces(); diff --git a/src/integrations/sentry/index.ts b/src/integrations/sentry/index.ts index 22ac9f7b..eb399b98 100644 --- a/src/integrations/sentry/index.ts +++ b/src/integrations/sentry/index.ts @@ -3,24 +3,79 @@ import { serializeEnvelope } from '@sentry/utils'; import type { Integration } from '../integration'; +import dataCache from '../../lib/dataCache'; +import EventList from './components/EventList'; +import TraceList from './components/TraceList'; +import SdkList from './components/SdkList'; + const HEADER = 'application/x-sentry-envelope'; export default function sentryIntegration() { return { name: 'sentry', forwardedContentType: [HEADER], + setup: () => { - console.log('sentry integration init 2'); hookIntoSentry(); }, - } satisfies Integration; + + processEvent({ data }) { + console.log('[spotlight] Received new envelope'); + const [rawHeader, ...rawEntries] = data.split('\n'); + const header = JSON.parse(rawHeader) as Envelope[0]; + console.log(`[Spotlight] Received new envelope from SDK ${header.sdk?.name || '(unknown)'}`); + + const items: Envelope[1][] = []; + for (let i = 0; i < rawEntries.length; i += 2) { + items.push([JSON.parse(rawEntries[i]), JSON.parse(rawEntries[i + 1])]); + } + + const envelope = [header, items] as Envelope; + dataCache.pushEnvelope(envelope); + + return envelope; + }, + tabs: [ + { + id: 'errors', + title: 'Errors', + content: EventList, + }, + { + id: 'traces', + title: 'Traces', + content: TraceList, + }, + { + id: 'sdks', + title: 'SDKs', + content: SdkList, + }, + ], + } satisfies Integration; } +type WindowWithSentry = Window & { + __SENTRY__?: { + hub: { + _stack: { + client: { + setupIntegrations: (val: boolean) => void; + on: (event: string, callback: (envelope: Envelope) => void) => void; + }; + }[]; + }; + }; +}; + function hookIntoSentry() { // A very hacky way to hook into Sentry's SDK // but we love hacks - (window as any).__SENTRY__.hub._stack[0].client.setupIntegrations(true); - (window as any).__SENTRY__.hub._stack[0].client.on('beforeEnvelope', (envelope: Envelope) => { + const sentryHub = (window as WindowWithSentry).__SENTRY__?.hub; + const sentryClient = sentryHub?._stack[0]?.client; + + sentryClient?.setupIntegrations(true); + sentryClient?.on('beforeEnvelope', (envelope: Envelope) => { fetch('http://localhost:8969/stream', { method: 'POST', body: serializeEnvelope(envelope), diff --git a/src/lib/duration.ts b/src/integrations/sentry/utils/duration.ts similarity index 100% rename from src/lib/duration.ts rename to src/integrations/sentry/utils/duration.ts diff --git a/src/lib/math.ts b/src/lib/math.ts deleted file mode 100644 index 7036db4a..00000000 --- a/src/lib/math.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function sum(arr: T[], cb: (item: T) => number): number { - return arr.reduce((acc, current) => acc + cb(current), 0); -} diff --git a/website/src/spotlight/overlay/index.ts b/website/src/spotlight/overlay/index.ts index b3c60436..f564fb55 100644 --- a/website/src/spotlight/overlay/index.ts +++ b/website/src/spotlight/overlay/index.ts @@ -7,7 +7,7 @@ export default { id: 'spotlight-plugin', name: 'Sentry Spotlight', icon: sentrylogo, - init(canvas, eventTarget) { + init(_canvas, eventTarget) { eventTarget.dispatchEvent( new CustomEvent('plugin-notification', { detail: { diff --git a/website/src/spotlight/snippets.ts b/website/src/spotlight/snippets.ts index 3f2882ea..6a2b2e47 100644 --- a/website/src/spotlight/snippets.ts +++ b/website/src/spotlight/snippets.ts @@ -61,7 +61,7 @@ console.log('[Spotlight]', globalThis.__SENTRY__); } ); -setTimeout(() => { - Sentry.captureMessage('does this now show up in spotlight?'); -}, 2000); +// setTimeout(() => { + Sentry.captureException(new Error('does this now show up in spotlight?')); +// }, 2000); `;