Skip to content
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

http: add perf_hooks detail for http request and client #43361

Merged
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
18 changes: 18 additions & 0 deletions doc/api/perf_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,24 @@ property will be an {Object} with two properties:
* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY`
* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE`

### HTTP ('http') Details

When `performanceEntry.type` is equal to `'http'`, the `performanceEntry.detail`
property will be an {Object} containing additional information.

If `performanceEntry.name` is equal to `HttpClient`, the `detail`
will contain the following properties: `req`, `res`. And the `req` property
will be an {Object} containing `method`, `url`, `headers`, the `res` property
will be an {Object} containing `statusCode`, `statusMessage`, `headers`.

If `performanceEntry.name` is equal to `HttpRequest`, the `detail`
will contain the following properties: `req`, `res`. And the `req` property
will be an {Object} containing `method`, `url`, `headers`, the `res` property
will be an {Object} containing `statusCode`, `statusMessage`, `headers`.
ShogunPanda marked this conversation as resolved.
Show resolved Hide resolved

This could add additional memory overhead and should only be used for
diagnostic purposes, not left turned on in production by default.

### HTTP/2 ('http2') Details

When `performanceEntry.type` is equal to `'http2'`, the
Expand Down
33 changes: 25 additions & 8 deletions lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const Agent = require('_http_agent');
const { Buffer } = require('buffer');
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
const { kOutHeaders, kNeedDrain, emitStatistics } = require('internal/http');
const { kOutHeaders, kNeedDrain } = require('internal/http');
const { connResetException, codes } = require('internal/errors');
const {
ERR_HTTP_HEADERS_SENT,
Expand All @@ -84,10 +84,10 @@ const {

const {
hasObserver,
startPerf,
stopPerf,
} = require('internal/perf/observe');

const { now } = require('internal/perf/utils');

const kClientRequestStatistics = Symbol('ClientRequestStatistics');

const { addAbortSignal, finished } = require('stream');
Expand Down Expand Up @@ -355,10 +355,17 @@ ClientRequest.prototype._finish = function _finish() {
DTRACE_HTTP_CLIENT_REQUEST(this, this.socket);
FunctionPrototypeCall(OutgoingMessage.prototype._finish, this);
if (hasObserver('http')) {
this[kClientRequestStatistics] = {
startTime: now(),
type: 'HttpClient',
};
startPerf(this, kClientRequestStatistics, {
type: 'http',
name: 'HttpClient',
detail: {
req: {
method: this.method,
url: `${this.protocol}//${this.host}${this.path}`,
headers: typeof this.getHeaders === 'function' ? this.getHeaders() : {},
},
},
});
}
};

Expand Down Expand Up @@ -627,7 +634,17 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
}

DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
emitStatistics(req[kClientRequestStatistics]);
if (req[kClientRequestStatistics] && hasObserver('http')) {
stopPerf(req, kClientRequestStatistics, {
detail: {
res: {
statusCode: res.statusCode,
statusMessage: res.statusMessage,
headers: res.headers,
},
},
});
}
req.res = res;
res.req = req;

Expand Down
32 changes: 24 additions & 8 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ const {
const {
kOutHeaders,
kNeedDrain,
emitStatistics
} = require('internal/http');
const {
defaultTriggerAsyncIdScope,
Expand Down Expand Up @@ -98,10 +97,10 @@ const kServerResponseStatistics = Symbol('ServerResponseStatistics');

const {
hasObserver,
startPerf,
stopPerf,
} = require('internal/perf/observe');

const { now } = require('internal/perf/utils');

const STATUS_CODES = {
100: 'Continue', // RFC 7231 6.2.1
101: 'Switching Protocols', // RFC 7231 6.2.2
Expand Down Expand Up @@ -199,18 +198,35 @@ function ServerResponse(req) {
}

if (hasObserver('http')) {
this[kServerResponseStatistics] = {
startTime: now(),
type: 'HttpRequest',
};
startPerf(this, kServerResponseStatistics, {
type: 'http',
name: 'HttpRequest',
detail: {
req: {
method: req.method,
url: req.url,
headers: req.headers,
},
},
});
}
}
ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype);
ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);

ServerResponse.prototype._finish = function _finish() {
DTRACE_HTTP_SERVER_RESPONSE(this.socket);
emitStatistics(this[kServerResponseStatistics]);
if (this[kServerResponseStatistics] && hasObserver('http')) {
stopPerf(this, kServerResponseStatistics, {
detail: {
res: {
statusCode: this.statusCode,
statusMessage: this.statusMessage,
headers: typeof this.getHeaders === 'function' ? this.getHeaders() : {},
},
},
});
}
OutgoingMessage.prototype._finish.call(this);
};

Expand Down
23 changes: 0 additions & 23 deletions lib/internal/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ const {

const { setUnrefTimeout } = require('internal/timers');

const { InternalPerformanceEntry } = require('internal/perf/performance_entry');

const {
enqueue,
hasObserver,
} = require('internal/perf/observe');

const { now } = require('internal/perf/utils');

let utcCache;

function utcDate() {
Expand All @@ -35,22 +26,8 @@ function resetCache() {
utcCache = undefined;
}

function emitStatistics(statistics) {
if (!hasObserver('http') || statistics == null) return;
const startTime = statistics.startTime;
const entry = new InternalPerformanceEntry(
statistics.type,
'http',
startTime,
now() - startTime,
undefined,
);
enqueue(entry);
}

module.exports = {
kOutHeaders: Symbol('kOutHeaders'),
kNeedDrain: Symbol('kNeedDrain'),
utcDate,
emitStatistics,
};
6 changes: 6 additions & 0 deletions test/parallel/test-http-perf_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ process.on('exit', () => {
} else if (entry.name === 'HttpRequest') {
numberOfHttpRequests++;
}
assert.strictEqual(typeof entry.detail.req.method, 'string');
assert.strictEqual(typeof entry.detail.req.url, 'string');
assert.strictEqual(typeof entry.detail.req.headers, 'object');
assert.strictEqual(typeof entry.detail.res.statusCode, 'number');
assert.strictEqual(typeof entry.detail.res.statusMessage, 'string');
assert.strictEqual(typeof entry.detail.res.headers, 'object');
});
assert.strictEqual(numberOfHttpClients, 2);
assert.strictEqual(numberOfHttpRequests, 2);
Expand Down