Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs/reference/esm.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Automatic instrumentation of ES modules is currently limited as described here.
| `@aws-sdk/client-s3` | >=3.15.0 <4 | | |
| `@aws-sdk/client-sns` | >=3.15.0 <4 | | |
| `@aws-sdk/client-sqs` | >=3.15.0 <4 | | |
| `@hapi/hapi` | >=20 | | |
| `cassandra-driver` | >=3.0.0 <5 | | |
| `express` | >=4.0.0 <6 | | |
| `fastify` | >=3.5.0 | | |
Expand Down
1 change: 1 addition & 0 deletions lib/instrumentation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const IITM_MODULES = {
// code. If a future aws-sdk v3 version switches to ESM imports internally,
// then this will be relevant.
// '@aws-sdk/smithy-client': { instrumentImportMod: false },
'@hapi/hapi': { instrumentImportMod: true },
'cassandra-driver': { instrumentImportMod: false },
express: { instrumentImportMod: false },
fastify: { instrumentImportMod: true },
Expand Down
10 changes: 9 additions & 1 deletion lib/instrumentation/modules/@hapi/hapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function simpleReprFromVal(val) {
return val;
}

module.exports = function (hapi, agent, { version, enabled }) {
module.exports = function (hapi, agent, { version, enabled, isImportMod }) {
if (!enabled) {
return hapi;
}
Expand All @@ -95,6 +95,14 @@ module.exports = function (hapi, agent, { version, enabled }) {
};
});

if (isImportMod) {
hapi.default = {
...hapi.default,
server: hapi.server,
Server: hapi.Server,
};
}

function patchServer(server) {
// Hooks that are always allowed
if (typeof server.on === 'function') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and other contributors where applicable.
* Licensed under the BSD 2-Clause License; you may not use this file except in
* compliance with the BSD 2-Clause License.
*/

// Usage:
// node --require=./start.js test/instrumentation/modules/fixtures/use-hapi-connectionless.js

const semver = require('semver');

const hapi = require('@hapi/hapi');

const server = hapi.server();

async function main() {
if (semver.satisfies(server.version, '<17')) {
await new Promise((resolve, reject) =>
server.initialize(function (err) {
if (err) {
reject(err);
return;
}

resolve();
}),
);
} else {
await server.initialize();
}

const customError = new Error('custom error');

server.log(['error'], customError);

const stringError = 'custom error';

server.log(['error'], stringError);

const objectError = {
error: 'I forgot to turn this into an actual Error',
};

server.log(['error'], objectError);

await server.stop();
}

main();
178 changes: 178 additions & 0 deletions test/instrumentation/modules/fixtures/use-hapi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright Elasticsearch B.V. and other contributors where applicable.
* Licensed under the BSD 2-Clause License; you may not use this file except in
* compliance with the BSD 2-Clause License.
*/

// Usage:
// node --require=./start.js test/instrumentation/modules/fixtures/use-hapi.js

const http = require('http');
const semver = require('semver');

const agent = require('../../../..');

const hapi = require('@hapi/hapi');

function handler(fn) {
if (semver.satisfies(server.version, '>=17')) return fn;

return function (request, reply) {
var p = new Promise(function (resolve, reject) {
resolve(fn(request));
});
p.then(reply, reply);
};
}

function startServer() {
if (semver.satisfies(server.version, '>=17')) return server.start();

return new Promise(function (resolve, reject) {
server.start(function (err) {
if (err) {
reject(err);
return;
}
resolve();
});
});
}

const server = hapi.server({ port: 3000 });
server.route({
method: 'POST',
path: '/hello/{name}',
handler: handler(function (request) {
return { hello: request.params.name };
}),
});
server.route({
method: 'GET',
path: '/error',
handler: handler(function (request) {
const customError = new Error('custom request error');

request.log(['elastic-apm', 'error'], customError);

const stringError = 'custom error';

request.log(['elastic-apm', 'error'], stringError);

const objectError = {
error: 'I forgot to turn this into an actual Error',
};

request.log(['elastic-apm', 'error'], objectError);

throw new Error('foo');
}),
});
server.route({
method: 'GET',
path: '/captureError',
handler: handler(function (request) {
agent.captureError(new Error());
return '';
}),
});

async function main() {
await startServer();

const customError = new Error('custom error');

server.log(['error'], customError);

const stringError = 'custom error';

server.log(['error'], stringError);

const objectError = {
error: 'I forgot to turn this into an actual Error',
};

server.log(['error'], objectError);

// Do a POST to test `captureBody`, wait for response, then exit.
const port = server.info.port;

await new Promise((resolve) => {
const data = JSON.stringify({ foo: 'bar' });
const req = http.request(
{
method: 'POST',
hostname: '127.0.0.1',
port,
path: '/hello/bob',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
},
},
function (res) {
console.log('client response:', res.statusCode, res.headers);
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
console.log('body:', body);
resolve();
});
},
);
req.write(data);
req.end();
});

await new Promise((resolve) => {
const req = http.request(
{
method: 'GET',
hostname: '127.0.0.1',
port,
path: '/error',
},
function (res) {
console.log('client response:', res.statusCode, res.headers);
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
console.log('body:', body);
resolve();
});
},
);
req.end();
});

await new Promise((resolve) => {
const req = http.request(
{
method: 'GET',
hostname: '127.0.0.1',
port,
path: '/captureError?foo=bar',
},
function (res) {
console.log('client response:', res.statusCode, res.headers);
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
console.log('body:', body);
resolve();
});
},
);
req.end();
});

server.stop();
}

main();
Loading
Loading