generated from honeycombio/.github
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add DeterministicSampler for easier sampling (#70)
## Which problem is this PR solving? - Closes #67 ## Short description of the changes - add Deterministic Sampler - add sampleRate info to debug - add unit tests and update smoke test ## How to verify that this has the expected result ```js const sdk = new HoneycombWebSDK({ apiKey: HONEYCOMB_API_KEY, serviceName: 'web-distro', debug: true, sampleRate: 2, instrumentations: [getWebAutoInstrumentations()], // add auto-instrumentation }); ``` With a sampleRate higher than one you may have to generate a few traces before it shows up in Honeycomb! But with debug enabled at least you can see the config logged, and also see the "Non recording span" if it's not going to send because it's being dropped with sampling. --------- Co-authored-by: Kent Quirk <[email protected]>
- Loading branch information
1 parent
b0909f8
commit 5318210
Showing
9 changed files
with
265 additions
and
5 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,72 @@ | ||
import { Attributes, Context, Link, SpanKind } from '@opentelemetry/api'; | ||
import { | ||
AlwaysOnSampler, | ||
Sampler, | ||
SamplingResult, | ||
TraceIdRatioBasedSampler, | ||
} from '@opentelemetry/sdk-trace-base'; | ||
import { getSampleRate } from './util'; | ||
import { HoneycombOptions } from './types'; | ||
|
||
/** | ||
* Builds and returns a Deterministic Sampler that uses the provided sample rate to | ||
* configure the inner sampler. | ||
* @param options The {@link HoneycombOptions} | ||
* @returns a {@link DeterministicSampler} | ||
*/ | ||
export const configureDeterministicSampler = (options?: HoneycombOptions) => { | ||
const sampleRate = getSampleRate(options); | ||
return new DeterministicSampler(sampleRate); | ||
}; | ||
|
||
/** | ||
* A {@link Sampler} that uses a deterministic sample rate to configure the sampler. | ||
*/ | ||
export class DeterministicSampler implements Sampler { | ||
private _sampleRate: number; | ||
private _sampler: Sampler; | ||
|
||
constructor(sampleRate: number) { | ||
this._sampleRate = sampleRate; | ||
switch (sampleRate) { | ||
// sample rate of 1 is default, send everything | ||
case 1: | ||
this._sampler = new AlwaysOnSampler(); | ||
break; | ||
default: { | ||
const ratio = 1.0 / sampleRate; | ||
this._sampler = new TraceIdRatioBasedSampler(ratio); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
shouldSample( | ||
context: Context, | ||
traceId: string, | ||
spanName: string, | ||
spanKind: SpanKind, | ||
attributes: Attributes, | ||
links: Link[], | ||
): SamplingResult { | ||
const result = this._sampler.shouldSample( | ||
context, | ||
traceId, | ||
spanName, | ||
spanKind, | ||
attributes, | ||
links, | ||
); | ||
return { | ||
...result, | ||
attributes: { | ||
...result.attributes, | ||
SampleRate: this._sampleRate, | ||
}, | ||
}; | ||
} | ||
|
||
toString(): string { | ||
return `DeterministicSampler(${this._sampler.toString()})`; | ||
} | ||
} |
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
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,108 @@ | ||
import { | ||
configureDeterministicSampler, | ||
DeterministicSampler, | ||
} from '../src/deterministic-sampler'; | ||
import { ROOT_CONTEXT, SpanKind, trace, TraceFlags } from '@opentelemetry/api'; | ||
import { | ||
SamplingDecision, | ||
SamplingResult, | ||
} from '@opentelemetry/sdk-trace-base'; | ||
|
||
const traceId = 'd4cda95b652f4a1592b449d5929fda1b'; | ||
const spanId = '6e0c63257de34c92'; | ||
const spanName = 'doStuff'; | ||
|
||
const getSamplingResult = (sampler: DeterministicSampler): SamplingResult => { | ||
return sampler.shouldSample( | ||
trace.setSpanContext(ROOT_CONTEXT, { | ||
traceId, | ||
spanId, | ||
traceFlags: TraceFlags.NONE, | ||
}), | ||
traceId, | ||
spanName, | ||
SpanKind.CLIENT, | ||
{}, | ||
[], | ||
); | ||
}; | ||
|
||
describe('deterministic sampler', () => { | ||
test('sampler with rate of 1 configures inner AlwaysOnSampler', () => { | ||
const sampler = new DeterministicSampler(1); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe('DeterministicSampler(AlwaysOnSampler)'); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.RECORD_AND_SAMPLED); | ||
expect(result.attributes).toEqual({ SampleRate: 1 }); | ||
}); | ||
|
||
test('sampler with rate of 10 configures inner TraceIdRatioBased sampler with a ratio of 0.1', () => { | ||
const sampler = new DeterministicSampler(10); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe( | ||
'DeterministicSampler(TraceIdRatioBased{0.1})', | ||
); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.NOT_RECORD); | ||
expect(result.attributes).toEqual({ SampleRate: 10 }); | ||
}); | ||
}); | ||
|
||
describe('configureDeterministicSampler', () => { | ||
test('sample rate of 1 configures inner AlwaysOnSampler', () => { | ||
const options = { | ||
sampleRate: 1, | ||
}; | ||
const sampler = configureDeterministicSampler(options); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe('DeterministicSampler(AlwaysOnSampler)'); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.RECORD_AND_SAMPLED); | ||
expect(result.attributes).toEqual({ SampleRate: 1 }); | ||
}); | ||
|
||
test('sample rate of 0 configures inner AlwaysOnSampler', () => { | ||
const options = { | ||
sampleRate: 0, | ||
}; | ||
const sampler = configureDeterministicSampler(options); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe('DeterministicSampler(AlwaysOnSampler)'); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.RECORD_AND_SAMPLED); | ||
expect(result.attributes).toEqual({ SampleRate: 1 }); | ||
}); | ||
|
||
test('sample rate of -42 configures inner AlwaysOn Sampler', () => { | ||
const options = { | ||
sampleRate: 0, | ||
}; | ||
const sampler = configureDeterministicSampler(options); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe('DeterministicSampler(AlwaysOnSampler)'); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.RECORD_AND_SAMPLED); | ||
expect(result.attributes).toEqual({ SampleRate: 1 }); | ||
}); | ||
|
||
test('sample rate of 10 configures inner TraceIdRatioBased sampler with a ratio of 0.1', () => { | ||
const options = { | ||
sampleRate: 10, | ||
}; | ||
const sampler = configureDeterministicSampler(options); | ||
expect(sampler).toBeInstanceOf(DeterministicSampler); | ||
expect(sampler.toString()).toBe( | ||
'DeterministicSampler(TraceIdRatioBased{0.1})', | ||
); | ||
|
||
const result = getSamplingResult(sampler); | ||
expect(result.decision).toBe(SamplingDecision.NOT_RECORD); | ||
expect(result.attributes).toEqual({ SampleRate: 10 }); | ||
}); | ||
}); |
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