Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11,223 changes: 11,054 additions & 169 deletions dist/lightstep-tracer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/lightstep-tracer.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/lightstep-tracer.min.js

Large diffs are not rendered by default.

95 changes: 92 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
"eventemitter3": "1.1.1",
"google-protobuf": "3.6.1",
"hex2dec": "1.0.1",
"long": "^4.0.0",
"opentracing": "^0.14.4",
"protobufjs": "^6.10.2",
"source-map-support": "0.3.3",
"thrift": "0.13.0"
},
Expand Down
11 changes: 11 additions & 0 deletions src/_leftpad.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Underscore.js-like wrapper to left pad a string to a certain length with a character
export default function _leftpad(str, len, ch) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this going to be used in node.js only ? , if so then use String.prototype.repeat instead

str = String(str);
let i = -1;
if (!ch && ch !== 0) ch = ' ';
len -= str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
187 changes: 187 additions & 0 deletions src/imp/propagator_envoy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import pb from 'protobufjs';
import long from 'long';
import _each from '../_each';
import _leftpad from '../_leftpad';
import SpanContextImp from './span_context_imp';
import LightStepPropagator from './propagator_ls';

const CARRIER_ENVOY_HEADER_KEY = 'x-ot-span-context';

const BINARY_PROTO = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this should be in here like this. I tried using a local file but it doesn't get generated with webpack. Any tips or recommendations for proto?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this should be in here like this. I tried using a local file but it doesn't get generated with webpack. Any tips or recommendations for proto?

I think we should generate the lightstep.proto in the same way that we generate the collector.proto. The resulting file would end up in the src/imp/generated_proto directory. See the Makefile.

If we do that, we can probably avoid the dependency on protobufjs since we already use google-protobuf.

nested : {
lightstep : {
options : { go_package : 'lightsteppb' },
nested : {
BinaryCarrier : {
fields : {
deprecated_text_ctx : {
rule : 'repeated',
type : 'bytes',
id : 1,
},
basic_ctx : { type : 'BasicTracerCarrier', id : 2 },
},
},
BasicTracerCarrier : {
fields : {
trace_id : { type : 'fixed64', id : 1 },
span_id : { type : 'fixed64', id : 2 },
sampled : { type : 'bool', id : 3 },
baggage_items : { keyType : 'string', type : 'string', id : 4 },
},
},
},
},
},
};


