Comparing version 0.10.1 to 0.11.0
142
index.d.ts
@@ -17,7 +17,23 @@ // TypeScript type definitions | ||
namespace sampler { | ||
class Sampler { | ||
constructor(evaluator: (traceId: TraceId) => boolean); | ||
shouldSample(traceId: TraceId): option.IOption<boolean> | ||
} | ||
class CountingSampler implements Sampler { | ||
constructor(sampleRate?: number); | ||
shouldSample(traceId: TraceId): option.IOption<boolean> | ||
} | ||
const neverSample: (traceId: TraceId) => boolean; | ||
const alwaysSample: (traceId: TraceId) => boolean; | ||
} | ||
class Tracer { | ||
constructor(args: { ctxImpl: Context<TraceId>, recorder: Recorder, sampler?: any, traceId128Bit?: boolean }); | ||
constructor(args: { ctxImpl: Context<TraceId>, recorder: Recorder, sampler?: sampler.Sampler, traceId128Bit?: boolean, localServiceName?: string, localEndpoint?: model.Endpoint }); | ||
id: TraceId; | ||
scoped<V>(callback: () => V): V; | ||
local<V>(name: string, callback: () => V): V; | ||
createRootId(): TraceId; | ||
@@ -39,2 +55,16 @@ createChildId(): TraceId; | ||
class TraceId { | ||
_traceId: option.IOption<string>; | ||
_parentId: option.IOption<string>; | ||
_spanId: string; | ||
_sampled: option.IOption<boolean>; | ||
_flags: number; | ||
readonly traceId: string; | ||
readonly parentId: string; | ||
readonly spanId: string; | ||
readonly sampled: option.IOption<boolean>; | ||
readonly flags: number; | ||
isDebug(): boolean; | ||
constructor(args?: { | ||
@@ -47,2 +77,4 @@ traceId?: option.IOption<string>, | ||
}); | ||
isDebug(): boolean; | ||
toString(): string; | ||
} | ||
@@ -91,5 +123,50 @@ | ||
namespace model { | ||
class Endpoint { | ||
constructor(args: { serviceName?: string, ipv4?: InetAddress, port?: number }); | ||
setServiceName(serviceName: string): void; | ||
setIpv4(ipv4: string): void; | ||
setPort(port: number): void; | ||
isEmpty(): void; | ||
} | ||
interface Annotation { | ||
timestamp: number; | ||
value: string; | ||
} | ||
class Span { | ||
readonly traceId: string; | ||
readonly parentId?: string; | ||
readonly id: string; | ||
readonly name?: string; | ||
readonly kind?: string; | ||
readonly timestamp?: number; | ||
readonly duration?: number; | ||
readonly localEndpoint?: Endpoint; | ||
readonly remoteEndpoint?: Endpoint; | ||
readonly annotations: Annotation[]; | ||
readonly tags: { [ key: string ]: string }; | ||
readonly debug: boolean; | ||
readonly shared: boolean; | ||
constructor(traceId: TraceId) | ||
setName(name: string): void; | ||
setKind(kind: string): void; | ||
setTimestamp(timestamp: number): void; | ||
setDuration(duration: number): void; | ||
setLocalEndpoint(endpoint: Endpoint): void; | ||
setRemoteEndpoint(endpoint: Endpoint): void; | ||
addAnnotation(timestamp: number, value: string): void; | ||
putTag(key: string, value: string): void; | ||
setDebug(debug: boolean): void; | ||
setShared(shared: boolean): void; | ||
} | ||
} | ||
/** Used by the HttpLogger transport to convert spans to JSON */ | ||
interface JsonEncoder { | ||
// TODO: make a typesafe def for model.Endpoint,Span using Span here | ||
encode: (span: any) => string; | ||
encode: (span: model.Span) => string; | ||
} | ||
@@ -103,12 +180,22 @@ | ||
interface IAnnotation { | ||
readonly annotationType: string | ||
} | ||
namespace Annotation { | ||
class ClientSend implements IAnnotation { } | ||
class ClientRecv implements IAnnotation { } | ||
class ServerSend implements IAnnotation { } | ||
class ServerRecv implements IAnnotation { } | ||
class ClientSend implements IAnnotation { | ||
readonly annotationType: string; | ||
} | ||
class ClientRecv implements IAnnotation { | ||
readonly annotationType: string; | ||
} | ||
class ServerSend implements IAnnotation { | ||
readonly annotationType: string; | ||
} | ||
class ServerRecv implements IAnnotation { | ||
readonly annotationType: string; | ||
} | ||
class Message implements IAnnotation { | ||
constructor(message: string); | ||
readonly annotationType: string; | ||
} | ||
@@ -118,2 +205,3 @@ | ||
constructor(serviceName: string); | ||
readonly annotationType: string; | ||
} | ||
@@ -123,2 +211,3 @@ | ||
constructor(name: string); | ||
readonly annotationType: string; | ||
} | ||
@@ -128,2 +217,3 @@ | ||
constructor(args: { host: InetAddress, port: number }); | ||
readonly annotationType: string; | ||
} | ||
@@ -133,2 +223,3 @@ | ||
constructor(args: { serviceName: string, host?: InetAddress, port?: number }); | ||
readonly annotationType: string; | ||
} | ||
@@ -138,2 +229,3 @@ | ||
constructor(args?: { host?: InetAddress, port?: number }); | ||
readonly annotationType: string; | ||
} | ||
@@ -143,2 +235,3 @@ | ||
constructor(key: string, value: boolean | string | number); | ||
readonly annotationType: string; | ||
} | ||
@@ -150,2 +243,5 @@ } | ||
static getLocalAddress(): InetAddress; | ||
ipv4(): string; | ||
toInt(): number; | ||
} | ||
@@ -161,4 +257,11 @@ | ||
interface Record { | ||
traceId: TraceId; | ||
timestamp: number; | ||
annotation: IAnnotation | ||
} | ||
/** The Tracer sends each annotation to a Recorder implementation */ | ||
interface Recorder { | ||
record: (rec: any) => void; | ||
record: (rec: Record) => void; | ||
} | ||
@@ -168,3 +271,3 @@ | ||
constructor(args: { logger: Logger, timeout?: number }); | ||
record: (rec: any) => void; | ||
record: (rec: Record) => void; | ||
} | ||
@@ -174,14 +277,21 @@ | ||
constructor(args?: { logger?: Logger }); | ||
record: (rec: any) => void; | ||
record: (rec: Record) => void; | ||
} | ||
class ExplicitContext { | ||
class ExplicitContext implements Context<TraceId> { | ||
setContext(ctx: TraceId): void; | ||
getContext(): TraceId; | ||
scoped<V>(callback: () => V): V; | ||
letContext<V>(ctx: TraceId, callback: () => V): V; | ||
} | ||
const sampler: () => void; | ||
class Request { | ||
namespace Request { | ||
function addZipkinHeaders(req: {headers: any}, traceId: TraceId): void; | ||
} | ||
/** The Logger (or transport) is what the Recorder uses to send spans to Zipkin. | ||
* @see https://github.com/openzipkin/zipkin-js/#transports Official transport implementations | ||
*/ | ||
interface Logger { | ||
logSpan(span: model.Span): void; | ||
} | ||
@@ -191,3 +301,3 @@ | ||
class HttpServer { | ||
constructor(args: { tracer: Tracer, serviceName: string, port: string | number }); | ||
constructor(args: { tracer: Tracer, port: number }); | ||
@@ -203,3 +313,3 @@ recordRequest( | ||
class HttpClient { | ||
constructor(args: { tracer: Tracer, serviceName: string, remoteServiceName?: string }); | ||
constructor(args: { tracer: Tracer, remoteServiceName?: string }); | ||
@@ -206,0 +316,0 @@ recordRequest<T>( |
@@ -76,2 +76,21 @@ 'use strict'; | ||
function LocalOperationStart(name) { | ||
this.name = name; | ||
} | ||
LocalOperationStart.prototype.toString = function () { | ||
return 'LocalOperationStart("' + this.name + '")'; | ||
}; | ||
var LocalOperationStop = function (_SimpleAnnotation5) { | ||
_inherits(LocalOperationStop, _SimpleAnnotation5); | ||
function LocalOperationStop() { | ||
_classCallCheck(this, LocalOperationStop); | ||
return _possibleConstructorReturn(this, (LocalOperationStop.__proto__ || Object.getPrototypeOf(LocalOperationStop)).apply(this, arguments)); | ||
} | ||
return LocalOperationStop; | ||
}(SimpleAnnotation); | ||
function Message(message) { | ||
@@ -152,3 +171,5 @@ this.message = message; | ||
LocalAddr: LocalAddr, | ||
BinaryAnnotation: BinaryAnnotation | ||
BinaryAnnotation: BinaryAnnotation, | ||
LocalOperationStart: LocalOperationStart, | ||
LocalOperationStop: LocalOperationStop | ||
}; | ||
@@ -155,0 +176,0 @@ |
@@ -117,2 +117,8 @@ 'use strict'; | ||
break; | ||
case 'LocalOperationStart': | ||
span.delegate.setName(rec.annotation.name); | ||
break; | ||
case 'LocalOperationStop': | ||
span.finish(); | ||
break; | ||
case 'Message': | ||
@@ -119,0 +125,0 @@ span.delegate.addAnnotation(rec.timestamp, rec.annotation.message); |
@@ -19,3 +19,3 @@ 'use strict'; | ||
_ref$serviceName = _ref.serviceName, | ||
serviceName = _ref$serviceName === undefined ? requiredArg('serviceName') : _ref$serviceName, | ||
serviceName = _ref$serviceName === undefined ? tracer.localEndpoint.serviceName : _ref$serviceName, | ||
remoteServiceName = _ref.remoteServiceName; | ||
@@ -22,0 +22,0 @@ |
@@ -41,3 +41,3 @@ 'use strict'; | ||
_ref$serviceName = _ref.serviceName, | ||
serviceName = _ref$serviceName === undefined ? requiredArg('serviceName') : _ref$serviceName, | ||
serviceName = _ref$serviceName === undefined ? tracer.localEndpoint.serviceName : _ref$serviceName, | ||
_ref$port = _ref.port, | ||
@@ -44,0 +44,0 @@ port = _ref$port === undefined ? requiredArg('port') : _ref$port; |
@@ -25,2 +25,7 @@ 'use strict'; | ||
var _require4 = require('../model'), | ||
Endpoint = _require4.Endpoint; | ||
var isPromise = require('is-promise'); | ||
function requiredArg(name) { | ||
@@ -39,3 +44,5 @@ throw new Error('Tracer: Missing required argument ' + name + '.'); | ||
_ref$traceId128Bit = _ref.traceId128Bit, | ||
traceId128Bit = _ref$traceId128Bit === undefined ? false : _ref$traceId128Bit; | ||
traceId128Bit = _ref$traceId128Bit === undefined ? false : _ref$traceId128Bit, | ||
localServiceName = _ref.localServiceName, | ||
localEndpoint = _ref.localEndpoint; | ||
@@ -47,2 +54,9 @@ _classCallCheck(this, Tracer); | ||
this.traceId128Bit = traceId128Bit; | ||
if (localEndpoint) { | ||
this._localEndpoint = localEndpoint; | ||
} else { | ||
this._localEndpoint = new Endpoint({ | ||
serviceName: localServiceName || 'unknown' | ||
}); | ||
} | ||
this._ctxImpl = ctxImpl; | ||
@@ -99,10 +113,54 @@ this._defaultTraceId = this.createRootId(); | ||
} | ||
// creates a span, timing the given callable, adding any error as a tag | ||
// if the callable returns a promise, a span stops after the promise resolves | ||
}, { | ||
key: 'letChildId', | ||
value: function letChildId(callable) { | ||
key: 'local', | ||
value: function local(operationName, callable) { | ||
var _this = this; | ||
if (typeof callable !== 'function') { | ||
throw new Error('you must pass a function'); | ||
} | ||
return this.scoped(function () { | ||
var traceId = _this.createChildId(); | ||
_this.setId(traceId); | ||
_this.recordServiceName(_this._localEndpoint.serviceName); | ||
_this.recordAnnotation(new Annotation.LocalOperationStart(operationName)); | ||
var result = void 0; | ||
try { | ||
result = callable(); | ||
} catch (err) { | ||
_this.recordBinary('error', err.message ? err.message : err.toString()); | ||
_this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
throw err; | ||
} | ||
// Finish the span on a synchronous success | ||
if (!isPromise(result)) { | ||
_this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
return result; | ||
} | ||
// Ensure the span representing the promise completes | ||
return result.then(function (output) { | ||
_this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
return output; | ||
}).catch(function (err) { | ||
_this.recordBinary('error', err.message ? err.message : err.toString()); | ||
_this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
throw err; | ||
}); | ||
}); | ||
} | ||
}, { | ||
key: 'letChildId', | ||
value: function letChildId(callable) { | ||
var _this2 = this; | ||
return this.scoped(function () { | ||
var traceId = _this2.createChildId(); | ||
_this2.setId(traceId); | ||
return callable(traceId); | ||
@@ -119,7 +177,13 @@ }); | ||
value: function recordAnnotation(annotation) { | ||
this.recorder.record(new Record({ | ||
traceId: this.id, | ||
timestamp: now(this._startTimestamp, this._startTick), | ||
annotation: annotation | ||
})); | ||
var _this3 = this; | ||
this.id.sampled.ifPresent(function (sampled) { | ||
// sampled present is different than sampled == true | ||
if (!sampled) return; | ||
_this3.recorder.record(new Record({ | ||
traceId: _this3.id, | ||
timestamp: now(_this3._startTimestamp, _this3._startTick), | ||
annotation: annotation | ||
})); | ||
}); | ||
} | ||
@@ -172,2 +236,7 @@ }, { | ||
} | ||
}, { | ||
key: 'localEndpoint', | ||
get: function get() { | ||
return this._localEndpoint; | ||
} | ||
}]); | ||
@@ -174,0 +243,0 @@ |
{ | ||
"name": "zipkin", | ||
"version": "0.10.1", | ||
"version": "0.11.0", | ||
"description": "The core tracer for zipkin.js", | ||
@@ -17,2 +17,3 @@ "main": "lib/index.js", | ||
"base64-js": "^1.1.2", | ||
"is-promise": "^2.1.0", | ||
"network-address": "^1.1.0" | ||
@@ -22,2 +23,3 @@ }, | ||
"babel-cli": "^6.23.0", | ||
"bluebird": "^3.5.1", | ||
"mocha": "^3.2.0", | ||
@@ -24,0 +26,0 @@ "typescript": "^2.4.2" |
@@ -26,4 +26,27 @@ # zipkin | ||
sampler: new zipkin.sampler.CountingSampler(0.01), // sample rate 0.01 will sample 1 % of all incoming requests | ||
traceId128Bit: true // to generate 128-bit trace IDs. 64-bit (false) is default | ||
traceId128Bit: true, // to generate 128-bit trace IDs. 64-bit (false) is default | ||
localServiceName: 'my-service' // indicates this node in your service graph | ||
}); | ||
``` | ||
### Local tracing | ||
Sometimes you have activity that precedes a remote request that you want to | ||
capture in a trace. `tracer.local` can time an operation, placing a | ||
corresponding span ID in scope so that any downstream commands end up in the | ||
same trace. | ||
Here's an example tracing a synchronous function: | ||
```javascript | ||
// A span representing checkout completes before result is returned | ||
const result = tracer.local('checkout', () => { | ||
return someComputation(); | ||
}); | ||
``` | ||
Here's an example tracing a function that returns a promise: | ||
```javascript | ||
// A span is in progress and completes when the promise is resolved. | ||
const result = tracer.local('checkout', () => { | ||
return createAPromise(); | ||
}); | ||
``` |
@@ -13,2 +13,11 @@ const InetAddress = require('./InetAddress'); | ||
function LocalOperationStart(name) { | ||
this.name = name; | ||
} | ||
LocalOperationStart.prototype.toString = function() { | ||
return `LocalOperationStart("${this.name}")`; | ||
}; | ||
class LocalOperationStop extends SimpleAnnotation {} | ||
function Message(message) { | ||
@@ -79,3 +88,5 @@ this.message = message; | ||
LocalAddr, | ||
BinaryAnnotation | ||
BinaryAnnotation, | ||
LocalOperationStart, | ||
LocalOperationStop | ||
}; | ||
@@ -82,0 +93,0 @@ |
@@ -95,2 +95,8 @@ const {now, hrtime} = require('./time'); | ||
break; | ||
case 'LocalOperationStart': | ||
span.delegate.setName(rec.annotation.name); | ||
break; | ||
case 'LocalOperationStop': | ||
span.finish(); | ||
break; | ||
case 'Message': | ||
@@ -97,0 +103,0 @@ span.delegate.addAnnotation(rec.timestamp, rec.annotation.message); |
@@ -11,3 +11,3 @@ const Annotation = require('../annotation'); | ||
tracer = requiredArg('tracer'), | ||
serviceName = requiredArg('serviceName'), | ||
serviceName = tracer.localEndpoint.serviceName, | ||
remoteServiceName | ||
@@ -14,0 +14,0 @@ }) { |
@@ -29,3 +29,3 @@ const Header = require('../httpHeaders'); | ||
tracer = requiredArg('tracer'), | ||
serviceName = requiredArg('serviceName'), | ||
serviceName = tracer.localEndpoint.serviceName, | ||
port = requiredArg('port') | ||
@@ -32,0 +32,0 @@ }) { |
@@ -9,3 +9,6 @@ const {None, Some, fromNullable} = require('../option'); | ||
const {now, hrtime} = require('../time'); | ||
const {Endpoint} = require('../model'); | ||
const isPromise = require('is-promise'); | ||
function requiredArg(name) { | ||
@@ -24,3 +27,5 @@ throw new Error(`Tracer: Missing required argument ${name}.`); | ||
// 64 and 128 bit incoming traces from upstream sources. | ||
traceId128Bit = false | ||
traceId128Bit = false, | ||
localServiceName, | ||
localEndpoint | ||
}) { | ||
@@ -30,2 +35,9 @@ this.recorder = recorder; | ||
this.traceId128Bit = traceId128Bit; | ||
if (localEndpoint) { | ||
this._localEndpoint = localEndpoint; | ||
} else { | ||
this._localEndpoint = new Endpoint({ | ||
serviceName: localServiceName || 'unknown' | ||
}); | ||
} | ||
this._ctxImpl = ctxImpl; | ||
@@ -75,2 +87,43 @@ this._defaultTraceId = this.createRootId(); | ||
// creates a span, timing the given callable, adding any error as a tag | ||
// if the callable returns a promise, a span stops after the promise resolves | ||
local(operationName, callable) { | ||
if (typeof callable !== 'function') { | ||
throw new Error('you must pass a function'); | ||
} | ||
return this.scoped(() => { | ||
const traceId = this.createChildId(); | ||
this.setId(traceId); | ||
this.recordServiceName(this._localEndpoint.serviceName); | ||
this.recordAnnotation(new Annotation.LocalOperationStart(operationName)); | ||
let result; | ||
try { | ||
result = callable(); | ||
} catch (err) { | ||
this.recordBinary('error', err.message ? err.message : err.toString()); | ||
this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
throw err; | ||
} | ||
// Finish the span on a synchronous success | ||
if (!isPromise(result)) { | ||
this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
return result; | ||
} | ||
// Ensure the span representing the promise completes | ||
return result | ||
.then((output) => { | ||
this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
return output; | ||
}) | ||
.catch((err) => { | ||
this.recordBinary('error', err.message ? err.message : err.toString()); | ||
this.recordAnnotation(new Annotation.LocalOperationStop()); | ||
throw err; | ||
}); | ||
}); | ||
} | ||
letChildId(callable) { | ||
@@ -92,8 +145,16 @@ return this.scoped(() => { | ||
get localEndpoint() { | ||
return this._localEndpoint; | ||
} | ||
recordAnnotation(annotation) { | ||
this.recorder.record(new Record({ | ||
traceId: this.id, | ||
timestamp: now(this._startTimestamp, this._startTick), | ||
annotation | ||
})); | ||
this.id.sampled.ifPresent(sampled => { | ||
// sampled present is different than sampled == true | ||
if (!sampled) return; | ||
this.recorder.record(new Record({ | ||
traceId: this.id, | ||
timestamp: now(this._startTimestamp, this._startTick), | ||
annotation | ||
})); | ||
}); | ||
} | ||
@@ -100,0 +161,0 @@ |
const sinon = require('sinon'); | ||
const lolex = require('lolex'); | ||
const Promise = require('bluebird'); | ||
const isPromise = require('is-promise'); | ||
const Tracer = require('../src/tracer'); | ||
const Annotation = require('../src/annotation'); | ||
const {Sampler} = require('../src/tracer/sampler'); | ||
const {Sampler, neverSample} = require('../src/tracer/sampler'); | ||
const ExplicitContext = require('../src/explicit-context'); | ||
const {Some} = require('../src/option'); | ||
const {Endpoint} = require('../src/model'); | ||
@@ -41,2 +43,150 @@ describe('Tracer', () => { | ||
it('should make a local span', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const localServiceName = 'smoothie-store'; | ||
const trace = new Tracer({ctxImpl, recorder, localServiceName}); | ||
ctxImpl.scoped(() => { | ||
const result = trace.local('buy-smoothie', () => { | ||
trace.recordBinary('taste', 'banana'); | ||
return 'smoothie'; | ||
}); | ||
expect(result).to.eql('smoothie'); | ||
expect(record.getCall(0).args[0].annotation).to.eql( | ||
new Annotation.ServiceName('smoothie-store') | ||
); | ||
expect(record.getCall(1).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStart('buy-smoothie') | ||
); | ||
expect(record.getCall(2).args[0].annotation).to.eql( | ||
new Annotation.BinaryAnnotation('taste', 'banana') | ||
); | ||
expect(record.getCall(3).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStop() | ||
); | ||
}); | ||
}); | ||
it('should complete a local span on error type', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const localServiceName = 'smoothie-store'; | ||
const trace = new Tracer({ctxImpl, recorder, localServiceName}); | ||
ctxImpl.scoped(() => { | ||
let error; | ||
try { | ||
trace.local('buy-smoothie', () => { | ||
throw new Error('no smoothies. try our cake'); | ||
}); | ||
} catch (err) { | ||
error = err; // error wasn't swallowed | ||
} | ||
// sanity check | ||
expect(error).to.eql(new Error('no smoothies. try our cake')); | ||
expect(record.getCall(0).args[0].annotation).to.eql( | ||
new Annotation.ServiceName('smoothie-store') | ||
); | ||
expect(record.getCall(1).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStart('buy-smoothie') | ||
); | ||
expect(record.getCall(2).args[0].annotation).to.eql( | ||
new Annotation.BinaryAnnotation('error', 'no smoothies. try our cake') | ||
); | ||
expect(record.getCall(3).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStop() // stopped on error | ||
); | ||
}); | ||
}); | ||
it('should make a local span for a promise', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const localServiceName = 'smoothie-store'; | ||
const trace = new Tracer({ctxImpl, recorder, localServiceName}); | ||
ctxImpl.scoped(() => { | ||
const promise = trace.local('buy-smoothie', () => | ||
Promise.delay(10) | ||
.then(() => { | ||
throw new Error('no smoothies. try our cake'); | ||
}) | ||
); | ||
expect(isPromise(promise)).to.eql(true); | ||
// but the start event's timestamp is still correct | ||
expect(record.getCall(0).args[0].annotation).to.eql( | ||
new Annotation.ServiceName('smoothie-store') | ||
); | ||
expect(record.getCall(1).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStart('buy-smoothie') | ||
); | ||
// hasn't finished yet, due to the delay | ||
expect(record.getCall(2)).to.eql(null); | ||
return promise.catch((error) => { | ||
expect(error).to.eql(new Error('no smoothies. try our cake')); | ||
expect(record.getCall(2).args[0].annotation).to.eql( | ||
new Annotation.BinaryAnnotation('error', 'no smoothies. try our cake') | ||
); | ||
expect(record.getCall(3).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStop() | ||
); | ||
}); | ||
}); | ||
}); | ||
it('should make a local span for a promise that produces an error', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const localServiceName = 'smoothie-store'; | ||
const trace = new Tracer({ctxImpl, recorder, localServiceName}); | ||
ctxImpl.scoped(() => { | ||
const promise = trace.local('buy-smoothie', () => | ||
Promise.delay(10) | ||
.then(() => { | ||
trace.recordBinary('taste', 'banana'); | ||
return 'smoothie'; | ||
}) | ||
); | ||
expect(isPromise(promise)).to.eql(true); | ||
// but the start event's timestamp is still correct | ||
expect(record.getCall(0).args[0].annotation).to.eql( | ||
new Annotation.ServiceName('smoothie-store') | ||
); | ||
expect(record.getCall(1).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStart('buy-smoothie') | ||
); | ||
// hasn't finished yet, due to the delay | ||
expect(record.getCall(2)).to.eql(null); | ||
return promise.then((result) => { | ||
expect(result).to.eql('smoothie'); | ||
expect(record.getCall(2).args[0].annotation).to.eql( | ||
new Annotation.BinaryAnnotation('taste', 'banana') | ||
); | ||
expect(record.getCall(3).args[0].annotation).to.eql( | ||
new Annotation.LocalOperationStop() | ||
); | ||
}); | ||
}); | ||
}); | ||
function runTest(bool, done) { | ||
@@ -68,2 +218,49 @@ const recorder = { | ||
it('should not record unsampled spans', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const sampler = new Sampler(neverSample); | ||
const trace = new Tracer({ctxImpl, recorder, sampler}); | ||
ctxImpl.scoped(() => { | ||
trace.recordAnnotation(new Annotation.ServerSend()); | ||
trace.recordMessage('error'); | ||
expect(record.getCall(0)).to.equal(null); | ||
}); | ||
}); | ||
it('should default to unknown endpoint', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const trace = new Tracer({ctxImpl, recorder}); | ||
expect(trace.localEndpoint).to.eql(new Endpoint({serviceName: 'unknown'})); | ||
}); | ||
it('should accept localServiceName parameter', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const trace = new Tracer({ctxImpl, recorder, localServiceName: 'robot'}); | ||
expect(trace.localEndpoint).to.eql(new Endpoint({serviceName: 'robot'})); | ||
}); | ||
it('should accept localEndpoint parameter', () => { | ||
const record = sinon.spy(); | ||
const recorder = {record}; | ||
const ctxImpl = new ExplicitContext(); | ||
const localEndpoint = new Endpoint({ | ||
serviceName: 'portal-service', | ||
ipv4: '10.57.50.83', | ||
port: 8080 | ||
}); | ||
const trace = new Tracer({ctxImpl, recorder, localEndpoint}); | ||
expect(trace.localEndpoint).to.eql(localEndpoint); | ||
}); | ||
it('should log timestamps in microseconds', () => { | ||
@@ -70,0 +267,0 @@ const clock = lolex.install(12345678); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
172414
65
5010
52
3
4
+ Addedis-promise@^2.1.0
+ Addedis-promise@2.2.2(transitive)