This repository has been archived by the owner on Mar 31, 2024. It is now read-only.
forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[APM] Add custom spans around async operations (elastic#90403)
Co-authored-by: Kibana Machine <[email protected]>
- Loading branch information
1 parent
f8b8d5b
commit a28318e
Showing
102 changed files
with
4,792 additions
and
4,213 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "@kbn/apm-utils", | ||
"main": "./target/index.js", | ||
"types": "./target/index.d.ts", | ||
"version": "1.0.0", | ||
"license": "SSPL-1.0 OR Elastic License 2.0", | ||
"private": true, | ||
"scripts": { | ||
"build": "../../node_modules/.bin/tsc", | ||
"kbn:bootstrap": "yarn build", | ||
"kbn:watch": "yarn build --watch" | ||
} | ||
} |
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,87 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import agent from 'elastic-apm-node'; | ||
import asyncHooks from 'async_hooks'; | ||
|
||
export interface SpanOptions { | ||
name: string; | ||
type?: string; | ||
subtype?: string; | ||
labels?: Record<string, string>; | ||
} | ||
|
||
export function parseSpanOptions(optionsOrName: SpanOptions | string) { | ||
const options = typeof optionsOrName === 'string' ? { name: optionsOrName } : optionsOrName; | ||
|
||
return options; | ||
} | ||
|
||
const runInNewContext = <T extends (...args: any[]) => any>(cb: T): ReturnType<T> => { | ||
const resource = new asyncHooks.AsyncResource('fake_async'); | ||
|
||
return resource.runInAsyncScope(cb); | ||
}; | ||
|
||
export async function withSpan<T>( | ||
optionsOrName: SpanOptions | string, | ||
cb: () => Promise<T> | ||
): Promise<T> { | ||
const options = parseSpanOptions(optionsOrName); | ||
|
||
const { name, type, subtype, labels } = options; | ||
|
||
if (!agent.isStarted()) { | ||
return cb(); | ||
} | ||
|
||
// When a span starts, it's marked as the active span in its context. | ||
// When it ends, it's not untracked, which means that if a span | ||
// starts directly after this one ends, the newly started span is a | ||
// child of this span, even though it should be a sibling. | ||
// To mitigate this, we queue a microtask by awaiting a promise. | ||
await Promise.resolve(); | ||
|
||
const span = agent.startSpan(name); | ||
|
||
if (!span) { | ||
return cb(); | ||
} | ||
|
||
// If a span is created in the same context as the span that we just | ||
// started, it will be a sibling, not a child. E.g., the Elasticsearch span | ||
// that is created when calling search() happens in the same context. To | ||
// mitigate this we create a new context. | ||
|
||
return runInNewContext(() => { | ||
// @ts-ignore | ||
if (type) { | ||
span.type = type; | ||
} | ||
if (subtype) { | ||
span.subtype = subtype; | ||
} | ||
|
||
if (labels) { | ||
span.addLabels(labels); | ||
} | ||
|
||
return cb() | ||
.then((res) => { | ||
span.outcome = 'success'; | ||
return res; | ||
}) | ||
.catch((err) => { | ||
span.outcome = 'failure'; | ||
throw err; | ||
}) | ||
.finally(() => { | ||
span.end(); | ||
}); | ||
}); | ||
} |
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,18 @@ | ||
{ | ||
"extends": "../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"declaration": true, | ||
"outDir": "./target", | ||
"stripInternal": false, | ||
"declarationMap": true, | ||
"types": [ | ||
"node" | ||
] | ||
}, | ||
"include": [ | ||
"./src/**/*.ts" | ||
], | ||
"exclude": [ | ||
"target" | ||
] | ||
} |
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
Oops, something went wrong.