11import { APIClient } from '@heroku-cli/command'
22import { Config } from '@oclif/core'
33import opentelemetry , { SpanStatusCode } from '@opentelemetry/api'
4+ import * as Sentry from '@sentry/node'
5+ import {
6+ SentryPropagator ,
7+ SentrySampler ,
8+ } from '@sentry/opentelemetry'
9+ import { GDPR_FIELDS , HEROKU_FIELDS , PCI_FIELDS } from './lib/data-scrubber/presets'
10+ import { Scrubber } from './lib/data-scrubber/scrubber'
11+ import { PII_PATTERNS } from './lib/data-scrubber/patterns'
12+
413const { Resource} = require ( '@opentelemetry/resources' )
514const { SemanticResourceAttributes} = require ( '@opentelemetry/semantic-conventions' )
615const { registerInstrumentations} = require ( '@opentelemetry/instrumentation' )
@@ -26,6 +35,22 @@ registerInstrumentations({
2635 instrumentations : [ ] ,
2736} )
2837
38+ const scrubber = new Scrubber ( {
39+ fields : [ ...HEROKU_FIELDS , ...GDPR_FIELDS , ...PCI_FIELDS ] ,
40+ patterns : [ ...PII_PATTERNS ] ,
41+ } )
42+
43+ const sentryClient = Sentry . init ( {
44+ dsn : 'https://76530569188e7ee2961373f37951d916@o4508609692368896.ingest.us.sentry.io/4508767754846208' ,
45+ environment : isDev ? 'development' : 'production' ,
46+ release : version ,
47+ tracesSampleRate : 1 , // needed to ensure we send OTEL data to Honeycomb
48+ beforeSend ( event ) {
49+ return scrubber . scrub ( event ) . data
50+ } ,
51+ skipOpenTelemetrySetup : true , // needed since we have our own OTEL setup
52+ } )
53+
2954const resource = Resource
3055 . default ( )
3156 . merge (
@@ -37,6 +62,7 @@ const resource = Resource
3762
3863const provider = new NodeTracerProvider ( {
3964 resource,
65+ sampler : sentryClient ? new SentrySampler ( sentryClient ) : undefined ,
4066} )
4167
4268const headers = { Authorization : `Bearer ${ process . env . IS_HEROKU_TEST_ENV !== 'true' ? getToken ( ) : '' } ` }
@@ -75,7 +101,11 @@ interface CLIError extends Error {
75101}
76102
77103export function initializeInstrumentation ( ) {
78- provider . register ( )
104+ provider . register ( {
105+ propagator : new SentryPropagator ( ) ,
106+ contextManager : new Sentry . SentryContextManager ( ) ,
107+ } )
108+ // provider.register()
79109}
80110
81111export function setupTelemetry ( config : any , opts : any ) {
@@ -152,7 +182,14 @@ export async function sendTelemetry(currentTelemetry: any) {
152182
153183 const telemetry = currentTelemetry
154184
155- await sendToHoneycomb ( telemetry )
185+ if ( telemetry instanceof Error ) {
186+ await Promise . all ( [
187+ sendToHoneycomb ( telemetry ) ,
188+ sendToSentry ( telemetry ) ,
189+ ] )
190+ } else {
191+ await sendToHoneycomb ( telemetry )
192+ }
156193}
157194
158195export async function sendToHoneycomb ( data : Telemetry | CLIError ) {
@@ -181,8 +218,18 @@ export async function sendToHoneycomb(data: Telemetry | CLIError) {
181218 }
182219
183220 span . end ( )
184- processor . forceFlush ( )
221+ await processor . forceFlush ( )
185222 } catch {
186223 debug ( 'could not send telemetry' )
187224 }
188225}
226+
227+ export async function sendToSentry ( data : CLIError ) {
228+ try {
229+ Sentry . captureException ( data )
230+ // ensures all events are sent to Sentry before exiting.
231+ await Sentry . flush ( )
232+ } catch {
233+ debug ( 'Could not send error report' )
234+ }
235+ }
0 commit comments