-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
142 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import type { Carrier, Hub } from '@sentry/core'; | ||
import { | ||
ensureHubOnCarrier, | ||
getCurrentHub as getCurrentHubCore, | ||
getHubFromCarrier, | ||
setAsyncContextStrategy, | ||
} from '@sentry/core'; | ||
import * as domain from 'domain'; | ||
import { EventEmitter } from 'events'; | ||
|
||
function getCurrentHub(): Hub | undefined { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any | ||
const activeDomain = (domain as any).active as Carrier; | ||
|
||
// If there's no active domain, just return undefined and the global hub will be used | ||
if (!activeDomain) { | ||
return undefined; | ||
} | ||
|
||
ensureHubOnCarrier(activeDomain); | ||
|
||
return getHubFromCarrier(activeDomain); | ||
} | ||
|
||
function runWithAsyncContext<T, A>(callback: (hub: Hub) => T, ...args: A[]): T { | ||
const local = domain.create(); | ||
|
||
for (const emitter of args) { | ||
if (emitter instanceof EventEmitter) { | ||
local.add(emitter); | ||
} | ||
} | ||
|
||
return local.bind(() => { | ||
const hub = getCurrentHubCore(); | ||
return callback(hub); | ||
})(); | ||
} | ||
|
||
/** | ||
* Sets the async context strategy to use Node.js domains. | ||
*/ | ||
export function setDomainAsyncContextStrategy(): void { | ||
setAsyncContextStrategy({ getCurrentHub, runWithAsyncContext }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { getCurrentHub, Hub, runWithAsyncContext, setAsyncContextStrategy } from '@sentry/core'; | ||
import * as domain from 'domain'; | ||
|
||
import { setDomainAsyncContextStrategy } from '../../src/async/domain'; | ||
|
||
describe('domains', () => { | ||
afterAll(() => { | ||
// clear the strategy | ||
setAsyncContextStrategy(undefined); | ||
}); | ||
|
||
test('without domain', () => { | ||
// @ts-ignore property active does not exist on domain | ||
expect(domain.active).toBeFalsy(); | ||
const hub = getCurrentHub(); | ||
expect(hub).toEqual(new Hub()); | ||
}); | ||
|
||
test('domain hub scope inheritance', () => { | ||
const globalHub = getCurrentHub(); | ||
globalHub.configureScope(scope => { | ||
scope.setExtra('a', 'b'); | ||
scope.setTag('a', 'b'); | ||
scope.addBreadcrumb({ message: 'a' }); | ||
}); | ||
runWithAsyncContext(hub => { | ||
expect(globalHub).toEqual(hub); | ||
}); | ||
}); | ||
|
||
test('domain hub single instance', () => { | ||
setDomainAsyncContextStrategy(); | ||
|
||
runWithAsyncContext(hub => { | ||
expect(hub).toBe(getCurrentHub()); | ||
}); | ||
}); | ||
|
||
test('concurrent domain hubs', done => { | ||
setDomainAsyncContextStrategy(); | ||
|
||
let d1done = false; | ||
let d2done = false; | ||
|
||
runWithAsyncContext(hub => { | ||
hub.getStack().push({ client: 'process' } as any); | ||
expect(hub.getStack()[1]).toEqual({ client: 'process' }); | ||
// Just in case so we don't have to worry which one finishes first | ||
// (although it always should be d2) | ||
setTimeout(() => { | ||
d1done = true; | ||
if (d2done) { | ||
done(); | ||
} | ||
}); | ||
}); | ||
|
||
runWithAsyncContext(hub => { | ||
hub.getStack().push({ client: 'local' } as any); | ||
expect(hub.getStack()[1]).toEqual({ client: 'local' }); | ||
setTimeout(() => { | ||
d2done = true; | ||
if (d1done) { | ||
done(); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |