@xylabs/telemetry 5.0.60 → 5.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/neutral/index.d.ts +1 -0
- package/dist/neutral/index.d.ts.map +1 -1
- package/dist/neutral/index.mjs +40 -7
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/neutral/span.d.ts +8 -2
- package/dist/neutral/span.d.ts.map +1 -1
- package/dist/neutral/timeBudget.d.ts +3 -0
- package/dist/neutral/timeBudget.d.ts.map +1 -0
- package/package.json +4 -3
- package/src/index.ts +1 -0
- package/src/span.ts +21 -6
- package/src/timeBudget.ts +30 -0
package/dist/neutral/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA"}
|
package/dist/neutral/index.mjs
CHANGED
|
@@ -7,6 +7,28 @@ import {
|
|
|
7
7
|
trace as TRACE_API
|
|
8
8
|
} from "@opentelemetry/api";
|
|
9
9
|
import { isDefined } from "@xylabs/typeof";
|
|
10
|
+
|
|
11
|
+
// src/timeBudget.ts
|
|
12
|
+
async function timeBudget(name, logger, func, budget, status = false) {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
const timer = status ? setInterval(() => {
|
|
15
|
+
const duration2 = Date.now() - start;
|
|
16
|
+
if (budget > 0 && duration2 > budget) {
|
|
17
|
+
logger?.warn(`Function [${name}] execution is exceeding budget: ${duration2}ms > ${budget}ms`);
|
|
18
|
+
}
|
|
19
|
+
}, Math.max(100, budget)) : void 0;
|
|
20
|
+
const result = await func();
|
|
21
|
+
const duration = Date.now() - start;
|
|
22
|
+
if (!timer && budget > 0 && duration > budget) {
|
|
23
|
+
logger?.warn(`Function [${name}] execution exceeded budget: ${duration}ms > ${budget}ms`);
|
|
24
|
+
}
|
|
25
|
+
if (timer) {
|
|
26
|
+
clearInterval(timer);
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/span.ts
|
|
10
32
|
function cloneContextWithoutSpan(activeCtx, configKeys = []) {
|
|
11
33
|
let newCtx = ROOT_CONTEXT;
|
|
12
34
|
const baggage = propagation.getBaggage(activeCtx);
|
|
@@ -67,13 +89,18 @@ function spanRoot(name, fn, tracer) {
|
|
|
67
89
|
return fn();
|
|
68
90
|
}
|
|
69
91
|
}
|
|
70
|
-
async function spanAsync(name, fn,
|
|
92
|
+
async function spanAsync(name, fn, {
|
|
93
|
+
timeBudgetLimit,
|
|
94
|
+
logger,
|
|
95
|
+
tracer
|
|
96
|
+
} = {}) {
|
|
71
97
|
const activeTracer = tracer ?? TRACE_API.getTracer(name);
|
|
98
|
+
const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn;
|
|
72
99
|
if (isDefined(activeTracer)) {
|
|
73
100
|
const span2 = activeTracer.startSpan(name);
|
|
74
101
|
return await context.with(TRACE_API.setSpan(context.active(), span2), async () => {
|
|
75
102
|
try {
|
|
76
|
-
const result = await
|
|
103
|
+
const result = await funcToRun();
|
|
77
104
|
span2.setStatus({ code: SpanStatusCode.OK });
|
|
78
105
|
return result;
|
|
79
106
|
} catch (ex) {
|
|
@@ -86,10 +113,15 @@ async function spanAsync(name, fn, tracer) {
|
|
|
86
113
|
}
|
|
87
114
|
});
|
|
88
115
|
} else {
|
|
89
|
-
return await
|
|
116
|
+
return await funcToRun();
|
|
90
117
|
}
|
|
91
118
|
}
|
|
92
|
-
async function spanRootAsync(name, fn,
|
|
119
|
+
async function spanRootAsync(name, fn, {
|
|
120
|
+
timeBudgetLimit,
|
|
121
|
+
logger,
|
|
122
|
+
tracer
|
|
123
|
+
} = {}) {
|
|
124
|
+
const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn;
|
|
93
125
|
const activeTracer = tracer ?? TRACE_API.getTracer(name);
|
|
94
126
|
if (isDefined(activeTracer)) {
|
|
95
127
|
const activeContext = context.active();
|
|
@@ -97,7 +129,7 @@ async function spanRootAsync(name, fn, tracer) {
|
|
|
97
129
|
const span2 = activeTracer.startSpan(name, {}, noSpanContext);
|
|
98
130
|
return await context.with(TRACE_API.setSpan(noSpanContext, span2), async () => {
|
|
99
131
|
try {
|
|
100
|
-
const result = await
|
|
132
|
+
const result = await funcToRun();
|
|
101
133
|
span2.setStatus({ code: SpanStatusCode.OK });
|
|
102
134
|
return result;
|
|
103
135
|
} catch (ex) {
|
|
@@ -110,7 +142,7 @@ async function spanRootAsync(name, fn, tracer) {
|
|
|
110
142
|
}
|
|
111
143
|
});
|
|
112
144
|
} else {
|
|
113
|
-
return await
|
|
145
|
+
return await funcToRun();
|
|
114
146
|
}
|
|
115
147
|
}
|
|
116
148
|
export {
|
|
@@ -118,6 +150,7 @@ export {
|
|
|
118
150
|
span,
|
|
119
151
|
spanAsync,
|
|
120
152
|
spanRoot,
|
|
121
|
-
spanRootAsync
|
|
153
|
+
spanRootAsync,
|
|
154
|
+
timeBudget
|
|
122
155
|
};
|
|
123
156
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/span.ts"],"sourcesContent":["import type {\n Context,\n Tracer,\n} from '@opentelemetry/api'\nimport {\n context, propagation, ROOT_CONTEXT, SpanStatusCode, trace as TRACE_API,\n} from '@opentelemetry/api'\nimport { isDefined } from '@xylabs/typeof'\n\nexport function cloneContextWithoutSpan(activeCtx: Context, configKeys: symbol[] = []): Context {\n // Start from root to ensure no span is propagated\n let newCtx = ROOT_CONTEXT\n\n // Copy baggage\n const baggage = propagation.getBaggage(activeCtx)\n if (baggage) {\n newCtx = propagation.setBaggage(newCtx, baggage)\n }\n\n // Copy custom config keys\n for (const key of configKeys) {\n const value = activeCtx.getValue(key)\n if (value !== undefined) {\n newCtx = newCtx.setValue(key, value)\n }\n }\n\n return newCtx\n}\n\nexport function span<T>(name: string, fn: () => T, tracer?: Tracer): T {\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n if (isDefined(activeTracer)) {\n const span = activeTracer.startSpan(name)\n return context.with(TRACE_API.setSpan(context.active(), span), () => {\n try {\n const result = fn()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return fn()\n }\n}\n\nexport function spanRoot<T>(name: string, fn: () => T, tracer?: Tracer): T {\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n if (isDefined(activeTracer)) {\n // Get current active context for configuration\n const activeContext = context.active()\n\n // Create a new context with no active span\n const noSpanContext = cloneContextWithoutSpan(activeContext)\n\n // Create a new span in the context without an active span\n const span = activeTracer.startSpan(name, {}, noSpanContext)\n\n // Use the active context but replace its span with our new root span\n return context.with(TRACE_API.setSpan(noSpanContext, span), () => {\n try {\n const result = fn()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return fn()\n }\n}\n\nexport async function spanAsync<T>(\n name: string,\n fn: () => Promise<T>,\n tracer
|
|
1
|
+
{"version":3,"sources":["../../src/span.ts","../../src/timeBudget.ts"],"sourcesContent":["import type {\n Context,\n Tracer,\n} from '@opentelemetry/api'\nimport {\n context, propagation, ROOT_CONTEXT, SpanStatusCode, trace as TRACE_API,\n} from '@opentelemetry/api'\nimport type { Logger } from '@xylabs/logger'\nimport { isDefined } from '@xylabs/typeof'\n\nimport { timeBudget } from './timeBudget.ts'\n\nexport interface SpanConfig {\n logger?: Logger | null\n timeBudgetLimit?: number\n tracer?: Tracer\n}\n\nexport function cloneContextWithoutSpan(activeCtx: Context, configKeys: symbol[] = []): Context {\n // Start from root to ensure no span is propagated\n let newCtx = ROOT_CONTEXT\n\n // Copy baggage\n const baggage = propagation.getBaggage(activeCtx)\n if (baggage) {\n newCtx = propagation.setBaggage(newCtx, baggage)\n }\n\n // Copy custom config keys\n for (const key of configKeys) {\n const value = activeCtx.getValue(key)\n if (value !== undefined) {\n newCtx = newCtx.setValue(key, value)\n }\n }\n\n return newCtx\n}\n\nexport function span<T>(name: string, fn: () => T, tracer?: Tracer): T {\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n if (isDefined(activeTracer)) {\n const span = activeTracer.startSpan(name)\n return context.with(TRACE_API.setSpan(context.active(), span), () => {\n try {\n const result = fn()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return fn()\n }\n}\n\nexport function spanRoot<T>(name: string, fn: () => T, tracer?: Tracer): T {\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n if (isDefined(activeTracer)) {\n // Get current active context for configuration\n const activeContext = context.active()\n\n // Create a new context with no active span\n const noSpanContext = cloneContextWithoutSpan(activeContext)\n\n // Create a new span in the context without an active span\n const span = activeTracer.startSpan(name, {}, noSpanContext)\n\n // Use the active context but replace its span with our new root span\n return context.with(TRACE_API.setSpan(noSpanContext, span), () => {\n try {\n const result = fn()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return fn()\n }\n}\n\nexport async function spanAsync<T>(\n name: string,\n fn: () => Promise<T>,\n {\n timeBudgetLimit, logger, tracer,\n }: SpanConfig = {},\n): Promise<T> {\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn\n if (isDefined(activeTracer)) {\n const span = activeTracer.startSpan(name)\n return await context.with(TRACE_API.setSpan(context.active(), span), async () => {\n try {\n const result = await funcToRun()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return await funcToRun()\n }\n}\n\nexport async function spanRootAsync<T>(\n name: string,\n fn: () => Promise<T>,\n {\n timeBudgetLimit, logger, tracer,\n }: SpanConfig = {},\n): Promise<T> {\n const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn\n const activeTracer = tracer ?? TRACE_API.getTracer(name)\n if (isDefined(activeTracer)) {\n const activeContext = context.active()\n\n const noSpanContext = cloneContextWithoutSpan(activeContext)\n\n // Create a new span in the context without an active span\n const span = activeTracer.startSpan(name, {}, noSpanContext)\n\n // Use the active context but replace its span with our new root span\n return await context.with(TRACE_API.setSpan(noSpanContext, span), async () => {\n try {\n const result = await funcToRun()\n span.setStatus({ code: SpanStatusCode.OK })\n return result\n } catch (ex) {\n const error = ex as Error\n span.recordException(error)\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })\n throw ex\n } finally {\n span.end()\n }\n })\n } else {\n return await funcToRun()\n }\n}\n","import type { Logger } from '@xylabs/logger'\n\nexport async function timeBudget<TResult>(\n name: string,\n logger: Logger | undefined,\n func: () => Promise<TResult>,\n budget: number,\n status = false,\n): Promise<TResult> {\n const start = Date.now()\n const timer = status\n ? setInterval(() => {\n const duration = Date.now() - start\n if ((budget > 0) && (duration > budget)) {\n logger?.warn(`Function [${name}] execution is exceeding budget: ${duration}ms > ${budget}ms`)\n }\n }, Math.max(100, budget))\n : undefined\n\n const result = await func()\n const duration = Date.now() - start\n\n if (!timer && (budget > 0) && (duration > budget)) {\n logger?.warn(`Function [${name}] execution exceeded budget: ${duration}ms > ${budget}ms`)\n }\n if (timer) {\n clearInterval(timer)\n }\n return result\n}\n"],"mappings":"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","names":["duration","span"]}
|
package/dist/neutral/span.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Context, Tracer } from '@opentelemetry/api';
|
|
2
|
+
import type { Logger } from '@xylabs/logger';
|
|
3
|
+
export interface SpanConfig {
|
|
4
|
+
logger?: Logger | null;
|
|
5
|
+
timeBudgetLimit?: number;
|
|
6
|
+
tracer?: Tracer;
|
|
7
|
+
}
|
|
2
8
|
export declare function cloneContextWithoutSpan(activeCtx: Context, configKeys?: symbol[]): Context;
|
|
3
9
|
export declare function span<T>(name: string, fn: () => T, tracer?: Tracer): T;
|
|
4
10
|
export declare function spanRoot<T>(name: string, fn: () => T, tracer?: Tracer): T;
|
|
5
|
-
export declare function spanAsync<T>(name: string, fn: () => Promise<T>, tracer?:
|
|
6
|
-
export declare function spanRootAsync<T>(name: string, fn: () => Promise<T>, tracer?:
|
|
11
|
+
export declare function spanAsync<T>(name: string, fn: () => Promise<T>, { timeBudgetLimit, logger, tracer, }?: SpanConfig): Promise<T>;
|
|
12
|
+
export declare function spanRootAsync<T>(name: string, fn: () => Promise<T>, { timeBudgetLimit, logger, tracer, }?: SpanConfig): Promise<T>;
|
|
7
13
|
//# sourceMappingURL=span.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../../src/span.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACP,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../../src/span.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EACP,MAAM,oBAAoB,CAAA;AAI3B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAK5C,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAmB9F;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAqBrE;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CA8BzE;AAED,wBAAsB,SAAS,CAAC,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,EACE,eAAe,EAAE,MAAM,EAAE,MAAM,GAChC,GAAE,UAAe,GACjB,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,EACE,eAAe,EAAE,MAAM,EAAE,MAAM,GAChC,GAAE,UAAe,GACjB,OAAO,CAAC,CAAC,CAAC,CA6BZ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeBudget.d.ts","sourceRoot":"","sources":["../../src/timeBudget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE5C,wBAAsB,UAAU,CAAC,OAAO,EACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,UAAQ,GACb,OAAO,CAAC,OAAO,CAAC,CAqBlB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xylabs/telemetry",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.62",
|
|
4
4
|
"description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hex",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@opentelemetry/api": "^1.9.0",
|
|
49
|
-
"@xylabs/
|
|
49
|
+
"@xylabs/logger": "~5.0.62",
|
|
50
|
+
"@xylabs/typeof": "~5.0.62"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@xylabs/ts-scripts-yarn3": "~7.2.32",
|
|
@@ -59,4 +60,4 @@
|
|
|
59
60
|
"publishConfig": {
|
|
60
61
|
"access": "public"
|
|
61
62
|
}
|
|
62
|
-
}
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
package/src/span.ts
CHANGED
|
@@ -5,8 +5,17 @@ import type {
|
|
|
5
5
|
import {
|
|
6
6
|
context, propagation, ROOT_CONTEXT, SpanStatusCode, trace as TRACE_API,
|
|
7
7
|
} from '@opentelemetry/api'
|
|
8
|
+
import type { Logger } from '@xylabs/logger'
|
|
8
9
|
import { isDefined } from '@xylabs/typeof'
|
|
9
10
|
|
|
11
|
+
import { timeBudget } from './timeBudget.ts'
|
|
12
|
+
|
|
13
|
+
export interface SpanConfig {
|
|
14
|
+
logger?: Logger | null
|
|
15
|
+
timeBudgetLimit?: number
|
|
16
|
+
tracer?: Tracer
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export function cloneContextWithoutSpan(activeCtx: Context, configKeys: symbol[] = []): Context {
|
|
11
20
|
// Start from root to ensure no span is propagated
|
|
12
21
|
let newCtx = ROOT_CONTEXT
|
|
@@ -86,14 +95,17 @@ export function spanRoot<T>(name: string, fn: () => T, tracer?: Tracer): T {
|
|
|
86
95
|
export async function spanAsync<T>(
|
|
87
96
|
name: string,
|
|
88
97
|
fn: () => Promise<T>,
|
|
89
|
-
|
|
98
|
+
{
|
|
99
|
+
timeBudgetLimit, logger, tracer,
|
|
100
|
+
}: SpanConfig = {},
|
|
90
101
|
): Promise<T> {
|
|
91
102
|
const activeTracer = tracer ?? TRACE_API.getTracer(name)
|
|
103
|
+
const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn
|
|
92
104
|
if (isDefined(activeTracer)) {
|
|
93
105
|
const span = activeTracer.startSpan(name)
|
|
94
106
|
return await context.with(TRACE_API.setSpan(context.active(), span), async () => {
|
|
95
107
|
try {
|
|
96
|
-
const result = await
|
|
108
|
+
const result = await funcToRun()
|
|
97
109
|
span.setStatus({ code: SpanStatusCode.OK })
|
|
98
110
|
return result
|
|
99
111
|
} catch (ex) {
|
|
@@ -106,15 +118,18 @@ export async function spanAsync<T>(
|
|
|
106
118
|
}
|
|
107
119
|
})
|
|
108
120
|
} else {
|
|
109
|
-
return await
|
|
121
|
+
return await funcToRun()
|
|
110
122
|
}
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
export async function spanRootAsync<T>(
|
|
114
126
|
name: string,
|
|
115
127
|
fn: () => Promise<T>,
|
|
116
|
-
|
|
128
|
+
{
|
|
129
|
+
timeBudgetLimit, logger, tracer,
|
|
130
|
+
}: SpanConfig = {},
|
|
117
131
|
): Promise<T> {
|
|
132
|
+
const funcToRun = isDefined(timeBudgetLimit) ? () => timeBudget(name, logger ?? console, fn, timeBudgetLimit) : fn
|
|
118
133
|
const activeTracer = tracer ?? TRACE_API.getTracer(name)
|
|
119
134
|
if (isDefined(activeTracer)) {
|
|
120
135
|
const activeContext = context.active()
|
|
@@ -127,7 +142,7 @@ export async function spanRootAsync<T>(
|
|
|
127
142
|
// Use the active context but replace its span with our new root span
|
|
128
143
|
return await context.with(TRACE_API.setSpan(noSpanContext, span), async () => {
|
|
129
144
|
try {
|
|
130
|
-
const result = await
|
|
145
|
+
const result = await funcToRun()
|
|
131
146
|
span.setStatus({ code: SpanStatusCode.OK })
|
|
132
147
|
return result
|
|
133
148
|
} catch (ex) {
|
|
@@ -140,6 +155,6 @@ export async function spanRootAsync<T>(
|
|
|
140
155
|
}
|
|
141
156
|
})
|
|
142
157
|
} else {
|
|
143
|
-
return await
|
|
158
|
+
return await funcToRun()
|
|
144
159
|
}
|
|
145
160
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Logger } from '@xylabs/logger'
|
|
2
|
+
|
|
3
|
+
export async function timeBudget<TResult>(
|
|
4
|
+
name: string,
|
|
5
|
+
logger: Logger | undefined,
|
|
6
|
+
func: () => Promise<TResult>,
|
|
7
|
+
budget: number,
|
|
8
|
+
status = false,
|
|
9
|
+
): Promise<TResult> {
|
|
10
|
+
const start = Date.now()
|
|
11
|
+
const timer = status
|
|
12
|
+
? setInterval(() => {
|
|
13
|
+
const duration = Date.now() - start
|
|
14
|
+
if ((budget > 0) && (duration > budget)) {
|
|
15
|
+
logger?.warn(`Function [${name}] execution is exceeding budget: ${duration}ms > ${budget}ms`)
|
|
16
|
+
}
|
|
17
|
+
}, Math.max(100, budget))
|
|
18
|
+
: undefined
|
|
19
|
+
|
|
20
|
+
const result = await func()
|
|
21
|
+
const duration = Date.now() - start
|
|
22
|
+
|
|
23
|
+
if (!timer && (budget > 0) && (duration > budget)) {
|
|
24
|
+
logger?.warn(`Function [${name}] execution exceeded budget: ${duration}ms > ${budget}ms`)
|
|
25
|
+
}
|
|
26
|
+
if (timer) {
|
|
27
|
+
clearInterval(timer)
|
|
28
|
+
}
|
|
29
|
+
return result
|
|
30
|
+
}
|