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

Commit d838da0

Browse files
authored
Tags: Add support for TagMetadata (#424)
* Tags: Add TagMetadata * fix review comments
1 parent 9741369 commit d838da0

File tree

10 files changed

+173
-38
lines changed

10 files changed

+173
-38
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file.
2121
- Add support for overriding sampling for a span.
2222
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-exporter-jaeger] packages.
2323
- Add support for recording Exemplars.
24+
- Add `TagMetadata` that defines the properties associated with a `Tag`.
2425

2526
## 0.0.9 - 2019-02-12
2627
- Add Metrics API.

packages/opencensus-core/src/exporters/console-exporter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export class ConsoleStatsExporter implements types.StatsEventListener {
9393
* Event called when a measurement is recorded
9494
* @param view recorded view from measurement
9595
* @param measurement recorded measurement
96+
* @param tags The tags to which the value is applied
9697
*/
9798
onRecord(
9899
views: View[], measurement: Measurement, tags: Map<TagKey, TagValue>) {

packages/opencensus-core/src/exporters/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface StatsEventListener {
4444
* @deprecated since version 0.0.9 - use {@link start} instead
4545
* @param views The views related to the measurement
4646
* @param measurement The recorded measurement
47+
* @param tags The tags to which the value is applied
4748
*/
4849
onRecord(
4950
views: View[], measurement: Measurement,

packages/opencensus-core/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export * from './resource/resource';
7676

7777
// interfaces
7878
export * from './stats/types';
79-
export * from './tags/types';
79+
export {TagKey, TagValue, TagMetadata, TagTtl} from './tags/types';
8080
export * from './resource/types';
8181

8282
// logger

packages/opencensus-core/src/tags/tag-map.ts

+45-10
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {TagKey, TagValue} from './types';
17+
import {TagKey, TagMetadata, TagTtl, TagValue, TagValueWithMetadata} from './types';
1818
import {isValidTagKey, isValidTagValue} from './validation';
1919

20-
/** TagMap is maps of TagKey -> TagValue */
20+
const UNLIMITED_PROPAGATION_MD = {
21+
tagTtl: TagTtl.UNLIMITED_PROPAGATION
22+
};
23+
24+
/** TagMap is maps of TagKey -> TagValueWithMetadata */
2125
export class TagMap {
22-
// A map mapping TagKey to to its respective TagValue.
23-
private readonly registeredTags: Map<TagKey, TagValue> = new Map();
26+
// A map mapping TagKey to to its respective TagValueWithMetadata.
27+
private readonly registeredTags: Map<TagKey, TagValueWithMetadata> =
28+
new Map();
2429

25-
/** Adds the key/value pair regardless of whether the key is present. */
26-
set(tagKey: TagKey, tagValue: TagValue): void {
30+
/**
31+
* Adds the key/value pair regardless of whether the key is present.
32+
* @param tagKey The TagKey which will be set.
33+
* @param tagValue The TagValue to set for the given key.
34+
* @param tagMetadata The TagMetadata associated with this Tag.
35+
*/
36+
set(tagKey: TagKey, tagValue: TagValue, tagMetadata?: TagMetadata): void {
2737
if (!isValidTagKey(tagKey)) {
2838
throw new Error(`Invalid TagKey name: ${tagKey.name}`);
2939
}
30-
3140
if (!isValidTagValue(tagValue)) {
3241
throw new Error(`Invalid TagValue: ${tagValue.value}`);
3342
}
@@ -39,16 +48,42 @@ export class TagMap {
3948
}
4049
}
4150
if (existingKey) this.registeredTags.delete(existingKey);
42-
this.registeredTags.set(tagKey, tagValue);
51+
const valueWithMetadata = this.getValueWithMetadata(tagValue, tagMetadata);
52+
this.registeredTags.set(tagKey, valueWithMetadata);
4353
}
4454

45-
/** Deletes a tag from the map if the key is in the map. */
55+
/**
56+
* Deletes a tag from the map if the key is in the map.
57+
* @param tagKey The TagKey which will be removed.
58+
*/
4659
delete(tagKey: TagKey): void {
4760
this.registeredTags.delete(tagKey);
4861
}
4962

50-
/** Gets the tags map. */
63+
/** Gets the tags map without metadata. */
5164
get tags() {
65+
const tagsWithoutMetadata: Map<TagKey, TagValue> = new Map();
66+
for (const [tagKey, valueWithMetadata] of this.registeredTags) {
67+
tagsWithoutMetadata.set(tagKey, valueWithMetadata.tagValue);
68+
}
69+
return tagsWithoutMetadata;
70+
}
71+
72+
/** Gets the tags map with metadata. */
73+
get tagsWithMetadata() {
5274
return this.registeredTags;
5375
}
76+
77+
/**
78+
* Constructs a new TagValueWithMetadata using tagValue and tagMetadata.
79+
* For backwards-compatibility this method still produces propagating Tags
80+
* (UNLIMITED_PROPAGATION) if tagMetadata is not provided or missing.
81+
*/
82+
private getValueWithMetadata(tagValue: TagValue, tagMetadata?: TagMetadata):
83+
TagValueWithMetadata {
84+
if (tagMetadata) {
85+
return {tagValue, tagMetadata};
86+
}
87+
return {tagValue, tagMetadata: UNLIMITED_PROPAGATION_MD};
88+
}
5489
}

packages/opencensus-core/src/tags/types.ts

+35
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,38 @@ export interface TagValue {
2525
/** The value of a tag. */
2626
readonly value: string;
2727
}
28+
29+
/** TagValueWithMetadata holds a TagValue and a TagMetadata. */
30+
export interface TagValueWithMetadata {
31+
/** The tag value */
32+
readonly tagValue: TagValue;
33+
/** The metadata for the tag */
34+
readonly tagMetadata: TagMetadata;
35+
}
36+
37+
/**
38+
* TagMetadata contains properties associated with a Tag.
39+
* Anytime a sender serializes a tag, sends it over the wire and receiver
40+
* deserializes the tag then the tag is considered to have travelled one hop.
41+
* There could be one or more proxy(ies) between sender and receiver. Proxies
42+
* are treated as transparent entities and they do not create additional hops.
43+
*/
44+
export interface TagMetadata {
45+
/**
46+
* For now, only special values of TagTtl are supported. In future,
47+
* additional properties may be added to address specific situations.
48+
*/
49+
readonly tagTtl: number;
50+
}
51+
52+
/** TagTtl is an integer that represents number of hops a tag can propagate */
53+
export enum TagTtl {
54+
/**
55+
* NO_PROPAGATION is considered to have local scope and is used within the
56+
* process it created.
57+
*/
58+
NO_PROPAGATION = 0,
59+
60+
/** UNLIMITED_PROPAGATION can propagate unlimited hops. */
61+
UNLIMITED_PROPAGATION = -1
62+
}

packages/opencensus-core/test/test-recorder.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import * as assert from 'assert';
18-
import {Recorder, TagMap} from '../src';
18+
import {Recorder, TagMap, TagTtl} from '../src';
1919
import {AggregationType, CountData, DistributionData, LastValueData, Measure, Measurement, MeasureType, MeasureUnit, SumData} from '../src/stats/types';
2020

2121
/** The order of how close values must be to be considerated almost equal */
@@ -218,6 +218,8 @@ describe('Recorder', () => {
218218
const CALLER_V = {value: 'some caller'};
219219
const METHOD_V = {value: 'some method'};
220220
const ORIGINATOR_V = {value: 'some originator'};
221+
const NO_PROPAGATION_MD = {tagTtl: TagTtl.NO_PROPAGATION};
222+
const UNLIMITED_PROPAGATION_MD = {tagTtl: TagTtl.UNLIMITED_PROPAGATION};
221223
let tagMap: TagMap;
222224

223225
beforeEach(() => {
@@ -233,6 +235,16 @@ describe('Recorder', () => {
233235
assert.deepStrictEqual(tagValues, [CALLER_V, METHOD_V]);
234236
});
235237

238+
it('should return tag values from tags and columns when using metadata',
239+
() => {
240+
const columns = [CALLER, METHOD];
241+
tagMap.set(CALLER, CALLER_V, NO_PROPAGATION_MD);
242+
tagMap.set(METHOD, METHOD_V, UNLIMITED_PROPAGATION_MD);
243+
const tagValues = Recorder.getTagValues(tagMap.tags, columns);
244+
assert.equal(tagValues.length, 2);
245+
assert.deepStrictEqual(tagValues, [CALLER_V, METHOD_V]);
246+
});
247+
236248
it('should return tag values from tags and columns with extra keys',
237249
() => {
238250
const columns = [CALLER, METHOD, ORIGINATOR];

packages/opencensus-core/test/test-tag-map.ts

+48-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import * as assert from 'assert';
1818
import {TagMap} from '../src/tags/tag-map';
19+
import {TagTtl} from '../src/tags/types';
1920

2021
describe('TagMap()', () => {
2122
let tagMap: TagMap;
@@ -25,19 +26,56 @@ describe('TagMap()', () => {
2526
const value1 = {value: 'value1'};
2627
const value2 = {value: 'value2'};
2728
const invalidValue1 = {value: 'a'.repeat(256)};
29+
const NO_PROPAGATION_MD = {tagTtl: TagTtl.NO_PROPAGATION};
30+
const UNLIMITED_PROPAGATION_MD = {tagTtl: TagTtl.UNLIMITED_PROPAGATION};
31+
32+
const expectedTagValueWithMetadata1 = {
33+
tagValue: {value: 'value1'},
34+
tagMetadata: {tagTtl: -1}
35+
};
36+
37+
const expectedTagValueWithMetadata2 = {
38+
tagValue: {value: 'value2'},
39+
tagMetadata: {tagTtl: -1}
40+
};
2841

2942
beforeEach(() => {
3043
tagMap = new TagMap();
3144
});
3245

3346
describe('set()', () => {
34-
it('should set tagkey and tagvalue', () => {
47+
it('should set tagkey and tagvalue with default TagMetadata', () => {
3548
tagMap.set(key1, value1);
36-
const tags = tagMap.tags;
49+
const tags = tagMap.tagsWithMetadata;
50+
assert.equal(tags.size, 1);
51+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata1);
52+
});
53+
54+
it('should set tagkey and tagvalue with NO_PROPAGATION TagTtl', () => {
55+
const expectedTagValueWithMetadata = {
56+
tagValue: {value: 'value1'},
57+
tagMetadata: {tagTtl: 0}
58+
};
59+
60+
tagMap.set(key1, value1, NO_PROPAGATION_MD);
61+
const tags = tagMap.tagsWithMetadata;
3762
assert.equal(tags.size, 1);
38-
assert.deepStrictEqual(tags.get(key1), value1);
63+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata);
3964
});
4065

66+
it('should set tagkey and tagvalue with UNLIMITED_PROPAGATION TagTtl',
67+
() => {
68+
const expectedTagValueWithMetadata = {
69+
tagValue: {value: 'value1'},
70+
tagMetadata: {tagTtl: -1}
71+
};
72+
73+
tagMap.set(key1, value1, UNLIMITED_PROPAGATION_MD);
74+
const tags = tagMap.tagsWithMetadata;
75+
assert.equal(tags.size, 1);
76+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata);
77+
});
78+
4179
it('should throw an error when invalid tagKey', () => {
4280
assert.throws(() => {
4381
tagMap.set(invalidKey1, value1);
@@ -52,34 +90,34 @@ describe('TagMap()', () => {
5290

5391
it('should not set duplicate tagkey and tagvalue', () => {
5492
tagMap.set(key1, value1);
55-
const tags = tagMap.tags;
93+
const tags = tagMap.tagsWithMetadata;
5694
assert.equal(tags.size, 1);
57-
assert.deepStrictEqual(tags.get(key1), value1);
95+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata1);
5896
tagMap.set(key1, value1);
5997
assert.equal(tags.size, 1);
6098
});
6199

62100
it('should update existing tagkey', () => {
63101
tagMap.set(key1, value1);
64-
const tags = tagMap.tags;
102+
const tags = tagMap.tagsWithMetadata;
65103
assert.equal(tags.size, 1);
66-
assert.deepStrictEqual(tags.get(key1), value1);
104+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata1);
67105
tagMap.set(key1, value2);
68106
assert.equal(tags.size, 1);
69-
assert.deepStrictEqual(tags.get(key1), value2);
107+
assert.deepStrictEqual(tags.get(key1), expectedTagValueWithMetadata2);
70108
});
71109
});
72110
describe('delete()', () => {
73111
it('should delete tagkey', () => {
74112
tagMap.set(key1, value1);
75-
const tags = tagMap.tags;
113+
const tags = tagMap.tagsWithMetadata;
76114
assert.equal(tags.size, 1);
77115
tagMap.delete(key1);
78116
assert.equal(tags.size, 0);
79117
});
80118
it('should delete missing tagkey1', () => {
81119
tagMap.set(key1, value1);
82-
const tags = tagMap.tags;
120+
const tags = tagMap.tagsWithMetadata;
83121
assert.equal(tags.size, 1);
84122
tagMap.delete(key2);
85123
assert.equal(tags.size, 1);

packages/opencensus-instrumentation-grpc/src/grpc.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {BasePlugin, CanonicalCode, PluginInternalFiles, RootSpan, Span, SpanContext, SpanKind, TagMap, TraceOptions} from '@opencensus/core';
17+
import {BasePlugin, CanonicalCode, PluginInternalFiles, RootSpan, Span, SpanContext, SpanKind, TagMap, TagTtl, TraceOptions} from '@opencensus/core';
1818
import {deserializeSpanContext, serializeSpanContext} from '@opencensus/propagation-binaryformat';
1919
import {EventEmitter} from 'events';
2020
import * as grpcTypes from 'grpc';
@@ -72,18 +72,19 @@ let Metadata: any;
7272
// tslint:disable-next-line:no-any
7373
let GrpcClientModule: any;
7474

75+
const UNLIMITED_PROPAGATION_MD = {
76+
tagTtl: TagTtl.UNLIMITED_PROPAGATION
77+
};
78+
7579
/** gRPC instrumentation plugin for Opencensus */
7680
export class GrpcPlugin extends BasePlugin {
77-
/**
78-
* Span grpc attributes
79-
*/
81+
/** Span grpc attributes */
8082
static readonly ATTRIBUTE_GRPC_KIND = 'grpc.kind'; // SERVER or CLIENT
8183
static readonly ATTRIBUTE_GRPC_METHOD = 'grpc.method';
8284
static readonly ATTRIBUTE_GRPC_STATUS_CODE = 'grpc.status_code';
8385
static readonly ATTRIBUTE_GRPC_ERROR_NAME = 'grpc.error_name';
8486
static readonly ATTRIBUTE_GRPC_ERROR_MESSAGE = 'grpc.error_message';
8587

86-
8788
protected readonly internalFileList: PluginInternalFiles = {
8889
'0.13 - 1.6': {
8990
'client': 'src/node/src/client.js',
@@ -92,15 +93,12 @@ export class GrpcPlugin extends BasePlugin {
9293
'^1.7': {'client': 'src/client.js', 'metadata': 'src/metadata.js'}
9394
};
9495

95-
9696
/** Constructs a new GrpcPlugin instance. */
9797
constructor() {
9898
super('grpc');
9999
}
100100

101-
/**
102-
* Patches gRPC incoming and outcoming request functions.
103-
*/
101+
/** Patches gRPC incoming and outcoming request functions. */
104102
protected applyPatch() {
105103
this.logger.debug('applying patch to %s@%s', this.moduleName, this.version);
106104

@@ -138,7 +136,7 @@ export class GrpcPlugin extends BasePlugin {
138136
private getPatchServer() {
139137
return (originalRegister: RegisterMethod) => {
140138
const plugin = this;
141-
plugin.logger.debug('pathcServer');
139+
plugin.logger.debug('patched server');
142140
return function register<RequestType, ResponseType>(
143141
// tslint:disable-next-line:no-any
144142
this: grpcTypes.Server&{handlers: any}, name: string,
@@ -232,7 +230,9 @@ export class GrpcPlugin extends BasePlugin {
232230

233231
// record stats
234232
const tags = new TagMap();
235-
tags.set(serverStats.GRPC_SERVER_METHOD, {value: rootSpan.name});
233+
tags.set(
234+
serverStats.GRPC_SERVER_METHOD, {value: rootSpan.name},
235+
UNLIMITED_PROPAGATION_MD);
236236
const req = call.hasOwnProperty('request') ? call.request : {};
237237
GrpcPlugin.recordStats(
238238
rootSpan.kind, tags, value, req, Date.now() - startTime);
@@ -377,7 +377,9 @@ export class GrpcPlugin extends BasePlugin {
377377

378378
// record stats
379379
const tags = new TagMap();
380-
tags.set(clientStats.GRPC_CLIENT_METHOD, {value: span.name});
380+
tags.set(
381+
clientStats.GRPC_CLIENT_METHOD, {value: span.name},
382+
UNLIMITED_PROPAGATION_MD);
381383
GrpcPlugin.recordStats(
382384
span.kind, tags, originalArgs, res, Date.now() - startTime);
383385

0 commit comments

Comments
 (0)