Skip to content

Commit

Permalink
Merge branch 'master' into test-utils
Browse files Browse the repository at this point in the history
  • Loading branch information
naseemkullah committed Dec 23, 2019
2 parents 200f499 + 1a2d926 commit de72e45
Show file tree
Hide file tree
Showing 12 changed files with 640 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ cache_2: &cache_2
- packages/opentelemetry-plugin-https/node_modules
- packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/node_modules
- packages/opentelemetry-plugin-mysql/node_modules
- packages/opentelemetry-exporter-prometheus/node_modules
- packages/opentelemetry-exporter-collector/node_modules
- packages/opentelemetry-plugin-xml-http-request/node_modules

node_unit_tests: &node_unit_tests
steps:
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.

## Unreleased

## 0.3.1

Released 2019-12-20

#### :bug: (Bug Fix)
* `opentelemetry-plugin-grpc`
* [#631](https://github.com/open-telemetry/opentelemetry-js/pull/631) fix(grpc): patch original client methods
* [#593](https://github.com/open-telemetry/opentelemetry-js/pull/593) fix: transpile to es2017 as esnext may result in unsupported JS code

#### :books: (Refine Doc)
* Other
* [#629](https://github.com/open-telemetry/opentelemetry-js/pull/629) ci: deploy documentation on releases
* [#581](https://github.com/open-telemetry/opentelemetry-js/pull/581) feat: add OpenTracing example

#### :rocket: (Enhancement)
* [#633](https://github.com/open-telemetry/opentelemetry-js/pull/633) chore: enable incremental builds

#### :sparkles: (Feature)
* `opentelemetry-plugin-xml-http-request`
* [#595](https://github.com/open-telemetry/opentelemetry-js/pull/595) feat: implement XMLHttpRequest plugin

## 0.3.0

Released 2019-12-13
Expand Down
3 changes: 2 additions & 1 deletion packages/opentelemetry-plugin-dns/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
"precompile": "tsc --version",
"compile": "tsc -p .",
"fix": "gts fix"
"fix": "gts fix",
"prepare": "npm run compile"
},
"keywords": [
"opentelemetry",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*!
* Copyright 2019, 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.
*/

export enum AttributeNames {
// required by https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md#databases-client-calls
COMPONENT = 'component',
DB_TYPE = 'db.type',
DB_INSTANCE = 'db.instance',
DB_STATEMENT = 'db.statement',
PEER_ADDRESS = 'peer.address',
PEER_HOSTNAME = 'peer.host',

// optional
DB_USER = 'db.user',
PEER_PORT = 'peer.port',
PEER_IPV4 = 'peer.ipv4',
PEER_IPV6 = 'peer.ipv6',
PEER_SERVICE = 'peer.service',

// PG-POOL specific -- not specified by spec
IDLE_TIMEOUT_MILLIS = 'idle.timeout.millis',
MAX_CLIENT = 'max',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*!
* Copyright 2019, 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.
*/

export * from './pg-pool';
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,120 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { BasePlugin } from '@opentelemetry/core';
import { CanonicalCode, SpanKind } from '@opentelemetry/types';
import { AttributeNames } from './enums';
import * as shimmer from 'shimmer';
import * as pgPoolTypes from 'pg-pool';
import {
PostgresPoolPluginOptions,
PgPoolCallback,
PgPoolExtended,
} from './types';
import * as utils from './utils';

export class PostgresPoolPlugin extends BasePlugin<typeof pgPoolTypes> {
protected _config: PostgresPoolPluginOptions;

static readonly COMPONENT = 'pg-pool';
static readonly DB_TYPE = 'sql';

readonly supportedVersions = ['2.*'];

constructor(readonly moduleName: string) {
super();
this._config = {};
}

protected patch(): typeof pgPoolTypes {
shimmer.wrap(
this._moduleExports.prototype,
'connect',
this._getPoolConnectPatch() as never
);

return this._moduleExports;
}

protected unpatch(): void {
shimmer.unwrap(this._moduleExports.prototype, 'connect');
}

private _getPoolConnectPatch() {
const plugin = this;
return (originalConnect: typeof pgPoolTypes.prototype.connect) => {
plugin._logger.debug(
`Patching ${PostgresPoolPlugin.COMPONENT}.prototype.connect`
);
return function connect(this: PgPoolExtended, callback?: PgPoolCallback) {
const jdbcString = utils.getJDBCString(this.options);
// setup span
const span = plugin._tracer.startSpan(
`${PostgresPoolPlugin.COMPONENT}.connect`,
{
kind: SpanKind.CLIENT,
parent: plugin._tracer.getCurrentSpan() || undefined,
attributes: {
[AttributeNames.COMPONENT]: PostgresPoolPlugin.COMPONENT, // required
[AttributeNames.DB_TYPE]: PostgresPoolPlugin.DB_TYPE, // required
[AttributeNames.DB_INSTANCE]: this.options.database, // required
[AttributeNames.PEER_HOSTNAME]: this.options.host, // required
[AttributeNames.PEER_ADDRESS]: jdbcString, // required
[AttributeNames.PEER_PORT]: this.options.port,
[AttributeNames.DB_USER]: this.options.user,
[AttributeNames.IDLE_TIMEOUT_MILLIS]: this.options
.idleTimeoutMillis,
[AttributeNames.MAX_CLIENT]: this.options.maxClient,
},
}
);

if (callback) {
const parentSpan = plugin._tracer.getCurrentSpan();
callback = utils.patchCallback(span, callback) as PgPoolCallback;
// If a parent span exists, bind the callback
if (parentSpan) {
callback = plugin._tracer.bind(callback);
}
}

const connectResult: unknown = originalConnect.call(
this,
callback as never
);

// No callback was provided, return a promise instead
if (connectResult instanceof Promise) {
const connectResultPromise = connectResult as Promise<unknown>;
return plugin._tracer.bind(
connectResultPromise
.then((result: any) => {
// Resturn a pass-along promise which ends the span and then goes to user's orig resolvers
return new Promise((resolve, _) => {
span.setStatus({ code: CanonicalCode.OK });
span.end();
resolve(result);
});
})
.catch((error: Error) => {
return new Promise((_, reject) => {
span.setStatus({
code: CanonicalCode.UNKNOWN,
message: error.message,
});
span.end();
reject(error);
});
})
);
}

// Else a callback was provided, so just return the result
return connectResult;
};
};
}
}

export const plugin = new PostgresPoolPlugin(PostgresPoolPlugin.COMPONENT);
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*!
* Copyright 2019, 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 pgTypes from 'pg';
import * as pgPoolTypes from 'pg-pool';

export interface PostgresPoolPluginOptions {}

export type PgPoolCallback = (
err: Error,
client: any,
done: (release?: any) => void
) => void;

export interface PgPoolOptionsParams {
database: string;
host: string;
port: number;
user: string;
idleTimeoutMillis: number; // the minimum amount of time that an object may sit idle in the pool before it is eligible for eviction due to idle time
maxClient: number; // maximum size of the pool
}

export interface PgPoolExtended extends pgPoolTypes<pgTypes.Client> {
options: PgPoolOptionsParams;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*!
* Copyright 2019, 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 { Span, CanonicalCode } from '@opentelemetry/types';
import { PgPoolOptionsParams, PgPoolCallback, PgPoolExtended } from './types';

export function getJDBCString(params: PgPoolOptionsParams) {
const host = params.host || 'localhost'; // postgres defaults to localhost
const port = params.port || 5432; // postgres defaults to port 5432
const database = params.database || '';
return `jdbc:postgresql://${host}:${port}/${database}`;
}

export function patchCallback(span: Span, cb: PgPoolCallback): PgPoolCallback {
return function patchedCallback(
this: PgPoolExtended,
err: Error,
res: object,
done: any
) {
if (err) {
span.setStatus({
code: CanonicalCode.UNKNOWN,
message: err.message,
});
} else if (res) {
span.setStatus({ code: CanonicalCode.OK });
}
span.end();
cb.call(this, err, res, done);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*!
* Copyright 2019, 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 {
SpanKind,
Attributes,
Event,
Span,
TimedEvent,
} from '@opentelemetry/types';
import * as assert from 'assert';
import { ReadableSpan } from '@opentelemetry/tracing';
import {
hrTimeToMilliseconds,
hrTimeToMicroseconds,
} from '@opentelemetry/core';

export const assertSpan = (
span: ReadableSpan,
kind: SpanKind,
attributes: Attributes,
events: Event[]
) => {
assert.strictEqual(span.spanContext.traceId.length, 32);
assert.strictEqual(span.spanContext.spanId.length, 16);
assert.strictEqual(span.kind, kind);

// check all the AttributeNames fields
Object.keys(span.attributes).forEach(key => {
assert.deepStrictEqual(span.attributes[key], attributes[key]);
});

assert.ok(span.endTime);
assert.strictEqual(span.links.length, 0);

assert.ok(
hrTimeToMicroseconds(span.startTime) < hrTimeToMicroseconds(span.endTime)
);
assert.ok(hrTimeToMilliseconds(span.endTime) > 0);

// events
assert.strictEqual(
span.events.length,
events.length,
'Should contain same number of events'
);
span.events.forEach((_: TimedEvent, index: number) => {
assert.deepStrictEqual(span.events[index], events[index]);
});
};

// Check if sourceSpan was propagated to targetSpan
export const assertPropagation = (
childSpan: ReadableSpan,
parentSpan: Span
) => {
const targetSpanContext = childSpan.spanContext;
const sourceSpanContext = parentSpan.context();
assert.strictEqual(targetSpanContext.traceId, sourceSpanContext.traceId);
assert.strictEqual(childSpan.parentSpanId, sourceSpanContext.spanId);
assert.strictEqual(
targetSpanContext.traceFlags,
sourceSpanContext.traceFlags
);
assert.notStrictEqual(targetSpanContext.spanId, sourceSpanContext.spanId);
};
Loading

0 comments on commit de72e45

Please sign in to comment.