forked from rdehouss/fastify-response-time
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
137 lines (112 loc) · 3.71 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"use strict";
const fastifyPlugin = require("fastify-plugin");
const symbolRequestTime = Symbol("RequestTimer");
const symbolServerTiming = Symbol("ServerTiming");
/**
*
* @param {string} name
* @param {number|string} duration
* @param {string} description
* @return {string}
*/
const genTick = (name, duration, description) => {
let val = name;
// Parse duration. If could not be converted to float, does not add it
duration = parseFloat(duration);
if (!isNaN(duration)) {
val += `;dur=${duration}`;
}
// Parse the description. If empty, doest not add it. If string with space, double quote value
if ("string" === typeof description) {
val += `;desc=${description.includes(" ") ? `"${description}"` : description}`;
}
return val;
};
/**
* Decorators
*
* @param {fastify} instance
* @param {function} instance.decorateReply
* @param {Object} opts
* @param {function} next
*/
module.exports = fastifyPlugin((instance, opts, next) => {
// Check the options, and corrects with the default values if inadequate
if (isNaN(opts.digits) || 0 > opts.digits) {
opts.digits = 2;
}
opts.header = opts.header || "X-Response-Time";
// Hook to be triggered on request (start time)
instance.addHook("onRequest", (request, reply, next) => {
// Store the start timer in nanoseconds resolution
// istanbul ignore next
request[symbolRequestTime] = process.hrtime();
reply[symbolServerTiming] = {};
next();
});
// Hook to be triggered just before response to be send
instance.addHook("onSend", (request, reply, payload, next) => {
// check if Server-Timing need to be added
const serverTiming = reply[symbolServerTiming];
const headers = [];
for (const name of Object.keys(serverTiming)) {
headers.push(serverTiming[name]);
}
if (headers.length) {
reply.header("Server-Timing", headers.join(","));
}
// Calculate the duration, in nanoseconds …
const hrDuration = process.hrtime(request[symbolRequestTime]);
// … convert it to milliseconds …
const duration = (hrDuration[0] * 1e3 + hrDuration[1] / 1e6).toFixed(opts.digits);
// … add the header to the response
reply.header(opts.header, duration);
next();
});
// Can be used to add custom timing information
instance.decorateReply("setServerTiming", function (name, duration, description) {
// Reference to the res object storing values …
const serverTiming = this[symbolServerTiming];
// … return if value already exists (all subsequent occurrences MUST be ignored without signaling an error) …
if (serverTiming.hasOwnProperty(name)) {
return false;
}
// … add the value the the list to send later
serverTiming[name] = genTick(name, duration, description);
// … return true, the value was added to the list
return true;
});
instance.decorateReply("asyncMeasureHr", async function (name, cb) {
const startHrTime = process.hrtime()
let result, error
try {
result = await cb()
} catch (err) {
error = err
}
const hrDuration = process.hrtime(startHrTime)
const duration = (hrDuration[0] * 1e3 + hrDuration[1] / 1e6).toFixed(2)
this.setServerTiming(name, duration)
if (error) {
throw error
}
return result
});
instance.decorateReply("syncMeasureHr", function (name, cb) {
const startHrTime = process.hrtime()
let result, error
try {
result = cb()
} catch (err) {
error = err
}
const hrDuration = process.hrtime(startHrTime)
const duration = (hrDuration[0] * 1e3 + hrDuration[1] / 1e6).toFixed(2)
this.setServerTiming(name, duration)
if (error) {
throw error
}
return result
});
next();
}, ">= 2.0");