Skip to content

Commit

Permalink
feat(opencensus-shim) add mapping logic and propagation shim (#3751)
Browse files Browse the repository at this point in the history
  • Loading branch information
aabmass committed May 5, 2023
1 parent 32ae641 commit 7255da9
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 5 deletions.
13 changes: 8 additions & 5 deletions experimental/packages/opentelemetry-shim-opencensus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"prepublishOnly": "npm run compile",
"compile": "tsc --build",
"clean": "tsc --build --clean",
"tdd": "npm run test -- --extension ts --watch",
"test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts",
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../../",
"lint": "eslint . --ext .ts",
Expand Down Expand Up @@ -44,21 +45,23 @@
"access": "public"
},
"devDependencies": {
"@opentelemetry/core": "1.11.0",
"@opentelemetry/context-async-hooks": "1.11.0",
"@opentelemetry/core": "1.12.0",
"@opentelemetry/context-async-hooks": "1.12.0",
"@opencensus/core": "0.1.0",
"@opentelemetry/api": ">=1.0.0 <1.5.0",
"@opentelemetry/api": "1.4.1",
"@types/mocha": "10.0.0",
"@types/node": "18.6.5",
"codecov": "3.8.3",
"mocha": "10.0.0",
"nyc": "15.1.0",
"sinon": "15.0.0",
"@types/sinon": "10.0.13",
"ts-mocha": "10.0.0",
"typescript": "4.4.4"
},
"peerDependencies": {
"@opencensus/core": "^0.1.0",
"@opentelemetry/api": ">=1.0.0 <1.5.0"
"@opentelemetry/api": "^1.0.0"
},
"dependencies": {
"@opentelemetry/core": "^1.0.0",
Expand All @@ -68,4 +71,4 @@
},
"homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-shim-opencensus",
"sideEffects": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as oc from '@opencensus/core';

import {
context,
propagation,
trace,
TextMapGetter,
TextMapSetter,
} from '@opentelemetry/api';
import { mapSpanContext, reverseMapSpanContext } from './transform';

class Getter implements TextMapGetter<void> {
constructor(private ocGetter: oc.HeaderGetter) {}
keys(): string[] {
return [];
}
get(carrier: void, key: string) {
return this.ocGetter.getHeader(key);
}
}

class Setter implements TextMapSetter<void> {
constructor(private ocSetter: oc.HeaderSetter) {}
set(carrier: void, key: string, value: string): void {
this.ocSetter.setHeader(key, value);
}
}

/**
* Bridges OpenTelemetry propagation API into OpenCensus. The global OTel propagator is called
* to implement the OpenCensus propagation API.
*/
export const shimPropagation: oc.Propagation = {
extract(getter: oc.HeaderGetter): oc.SpanContext | null {
const extracted = propagation.extract(
context.active(),
null,
new Getter(getter)
);

const otelSc = trace.getSpanContext(extracted);
return otelSc ? reverseMapSpanContext(otelSc) : null;
},

inject(setter: oc.HeaderSetter, spanContext: oc.SpanContext): void {
const ctx = trace.setSpanContext(
context.active(),
mapSpanContext(spanContext)
);
propagation.inject(ctx, null, new Setter(setter));
},

generate(): oc.SpanContext {
// Reading OpenCensus code, it looks like this should generate a new random span context.
// However, it doesn't appear to be used based on my testing. Options for implementing:
//
// - Return the invalid span context
// - Use the OTel ID generator, however this package should be an API-only bridge
// - Copy implementation from OpenCensus noop-propagation.ts
throw new Error('shimPropagation.generate() is not yet implemented');
},
};
105 changes: 105 additions & 0 deletions experimental/packages/opentelemetry-shim-opencensus/src/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as oc from '@opencensus/core';
import {
Attributes,
SpanContext,
SpanKind,
TimeInput,
diag,
} from '@opentelemetry/api';
import { TraceState } from '@opentelemetry/core';

