Skip to content
This repository has been archived by the owner on Sep 13, 2022. It is now read-only.

Commit

Permalink
Add BaggageRestrictionManager and BaggageSetter (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
black-adder authored Aug 21, 2017
1 parent f29839b commit 5bd0763
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 46 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@
"add-license": "./node_modules/.bin/uber-licence",
"check-license": "./node_modules/.bin/uber-licence --dry",
"check-ls": "npm ls --loglevel=http --parseable 1>/dev/null && echo '# npm is in a good state'",
"cover": "babel-node ./node_modules/.bin/babel-istanbul cover ./node_modules/.bin/_mocha -- test/ test/samplers/",
"cover": "babel-node ./node_modules/.bin/babel-istanbul cover ./node_modules/.bin/_mocha -- test/ test/samplers/ test/baggage/",
"coveralls": "npm run cover && cat ./coverage/lcov.info | coveralls",
"flow": "flow",
"lint": "eslint $(ls src/ | grep '.js$') && echo '# linter passed'",
"lint-ci": "npm run lint && echo '# TODO: ADD npm run check-license'",
"test": "make test",
"test-ci": "npm run test && npm run lint-ci",
"test-all": "npm run test-core && npm run test-samplers && npm run test-crossdock",
"test-all": "npm run test-core && npm run test-samplers && npm run test-crossdock && npm run test-baggage",
"test-core": "mocha --compilers js:babel-core/register test",
"test-samplers": "mocha --compilers js:babel-core/register test/samplers",
"test-baggage": "mocha --compilers js:babel-core/register test/baggage",
"test-crossdock": "mocha --compilers js:babel-register crossdock/test",
"show-cover": "open coverage/lcov-report/index.html"
}
Expand Down
32 changes: 32 additions & 0 deletions src/_flow/baggage_restriction_manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @flow
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Restriction from '../baggage/restriction.js'

/**
* BaggageRestrictionManager is an interface for a class that manages baggage
* restrictions for baggage keys. The manager will return a Restriction
* for a specific baggage key which will determine whether the baggage key is
* allowed and any other applicable restrictions on the baggage value.
*/
declare interface BaggageRestrictionManager {
getRestriction(service: string, key: string): Restriction;
}
89 changes: 89 additions & 0 deletions src/baggage/baggage_setter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// @flow
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Span from '../span.js';
import SpanContext from '../span_context.js';
import Metrics from '../metrics/metrics.js'

/**
* BaggageSetter is a class that sets a baggage key:value and the associated
* logs on a Span.
*/
export default class BaggageSetter {
_restrictionManager: BaggageRestrictionManager;
_metrics: Metrics;

constructor(restrictionManager: BaggageRestrictionManager, metrics: Metrics) {
this._restrictionManager = restrictionManager;
this._metrics = metrics;
}

/**
* Sets the baggage key:value on the span and the corresponding logs.
* A SpanContext is returned with the new baggage key:value set.
*
* @param {Span} span - The span to set the baggage on.
* @param {string} key - The baggage key to set.
* @param {string} baggageValue - The baggage value to set.
* @return {SpanContext} - The SpanContext with the baggage set if applicable.
*/
setBaggage(span: Span, key: string, baggageValue: string): SpanContext {
let value = baggageValue;
let truncated = false;
let prevItem = '';
let restriction = this._restrictionManager.getRestriction(span.serviceName, key);
if (!restriction.keyAllowed) {
this._logFields(span, key, value, prevItem, truncated, restriction.keyAllowed);
this._metrics.baggageUpdateFailure.increment(1);
return span.context();
}
if (value.length > restriction.maxValueLength) {
truncated = true;
value = value.substring(0, restriction.maxValueLength);
this._metrics.baggageTruncate.increment(1);
}
prevItem = span.getBaggageItem(key);
this._logFields(span, key, value, prevItem, truncated, restriction.keyAllowed);
this._metrics.baggageUpdateSuccess.increment(1);
return span.context().withBaggageItem(key, value);
}

_logFields(span: Span, key: string, value: string, prevItem: string, truncated: boolean, valid: boolean) {
if (!span.context().isSampled()) {
return
}
let fields: { [key: string]: string } = {
event: 'baggage',
key: key,
value: value,
};
if (prevItem) {
fields.override = 'true';
}
if (truncated) {
fields.truncated = 'true';
}
if (!valid) {
fields.invalid = 'true';
}
span.log(fields);
}
}
40 changes: 40 additions & 0 deletions src/baggage/default_baggage_restriction_manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @flow
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Restriction from './restriction.js'

export const DEFAULT_MAX_VALUE_LENGTH = 2048;

/**
* Creates a BaggageRestrictionManager that allows any baggage key.
*/
export default class DefaultBaggageRestrictionManager {
_restriction: Restriction;

constructor(maxValueLength: ?number) {
let length = maxValueLength || DEFAULT_MAX_VALUE_LENGTH;
this._restriction = new Restriction(true, length);
}

getRestriction(service: string, key: string): Restriction {
return this._restriction;
}
}
42 changes: 42 additions & 0 deletions src/baggage/restriction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// @flow
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

