Skip to content

Commit

Permalink
http: add perf_hooks detail for http request and client
Browse files Browse the repository at this point in the history
  • Loading branch information
theanarkh committed May 7, 2022
1 parent 3bc23f5 commit be94dcd
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 40 deletions.
16 changes: 16 additions & 0 deletions doc/api/perf_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,22 @@ 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`.

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

When `performanceEntry.type` is equal to `'http2'`, the
Expand Down
32 changes: 23 additions & 9 deletions lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,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 @@ -76,7 +76,8 @@ const {
} = require('internal/dtrace');

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

const kClientRequestStatistics = Symbol('ClientRequestStatistics');
Expand Down Expand Up @@ -343,12 +344,17 @@ ObjectSetPrototypeOf(ClientRequest, OutgoingMessage);
ClientRequest.prototype._finish = function _finish() {
DTRACE_HTTP_CLIENT_REQUEST(this, this.socket);
FunctionPrototypeCall(OutgoingMessage.prototype._finish, this);
if (hasObserver('http')) {
this[kClientRequestStatistics] = {
startTime: process.hrtime(),
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() : {},
},
},
});
};

ClientRequest.prototype._implicitHeader = function _implicitHeader() {
Expand Down Expand Up @@ -616,7 +622,15 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
}

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

Expand Down
31 changes: 22 additions & 9 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ const { OutgoingMessage } = require('_http_outgoing');
const {
kOutHeaders,
kNeedDrain,
emitStatistics
} = require('internal/http');
const {
defaultTriggerAsyncIdScope,
Expand Down Expand Up @@ -93,7 +92,8 @@ const kServerResponse = Symbol('ServerResponse');
const kServerResponseStatistics = Symbol('ServerResponseStatistics');

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

const STATUS_CODES = {
Expand Down Expand Up @@ -192,19 +192,32 @@ function ServerResponse(req) {
this.shouldKeepAlive = false;
}

if (hasObserver('http')) {
this[kServerResponseStatistics] = {
startTime: process.hrtime(),
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]);
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
22 changes: 0 additions & 22 deletions lib/internal/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ const {

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

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

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

let utcCache;

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

function emitStatistics(statistics) {
if (!hasObserver('http') || statistics == null) return;
const startTime = statistics.startTime;
const diff = process.hrtime(startTime);
const entry = new InternalPerformanceEntry(
statistics.type,
'http',
startTime[0] * 1000 + startTime[1] / 1e6,
diff[0] * 1000 + diff[1] / 1e6,
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

0 comments on commit be94dcd

Please sign in to comment.