Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0149bcf
feat: initial implementation of restify instrumentation
rauno56 Apr 5, 2021
96f73e6
feat: create spans instead of altering parent spans
rauno56 Apr 6, 2021
4f920f5
feat: clean examples up
rauno56 Apr 6, 2021
92af233
feat: make exporter configurable like other examples
rauno56 Apr 7, 2021
3c37d7f
feat: implement disabling restify instrumentation
rauno56 Apr 8, 2021
32b8931
test: be more verbose in tests and check all spans
rauno56 Apr 8, 2021
ee64e4d
fix: remove the handlers from req, those events seem not to be fired
rauno56 Apr 8, 2021
b6c8318
refactor: export the instrumentation as default
rauno56 Apr 8, 2021
a46adb7
fix: thrown errors are generally not caught in restify
rauno56 Apr 8, 2021
5d67867
feat: enable the instrumentation for all versions after 4.0.0
rauno56 Apr 8, 2021
c4657df
docs: update docs and metadata in package.json and README.md files
rauno56 Apr 8, 2021
cbbdea0
style: fix linting errors
rauno56 Apr 9, 2021
c760527
style: make _moduleVersion optional
rauno56 Apr 12, 2021
4ca8036
feat: use more descriptive span name
rauno56 Apr 13, 2021
e9ab22e
feat: rename HTTP span
rauno56 Apr 13, 2021
0dc22aa
feat: remove creating additional span on the client side
rauno56 Apr 13, 2021
6c380a2
docs: remove TODO
rauno56 Apr 13, 2021
3b8072b
feat: update version
rauno56 Apr 13, 2021
fa55dab
test: test more verbose API
rauno56 Apr 13, 2021
ac8d41b
fix: update version
rauno56 Apr 14, 2021
520d54e
fix: update the example in README
rauno56 Apr 14, 2021
b5be54c
fix: reference @opentelemetry/instrumentation-http correctly
rauno56 Apr 14, 2021
14772ba
feat: remove lodash.once
rauno56 Apr 15, 2021
c8914d1
style: rename constants to be more verbose
rauno56 Apr 15, 2021
88f6052
feat: handle cases with promises
rauno56 Apr 15, 2021
df87efd
feat: support async and promise-returning handlers
rauno56 Apr 15, 2021
4b58434
style: use RestifyInstrumentation as the import name
rauno56 Apr 15, 2021
c4dee5c
feat: let the example be broken until the publish for consistency
rauno56 Apr 15, 2021
f8bcb5c
fix: remove lodash.once types
rauno56 Apr 15, 2021
fd906b1
Merge branch 'main' into feat/restify-instrumentation
vmarchaud Apr 15, 2021
f4f4b08
Merge branch 'main' into feat/restify-instrumentation
rauno56 Apr 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: support async and promise-returning handlers
  • Loading branch information
rauno56 committed Apr 15, 2021
commit df87efd7d28fd650708c190e056af0587f1c3096
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
isWrapped,
} from '@opentelemetry/instrumentation';
import { HttpAttribute } from '@opentelemetry/semantic-conventions';
import { types as checkType } from 'util';
import { isPromise, isAsyncFunction } from './utils';

const { diag } = api;

