UNPKG

10.1 kBJavaScriptView Raw
1Object.defineProperty(exports, "__esModule", { value: true });
2var tslib_1 = require("tslib");
3var apm_1 = require("@sentry/apm");
4var core_1 = require("@sentry/core");
5var utils_1 = require("@sentry/utils");
6var cookie = require("cookie");
7var domain = require("domain");
8var os = require("os");
9var url = require("url");
10var sdk_1 = require("./sdk");
11var DEFAULT_SHUTDOWN_TIMEOUT = 2000;
12/**
13 * Express compatible tracing handler.
14 * @see Exposed as `Handlers.tracingHandler`
15 */
16function tracingHandler() {
17 return function sentryTracingMiddleware(req, res, next) {
18 // TODO: At this point req.route.path we use in `extractTransaction` is not available
19 // but `req.path` or `req.url` should do the job as well. We could unify this here.
20 var reqMethod = (req.method || '').toUpperCase();
21 var reqUrl = req.url;
22 var hub = core_1.getCurrentHub();
23 var transaction = hub.startSpan({
24 transaction: reqMethod + "|" + reqUrl,
25 });
26 hub.configureScope(function (scope) {
27 scope.setSpan(transaction);
28 });
29 res.once('finish', function () {
30 transaction.setHttpStatus(res.statusCode);
31 transaction.finish();
32 });
33 next();
34 };
35}
36exports.tracingHandler = tracingHandler;
37/** JSDoc */
38function extractTransaction(req, type) {
39 try {
40 // Express.js shape
41 var request = req;
42 switch (type) {
43 case 'path': {
44 return request.route.path;
45 }
46 case 'handler': {
47 return request.route.stack[0].name;
48 }
49 case 'methodPath':
50 default: {
51 var method = request.method.toUpperCase();
52 var path = request.route.path;
53 return method + "|" + path;
54 }
55 }
56 }
57 catch (_oO) {
58 return undefined;
59 }
60}
61/** Default request keys that'll be used to extract data from the request */
62var DEFAULT_REQUEST_KEYS = ['cookies', 'data', 'headers', 'method', 'query_string', 'url'];
63/** JSDoc */
64function extractRequestData(req, keys) {
65 var request = {};
66 var attributes = Array.isArray(keys) ? keys : DEFAULT_REQUEST_KEYS;
67 // headers:
68 // node, express: req.headers
69 // koa: req.header
70 var headers = (req.headers || req.header || {});
71 // method:
72 // node, express, koa: req.method
73 var method = req.method;
74 // host:
75 // express: req.hostname in > 4 and req.host in < 4
76 // koa: req.host
77 // node: req.headers.host
78 var host = req.hostname || req.host || headers.host || '<no host>';
79 // protocol:
80 // node: <n/a>
81 // express, koa: req.protocol
82 var protocol = req.protocol === 'https' || req.secure || (req.socket || {}).encrypted
83 ? 'https'
84 : 'http';
85 // url (including path and query string):
86 // node, express: req.originalUrl
87 // koa: req.url
88 var originalUrl = (req.originalUrl || req.url);
89 // absolute url
90 var absoluteUrl = protocol + "://" + host + originalUrl;
91 attributes.forEach(function (key) {
92 switch (key) {
93 case 'headers':
94 request.headers = headers;
95 break;
96 case 'method':
97 request.method = method;
98 break;
99 case 'url':
100 request.url = absoluteUrl;
101 break;
102 case 'cookies':
103 // cookies:
104 // node, express, koa: req.headers.cookie
105 request.cookies = cookie.parse(headers.cookie || '');
106 break;
107 case 'query_string':
108 // query string:
109 // node: req.url (raw)
110 // express, koa: req.query
111 request.query_string = url.parse(originalUrl || '', false).query;
112 break;
113 case 'data':
114 // body data:
115 // node, express, koa: req.body
116 var data = req.body;
117 if (method === 'GET' || method === 'HEAD') {
118 if (typeof data === 'undefined') {
119 data = '<unavailable>';
120 }
121 }
122 if (data && !utils_1.isString(data)) {
123 // Make sure the request body is a string
124 data = JSON.stringify(utils_1.normalize(data));
125 }
126 request.data = data;
127 break;
128 default:
129 if ({}.hasOwnProperty.call(req, key)) {
130 request[key] = req[key];
131 }
132 }
133 });
134 return request;
135}
136/** Default user keys that'll be used to extract data from the request */
137var DEFAULT_USER_KEYS = ['id', 'username', 'email'];
138/** JSDoc */
139function extractUserData(user, keys) {
140 var extractedUser = {};
141 var attributes = Array.isArray(keys) ? keys : DEFAULT_USER_KEYS;
142 attributes.forEach(function (key) {
143 if (user && key in user) {
144 extractedUser[key] = user[key];
145 }
146 });
147 return extractedUser;
148}
149/**
150 * Enriches passed event with request data.
151 *
152 * @param event Will be mutated and enriched with req data
153 * @param req Request object
154 * @param options object containing flags to enable functionality
155 * @hidden
156 */
157function parseRequest(event, req, options) {
158 // tslint:disable-next-line:no-parameter-reassignment
159 options = tslib_1.__assign({ ip: false, request: true, serverName: true, transaction: true, user: true, version: true }, options);
160 if (options.version) {
161 event.extra = tslib_1.__assign({}, event.extra, { node: global.process.version });
162 }
163 if (options.request) {
164 event.request = tslib_1.__assign({}, event.request, extractRequestData(req, options.request));
165 }
166 if (options.serverName && !event.server_name) {
167 event.server_name = global.process.env.SENTRY_NAME || os.hostname();
168 }
169 if (options.user) {
170 var extractedUser = req.user ? extractUserData(req.user, options.user) : {};
171 if (Object.keys(extractedUser)) {
172 event.user = tslib_1.__assign({}, event.user, extractedUser);
173 }
174 }
175 // client ip:
176 // node: req.connection.remoteAddress
177 // express, koa: req.ip
178 if (options.ip) {
179 var ip = req.ip || (req.connection && req.connection.remoteAddress);
180 if (ip) {
181 event.user = tslib_1.__assign({}, event.user, { ip_address: ip });
182 }
183 }
184 if (options.transaction && !event.transaction) {
185 var transaction = extractTransaction(req, options.transaction);
186 if (transaction) {
187 event.transaction = transaction;
188 }
189 }
190 return event;
191}
192exports.parseRequest = parseRequest;
193/**
194 * Express compatible request handler.
195 * @see Exposed as `Handlers.requestHandler`
196 */
197function requestHandler(options) {
198 return function sentryRequestMiddleware(req, res, next) {
199 if (options && options.flushTimeout && options.flushTimeout > 0) {
200 // tslint:disable-next-line: no-unbound-method
201 var _end_1 = res.end;
202 res.end = function (chunk, encoding, cb) {
203 var _this = this;
204 sdk_1.flush(options.flushTimeout)
205 .then(function () {
206 _end_1.call(_this, chunk, encoding, cb);
207 })
208 .then(null, function (e) {
209 utils_1.logger.error(e);
210 });
211 };
212 }
213 var local = domain.create();
214 local.add(req);
215 local.add(res);
216 local.on('error', next);
217 local.run(function () {
218 core_1.getCurrentHub().configureScope(function (scope) {
219 return scope.addEventProcessor(function (event) { return parseRequest(event, req, options); });
220 });
221 next();
222 });
223 };
224}
225exports.requestHandler = requestHandler;
226/** JSDoc */
227function getStatusCodeFromResponse(error) {
228 var statusCode = error.status || error.statusCode || error.status_code || (error.output && error.output.statusCode);
229 return statusCode ? parseInt(statusCode, 10) : 500;
230}
231/** Returns true if response code is internal server error */
232function defaultShouldHandleError(error) {
233 var status = getStatusCodeFromResponse(error);
234 return status >= 500;
235}
236/**
237 * Express compatible error handler.
238 * @see Exposed as `Handlers.errorHandler`
239 */
240function errorHandler(options) {
241 return function sentryErrorMiddleware(error, req, res, next) {
242 var shouldHandleError = (options && options.shouldHandleError) || defaultShouldHandleError;
243 if (shouldHandleError(error)) {
244 core_1.withScope(function (scope) {
245 if (req.headers && utils_1.isString(req.headers['sentry-trace'])) {
246 var span = apm_1.Span.fromTraceparent(req.headers['sentry-trace']);
247 scope.setSpan(span);
248 }
249 var eventId = core_1.captureException(error);
250 res.sentry = eventId;
251 next(error);
252 });
253 return;
254 }
255 next(error);
256 };
257}
258exports.errorHandler = errorHandler;
259/**
260 * @hidden
261 */
262function logAndExitProcess(error) {
263 console.error(error && error.stack ? error.stack : error);
264 var client = core_1.getCurrentHub().getClient();
265 if (client === undefined) {
266 utils_1.logger.warn('No NodeClient was defined, we are exiting the process now.');
267 global.process.exit(1);
268 return;
269 }
270 var options = client.getOptions();
271 var timeout = (options && options.shutdownTimeout && options.shutdownTimeout > 0 && options.shutdownTimeout) ||
272 DEFAULT_SHUTDOWN_TIMEOUT;
273 utils_1.forget(client.close(timeout).then(function (result) {
274 if (!result) {
275 utils_1.logger.warn('We reached the timeout for emptying the request buffer, still exiting now!');
276 }
277 global.process.exit(1);
278 }));
279}
280exports.logAndExitProcess = logAndExitProcess;
281//# sourceMappingURL=handlers.js.map
\No newline at end of file