function exhaust(value: never) {
diag.warn('Could not handle enum value %s', value);
}

export function mapSpanKind(
kind: oc.SpanKind | undefined
): SpanKind | undefined {
switch (kind) {
case undefined:
return undefined;
case oc.SpanKind.UNSPECIFIED:
return SpanKind.INTERNAL;
case oc.SpanKind.CLIENT:
return SpanKind.CLIENT;
case oc.SpanKind.SERVER:
return SpanKind.SERVER;
default:
exhaust(kind);
return undefined;
}
}

export function mapSpanContext({
spanId,
traceId,
options,
traceState,
}: oc.SpanContext): SpanContext {
return {
spanId,
traceId,
traceFlags: options ?? 0,
traceState:
traceState === undefined ? undefined : new TraceState(traceState),
};
}

export function reverseMapSpanContext({
spanId,
traceId,
traceFlags,
traceState,
}: SpanContext): oc.SpanContext {
return {
spanId: spanId,
traceId: traceId,
options: traceFlags,
traceState: traceState?.serialize(),
};
}

// Copied from Java
// https://github.com/open-telemetry/opentelemetry-java/blob/0d3a04669e51b33ea47b29399a7af00012d25ccb/opencensus-shim/src/main/java/io/opentelemetry/opencensusshim/SpanConverter.java#L24-L27
const MESSAGE_EVENT_ATTRIBUTE_KEY_TYPE = 'message.event.type';
const MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_UNCOMPRESSED =
'message.event.size.uncompressed';
const MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_COMPRESSED =
'message.event.size.compressed';

export function mapMessageEvent(
type: oc.MessageEventType,
id: number,
timestamp?: number,
uncompressedSize?: number,
compressedSize?: number
): [string, Attributes, TimeInput | undefined] {
const attributes: Attributes = {
[MESSAGE_EVENT_ATTRIBUTE_KEY_TYPE]: oc.MessageEventType[type],
};
if (uncompressedSize !== undefined) {
attributes[MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_UNCOMPRESSED] =
uncompressedSize;
}
if (compressedSize !== undefined) {
attributes[MESSAGE_EVENT_ATTRIBUTE_KEY_SIZE_COMPRESSED] = compressedSize;
}

return [id.toString(), attributes, timestamp];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { shimPropagation } from '../src/propagation';

import * as oc from '@opencensus/core';
import { propagation } from '@opentelemetry/api';
import { W3CTraceContextPropagator } from '@opentelemetry/core';
import * as assert from 'assert';
import * as sinon from 'sinon';

const dummyGetterWithHeader: oc.HeaderGetter = {
getHeader(name) {
if (name === 'traceparent') {
return '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01';
}
return undefined;
},
};
const dummyGetterWithoutHeader: oc.HeaderGetter = {
getHeader() {
return undefined;
},
};

describe('propagation', () => {
describe('shimPropagation', () => {
beforeEach(() => {
propagation.setGlobalPropagator(new W3CTraceContextPropagator());
});
afterEach(() => {
propagation.disable();
});

describe('extract', () => {
it('should extract when header is available', () => {
assert.deepStrictEqual(shimPropagation.extract(dummyGetterWithHeader), {
options: 1,
spanId: '00f067aa0ba902b7',
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
traceState: undefined,
});
});
it('should return null when header is not available', () => {
assert.deepStrictEqual(
shimPropagation.extract(dummyGetterWithoutHeader),
null
);
});
});

describe('inject', () => {
it('should inject when span context is provided', () => {
const setHeaderFake = sinon.fake<[string, string]>();
const headerSetter: oc.HeaderSetter = {
setHeader: setHeaderFake,
};
shimPropagation.inject(headerSetter, {
options: 1,
spanId: '00f067aa0ba902b7',
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
traceState: undefined,
});
sinon.assert.calledWith(
setHeaderFake,
'traceparent',
'00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
);
});
});
});
});
Loading

0 comments on commit 7255da9

Please sign in to comment.