Skip to content

Commit

Permalink
Enable instrumentation packages
Browse files Browse the repository at this point in the history
Enabled default set of instrumentation packages and added ability to
provide custom ones as well.
  • Loading branch information
owais committed Mar 3, 2021
1 parent 49cd3a3 commit 6905874
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 83 deletions.
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,54 @@ startTracing();
// rest of your application entry point script
```

## Using additional instrumentation plugins

If you setup tracing manually by calling the `startTracing()` method, you can use custom or 3rd party instrumentations as long as they implement the [OpenTelemetry JS Instrumentation interface](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation). Custom instrumentations can be enabled by passing them to the `startTracing()` method as follows:

```js
const { startTracing } = require('splunk-otel-js');

startTracing({
instrumentations: [
new MyCustomInstrumentation(),
new AnotherInstrumentation(),
]
});
```

You can also add the default set of instrumentation to the list as follows:


```js
const { startTracing } = require('splunk-otel-js');
const { getInstrumentations } = require('splunk-otel-js/instrumentations');

startTracing({
instrumentations: [
...getInstrumentations(),
new MyCustomInstrumentation(),
new AnotherInstrumentation(),
]
});
```
## Default Instrumentation Packages

By default the following instrumentations will automatically be enabled if they are installed. In order to use
any of these instrumentations, you'll need to install them with npm and then run your app with `-r @splunk/otel/lib/instrument`
flag as described above.

```
@opentelemetry/instrumentation-http
@opentelemetry/instrumentation-dns
@opentelemetry/instrumentation-graphql
@opentelemetry/instrumentation-grpc
@opentelemetry/instrumentation-koa
@opentelemetry/hapi-instrumentation
```

You can find more instrumentation packages over at the [OpenTelemetry Registry](https://opentelemetry.io/registry/?language=js) and enable them manually
as described above.

## Troubleshooting

TODO:
Expand Down
20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
"gts": "2.0.2",
"mocha": "7.2.0",
"nyc": "15.1.0",
"prettier": ">=1.13.0",
"rewire": "^5.0.0",
"rimraf": "3.0.2",
"shimmer": "1.2.1",
"sinon": "^9.2.4",
Expand All @@ -65,15 +67,15 @@
"typescript": "3.9.7"
},
"dependencies": {
"@opentelemetry/api": "^0.17.0",
"@opentelemetry/context-async-hooks": "^0.17.0",
"@opentelemetry/core": "^0.17.0",
"@opentelemetry/exporter-jaeger": "^0.17.0",
"@opentelemetry/instrumentation": "^0.17.0",
"@opentelemetry/node": "^0.17.0",
"@opentelemetry/propagator-b3": "^0.17.0",
"@opentelemetry/resources": "^0.17.0",
"@opentelemetry/tracing": "^0.17.0",
"@opentelemetry/api": "^0.18.0",
"@opentelemetry/context-async-hooks": "^0.18.0",
"@opentelemetry/core": "^0.18.0",
"@opentelemetry/exporter-jaeger": "^0.18.0",
"@opentelemetry/instrumentation": "^0.18.0",
"@opentelemetry/node": "^0.18.0",
"@opentelemetry/propagator-b3": "^0.18.0",
"@opentelemetry/resources": "^0.18.0",
"@opentelemetry/tracing": "^0.18.0",
"jaeger-client": "^3.15.0"
}
}
43 changes: 43 additions & 0 deletions src/instrumentations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Splunk Inc.
*
* 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
*
* http://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 { InstrumentationOption } from '@opentelemetry/instrumentation';

import { load } from './loader';

const supportedInstrumentations: [string, string][] = [
['@opentelemetry/instrumentation-http', 'HttpInstrumentation'],
['@opentelemetry/instrumentation-dns', 'DnsInstrumentation'],
['@opentelemetry/instrumentation-graphql', 'GraphQLInstrumentation'],
['@opentelemetry/instrumentation-grpc', 'GrpcInstrumentation'],
['@opentelemetry/instrumentation-koa', 'KoaInstrumentation'],
['@opentelemetry/hapi-instrumentation', 'HapiInstrumentation'],
];

export function getInstrumentations(): InstrumentationOption[] {
const result = [];

// Defensively load all supported instrumentations
for (const i in supportedInstrumentations) {
const [module, name] = supportedInstrumentations[i];
const Instrumentation = load(module, name);
if (Instrumentation != null) {
result.push(new Instrumentation());
}
}

return result;
}
34 changes: 34 additions & 0 deletions src/instrumentations/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2021 Splunk Inc.
*
* 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
*
* http://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 { diag } from '@opentelemetry/api';
import { Instrumentation } from '@opentelemetry/instrumentation';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type InstrumentationConstructor = { new (config?: any): Instrumentation };

export function load(
module: string,
instrumentation: string
): InstrumentationConstructor | null {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require(module)[instrumentation];
} catch (e) {
diag.info('cannot find instrumentation package: ' + instrumentation);
}
return null;
}
9 changes: 9 additions & 0 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ import {
BatchSpanProcessor,
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
import { InstrumentationOption } from '@opentelemetry/instrumentation';
import { env } from 'process';

import { getInstrumentations } from './instrumentations';

const defaultEndpoint = 'http://localhost:9080/v1/trace';
const defaultServiceName = 'unnamed-node-service';
const defaultMaxAttrLength = 1200;
Expand All @@ -29,6 +32,7 @@ export interface Options {
serviceName: string;
accessToken?: string;
maxAttrLength: number;
instrumentations: InstrumentationOption[];
spanProcessor: typeof SimpleSpanProcessor | typeof BatchSpanProcessor;
}

Expand All @@ -47,12 +51,17 @@ export function _setDefaultOptions(options: Partial<Options> = {}): Options {
options.endpoint =
options.endpoint || env.SPLK_TRACE_EXPORTER_URL || defaultEndpoint;

if (options.instrumentations === undefined) {
options.instrumentations = getInstrumentations();
}

options.spanProcessor = options.spanProcessor || BatchSpanProcessor;
return {
endpoint: options.endpoint,
serviceName: options.serviceName,
accessToken: options.accessToken,
maxAttrLength: options.maxAttrLength,
instrumentations: options.instrumentations,
spanProcessor: options.spanProcessor,
};
}
5 changes: 4 additions & 1 deletion src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export function startTracing(opts: Partial<Options> = {}): void {
resource: new EnvResourceDetector().detect(),
});

registerInstrumentations({ tracerProvider: provider });
registerInstrumentations({
tracerProvider: provider,
instrumentations: options.instrumentations,
});

provider.register({ contextManager: new AsyncHooksContextManager() });

Expand Down
93 changes: 93 additions & 0 deletions test/instrumentations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2021 Splunk Inc.
*
* 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
*
* http://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 sinon from 'sinon';
import * as assert from 'assert';
import * as rewire from 'rewire';

import * as instrumentations from '../src/instrumentations';
import * as loader from '../src/instrumentations/loader';

describe('instrumentations', () => {
it('does not load if packages are not installed', () => {
const inst = instrumentations.getInstrumentations();
assert.strictEqual(inst.length, 0);
});

it('load instrumentations if they are not installed', () => {
const loadStub = sinon.stub(loader, 'load');
const inst = instrumentations.getInstrumentations();
sinon.assert.callCount(loadStub, 6);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/instrumentation-http',
'HttpInstrumentation'
);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/instrumentation-dns',
'DnsInstrumentation'
);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/instrumentation-graphql',
'GraphQLInstrumentation'
);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/instrumentation-grpc',
'GrpcInstrumentation'
);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/instrumentation-koa',
'KoaInstrumentation'
);
sinon.assert.calledWith(
loader.load,
'@opentelemetry/hapi-instrumentation',
'HapiInstrumentation'
);

loadStub.reset();
loadStub.restore();
});

it('loader silently fails when package is not installed', () => {
const loader = require('../src/instrumentations/loader');
const result = loader.load(
'@opentelemetry/instrumentation-http',
'HttpInstrumentation'
);
assert.strictEqual(result, null);
});

it('loader imports and returns object when package is available', () => {
const HttpInstrumentation = function () {};
const loader = rewire('../src/instrumentations/loader');
const revert = loader.__set__('require', module => {
return { HttpInstrumentation };
});

const got = loader.load(
'@opentelemetry/instrumentation-http',
'HttpInstrumentation'
);
assert.strictEqual(got, HttpInstrumentation);

revert();
});
});
Loading

0 comments on commit 6905874

Please sign in to comment.