Skip to content

Commit

Permalink
more wip work
Browse files Browse the repository at this point in the history
  • Loading branch information
AbhiPrasad committed Oct 24, 2022
1 parent e0587d8 commit c9bd9dc
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 109 deletions.
9 changes: 8 additions & 1 deletion packages/opentelemetry-node/package.json
Expand Up @@ -54,5 +54,12 @@
},
"volta": {
"extends": "../../package.json"
}
},
"sideEffects": [
"./cjs/index.js",
"./esm/index.js",
"./build/npm/cjs/index.js",
"./build/npm/esm/index.js",
"./src/index.ts"
]
}
102 changes: 2 additions & 100 deletions packages/opentelemetry-node/src/index.ts
@@ -1,101 +1,3 @@
import { Context } from '@opentelemetry/api';
import { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { getCurrentHub } from '@sentry/core';
import { Span as SentrySpan } from '@sentry/types';
import '@sentry/tracing';

/**
* Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via
* the Sentry SDK.
*/
export class SentrySpanProcessor implements OtelSpanProcessor {
private readonly _map: Record<SentrySpan['spanId'], [SentrySpan, SentrySpan | undefined]> = {};

/**
* @inheritDoc
*/
public onStart(otelSpan: OtelSpan, _parentContext: Context): void {
const hub = getCurrentHub();
if (!hub) {
return;
}
const scope = hub.getScope();
if (!scope) {
return;
}

// if isSentryRequest(otelSpan) return;

const otelSpanId = otelSpan.spanContext().spanId;

const sentryParentSpan = scope.getSpan();
if (sentryParentSpan) {
const sentryChildSpan = sentryParentSpan.startChild({
description: otelSpan.name,
// instrumentor: 'otel',
startTimestamp: otelSpan.startTime[0],
});
sentryChildSpan.spanId = otelSpanId;

this._map[otelSpanId] = [sentryChildSpan, sentryParentSpan];
scope.setSpan(sentryChildSpan);
} else {
// const traceCtx = getTraceData(otelSpan);
const transaction = hub.startTransaction({
name: otelSpan.name,
// ...traceCtx,
// instrumentor: 'otel',
startTimestamp: otelSpan.startTime[0],
});
transaction.spanId = otelSpanId;

this._map[otelSpanId] = [transaction, undefined];
scope.setSpan(transaction);
}
}

/**
* @inheritDoc
*/
public onEnd(otelSpan: OtelSpan): void {
const hub = getCurrentHub();
if (!hub) {
return;
}
const scope = hub.getScope();
if (!scope) {
return;
}

const otelSpanId = otelSpan.spanContext().spanId;
const mapVal = this._map[otelSpanId];

if (mapVal) {
const [sentrySpan, sentryParentSpan] = mapVal;

// updateSpanWithOtelData(sentrySpan, otelSpan);

sentrySpan.finish(otelSpan.endTime[0]);
scope.setSpan(sentryParentSpan);
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this._map[otelSpanId];
}
}

/**
* @inheritDoc
*/
public shutdown(): Promise<void> {
return Promise.resolve();
}

/**
* @inheritDoc
*/
public async forceFlush(): Promise<void> {
const client = getCurrentHub().getClient();
if (client) {
return client.flush().then();
}
return Promise.resolve();
}
}
export { SentrySpanProcessor } from './spanprocessor';
115 changes: 115 additions & 0 deletions packages/opentelemetry-node/src/spanprocessor.ts
@@ -0,0 +1,115 @@
import { Context } from '@opentelemetry/api';
import { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { getCurrentHub } from '@sentry/core';
import { Span as SentrySpan, TransactionContext } from '@sentry/types';

/**
* Converts OpenTelemetry Spans to Sentry Spans and sends them to Sentry via
* the Sentry SDK.
*/
export class SentrySpanProcessor implements OtelSpanProcessor {
private readonly _map: Record<SentrySpan['spanId'], [SentrySpan, SentrySpan | undefined]> = {};

/**
* @inheritDoc
*/
public onStart(otelSpan: OtelSpan, _parentContext: Context): void {
const hub = getCurrentHub();
if (!hub) {
return;
}
const scope = hub.getScope();
if (!scope) {
return;
}

// if isSentryRequest(otelSpan) return;

const otelSpanId = otelSpan.spanContext().spanId;

const sentryParentSpan = scope.getSpan();
if (sentryParentSpan) {
const sentryChildSpan = sentryParentSpan.startChild({
description: otelSpan.name,
// instrumentor: 'otel',
startTimestamp: otelSpan.startTime[0],
});
sentryChildSpan.spanId = otelSpanId;
console.log(sentryParentSpan, sentryChildSpan, otelSpan);

this._map[otelSpanId] = [sentryChildSpan, sentryParentSpan];
scope.setSpan(sentryChildSpan);
} else {
const traceCtx = getTraceData(otelSpan);
const transaction = hub.startTransaction({
name: otelSpan.name,
...traceCtx,
// instrumentor: 'otel',
startTimestamp: otelSpan.startTime[0],
});
transaction.spanId = otelSpanId;

this._map[otelSpanId] = [transaction, undefined];

scope.setSpan(transaction);
}
}

/**
* @inheritDoc
*/
public onEnd(otelSpan: OtelSpan): void {
const hub = getCurrentHub();
if (!hub) {
return;
}
const scope = hub.getScope();
if (!scope) {
return;
}

const otelSpanId = otelSpan.spanContext().spanId;
const mapVal = this._map[otelSpanId];

if (mapVal) {
const [sentrySpan, sentryParentSpan] = mapVal;

// updateSpanWithOtelData(sentrySpan, otelSpan);

sentrySpan.finish(otelSpan.endTime[0]);
scope.setSpan(sentryParentSpan);
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this._map[otelSpanId];
}
}

/**
* @inheritDoc
*/
public shutdown(): Promise<void> {
return Promise.resolve();
}

/**
* @inheritDoc
*/
public async forceFlush(): Promise<void> {
const client = getCurrentHub().getClient();
if (client) {
return client.flush().then();
}
return Promise.resolve();
}
}

function getTraceData(otelSpan: OtelSpan): Partial<TransactionContext> {
const spanContext = otelSpan.spanContext();
const traceId = spanContext.traceId;
const spanId = spanContext.spanId;

const parentSpanId = otelSpan.parentSpanId;
return { spanId, traceId, parentSpanId };
}

// function updateSpanWithOtelData(sentrySpan: SentrySpan, otelSpan: OtelSpan): void {
// }
7 changes: 0 additions & 7 deletions packages/opentelemetry-node/test/index.test.ts

This file was deleted.

80 changes: 80 additions & 0 deletions packages/opentelemetry-node/test/spanprocessor.test.ts
@@ -0,0 +1,80 @@
import * as OpenTelemetry from '@opentelemetry/api';
import { BasicTracerProvider, Span as OtelSpan } from '@opentelemetry/sdk-trace-base';
import { Hub, makeMain } from '@sentry/core';
import { addExtensionMethods, Span as SentrySpan, Transaction } from '@sentry/tracing';

import { SentrySpanProcessor } from '../src/spanprocessor';

// Integration Test of SentrySpanProcessor

beforeAll(() => {
addExtensionMethods();
});

describe('SentrySpanProcessor', () => {
let hub: Hub;
beforeEach(() => {
hub = new Hub();
makeMain(hub);

const provider = new BasicTracerProvider();
provider.addSpanProcessor(new SentrySpanProcessor());
provider.register();
});

describe('onStart', () => {
it('create a transaction', () => {
const otelSpan = OpenTelemetry.trace.getTracer('default').startSpan('GET /users') as OtelSpan;
const sentrySpanTransaction = hub.getScope()?.getSpan() as Transaction;
expect(sentrySpanTransaction).toBeInstanceOf(Transaction);

// Make sure name is set
expect(sentrySpanTransaction?.name).toBe('GET /users');

// Enforce we use otel timestamps
expect(sentrySpanTransaction.startTimestamp).toEqual(otelSpan.startTime[0]);

// Check for otel trace context
expect(sentrySpanTransaction.traceId).toEqual(otelSpan.spanContext().traceId);
expect(sentrySpanTransaction.parentSpanId).toEqual(otelSpan.parentSpanId);
expect(sentrySpanTransaction.spanId).toEqual(otelSpan.spanContext().spanId);
});

it.only('creates a child span if there is a running transaction', () => {
const tracer = OpenTelemetry.trace.getTracer('default');

tracer.startActiveSpan('GET /users', parentOtelSpan => {
// console.log((parentOtelSpan as any).spanContext());
// console.log(hub.getScope()?.getSpan()?.traceId);
tracer.startActiveSpan('SELECT * FROM users;', child => {
const childOtelSpan = child as OtelSpan;

const sentrySpan = hub.getScope()?.getSpan();
expect(sentrySpan).toBeInstanceOf(SentrySpan);
// console.log(hub.getScope()?.getSpan()?.traceId);
// console.log(sentrySpan);

// Make sure name is set
expect(sentrySpan?.description).toBe('SELECT * FROM users;');

// Enforce we use otel timestamps
expect(sentrySpan?.startTimestamp).toEqual(childOtelSpan.startTime[0]);

// Check for otel trace context
expect(sentrySpan?.spanId).toEqual(childOtelSpan.spanContext().spanId);

childOtelSpan.end();
});

parentOtelSpan.end();
});
});
});

// it('Creates a transaction if there is no running ', () => {
// const otelSpan = OpenTelemetry.trace.getTracer('default').startSpan('GET /users') as OtelSpan;
// processor.onStart(otelSpan, OpenTelemetry.context.active());

// const sentrySpanTransaction = hub.getScope()?.getSpan() as Transaction;
// });
});
2 changes: 1 addition & 1 deletion packages/tracing/src/span.ts
Expand Up @@ -153,7 +153,7 @@ export class Span implements SpanInterface {
* @inheritDoc
*/
public startChild(
spanContext?: Pick<SpanContext, Exclude<keyof SpanContext, 'spanId' | 'sampled' | 'traceId' | 'parentSpanId'>>,
spanContext?: Pick<SpanContext, Exclude<keyof SpanContext, 'sampled' | 'traceId' | 'parentSpanId'>>,
): Span {
const childSpan = new Span({
...spanContext,
Expand Down

0 comments on commit c9bd9dc

Please sign in to comment.