Expand Down Expand Up @@ -56,7 +56,9 @@ export class RestifyInstrumentation extends InstrumentationBase<
'restify/lib/server.js',
constants.SUPPORTED_VERSIONS,
(moduleExports, moduleVersion) => {
diag.debug(`Applying patch for ${constants.MODULE_NAME}@${moduleVersion}`);
diag.debug(
`Applying patch for ${constants.MODULE_NAME}@${moduleVersion}`
);
this._isDisabled = false;
const Server: any = moduleExports;
for (const name of constants.RESTIFY_METHODS) {
Expand All @@ -82,7 +84,9 @@ export class RestifyInstrumentation extends InstrumentationBase<
return moduleExports;
},
(moduleExports, moduleVersion) => {
diag.debug(`Removing patch for ${constants.MODULE_NAME}@${moduleVersion}`);
diag.debug(
`Removing patch for ${constants.MODULE_NAME}@${moduleVersion}`
);
this._isDisabled = true;
if (moduleExports) {
const Server: any = moduleExports;
Expand Down Expand Up @@ -200,24 +204,26 @@ export class RestifyInstrumentation extends InstrumentationBase<

const wrapPromise = (promise: Promise<unknown>) => {
return promise
.catch((err) => {
span.recordException(err);
throw err;
.then(value => {
span.end();
return value;
})
.finally(() => {
.catch(err => {
span.recordException(err);
span.end();
throw err;
});
}
};

return api.context.with(
api.setSpan(api.context.active(), span),
(req: types.Request, res: restify.Response, next: restify.Next) => {
if (checkType.isAsyncFunction(handler)) {
if (isAsyncFunction(handler)) {
return wrapPromise(handler(req, res, next));
}
try {
const result = handler(req, res, next);
if (checkType.isPromise(result)) {
if (isPromise(result)) {
return wrapPromise(result);
}
span.end();
Expand Down
38 changes: 38 additions & 0 deletions plugins/node/opentelemetry-instrumentation-restify/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.
*/

// util.types.isPromise is supported from 10.0.0
export const isPromise = (value: any): value is Promise<unknown> => {
if (
typeof value.then === 'function' &&
typeof value.catch === 'function' &&
value.toString() === '[object Promise]'
) {
return true;
}
return false;
};

// util.types.isAsyncFunction is supported from 10.0.0
export const isAsyncFunction = (value: unknown) => {
if (
typeof value === 'function' &&
value.constructor?.name === 'AsyncFunction'
) {
return true;
}
return false;
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ const httpRequest = {
},
};
const noop = (value: unknown) => {};
const defer = (): { promise: Promise<unknown>, resolve: Function, reject: Function } => {
const defer = (): {
promise: Promise<unknown>;
resolve: Function;
reject: Function;
} => {
let resolve = noop;
let reject = noop;
const promise = new Promise((res, rej) => {
Expand Down Expand Up @@ -301,14 +305,8 @@ describe('Restify Instrumentation', () => {
});

it('should work with async handlers', async () => {
const {
promise: work,
resolve: resolveWork,
} = defer();
const {
promise: started,
resolve: resolveStarted,
} = defer();
const { promise: work, resolve: resolveWork } = defer();
const { promise: started, resolve: resolveStarted } = defer();
// status to assert the correctness of the test
let status = 'uninit';
const asyncHandler: restify.RequestHandler = async (req, res, next) => {
Expand All @@ -319,20 +317,17 @@ describe('Restify Instrumentation', () => {
return getHandler(req, res, next);
};
const testLocalServer = await createServer((server: restify.Server) => {
server.get(
'/route/:param',
asyncHandler
);
server.get('/route/:param', asyncHandler);
});
const testLocalPort = testLocalServer.address().port;

try {
const requestPromise = httpRequest.get(
`http://localhost:${testLocalPort}/route/hello`
).then((res) => {
// assert request results
assert.strictEqual(res, '{"route":"hello"}');
});
const requestPromise = httpRequest
.get(`http://localhost:${testLocalPort}/route/hello`)
.then(res => {
// assert request results
assert.strictEqual(res, '{"route":"hello"}');
});

// assert pre request state
assert.strictEqual(status, 'uninit');
Expand Down Expand Up @@ -367,39 +362,34 @@ describe('Restify Instrumentation', () => {
});

it('should work with promise-returning handlers', async () => {
const {
promise: work,
resolve: resolveWork,
} = defer();
const {
promise: started,
resolve: resolveStarted,
} = defer();
const { promise: work, resolve: resolveWork } = defer();
const { promise: started, resolve: resolveStarted } = defer();
// status to assert the correctness of the test
let status = 'uninit';
const promiseReturningHandler: restify.RequestHandler = (req, res, next) => {
const promiseReturningHandler: restify.RequestHandler = (
req,
res,
next
) => {
status = 'started';
resolveStarted();
return work.then(() => {
status = 'done';
return getHandler(req, res, next)
return getHandler(req, res, next);
});
};
const testLocalServer = await createServer((server: restify.Server) => {
server.get(
'/route/:param',
promiseReturningHandler
);
server.get('/route/:param', promiseReturningHandler);
});
const testLocalPort = testLocalServer.address().port;

try {
const requestPromise = httpRequest.get(
`http://localhost:${testLocalPort}/route/hello`
).then((res) => {
// assert request results
assert.strictEqual(res, '{"route":"hello"}');
});
const requestPromise = httpRequest
.get(`http://localhost:${testLocalPort}/route/hello`)
.then(res => {
// assert request results
assert.strictEqual(res, '{"route":"hello"}');
});

// assert pre request state
assert.strictEqual(status, 'uninit');
Expand All @@ -425,7 +415,10 @@ describe('Restify Instrumentation', () => {
span.attributes['restify.type'],
'request_handler'
);
assert.strictEqual(span.attributes['restify.name'], 'promiseReturningHandler');
assert.strictEqual(
span.attributes['restify.name'],
'promiseReturningHandler'
);
assert.strictEqual(span.attributes['restify.version'], 'n/a');
}
} finally {
Expand Down