From 4ed492533764aede7222b03783b43763a5f0a57c Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 2 Jul 2024 14:46:08 +1000 Subject: [PATCH 1/5] feat: change the sampler's signature for better decisions --- src/async.ts | 7 ++++--- src/index.d.ts | 18 +++++++++++------- src/index.ts | 5 +++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/async.ts b/src/async.ts index 62048d5..61fd1b9 100644 --- a/src/async.ts +++ b/src/async.ts @@ -12,7 +12,7 @@ import { span_buffer, wait_promises } from './_internal'; export { configure, report } from './_internal'; type API = { - sampler: Sampler | boolean; + sampler: typeof Sampler | boolean; scope: { name: string }; clock: ClockLike; }; @@ -40,8 +40,9 @@ export function span(name: string, parent_id?: Traceparent | string) { const parent = (typeof parent_id === 'string' ? tctx.parse(parent_id) : (parent_id || current_span?.traceparent)); const id = parent?.child() || tctx.make(); - const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(name, id, scope); - !is_sampling ? tctx.unsample(id) : tctx.sample(id); + const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(id.parent_id, parent, name, scope); + if (is_sampling) tctx.sample(id); + else tctx.unsample(id); const span_obj: Span = { id, parent, name, diff --git a/src/index.d.ts b/src/index.d.ts index 1ace399..df492e7 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -27,7 +27,7 @@ export type Options = { /** * @borrows {@link Sampler} */ - sampler?: Sampler | boolean; + sampler?: typeof Sampler | boolean; clock?: ClockLike; }; @@ -47,20 +47,24 @@ export type Context = { * Return true if the span should be sampled, and reported to the {@link Exporter}. * Return false if the span should not be sampled, and not reported to the {@link Exporter}. */ -export type Sampler = ( +export function Sampler( /** - * The name of the span. + * The id of the new span looking for a sampling decision. + */ + id: string, + /** + * The parent id of the new span looking for a sampling decision. */ - name: string, + parent: Traceparent | undefined, /** - * The traceparent id of the span. + * The name of the span. */ - id: Traceparent, + name: string, /** * The tracer this span belongs to. */ tracer: { readonly name: string }, -) => boolean; +): boolean; // --- spans diff --git a/src/index.ts b/src/index.ts index 81bfc92..204aff5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,8 +24,9 @@ export function tracer(name: string, options?: Options): Tracer { const parent = (typeof parent_id === 'string' ? tctx.parse(parent_id) : parent_id); const id = parent?.child() || tctx.make(); - const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(name, id, scope); - !is_sampling ? tctx.unsample(id) : tctx.sample(id); + const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(id.parent_id, parent, name, scope); + if (is_sampling) tctx.sample(id); + else tctx.unsample(id); const span_obj: Span = { id, parent, name, From 80ad729fb0d70647cea479f69b45fe5c749e2f5c Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 2 Jul 2024 16:05:42 +1000 Subject: [PATCH 2/5] chore: leverage new tctx latest --- package.json | 2 +- src/async.test.ts | 2 +- src/async.ts | 12 ++++++------ src/index.ts | 12 ++++++------ src/rian.test.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 7c4518c..34d7903 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "prettier": "@marais/prettier", "dependencies": { "flattie": "^1.1.1", - "tctx": "^0.1.0" + "tctx": "^0.2.3" }, "devDependencies": { "@marais/prettier": "0.0.4", diff --git a/src/async.test.ts b/src/async.test.ts index 050eeea..74aaabb 100644 --- a/src/async.test.ts +++ b/src/async.test.ts @@ -3,7 +3,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { suite, test } from 'uvu'; import * as assert from 'uvu/assert'; -import { make } from 'tctx'; +import { make } from 'tctx/traceparent'; import type { Exporter } from 'rian'; import * as rian from 'rian/async'; diff --git a/src/async.ts b/src/async.ts index 61fd1b9..5b02887 100644 --- a/src/async.ts +++ b/src/async.ts @@ -4,8 +4,8 @@ import type { CallableScope, ClockLike, Options, Sampler, Scope, Span } from 'ri import type { Tracer } from 'rian/async'; import { measure } from 'rian/utils'; -import { type Traceparent } from 'tctx'; -import * as tctx from 'tctx'; +import { type Traceparent } from 'tctx/traceparent'; +import * as traceparent from 'tctx/traceparent'; import { span_buffer, wait_promises } from './_internal'; @@ -37,12 +37,12 @@ export function span(name: string, parent_id?: Traceparent | string) { const should_sample = api.sampler; // --- - const parent = (typeof parent_id === 'string' ? tctx.parse(parent_id) : (parent_id || current_span?.traceparent)); - const id = parent?.child() || tctx.make(); + const parent = (typeof parent_id === 'string' ? traceparent.parse(parent_id) : (parent_id || current_span?.traceparent)); + const id = parent?.child() || traceparent.make(); const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(id.parent_id, parent, name, scope); - if (is_sampling) tctx.sample(id); - else tctx.unsample(id); + if (is_sampling) traceparent.sample(id); + else traceparent.unsample(id); const span_obj: Span = { id, parent, name, diff --git a/src/index.ts b/src/index.ts index 204aff5..18a5fae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,8 @@ import type { CallableScope, Options, Span, Tracer } from 'rian'; import { measure } from 'rian/utils'; import { span_buffer, wait_promises } from './_internal'; -import { type Traceparent } from 'tctx'; -import * as tctx from 'tctx'; +import { type Traceparent } from 'tctx/traceparent'; +import * as traceparent from 'tctx/traceparent'; export { report, configure } from './_internal'; @@ -21,12 +21,12 @@ export function tracer(name: string, options?: Options): Tracer { parent_id?: Traceparent | string, ): CallableScope => { // --- - const parent = (typeof parent_id === 'string' ? tctx.parse(parent_id) : parent_id); - const id = parent?.child() || tctx.make(); + const parent = (typeof parent_id === 'string' ? traceparent.parse(parent_id) : parent_id); + const id = parent?.child() || traceparent.make(); const is_sampling = typeof should_sample == 'boolean' ? should_sample : should_sample(id.parent_id, parent, name, scope); - if (is_sampling) tctx.sample(id); - else tctx.unsample(id); + if (is_sampling) traceparent.sample(id); + else traceparent.unsample(id); const span_obj: Span = { id, parent, name, diff --git a/src/rian.test.ts b/src/rian.test.ts index 42dc9af..6633b2a 100644 --- a/src/rian.test.ts +++ b/src/rian.test.ts @@ -1,5 +1,5 @@ import { spy, spyOn } from 'nanospy'; -import { is_sampled } from 'tctx'; +import { is_sampled } from 'tctx/traceparent'; import { suite, test } from 'uvu'; import * as assert from 'uvu/assert'; From 44e2a546db65be7b0f359db77bfafc52e2c11241 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 2 Jul 2024 16:05:54 +1000 Subject: [PATCH 3/5] chore: bump bun lockfile --- bun.lockb | Bin 28641 -> 28641 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index c5404f5febb2b686b4d35f352e455c64d2dd9044..08d299d2fef702da64c39a5e817cc73c86c13aa2 100755 GIT binary patch delta 172 zcmV;d08{_r-vQy@0gx^rV4o+HLE1%bq^#fAhuuTiG|kNjt=#f}I7A;r{UndNu}($> zlMo0jlYj*Xv&aRd7eGg3OO&#KBcNi9jyO@^df5bUg@rw-VU`Ec?{ngbxfLt@b)>e5 z`2qFv1t7W3GHtUvkE}PFhh17r0~5p-O=7dH9dt4sGA=U!0C4~S01f~EfR*aV avjPELF#rGn0JA<�LX1E;F+QSlkzx?MEa4 delta 168 zcmV;Z09XIv-vQy@0gx^rl9ko|KCeG6*jJ_|{lcbDk?-&F^%eVbgK%*M)A2I*u}($> zlavoMv&aRd7eI=BuPFROs_@1zglV%!=BD{^j5Um7L5zwu{ufk3sI`V=)2aVvjPEL WF)lGKFta{T#0LX0E;6$PSlkyd2~3Ru From 7c78208daa8691ce5e9a5fe65775e4324d742a16 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 3 Jul 2024 15:41:16 +1000 Subject: [PATCH 4/5] chore: `type Sampler` is better --- src/async.ts | 2 +- src/index.d.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/async.ts b/src/async.ts index 5b02887..1804b01 100644 --- a/src/async.ts +++ b/src/async.ts @@ -12,7 +12,7 @@ import { span_buffer, wait_promises } from './_internal'; export { configure, report } from './_internal'; type API = { - sampler: typeof Sampler | boolean; + sampler: Sampler | boolean; scope: { name: string }; clock: ClockLike; }; diff --git a/src/index.d.ts b/src/index.d.ts index df492e7..b150214 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -27,7 +27,7 @@ export type Options = { /** * @borrows {@link Sampler} */ - sampler?: typeof Sampler | boolean; + sampler?: Sampler | boolean; clock?: ClockLike; }; @@ -47,7 +47,7 @@ export type Context = { * Return true if the span should be sampled, and reported to the {@link Exporter}. * Return false if the span should not be sampled, and not reported to the {@link Exporter}. */ -export function Sampler( +export type Sampler = ( /** * The id of the new span looking for a sampling decision. */ @@ -64,7 +64,7 @@ export function Sampler( * The tracer this span belongs to. */ tracer: { readonly name: string }, -): boolean; +) => boolean; // --- spans From d8b82bd23b70bb361bd51b14e324cead6bb4fa9b Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 3 Jul 2024 15:42:39 +1000 Subject: [PATCH 5/5] test: writes some tests for the sampler prop --- src/rian.test.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/rian.test.ts b/src/rian.test.ts index 6633b2a..0287493 100644 --- a/src/rian.test.ts +++ b/src/rian.test.ts @@ -5,6 +5,7 @@ import * as assert from 'uvu/assert'; import * as rian from 'rian'; import * as utils from 'rian/utils'; +import { traceparent } from 'tctx'; const noop = () => {}; @@ -229,9 +230,71 @@ buffer('flush all', async () => { assert.equal(spans[1].name, 'span 2'); }); +const sampler = suite('sampler'); + +sampler('should allow a sampler', async () => { + let should_sample = false; + + const tracer = rian.tracer('test', { + sampler: () => should_sample, + }); + + tracer.span('not sampled')(() => { }); + should_sample = true; + tracer.span('sampled')(() => { }); + + const exporter = spy(returns); + const scopedSpans: rian.ScopedSpans[] = await rian.report(exporter); + + assert.equal(scopedSpans.length, 1); + assert.equal(scopedSpans.at(0)!.spans.length, 1); + assert.equal(scopedSpans.at(0)?.spans.at(0)?.name, 'sampled'); +}); + +sampler('allow a sampler to make a decision from its parent', async () => { + let no_parent_sample = true; + + const S: rian.Sampler = (_id, parentId) => { + if (!parentId) return no_parent_sample; + return traceparent.is_sampled(parentId); + } + + const tracer = rian.tracer('test', { sampler: S }); + + tracer.span('sampled#1')((s) => { + no_parent_sample = false; + s.span('sampled#1.1')(() => { }) + }); + + no_parent_sample = false; + tracer.span('not sampled#1')((s) => { + s.span('not sampled#1.1')(() => { }) + }); + + no_parent_sample = true; + tracer.span('sampled#2')((s) => { + s.span('sampled#2.1')(() => { }) + }); + + no_parent_sample = false; + + const exporter = spy(returns); + const scopedSpans: rian.ScopedSpans[] = await rian.report(exporter); + + assert.equal(scopedSpans.length, 1); + assert.equal(scopedSpans.at(0)!.spans.length, 4); + assert.equal(scopedSpans.at(0)!.spans.map(s => s.name), [ + 'sampled#1', + 'sampled#1.1', + 'sampled#2', + 'sampled#2.1', + ]); +}); + test.run(); fn.run(); measure.run(); sampled.run(); events.run(); buffer.run(); +sampler.run();