Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 85d590e

Browse files
committed
Save
1 parent bb5fe10 commit 85d590e

File tree

14 files changed

+187
-131
lines changed

14 files changed

+187
-131
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
target/emulator/
1+
target/
2+

README.md

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,32 @@
99
![GitHub last commit](https://img.shields.io/github/last-commit/newrelic-experimental/newrelic-experimental-FIT-template)
1010
![GitHub Release Date](https://img.shields.io/github/release-date/newrelic-experimental/newrelic-experimental-FIT-template)
1111

12-
1312
![GitHub issues](https://img.shields.io/github/issues/newrelic-experimental/newrelic-experimental-FIT-template)
1413
![GitHub issues closed](https://img.shields.io/github/issues-closed/newrelic-experimental/newrelic-experimental-FIT-template)
1514
![GitHub pull requests](https://img.shields.io/github/issues-pr/newrelic-experimental/newrelic-experimental-FIT-template)
1615
![GitHub pull requests closed](https://img.shields.io/github/issues-pr-closed/newrelic-experimental/newrelic-experimental-FIT-template)
1716

18-
# [Project Name] [build badges go here when available]
19-
20-
>[Brief description - what is the project and value does it provide? How often should users expect to get releases? How is versioning set up? Where does this project want to go?]
17+
# Apigee Distributed Tracing
2118

2219
## Installation
2320

24-
> [Include a step-by-step procedure on how to get your code installed. Be sure to include any third-party dependencies that need to be installed separately]
25-
26-
## Getting Started
27-
28-
>[Simple steps to start working with the software similar to a "Hello World"]
29-
30-
## Usage
31-
32-
>[**Optional** - Include more thorough instructions on how to use the software. This section might not be needed if the Getting Started section is enough. Remove this section if it's not needed.]
21+
## Development
3322

34-
## Building
35-
36-
>[**Optional** - Include this section if users will need to follow specific instructions to build the software from source. Be sure to include any third party build dependencies that need to be installed separately. Remove this section if it's not needed.]
23+
### Helpful links
24+
- [Apigee development with VSCode](https://cloud.google.com/apigee/docs/api-platform/local-development/vscode/get-started)
25+
- [Apigee JavaScript object model](https://cloud.google.com/apigee/docs/api-platform/reference/javascript-object-model)
3726

3827
## Testing
3928

40-
>[**Optional** - Include instructions on how to run tests if we include tests with the codebase. Remove this section if it's not needed.]
41-
4229
## Support
4330

4431
New Relic has open-sourced this project. This project is provided AS-IS WITHOUT WARRANTY OR DEDICATED SUPPORT. Issues and contributions should be reported to the project here on GitHub.
4532

46-
>[Choose 1 of the 2 options below for Support details, and remove the other one.]
47-
48-
>[Option 1 - no specific thread in Community]
49-
>We encourage you to bring your experiences and questions to the [Explorers Hub](https://discuss.newrelic.com) where our community members collaborate on solutions and new ideas.
50-
51-
>[Option 2 - thread in Community]
52-
>New Relic hosts and moderates an online forum where customers can interact with New Relic employees as well as other customers to get help and share best practices. Like all official New Relic open source projects, there's a related Community topic in the New Relic Explorers Hub.
53-
>You can find this project's topic/threads here: [URL for Community thread]
33+
We encourage you to bring your experiences and questions to the [Explorers Hub](https://discuss.newrelic.com) where our community members collaborate on solutions and new ideas.
5434

5535
## Contributing
5636

57-
We encourage your contributions to improve [Project Name]! Keep in mind when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project. If you have any questions, or to execute our corporate CLA, required if your contribution is on behalf of a company, please drop us an email at [email protected].
37+
We encourage your contributions to improve Apigee Distributed Tracing! Keep in mind when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project. If you have any questions, or to execute our corporate CLA, required if your contribution is on behalf of a company, please drop us an email at [email protected].
5838

5939
**A note about vulnerabilities**
6040

@@ -64,6 +44,4 @@ If you believe you have found a security vulnerability in this project or any of
6444

6545
## License
6646

67-
[Project Name] is licensed under the [Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License.
68-
69-
>[If applicable: [Project Name] also uses source code from third-party libraries. You can find full details on which libraries are used and the terms under which they are licensed in the third-party notices document.]
47+
Apigee Distributed Tracing is licensed under the [Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="Add-Trace-Context-To-Pub-Sub-Message">
3+
<DisplayName>Add-Trace-Context-To-Pub-Sub-Message</DisplayName>
4+
<Properties/>
5+
<ResourceURL>jsc://Add-Trace-Context-To-Pub-Sub-Message.js</ResourceURL>
6+
</Javascript>
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="DT-Request-Policy">
3-
<DisplayName>DT-Request-Policy</DisplayName>
4-
<Properties/>
5-
<ResourceURL>jsc://JavaScript-1.js</ResourceURL>
2+
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="Trace-Context-Request">
3+
<DisplayName>Trace-Context-Request</DisplayName>
4+
<Properties>
5+
<!-- The odds of a Trace being sampled, 0-100. 0 = tracing off, 100 = trace everything. -->
6+
<Property name="SampleOddsPercentage">100</Property>
7+
<!-- The name of the Request/Response pair. Must be unique withing a Proxy. -->
8+
<Property name="RequestResponsePairName">Initial</Property>
9+
<!-- How to determine the start time for this Trace. Any Flow Variable (https://cloud.google.com/apigee/docs/api-platform/reference/variables-reference) ending in '.timestamp' is valid. Default is `Date.now()'-->
10+
<Property name="TraceStartTimestamp"></Property>
11+
</Properties>
12+
<ResourceURL>jsc://Trace-Context-Request.js</ResourceURL>
613
</Javascript>
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="DT-Response-Policy">
3-
<DisplayName>DT-Response-Policy</DisplayName>
4-
<Properties/>
5-
<ResourceURL>jsc://DT-Response.js</ResourceURL>
2+
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="Trace-Context-Response">
3+
<DisplayName>Trace-Context-Response</DisplayName>
4+
<Properties>
5+
<!-- The name of the Request/Response pair. Must be unique within a Proxy. -->
6+
<Property name="RequestResponsePairName">Initial</Property>
7+
<!-- Custom attributes to include with the NR Span object. A comma separated list of key:value pairs -->
8+
<Property name="AdditionalSpanAttributes"></Property>
9+
<!-- How to determine the end time for this Trace. Any Flow Variable (https://cloud.google.com/apigee/docs/api-platform/reference/variables-reference) ending in '.timestamp' is valid. Default is `Date.now()'-->
10+
<Property name="TraceEndTimestamp"></Property>
11+
</Properties>
12+
<ResourceURL>jsc://Trace-Context-Response.js</ResourceURL>
613
</Javascript>

src/main/apigee/apiproxies/W3C-Trace-Context/apiproxy/proxies/default.xml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,22 @@
22
<PreFlow name="PreFlow">
33
<Request>
44
<Step>
5-
<Name>DT-Request-Policy</Name>
5+
<Name>Trace-Context-Request</Name>
66
</Step>
77
<Step>
8-
<Name>remove-query-param-apikey</Name>
9-
</Step>
10-
<Step>
11-
<Name>impose-quota</Name>
8+
<Name>Add-Trace-Context-To-Pub-Sub-Message</Name>
129
</Step>
1310
</Request>
1411
</PreFlow>
1512
<PostFlow name="PostFlow">
1613
<Response>
1714
<Step>
18-
<Name>DT-Response-Policy</Name>
15+
<Name>Trace-Context-Response</Name>
1916
</Step>
2017
</Response>
2118
</PostFlow>
2219
<HTTPProxyConnection>
23-
<BasePath>/DT-Test</BasePath>
20+
<BasePath>/Trace-Context</BasePath>
2421
</HTTPProxyConnection>
2522
<RouteRule name="default-route">
2623
<TargetEndpoint>default</TargetEndpoint>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"use strict"
2+
// Get the PubSub message
3+
// Add the Trace Context
4+
// TODO What is the format of adding the tracecontext to the JSON payload?
5+
// Rewrite the message
Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,130 @@
1-
print("Hello from Javascript");
1+
"use strict"
2+
function randomHexString(len) {
3+
var r = '';
4+
var maxlen = 8,
5+
min = Math.pow(16, Math.min(len, maxlen) - 1),
6+
max = Math.pow(16, Math.min(len, maxlen)) - 1,
7+
n = Math.floor(Math.random() * (max - min + 1)) + min,
8+
r = n.toString(16);
9+
while (r.length < len) {
10+
r = r + randHex(len - maxlen);
11+
}
12+
return r;
13+
}
14+
15+
function generateValidID(len) {
16+
var id = randomHexString(len);
17+
// IDs must not be all zeros
18+
while (! /[1-9a-f]/.test(id)) {
19+
id = randomHexString(len);
20+
}
21+
return id;
22+
}
23+
24+
function generateSampled() {
25+
// Give us a number 0 - 99 inclusive
26+
var n = Math.floor(Math.random() * 100);
27+
n = n + 1;
28+
if (n <= properties.SampleOddsPercentage) {
29+
return '01';
30+
}
31+
return '00';
32+
}
33+
34+
var traceparent = context.getVariable("request.header.traceparent");
35+
var tracestate = context.getVariable("request.header.tracestate");
36+
//
37+
// Note:
38+
// In W3C speak "Parent ID" is only the "Parent's ID" as received in an incoming traceparent header. Once we create a "Span" we become the "Parent ID"- what
39+
// most would term "the Span ID". `requestParentID` is therefore the incoming Span's ID. This code sticks with W3C terminology for consistency.
40+
var versionFormat, requrestVersionFormat, traceID, parentID, traceFlags, requestParentID
41+
42+
function newTraceparent() {
43+
versionFormat = '00';
44+
traceID = generateValidID(16);
45+
parentID = generateValidID(8); // aka SpanID
46+
traceFlags = generateSampled();
47+
requestParentID = ""; // New Relic's Trace API expects a null parent.id attribute if this is the root span
48+
tracestate = '';
49+
}
50+
51+
// 4.2 No traceparent Received
52+
// If no traceparent header is received:
53+
if (traceparent == null) {
54+
// Create an initial traceparent and tracestate
55+
// The vendor checks an incoming request for a traceparent and a tracestate header.
56+
// Because the traceparent header is not received, the vendor creates a new trace-id and parent-id that represents the current request.
57+
// If a tracestate header is received without an accompanying traceparent header, it is invalid and MUST be discarded.
58+
// The vendor SHOULD create a new tracestate header and add a new key/value pair.
59+
// The vendor sets the traceparent and tracestate header for the outgoing request.
60+
newTraceparent();
61+
}
62+
// 4.3 A traceparent is Received
63+
// If a traceparent header is received:
64+
else {
65+
// The vendor checks an incoming request for a traceparent and a tracestate header.
66+
// Because the traceparent header is present, the vendor tries to parse the version of the traceparent header.
67+
// If the version cannot be parsed, the vendor creates a new traceparent header and deletes tracestate.
68+
// If the version number is higher than supported by the tracer, the vendor uses the format defined in this specification (00) to parse trace-id and parent-id.
69+
// The vendor will only parse the trace-flags values supported by this version of this specification and ignore all other values.
70+
// If parsing fails, the vendor creates a new traceparent header and deletes the tracestate. Vendors will set all unparsed / unknown trace-flags to 0 on outgoing requests.
71+
if (!/^[\da-f]{2}-[\da-f]{32}-[\da-f]{16}-[\da-f]{2}$/.test(traceparent)) {
72+
print('Error: invalid traceparent: ' + traceparent);
73+
newTraceparent();
74+
} else {
75+
// If the vendor supports the version number, it validates trace-id and parent-id. If either trace-id, parent-id or trace-flags are invalid, the vendor creates a new traceparent header and deletes tracestate.
76+
var values = traceparent.split('-');
77+
requestVersionFormat = values[0];
78+
traceId = values[1];
79+
parentID = values[2];
80+
traceFlags = values[2];
81+
if (/[1-9a-f]/.test(traceID)) {
82+
if (/[1-9a-f]/.test(parentID)) {
83+
if (/[0-9a-f]/.test(traceFlags)) {
84+
// All good, generate our ParentID
85+
// The vendor MUST modify the traceparent header:
86+
// Update parent-id: The value of property parent-id MUST be set to a value representing the ID of the current operation.
87+
// Update sampled: The value of sampled reflects the caller's recording behavior. The value of the sampled flag of trace-flags MAY be set to 1 if the trace data is likely to be recorded or to 0 otherwise. Setting the flag is no guarantee that the trace will be recorded but increases the likeliness of end-to-end recorded traces.
88+
requestParentID = parentID;
89+
parentID = generateValidID(8);
90+
traceFlags = generateSampled();
91+
} else {
92+
print('Error: invalid traceFlags: ' + traceFlags);
93+
newTraceparent();
94+
}
95+
} else {
96+
print('Error: invalid parentID: ' + parentID);
97+
newTraceparent();
98+
}
99+
} else {
100+
print('Error: invalid traceID: ' + traceID);
101+
newTraceparent();
102+
}
103+
104+
// The vendor MAY validate the tracestate header. If the tracestate header cannot be parsed the vendor MAY discard the entire header. Invalid tracestate entries MAY also be discarded.
105+
var states = tracestate.split(',');
106+
if (states.length > 32) {
107+
print("Warning: tracestate has too many values: " + states.length);
108+
}
109+
}
110+
}
111+
112+
// TODO Does NR have any standard fields for tracestate?
113+
// TODO
114+
// The vendor MAY modify the tracestate header:
115+
// Update a key value: The value of any key can be updated. Modified keys MUST be moved to the beginning (left) of the list.
116+
// Add a new key/value pair: The new key-value pair MUST be added to the beginning (left) of the list.
117+
// Delete a key/value pair: Any key/value pair MAY be deleted. Vendors SHOULD NOT delete keys that weren't generated by themselves. Deletion of any key/value pair MAY break correlation in other systems.
118+
119+
// The vendor sets the traceparent and tracestate header for the outgoing request.
120+
traceparent = versionFormat + '-' + traceID + '-' + parentID + '-' + traceFlags;
121+
122+
context.targetRequest.headers['traceparent'] = traceparent;
123+
context.targetRequest.headers['tracestate'] = tracestate;
124+
125+
// Values needed by the Reponse to write to New Relic
126+
context.setVariable("traceContext.requestParentID", requestParentID);
127+
context.setVariable("traceContext.parentID", parentID);
128+
context.setVariable("traceContext.traceID", traceID);
129+
context.setVariable("traceContext.traceFlags", traceFlags);
130+
context.setVariable("traceContext.parentStartTime", context.getVariable("client.received.start.timestamp"));
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
"use strict"
2+
var traceId;
13
try {
24
//const buf = crypto.randomBytes(16);
35
//var _timeNow = Number(context.getVariable('system.timestamp'));
46
//traceId = crypto.dateFormat('YYYY-MM-DD HH:mm:ss.SSS','CST', _timeNow);
5-
traceId = createUuid();
7+
8+
traceId = crypto.randomBytes(16);
69
} catch (err) {
7-
traceId = 'TraceID'
8-
context.proxyResponse.headers['DT-Response-Err'] = err;
10+
traceId = 'TraceID';
11+
context.proxyResponse.headers['W3C-Trace-Context-Response-Err'] = err;
12+
context.proxyResponse.headers['W3C-Trace-Context-Response-Debug'] = Object.getOwnPropertyNames(crypto).filter(item => typeof crypto[item] === 'function');
913
}
10-
context.proxyResponse.headers['DT-Response'] = traceId;
14+
context.proxyResponse.headers['W3C-Trace-Context-Response'] = traceId;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"proxies" : ["DT-Test"],
2+
"proxies" : ["W3C-Trace-Context"],
33
"sharedflows" : []
44
}

0 commit comments

Comments
 (0)