Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
it('should observe HTTP request metrics without parent', function () {
// init
const prometheusReporter = new PrometheusReporter()
const httpRequestDurationSeconds = prometheusReporter._metricshttpRequestDurationSeconds()
const metricsStub = {
observe: this.sandbox.spy()
}
this.sandbox.stub(httpRequestDurationSeconds, 'labels').callsFake(() => metricsStub)
// generate data
const tracer = new Tracer('service')
const span = tracer.startSpan('http_request')
span.setTag(Tags.HTTP_METHOD, 'GET')
span.setTag(Tags.HTTP_STATUS_CODE, 200)
span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
clock.tick(100)
span.finish()
prometheusReporter.reportFinish(span)
// assert
expect(httpRequestDurationSeconds.labels).to.have.callCount(1)
expect(httpRequestDurationSeconds.labels)
.to.be.calledWith(PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN, 'GET', 200)
expect(metricsStub.observe).to.have.callCount(1)
expect(metricsStub.observe).to.be.calledWith(0.1)
})
}
})
const tracer = new Tracer('my-service', [reporter])
const span1 = tracer.startSpan('http_request')
span1.setTag(Tags.HTTP_URL, 'http://127.0.0.1/foo')
span1.setTag(Tags.HTTP_METHOD, 'GET')
span1.setTag(Tags.HTTP_STATUS_CODE, 200)
span1.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
clock.tick(100)
span1.finish()
// will be ignored
const span2 = tracer.startSpan('http_request')
span2.setTag(Tags.HTTP_URL, 'http://127.0.0.1/bar')
span2.setTag(Tags.HTTP_METHOD, 'GET')
span2.setTag(Tags.HTTP_STATUS_CODE, 200)
span2.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_RPC_SERVER)
clock.tick(300)
span2.finish()
const labelStr1 = `parent_service="${PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN}",name="http_request"`
const labelStr2 = `parent_service="${PrometheusReporter.LABEL_PARENT_SERVICE_UNKNOWN}",method="GET",code="200"`
expect(reporter.metrics()).to.be.equal(dedent`
# HELP operation_duration_seconds Duration of operations in second
# TYPE operation_duration_seconds histogram
operation_duration_seconds_bucket{le="0.005",${labelStr1}} 0
operation_duration_seconds_bucket{le="0.01",${labelStr1}} 0
operation_duration_seconds_bucket{le="0.025",${labelStr1}} 0
operation_duration_seconds_bucket{le="0.05",${labelStr1}} 0
operation_duration_seconds_bucket{le="0.1",${labelStr1}} 1
return cls.runAndReturn(() => {
// start
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
const parentSpanContexts = tracers.map((tracer) => tracer.extract(FORMAT_HTTP_HEADERS, req.headers))
const spans = parentSpanContexts.map((parentSpanContext, key) =>
cls.startRootSpan(tracers[key], OPERATION_NAME, {
childOf: parentSpanContext,
tags: {
[Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_SERVER,
[Tags.HTTP_URL]: url,
[Tags.HTTP_METHOD]: req.method
}
}))
debug(`Operation started ${OPERATION_NAME}`, {
[Tags.HTTP_URL]: url,
[Tags.HTTP_METHOD]: req.method
})
if (req.connection.remoteAddress) {
spans.forEach((span) => span.log({ peerRemoteAddress: req.connection.remoteAddress }))
}
// end
const originalEnd = res.end
res.end = function (...args) {
res.end = originalEnd
const returned = res.end.call(this, ...args)
if (req.route && req.route.path) {
spans.forEach((span) => span.setTag(TAG_REQUEST_PATH, req.route.path))
}
server.get('/', (req, res) => res.send('ok'))
await request(server)
.get('/')
.expect(200)
.end()
// FIXME: should be undefined, but the dummy tracer returns an empty span context
const childOf = new SpanContext()
expect(cls.startRootSpan).to.be.calledWith(tracer, instrumentation.OPERATION_NAME, {
childOf,
tags: {
[Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_SERVER,
[Tags.HTTP_URL]: '/',
[Tags.HTTP_METHOD]: 'GET'
}
})
expect(mockSpan.log).to.be.calledWith({ peerRemoteAddress: '::ffff:127.0.0.1' })
expect(mockSpan.setTag).to.be.calledWith(instrumentation.TAG_REQUEST_PATH, '/')
expect(mockSpan.setTag).to.be.calledWith(Tags.HTTP_STATUS_CODE, 200)
expect(mockSpan.finish).to.have.callCount(1)
})
it('should get a tag value', () => {
const tracer = new Tracer('service-1')
const spanContext = new SpanContext('service-1')
const span = new Span(tracer, 'operation', spanContext)
span.setTag(Tags.HTTP_METHOD, 'GET')
expect(span.getTag(Tags.HTTP_METHOD)).to.be.equal('GET')
})
})
it('should add a tag', () => {
const tracer = new Tracer('service-1')
const spanContext = new SpanContext('service-1')
const span = new Span(tracer, 'operation', spanContext)
const tags = {
[Tags.HTTP_METHOD]: 'GET',
[Tags.SPAN_KIND_RPC_CLIENT]: true
}
span.addTags(tags)
expect(span._tags).to.be.eql(tags)
})
})
_reportHttpRequestFinish (span) {
assert(span instanceof Span, 'span is required')
this._metricshttpRequestDurationSeconds()
.labels(
PrometheusReporter.getParentService(span),
span.getTag(Tags.HTTP_METHOD),
span.getTag(Tags.HTTP_STATUS_CODE)
)
.observe(span.duration() / 1000)
}
const spans = parentSpanContexts.map((parentSpanContext, key) =>
cls.startRootSpan(tracers[key], OPERATION_NAME, {
childOf: parentSpanContext,
tags: {
[Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_SERVER,
[Tags.HTTP_URL]: req.url,
[Tags.HTTP_METHOD]: req.method
}
}))
}
}))
const timings = {
begin: undefined,
dnsLookup: undefined,
tcpConnection: undefined,
firstByte: undefined,
tlsHandshake: undefined,
end: undefined
}
let isFinish = false
debug(`Operation started ${OPERATION_NAME}`, {
[Tags.HTTP_URL]: uri,
[Tags.HTTP_METHOD]: method
})
options = _.isString(options) ? url.parse(options) : _.merge({}, options)
options.headers = options.headers || {}
tracers.forEach((tracer, key) => tracer.inject(spans[key], FORMAT_HTTP_HEADERS, options.headers))
timings.begin = Date.now()
function finish () {
if (httpTimings) {
timings.end = Date.now()
addTimings(tracers, spans, timings)
}
spans.forEach((span) => span.finish())