Skip to content

Commit 144ea0d

Browse files
dszakallasgergelyke
authored andcommitted
fix(edges): support incoming-edge metrics
1 parent deea150 commit 144ea0d

16 files changed

+488
-188
lines changed

e2e/reportApmMetrics.spec.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@ test('should report apm metrics', function (t) {
2323
function (uri, requestBody) {
2424
t.pass('collector sent rpm metrics')
2525
})
26-
serviceMocks.mockEdgeMetricsRequest(
26+
serviceMocks.mockExternalEdgeMetricsRequest(
2727
TRACE_COLLECTOR_API_URL,
2828
TRACE_API_KEY_TEST,
29-
TRACE_SERVICE_KEY_TEST,
30-
1,
31-
function (uri, requestBody) {
32-
t.pass('collector sent edge metrics')
33-
})
29+
42,
30+
Number.MAX_SAFE_INTEGER)
31+
serviceMocks.mockIncomingEdgeMetricsRequest(
32+
TRACE_COLLECTOR_API_URL,
33+
TRACE_API_KEY_TEST,
34+
42,
35+
Number.MAX_SAFE_INTEGER)
3436
serviceMocks.mockApmMetricsRequest(
3537
TRACE_COLLECTOR_API_URL,
3638
TRACE_API_KEY_TEST,

e2e/reportHttpRequest.spec.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ test('should report http requests', function (t) {
2828
TRACE_API_KEY_TEST,
2929
DUMMY_SERVICE_KEY,
3030
Number.MAX_SAFE_INTEGER) // set the times parameter high so the http mock catches all
31-
serviceMocks.mockEdgeMetricsRequest(
31+
serviceMocks.mockExternalEdgeMetricsRequest(
32+
TRACE_COLLECTOR_API_URL,
33+
TRACE_API_KEY_TEST,
34+
42,
35+
Number.MAX_SAFE_INTEGER)
36+
serviceMocks.mockIncomingEdgeMetricsRequest(
3237
TRACE_COLLECTOR_API_URL,
3338
TRACE_API_KEY_TEST,
3439
42,

e2e/utils/serviceMocks.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,24 @@ function mockRpmMetricsRequest (url, apiKey, serviceKey, maxTimes, callback) {
4242
.reply(callback || 200)
4343
}
4444

45-
function mockEdgeMetricsRequest (url, apiKey, serviceKey, maxTimes, callback) {
45+
function mockExternalEdgeMetricsRequest (url, apiKey, serviceKey, maxTimes, callback) {
4646
return nock(url, {
4747
reqheaders: {
4848
'Authorization': 'Bearer ' + apiKey
4949
}
5050
})
51-
.post('/service/42/edge-metrics')
51+
.post('/service/42/edge-external')
52+
.times(maxTimes)
53+
.reply(callback || 200)
54+
}
55+
56+
function mockIncomingEdgeMetricsRequest (url, apiKey, serviceKey, maxTimes, callback) {
57+
return nock(url, {
58+
reqheaders: {
59+
'Authorization': 'Bearer ' + apiKey
60+
}
61+
})
62+
.post('/service/42/edge-incoming')
5263
.times(maxTimes)
5364
.reply(callback || 200)
5465
}
@@ -67,6 +78,7 @@ module.exports = {
6778
mockServiceKeyRequest: mockServiceKeyRequest,
6879
mockApmMetricsRequest: mockApmMetricsRequest,
6980
mockRpmMetricsRequest: mockRpmMetricsRequest,
70-
mockEdgeMetricsRequest: mockEdgeMetricsRequest,
81+
mockExternalEdgeMetricsRequest: mockExternalEdgeMetricsRequest,
82+
mockIncomingEdgeMetricsRequest: mockIncomingEdgeMetricsRequest,
7183
mockHttpTransactionRequest: mockHttpTransactionRequest
7284
}

lib/agent/api/index.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ function CollectorApi (options) {
1515
this.COLLECTOR_API_SERVICE = url.resolve(options.collectorApiUrl, options.collectorApiServiceEndpoint)
1616
this.COLLECTOR_API_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiApmMetricsEndpoint)
1717
this.COLLECTOR_API_RPM_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiRpmMetricsEndpoint)
18-
this.COLLECTOR_API_EDGE_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiEdgeMetricsEndpoint)
18+
this.COLLECTOR_API_INCOMING_EDGE_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiIncomingEdgeMetricsEndpoint)
19+
this.COLLECTOR_API_EXTERNAL_EDGE_METRICS = url.resolve(options.collectorApiUrl, options.collectorApiExternalEdgeMetricsEndpoint)
1920

2021
this.collectorLanguage = options.collectorLanguage
2122
this.apiKey = options.apiKey
@@ -100,16 +101,29 @@ CollectorApi.prototype.sendApmMetrics = function (data) {
100101
this._send(url, this._withInstanceInfo(data))
101102
}
102103

103-
CollectorApi.prototype.sendEdgeMetrics = function (data) {
104+
CollectorApi.prototype.sendExternalEdgeMetrics = function (data) {
104105
if (isNaN(this.serviceKey)) {
105106
debug('Service id not present, cannot send metrics')
106107
return
107108
}
108109

109-
var url = util.format(this.COLLECTOR_API_EDGE_METRICS, this.serviceKey)
110+
var url = util.format(this.COLLECTOR_API_EXTERNAL_EDGE_METRICS, this.serviceKey)
110111
this._send(url, this._withInstanceInfo(data))
111112
}
112113

114+
CollectorApi.prototype.sendIncomingEdgeMetrics = function (data) {
115+
if (isNaN(this.serviceKey)) {
116+
debug('Service id not present, cannot send metrics')
117+
return
118+
}
119+
120+
var url = util.format(this.COLLECTOR_API_INCOMING_EDGE_METRICS, this.serviceKey)
121+
this._send(url, this._withInstanceInfo({
122+
timestamp: (new Date()).toISOString(),
123+
data: data
124+
}))
125+
}
126+
113127
CollectorApi.prototype.sendSamples = function (samples, sync) {
114128
var url = this.COLLECTOR_API_SAMPLE
115129
var metadata = {

lib/agent/api/index.spec.js

+40
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ describe('The Trace CollectorApi module', function () {
1919
collectorApiApmMetricsEndpoint: '/service/%s/apm-metrics',
2020
collectorApiRpmMetricsEndpoint: '/service/%s/rpm-metrics',
2121
collectorApiEdgeMetricsEndpoint: '/service/%s/edge-metrics',
22+
collectorApiIncomingEdgeMetricsEndpoint: '/service/%s/edge-incoming',
23+
collectorApiExternalEdgeMetricsEndpoint: '/service/%s/edge-external',
2224
system: {
2325
hostname: 'test.org',
2426
processVersion: '4.3.1',
@@ -109,6 +111,44 @@ describe('The Trace CollectorApi module', function () {
109111
collectorApi.sendSamples(data)
110112
})
111113

114+
it('sends incomingEdgeMetrics with the required properties', function (done) {
115+
var serviceKey = 12
116+
var collectorApi = CollectorApi.create(defaultConfig)
117+
collectorApi.serviceKey = 12
118+
var data = {
119+
test: 'clearly a mock'
120+
}
121+
122+
this.sandbox.stub(Date.prototype, 'toISOString', function () {
123+
return 'time'
124+
})
125+
126+
var path = util.format(defaultConfig.collectorApiIncomingEdgeMetricsEndpoint, serviceKey)
127+
128+
nock(defaultConfig.collectorApiUrl, {
129+
reqheaders: {
130+
'Authorization': 'Bearer testApiKey',
131+
'Content-Type': 'application/json',
132+
'X-Reporter-Version': libPackage.version,
133+
'X-Reporter-Language': defaultConfig.collectorLanguage
134+
}
135+
})
136+
.post(path, function (body) {
137+
expect(body).to.eql({
138+
pid: defaultConfig.system.processId,
139+
hostname: defaultConfig.system.hostname,
140+
timestamp: 'time',
141+
data: data
142+
})
143+
return true
144+
})
145+
.reply(201, function () {
146+
done()
147+
})
148+
149+
collectorApi.sendIncomingEdgeMetrics(data)
150+
})
151+
112152
it('can get the service key of serviceName from HttpTransaction server', function (done) {
113153
var collectorApi = CollectorApi.create(defaultConfig)
114154
var data = {

lib/agent/index.js

+28-14
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ function Agent (options) {
3939
config: this.config
4040
})
4141

42-
this.edgeMetrics = Metrics.edge.create({
42+
this.externalEdgeMetrics = Metrics.externalEdge.create({
43+
collectorApi: this.collectorApi,
44+
config: this.config
45+
})
46+
47+
this.incomingEdgeMetrics = Metrics.incomingEdge.create({
4348
collectorApi: this.collectorApi,
4449
config: this.config
4550
})
@@ -78,10 +83,19 @@ Agent.prototype.getConfig = function () {
7883
Agent.prototype.serverReceive = function (data) {
7984
this.totalRequestCount++
8085
var spanId = data.spanId
81-
var parentId = data.parentId
8286
var originTime = data.originTime
83-
8487
var span = this.openSpan(data)
88+
var parentId
89+
90+
if (!isNaN(data.parentId)) {
91+
parentId = parseInt(data.parentId, 10)
92+
}
93+
94+
this.incomingEdgeMetrics.report({
95+
serviceKey: data.parentId,
96+
protocol: data.protocol,
97+
transportDelay: data.time - data.originTime
98+
})
8599

86100
span.events.push({
87101
time: data.time || microtime.now(),
@@ -182,17 +196,17 @@ Agent.prototype.clientSend = function (data) {
182196
Agent.prototype.clientReceive = function (data) {
183197
var span = this.findSpan(data.id)
184198

185-
this.edgeMetrics.report({
186-
targetHost: data.host,
187-
targetServiceKey: data.targetServiceKey,
188-
protocol: data.protocol,
189-
networkDelay: {
190-
incoming: data.networkDelayIncoming,
191-
outgoing: data.networkDelayOutgoing
192-
},
193-
status: data.status,
194-
responseTime: data.responseTime
195-
})
199+
// report external edges only if service is not instrumented by Trace
200+
if (typeof data.targetServiceKey === 'undefined') {
201+
if (!data.statusCode || data.statusCode < 500) {
202+
this.externalEdgeMetrics.report({
203+
targetHost: data.host,
204+
protocol: data.protocol,
205+
status: data.status,
206+
responseTime: data.responseTime
207+
})
208+
}
209+
}
196210

197211
if (!span) {
198212
return

lib/agent/index.spec.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe('The Trace agent', function () {
1111
var options
1212
var collectorApi
1313
var rpmMetrics
14+
var incomingEdgeMetrics
1415
var transactionId
1516
var spanId
1617
var parentId
@@ -37,9 +38,13 @@ describe('The Trace agent', function () {
3738
addResponseTime: this.sandbox.spy(),
3839
addStatusCode: this.sandbox.spy()
3940
}
41+
incomingEdgeMetrics = {
42+
report: this.sandbox.spy()
43+
}
4044
this.sandbox.stub(CollectorApi, 'create').returns(collectorApi)
4145
this.sandbox.stub(Metrics.apm, 'create')
4246
this.sandbox.stub(Metrics.rpm, 'create').returns(rpmMetrics)
47+
this.sandbox.stub(Metrics.incomingEdge, 'create').returns(incomingEdgeMetrics)
4348
this.sandbox.stub(microtime, 'now').returns(time)
4449
options = {
4550
config: {
@@ -51,15 +56,25 @@ describe('The Trace agent', function () {
5156
})
5257

5358
it('does server receive', function () {
59+
var originTime = time - 1000
60+
5461
agent.serverReceive({
5562
id: transactionId,
5663
spanId: spanId,
5764
parentId: parentId,
5865
url: url,
5966
host: host,
60-
method: 'GET'
67+
method: 'GET',
68+
originTime: originTime,
69+
protocol: 'http',
70+
time: time
6171
})
6272

73+
expect(incomingEdgeMetrics.report).to.have.been.calledWith({
74+
transportDelay: time - originTime,
75+
serviceKey: parentId,
76+
protocol: 'http'
77+
})
6378
expect(agent.totalRequestCount).to.eql(1)
6479
expect(agent.partials[transactionId]).to.eql({
6580
requestId: transactionId,
@@ -73,6 +88,40 @@ describe('The Trace agent', function () {
7388
endpoint: url,
7489
method: 'GET',
7590
parent: 0,
91+
originTime: originTime
92+
}
93+
}]
94+
})
95+
})
96+
97+
it('does server receive when there is no parentId', function () {
98+
agent.serverReceive({
99+
id: transactionId,
100+
spanId: spanId,
101+
url: url,
102+
host: host,
103+
method: 'GET',
104+
protocol: 'http'
105+
})
106+
107+
expect(incomingEdgeMetrics.report).to.have.been.calledWith({
108+
protocol: 'http',
109+
serviceKey: undefined,
110+
transportDelay: NaN
111+
})
112+
expect(agent.totalRequestCount).to.eql(1)
113+
expect(agent.partials[transactionId]).to.eql({
114+
requestId: transactionId,
115+
isSampled: false,
116+
isForceSampled: false,
117+
events: [{
118+
type: 'sr',
119+
time: time,
120+
data: {
121+
rpcId: spanId,
122+
endpoint: url,
123+
method: 'GET',
124+
parent: undefined,
76125
originTime: undefined
77126
}
78127
}]

0 commit comments

Comments
 (0)