Skip to content

Commit 6477267

Browse files
authored
Emit integration invoke error metric for destination load failures (#1341)
1 parent 99cd853 commit 6477267

File tree

6 files changed

+140
-15
lines changed

6 files changed

+140
-15
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-next': patch
3+
---
4+
5+
Emit analytics_js.integration.invoke.error metric for destination load and build failures that were previously silent

packages/browser/src/browser/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ async function registerPlugins(
173173
options,
174174
tsubMiddleware,
175175
pluginSources
176-
).catch(() => [])
176+
).catch((err) => {
177+
console.error('Failed to load remote plugins', err)
178+
return [] as Plugin[]
179+
})
177180

178181
const basePlugins = [envEnrichment, ...legacyDestinations, ...remotePlugins]
179182

packages/browser/src/plugins/ajs-destination/__tests__/index.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { AMPLITUDE_WRITEKEY } from '../../../test-helpers/test-writekeys'
1111
import { PersistedPriorityQueue } from '../../../lib/priority-queue/persisted'
1212
import * as Factory from '../../../test-helpers/factories'
1313
import { cdnSettingsMinimal } from '../../../test-helpers/fixtures'
14+
import * as MetricHelpers from '../../../core/stats/metric-helpers'
15+
import * as ScriptLoader from '../../../lib/load-script'
1416

1517
const cdnResponse: CDNSettings = {
1618
...cdnSettingsMinimal,
@@ -838,3 +840,65 @@ describe('option overrides', () => {
838840
)
839841
})
840842
})
843+
844+
describe('load error metrics', () => {
845+
let metricSpy: jest.SpyInstance
846+
847+
beforeEach(() => {
848+
jest.restoreAllMocks()
849+
jest.resetAllMocks()
850+
metricSpy = jest.spyOn(MetricHelpers, 'recordIntegrationMetric')
851+
})
852+
853+
it('records integration invoke error metric when loadIntegration fails', async () => {
854+
jest
855+
.spyOn(ScriptLoader, 'loadScript')
856+
.mockRejectedValue(new Error('Script load failed'))
857+
858+
const dest = new LegacyDestination(
859+
'Broken Integration',
860+
'latest',
861+
'writeKey',
862+
{},
863+
{}
864+
)
865+
866+
const ctx = Context.system()
867+
868+
await expect(dest.load(ctx, {} as Analytics)).rejects.toThrow(
869+
'Script load failed'
870+
)
871+
872+
expect(metricSpy).toHaveBeenCalledWith(ctx, {
873+
integrationName: 'Broken Integration',
874+
methodName: 'load',
875+
type: 'classic',
876+
didError: true,
877+
})
878+
})
879+
880+
it('records integration invoke error metric when buildIntegration fails', async () => {
881+
// Provide an integrationSource that will cause buildIntegration to throw
882+
const badSource = {} as any
883+
884+
const dest = new LegacyDestination(
885+
'Bad Build Integration',
886+
'latest',
887+
'writeKey',
888+
{},
889+
{},
890+
badSource
891+
)
892+
893+
const ctx = Context.system()
894+
895+
await expect(dest.load(ctx, {} as Analytics)).rejects.toThrow()
896+
897+
expect(metricSpy).toHaveBeenCalledWith(ctx, {
898+
integrationName: 'Bad Build Integration',
899+
methodName: 'load',
900+
type: 'classic',
901+
didError: true,
902+
})
903+
})
904+
})

packages/browser/src/plugins/ajs-destination/index.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -134,20 +134,30 @@ export class LegacyDestination implements InternalPluginWithAddMiddleware {
134134
return
135135
}
136136

137-
const integrationSource =
138-
this.integrationSource ??
139-
(await loadIntegration(
140-
ctx,
141-
this.name,
142-
this.version,
143-
this.options.obfuscate
144-
))
145-
146-
this.integration = buildIntegration(
147-
integrationSource,
148-
this.settings,
149-
analyticsInstance
150-
)
137+
try {
138+
const integrationSource =
139+
this.integrationSource ??
140+
(await loadIntegration(
141+
ctx,
142+
this.name,
143+
this.version,
144+
this.options.obfuscate
145+
))
146+
147+
this.integration = buildIntegration(
148+
integrationSource,
149+
this.settings,
150+
analyticsInstance
151+
)
152+
} catch (error) {
153+
recordIntegrationMetric(ctx, {
154+
integrationName: this.name,
155+
methodName: 'load',
156+
type: 'classic',
157+
didError: true,
158+
})
159+
throw error
160+
}
151161

152162
this.onReady = new Promise((resolve) => {
153163
const onReadyFn = (): void => {

packages/browser/src/plugins/remote-loader/__tests__/index.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { InitOptions } from '../../../core/analytics'
77
import { Context } from '../../../core/context'
88
import { tsubMiddleware } from '../../routing-middleware'
99
import { cdnSettingsMinimal } from '../../../test-helpers/fixtures'
10+
import * as MetricHelpers from '../../../core/stats/metric-helpers'
1011

1112
const pluginFactory = jest.fn()
1213

@@ -966,4 +967,40 @@ describe('Remote Loader', () => {
966967

967968
expect(newCtx.event.name).toEqual('foobar')
968969
})
970+
971+
it('records integration invoke error metric when plugin fails to load', async () => {
972+
const metricSpy = jest.spyOn(MetricHelpers, 'recordIntegrationMetric')
973+
974+
// @ts-expect-error not gonna return a script tag sorry
975+
jest.spyOn(loader, 'loadScript').mockImplementation(() => {
976+
window['flaky'] = (): never => {
977+
throw Error('aaay')
978+
}
979+
return Promise.resolve(true)
980+
})
981+
982+
await remoteLoader(
983+
{
984+
...cdnSettingsMinimal,
985+
remotePlugins: [
986+
{
987+
name: 'flaky plugin',
988+
creationName: 'Flaky Plugin',
989+
url: 'cdn/path/to/flaky.js',
990+
libraryName: 'flaky',
991+
settings: {},
992+
},
993+
],
994+
},
995+
{},
996+
{}
997+
)
998+
999+
expect(metricSpy).toHaveBeenCalledWith(expect.any(Context), {
1000+
integrationName: 'flaky plugin',
1001+
methodName: 'load',
1002+
type: 'action',
1003+
didError: true,
1004+
})
1005+
})
9691006
})

packages/browser/src/plugins/remote-loader/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ export async function remoteLoader(
309309
}
310310
} catch (error) {
311311
console.warn('Failed to load Remote Plugin', error)
312+
recordIntegrationMetric(Context.system(), {
313+
integrationName: remotePlugin.name,
314+
methodName: 'load',
315+
type: 'action',
316+
didError: true,
317+
})
312318
}
313319
}
314320
)

0 commit comments

Comments
 (0)