/**
* Restriction determines whether a baggage key is allowed and contains any
* restrictions on the baggage value.
*/
export default class Restriction {
_keyAllowed: boolean;
_maxValueLength: number;

constructor(keyAllowed: boolean, maxValueLength: number) {
this._keyAllowed = keyAllowed;
this._maxValueLength = maxValueLength;
}

get keyAllowed(): boolean {
return this._keyAllowed;
}

get maxValueLength(): number {
return this._maxValueLength;
}
}
13 changes: 13 additions & 0 deletions src/metrics/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export default class Metrics {
samplerUpdated: Counter;
samplerQueryFailure: Counter;
samplerParsingFailure: Counter;
baggageUpdateSuccess: Counter;
baggageUpdateFailure: Counter;
baggageTruncate: Counter;

constructor(factory: MetricsFactory) {
this._factory = factory;
Expand Down Expand Up @@ -115,5 +118,15 @@ export default class Metrics {
state: 'failure',
phase: 'parsing'
});

this.baggageUpdateSuccess = this._factory.createCounter('baggage-update', {
result: 'ok',
});

this.baggageUpdateFailure = this._factory.createCounter('baggage-update', {
result: 'err',
});

this.baggageTruncate = this._factory.createCounter('baggage-trucate');
}
}
25 changes: 10 additions & 15 deletions src/span.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
// THE SOFTWARE.

import * as constants from './constants.js';
import NullLogger from './logger.js';
import SpanContext from './span_context.js';
import * as opentracing from 'opentracing';
import Utils from './util.js';
import BaggageSetter from './baggage/baggage_setter';

export default class Span {
_tracer: any;
Expand All @@ -36,6 +36,7 @@ export default class Span {
_tags: Array<Tag>;
static _baggageHeaderCache: any;
_references: Array<Reference>;
_baggageSetter: BaggageSetter;

constructor(tracer: any,
operationName: string,
Expand All @@ -49,6 +50,7 @@ export default class Span {
this._startTime = startTime;
this._logger = tracer._logger;
this._references = references;
this._baggageSetter = tracer._baggageSetter;
this._logs = [];
this._tags = [];
}
Expand All @@ -57,6 +59,10 @@ export default class Span {
return this._operationName;
}

get serviceName(): string {
return this._tracer._serviceName;
}

static _getBaggageHeaderCache() {
if (!Span._baggageHeaderCache) {
Span._baggageHeaderCache = {};
Expand Down Expand Up @@ -95,26 +101,15 @@ export default class Span {
* @return {Span} - returns this span.
**/
setBaggageItem(key: string, value: string): Span {
// TODO emit a metric whenever baggage is updated
let normalizedKey = this._normalizeBaggageKey(key);
if (this._spanContext.isSampled()) {
let logs: { [key: string]: string } = {
'event': 'baggage',
'key': key,
'value': value,
};
if (this.getBaggageItem(normalizedKey)) {
logs['override'] = 'true';
}
this.log(logs);
}

// We create a new instance of the context here instead of just adding
// another entry to the baggage dictionary. By doing so we keep the
// baggage immutable so that it can be passed to children spans as is.
// If it was mutable, we would have to make a copy of the dictionary
// for every child span, which on average we expect to occur more
// for every child span, which on average we expect to occur more
// frequently than items being added to the baggage.
this._spanContext = this._spanContext.withBaggageItem(normalizedKey, value);
this._spanContext = this._baggageSetter.setBaggage(this, normalizedKey, value);
return this;
}

Expand Down
8 changes: 8 additions & 0 deletions src/tracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import NullLogger from './logger';
import Utils from './util';
import Metrics from './metrics/metrics';
import NoopMetricFactory from './metrics/noop/metric_factory';
import DefaultBaggageRestrictionManager from './baggage/default_baggage_restriction_manager';
import os from 'os';
import BaggageSetter from './baggage/baggage_setter';

export default class Tracer {
_serviceName: string;
Expand All @@ -44,6 +46,7 @@ export default class Tracer {
_injectors: any;
_extractors: any;
_metrics: any;
_baggageSetter: BaggageSetter;

/**
* @param {String} [serviceName] - name of the current service or application.
Expand All @@ -54,6 +57,8 @@ export default class Tracer {
* as process-level tags on the Tracer itself.
* @param {Object} [options.metrics] - instance of the Metrics class from ./metrics/metrics.js.
* @param {Object} [options.logger] - a logger matching NullLogger API from ./logger.js.
* @param {Object} [options.baggageRestrictionManager] - a baggageRestrictionManager matching
* BaggageRestrictionManager API from ./baggage.js.
*/
constructor(serviceName: string,
reporter: Reporter = new NoopReporter(),
Expand All @@ -70,6 +75,9 @@ export default class Tracer {
this._reporter = reporter;
this._sampler = sampler;
this._logger = options.logger || new NullLogger();
this._baggageSetter = new BaggageSetter(
options.baggageRestrictionManager || new DefaultBaggageRestrictionManager(),
this._metrics);
this._injectors = {};
this._extractors = {};

Expand Down
Loading

0 comments on commit 5bd0763

Please sign in to comment.