export default class EnvoyPropagator extends LightStepPropagator {
constructor(tracer) {
super(tracer);
this._tracer = tracer;
this._envoyHeaderKey = CARRIER_ENVOY_HEADER_KEY;
this._carrierPb = pb.Root.fromJSON(BINARY_PROTO);
}

inject(spanContext, carrier) {
if (!carrier) {
this._tracer._error('Unexpected null carrier in call to inject');
return;
}
if (typeof carrier !== 'object') {
this._tracer._error(
`Unexpected '${typeof carrier}' FORMAT_BINARY carrier in call to inject`,
);
return;
}

let basicContext = {
span_id : long.fromString(spanContext._guid, 16),
trace_id : long.fromString(spanContext._traceGUID, 16),
sampled : true,
baggage_items : {},
};
spanContext.forEachBaggageItem((key, value) => {
basicContext.baggage_items[key] = value;
});

let binaryCarrier = this._carrierPb.lookupType('BinaryCarrier');

let payload = {
basic_ctx : basicContext,
};

let err = binaryCarrier.verify(payload);
if (err) {
this._tracer._error(`Invalid Span Context: ${err}`);
return null;
}
let msg = binaryCarrier.create(payload);
let buffer = binaryCarrier.encode(msg).finish();
let bufferString = pb.util.base64.encode(buffer, 0, buffer.length);
carrier[this._envoyHeaderKey] = bufferString;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be mistaken, but for binary, isn't the carrier just a byte array? If so, I don't think there is a notion of a string key, with a binary value. I think it just writes bytes into the array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it still needs a key in the headers, and you still need to base64 the output. maybe this would be one-step with google protos?

keep in mind that adding another proto will cause the tracer file size to creep up again, to your above point

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do any of the other Lightstep tracers have an EnvoyPropagator? As I look around I only see BinaryPropagator (this is true for Go and Python anyway).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C++ has one too that just uses the binary propagator to do the encoding but then puts it in the envoy key header.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that we should probably try to do the same. Would it be possible to split to this work into a BinaryPropagator and an Envoy propagator (or instrumentation) that uses it to do the header manipulation?


return carrier;
}

extract(carrier) {
// Iterate over the contents of the carrier and set the properties
// accordingly.
let foundFields = 0;
let spanGUID = null;
let traceGUID = null;
let sampled = true;

if (carrier[this._envoyHeaderKey] === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the same comment for inject about the carrier being a byte array applies here as well.

// This is not an error per se, there was simply no SpanContext
// in the carrier.
return null;
}

// Decode context
const binaryData = Buffer.from(carrier[this._envoyHeaderKey], 'base64');
let binaryCarrier = this._carrierPb.lookupType('BinaryCarrier');
let msg = binaryCarrier.decode(binaryData);
let basicContext = msg.basic_ctx.toJSON();

if (basicContext === undefined) {
// This is not an error per se, there was simply no SpanContext
// in the carrier.
return null;
}

// Validate span context
_each(basicContext, (value, key) => {
key = key.toLowerCase();
if (key === 'baggage_items') {
// We will address baggage after span context is verified
return;
}

switch (key) {
case 'trace_id':
foundFields++;
// left pad to length of 16
// long is used because JS only supports up to 53 bit integers
traceGUID = _leftpad(
long.fromValue(value).toString(16),
16,
'0',
);
break;
case 'span_id':
foundFields++;
// left pad to length of 16
// long is used because JS only supports up to 53 bit integers
spanGUID = _leftpad(
long.fromValue(value).toString(16),
16,
'0',
);
// left pad

break;
case 'sampled':
switch (value) {
case 0:
case '0':
case false:
case 'false':
sampled = false;
break;
default:
sampled = true;
break;
}
break;
default:
this._tracer._error(
`Unrecognized carrier key '${key}'. Ignoring.`,
);
break;
}
});

if (foundFields === 0) {
// This is not an error per se, there was simply no SpanContext
// in the carrier.
return null;
}
if (foundFields < 2) {
// A partial SpanContext suggests some sort of data corruption.
this._tracer._error(`Only found a partial SpanContext: ${carrier}`);
return null;
}

let spanContext = new SpanContextImp(spanGUID, traceGUID, sampled);

if (basicContext.baggage_items !== undefined) {
_each(basicContext.baggage_items, (value, key) => {
spanContext.setBaggageItem(key.toLowerCase(), value);
});
}

return spanContext;
}
}
3 changes: 1 addition & 2 deletions src/imp/tracer_imp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { Platform, ProtoTransport, ThriftTransport } from '../platform_abstracti
import AuthImp from './auth_imp';
import RuntimeImp from './runtime_imp';
import ReportImp from './report_imp';
import UnsupportedPropagator from './propagator';
import LightStepPropagator from './propagator_ls';

const ClockState = require('./util/clock_state');
Expand Down Expand Up @@ -81,7 +80,7 @@ export default class Tracer extends opentracing.Tracer {
this._propagators = {};
this._propagators[this._opentracing.FORMAT_HTTP_HEADERS] = new LightStepPropagator(this);
this._propagators[this._opentracing.FORMAT_TEXT_MAP] = new LightStepPropagator(this);
this._propagators[this._opentracing.FORMAT_BINARY] = new UnsupportedPropagator(this,
this._propagators[this._opentracing.FORMAT_BINARY] = new LightStepPropagator(this,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have the LightstepPropagator now have a default for FORMAT_BINARY?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like go uses the BinaryPropagator by default: https://github.com/lightstep/lightstep-tracer-go/blob/master/tracer.go#L180. I think that corresponds to the EnvoyPropagator from this PR.

I think this makes sense, but have we thought about how this will work with or impact the no-protobuf version of this library?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FORMAT_BINARY should correspond to the binary propagator. BinaryPropagator/EnvoyPropagator/whatever are all the same.

Theoretically this shouldn't impact the no-pb version (as FORMAT_BINARY should map to UnsupportedPropagator correctly over there)

this._opentracing.FORMAT_BINARY);

if (opts && opts.propagators) {
Expand Down
3 changes: 3 additions & 0 deletions src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import Tracer from './imp/tracer_imp';
import LightStepPropagator from './imp/propagator_ls';
import B3Propagator from './imp/propagator_b3';
import DDPropagator from './imp/propagator_dd';
import EnvoyPropagator from './imp/propagator_envoy';
import SpanContext from './imp/span_context_imp';
import { Platform } from './platform_abstraction_layer';


const library = {
Tracer : Tracer,
LightStepPropagator : LightStepPropagator,
B3Propagator : B3Propagator,
DDPropagator : DDPropagator,
EnvoyPropagator : EnvoyPropagator,
SpanContext : SpanContext,
};

Expand Down
Loading