New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hapi 17 #146
Hapi 17 #146
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,113 +14,148 @@ module.exports = function (hapi, agent, version) { | |
|
||
debug('shimming hapi.Server.prototype.initialize') | ||
|
||
shimmer.wrap(hapi.Server.prototype, 'initialize', function (orig) { | ||
return function () { | ||
// Hooks that are always allowed | ||
if (typeof this.on === 'function') { | ||
this.on('request-error', function (req, err) { | ||
agent.captureError(err, {request: req.raw && req.raw.req}) | ||
}) | ||
if (semver.satisfies(version, '>=17')) { | ||
shimmer.massWrap(hapi, ['Server', 'server'], function (orig) { | ||
return function (options) { | ||
var res = orig.apply(this, arguments) | ||
patchServer(res) | ||
return res | ||
} | ||
}) | ||
} else { | ||
shimmer.wrap(hapi.Server.prototype, 'initialize', function (orig) { | ||
return function () { | ||
patchServer(this) | ||
return orig.apply(this, arguments) | ||
} | ||
}) | ||
} | ||
|
||
this.on('log', function (event, tags) { | ||
if (!event || !tags.error) return | ||
function patchServer (server) { | ||
// Hooks that are always allowed | ||
if (typeof server.on === 'function') { | ||
attachEvents(server) | ||
} else if (typeof server.events.on === 'function') { | ||
attachEvents(server.events) | ||
} else { | ||
debug('unable to enable hapi error tracking') | ||
} | ||
|
||
var payload = {custom: event} // TODO: Find better location to put this than custom | ||
// Prior to hapi 17, when the server has no connections we can't make | ||
// connection lifecycle hooks (in hapi 17+ the server always has | ||
// connections, though the `server.connections` property doesn't exists, | ||
// so this if-statement wont fire) | ||
var conns = server.connections | ||
if (conns && conns.length === 0) { | ||
debug('unable to enable hapi instrumentation on connectionless server') | ||
return | ||
} | ||
|
||
var err = event.data | ||
if (!(err instanceof Error) && typeof err !== 'string') { | ||
err = 'hapi server emitted a log event tagged error' | ||
} | ||
// Hooks that are only allowed when the hapi server has connections | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a 100% correct statement anymore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The check is still fine afaik There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @watson Any thoughts on wording here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AdriVanHoudt Do you mean related to the removal of multi-connections support in hapi 17? What would be a more appropriate comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I can tell hapi 17 will have it always. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AdriVanHoudt how about just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated. |
||
// (with hapi 17+ this is always the case) | ||
if (typeof server.ext === 'function') { | ||
server.ext('onPreAuth', onPreAuth) | ||
server.ext('onPreResponse', onPreResponse) | ||
} else { | ||
debug('unable to enable automatic hapi transaction naming') | ||
} | ||
} | ||
|
||
agent.captureError(err, payload) | ||
function attachEvents (emitter) { | ||
if (semver.satisfies(version, '<17')) { | ||
emitter.on('request-error', function (request, error) { | ||
agent.captureError(error, { | ||
request: request.raw && request.raw.req | ||
}) | ||
}) | ||
} | ||
|
||
this.on('request', function (req, event, tags) { | ||
if (!event || !tags.error) return | ||
|
||
var payload = { | ||
custom: event, // TODO: Find better location to put this than custom | ||
request: req.raw && req.raw.req | ||
} | ||
emitter.on('log', function (event, tags) { | ||
captureError('log', null, event, tags) | ||
}) | ||
|
||
var err = event.data | ||
if (!(err instanceof Error) && typeof err !== 'string') { | ||
err = 'hapi server emitted a request event tagged error' | ||
} | ||
emitter.on('request', function (req, event, tags) { | ||
captureError('request', req, event, tags) | ||
}) | ||
} | ||
|
||
agent.captureError(err, payload) | ||
}) | ||
} else { | ||
debug('unable to enable hapi error tracking') | ||
} | ||
function captureError (type, req, event, tags) { | ||
if (!event || !tags.error) return | ||
|
||
// TODO: Find better location to put this than custom | ||
var payload = { | ||
custom: { | ||
tags: event.tags, | ||
internals: event.internals, | ||
// Moved from data to error in hapi 17 | ||
data: event.data || event.error | ||
}, | ||
request: req && req.raw && req.raw.req | ||
} | ||
|
||
// When the hapi server has no connections we don't make connection | ||
// lifecycle hooks | ||
if (this.connections.length === 0) { | ||
debug('unable to enable hapi instrumentation on connectionless server') | ||
return orig.apply(this, arguments) | ||
} | ||
var err = payload.custom.data | ||
if (!(err instanceof Error) && typeof err !== 'string') { | ||
err = 'hapi server emitted a ' + type + ' event tagged error' | ||
} | ||
|
||
// Hooks that are only allowed when the hapi server has connections | ||
if (typeof this.ext === 'function') { | ||
this.ext('onPreAuth', function (request, reply) { | ||
debug('received hapi onPreAuth event') | ||
|
||
// Record the fact that the preAuth extension have been called. This | ||
// info is useful later to know if this is a CORS preflight request | ||
// that is automatically handled by hapi (as those will not trigger | ||
// the onPreAuth extention) | ||
request._elastic_apm_onPreAuth = true | ||
|
||
if (request.route) { | ||
// fingerprint was introduced in hapi 11 and is a little more | ||
// stable in case the param names change | ||
// - path example: /foo/{bar*2} | ||
// - fingerprint example: /foo/?/? | ||
var fingerprint = request.route.fingerprint || request.route.path | ||
|
||
if (fingerprint) { | ||
var name = (request.raw && request.raw.req && request.raw.req.method) || | ||
(request.route.method && request.route.method.toUpperCase()) | ||
|
||
if (typeof name === 'string') { | ||
name = name + ' ' + fingerprint | ||
} else { | ||
name = fingerprint | ||
} | ||
|
||
agent._instrumentation.setDefaultTransactionName(name) | ||
} | ||
} | ||
|
||
return reply.continue() | ||
}) | ||
agent.captureError(err, payload) | ||
} | ||
|
||
this.ext('onPreResponse', function (request, reply) { | ||
debug('received hapi onPreResponse event') | ||
|
||
// Detection of CORS preflight requests: | ||
// There is no easy way in hapi to get the matched route for a | ||
// CORS preflight request that matches any of the autogenerated | ||
// routes created by hapi when `cors: true`. The best solution is to | ||
// detect the request "fingerprint" using the magic if-sentence below | ||
// and group all those requests into on type of transaction | ||
if (!request._elastic_apm_onPreAuth && | ||
request.route && request.route.path === '/{p*}' && | ||
request.raw && request.raw.req && request.raw.req.method === 'OPTIONS' && | ||
request.raw.req.headers['access-control-request-method']) { | ||
agent._instrumentation.setDefaultTransactionName('CORS preflight') | ||
} | ||
|
||
return reply.continue() | ||
}) | ||
} else { | ||
debug('unable to enable automatic hapi transaction naming') | ||
function onPreAuth (request, reply) { | ||
debug('received hapi onPreAuth event') | ||
|
||
// Record the fact that the preAuth extension have been called. This | ||
// info is useful later to know if this is a CORS preflight request | ||
// that is automatically handled by hapi (as those will not trigger | ||
// the onPreAuth extention) | ||
request._elastic_apm_onPreAuth = true | ||
|
||
if (request.route) { | ||
// fingerprint was introduced in hapi 11 and is a little more | ||
// stable in case the param names change | ||
// - path example: /foo/{bar*2} | ||
// - fingerprint example: /foo/?/? | ||
var fingerprint = request.route.fingerprint || request.route.path | ||
|
||
if (fingerprint) { | ||
var name = (request.raw && request.raw.req && request.raw.req.method) || | ||
(request.route.method && request.route.method.toUpperCase()) | ||
|
||
if (typeof name === 'string') { | ||
name = name + ' ' + fingerprint | ||
} else { | ||
name = fingerprint | ||
} | ||
|
||
agent._instrumentation.setDefaultTransactionName(name) | ||
} | ||
} | ||
|
||
return semver.satisfies(version, '>=17') | ||
? reply.continue | ||
: reply.continue() | ||
} | ||
|
||
return orig.apply(this, arguments) | ||
function onPreResponse (request, reply) { | ||
debug('received hapi onPreResponse event') | ||
|
||
// Detection of CORS preflight requests: | ||
// There is no easy way in hapi to get the matched route for a | ||
// CORS preflight request that matches any of the autogenerated | ||
// routes created by hapi when `cors: true`. The best solution is to | ||
// detect the request "fingerprint" using the magic if-sentence below | ||
// and group all those requests into on type of transaction | ||
if (!request._elastic_apm_onPreAuth && | ||
request.route && request.route.path === '/{p*}' && | ||
request.raw && request.raw.req && request.raw.req.method === 'OPTIONS' && | ||
request.raw.req.headers['access-control-request-method']) { | ||
agent._instrumentation.setDefaultTransactionName('CORS preflight') | ||
} | ||
}) | ||
|
||
return semver.satisfies(version, '>=17') | ||
? reply.continue | ||
: reply.continue() | ||
} | ||
|
||
return hapi | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs an hapi 17 check since in hapi 17 connections are not a thing any more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existence check of
server.connections
covers that.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess what @AdriVanHoudt means is that outputting the debug message "unable to enable hapi instrumentation on connectionless server" isn't really that nice if running hapi 17?
So it should be more like
if (!hapi17plus && conns && cons.length === 0) {
wherehapi17plus
is a boolean that's true only of we're running hapi version 17 or higher.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd never reach that debug message on hapi 17, because
server.connections
would have to exist.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah my bad 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it was more for correctness ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I see it can be a little confusing to read because of the above comment. How about just updating the comment to something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated.