Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
Refactored autobreadcrumb hooks, added per-breadcrumb-type config opt…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
LewisJEllis committed Nov 28, 2016
1 parent e85685c commit 461ec68
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 105 deletions.
121 changes: 121 additions & 0 deletions lib/breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use strict';

var util = require('util');

/**
* Polyfill a method
* @param obj object e.g. `document`
* @param name method name present on object e.g. `addEventListener`
* @param replacement replacement function
* @param track {optional} record instrumentation to an array
*/
function fill(obj, name, replacement, track) {
var orig = obj[name];
obj[name] = replacement(orig);
if (track) {
track.push([obj, name, orig]);
}
}

function wrapConsole(Raven) {
var wrapConsoleMethod = function (level) {
if (!(level in console)) {
return;
}

fill(console, level, function (originalConsoleLevel) {
var sentryLevel = level === 'warn'
? 'warning'
: level;

return function () {
var args = [].slice.call(arguments);

var msg = '' + args.join(' ');
var data = {
level: sentryLevel,
logger: 'console',
extra: {
'arguments': args
}
};

Raven.captureBreadcrumb({
message: msg,
level: data.level,
category: 'console'
});

originalConsoleLevel.apply(console, args);
};
});
};

['debug', 'info', 'warn', 'error', 'log'].forEach(wrapConsoleMethod);
}

function wrapHttp(Raven) {
var http = require('http');
var OrigClientRequest = http.ClientRequest;
var ClientRequest = function (options, cb) {
// Note: this won't capture a breadcrumb if a response never comes
// It would be useful to know if that was the case, though, so
// todo: revisit to see if we can capture sth indicating response never came
// possibility: capture one breadcrumb for "req sent" and one for "res recvd"
// seems excessive but solves the problem and *is* strictly more information
// could be useful for weird response sequencing bug scenarios
var self = this;
OrigClientRequest.call(self, options, cb);

var method = self.method;
var url = (self.agent && self.agent.protocol || '') + '//' +
(self._headers && self._headers.host || '') +
self.path;

self.once('response', function (response) {
Raven.captureBreadcrumb({
type: 'http',
category: 'http',
data: {
method: method,
url: url,
status_code: response.statusCode
}
});
});
};
util.inherits(ClientRequest, OrigClientRequest);
http.ClientRequest = ClientRequest;

// http.request orig refs module-internal ClientRequest, not exported one, so
// it still points at orig ClientRequest after our monkeypatch; these reimpls
// just get that reference updated to use our new ClientRequest
http.request = function (options, cb) {
return new http.ClientRequest(options, cb);
};

http.get = function (options, cb) {
var req = http.request(options, cb);
req.end();
return req;
};
}

function wrapPostgres(Raven) {
// Using fill helper here is hard because of `this` binding
var pg = require('pg');
var origQuery = pg.Connection.prototype.query;
pg.Connection.prototype.query = function (text) {
Raven.captureBreadcrumb({
category: 'postgres',
message: text
});
origQuery.call(this, text);
};
}

module.exports = {
wrapConsole: wrapConsole,
wrapHttp: wrapHttp,
wrapPostgres: wrapPostgres
};
121 changes: 16 additions & 105 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var transports = require('./transports');
var nodeUtil = require('util'); // nodeUtil to avoid confusion with "utils"
var events = require('events');
var domain = require('domain');
var autoBreadcrumbs = require('./breadcrumbs');

var extend = utils.extend;

Expand Down Expand Up @@ -80,14 +81,17 @@ extend(Raven.prototype, {
}

registerExceptionHandler(this, cb);
if (opts && opts.unhandledRejection) {
registerRejectionHandler(this, cb);
}
if (opts) {
if (opts.unhandledRejection) {
registerRejectionHandler(this, cb);
}

if (opts.autoBreadcrumbs) {
wrapConsole(this);
wrapHttp(this);
wrapPostgres(this);
if (opts.autoBreadcrumbs) {
utils.consoleAlert('Enabling automatic breadcrumbs');
opts.autoBreadcrumbs.console && autoBreadcrumbs.wrapConsole(this);
opts.autoBreadcrumbs.http && autoBreadcrumbs.wrapHttp(this);
opts.autoBreadcrumbs.postgres && autoBreadcrumbs.wrapPostgres(this);
}
}

return this;
Expand Down Expand Up @@ -318,7 +322,11 @@ extend(Raven.prototype, {
},

getContext: function getContext() {
return domain.active ? domain.active.sentryContext : this._globalContext;
if (domain.active) {
return domain.active.sentryContext;
}
utils.consoleAlert('getContext called without context; this may indicate incorrect setup - refer to docs on contexts');
return this._globalContext;
},

setCallbackHelper: function (propertyName, callback) {
Expand Down Expand Up @@ -496,100 +504,3 @@ function patchGlobal(client, cb) {

registerExceptionHandler(client, cb);
}

function wrapConsole(Raven) {
var oldConsole = console;
['log', 'error'].forEach(function (method) {
var oldConsoleMethod = oldConsole[method];
console[method] = function () {
var args = [].slice.call(arguments);
var outputStr = '' + args.join(' ');

Raven.captureBreadcrumb({
category: 'console',
level: 'log',
message: outputStr
});

Function.prototype.apply.call(
oldConsoleMethod,
oldConsole,
args
);
};
});
}

function wrapHttp(Raven) {
var http = require('http');
var OldClientRequest = http.ClientRequest;
var ClientRequest = function (options, cb) {
// todo still capture breadcrumb if response never comes? or is it guaranteed to?
var self = this;
OldClientRequest.call(self, options, cb);

var method = self.method;
var url = (self.agent && self.agent.protocol || '') +
(self._headers && self._headers.host || '') +
self.path;

self.once('response', function (response) {
Raven.captureBreadcrumb({
type: 'http',
category: 'http',
data: {
method: method,
url: url,
status_code: response.statusCode
}
});
});
};
nodeUtil.inherits(ClientRequest, OldClientRequest);
http.ClientRequest = ClientRequest;

http.request = function (options, cb) {
return new http.ClientRequest(options, cb);
};

http.get = function (options, cb) {
var req = http.request(options, cb);
req.end();
return req;
};
}


function wrapPostgres(Raven) {
var pg = require('pg');
var oldQuery = pg.Connection.prototype.query;
pg.Connection.prototype.query = function (text) {
Raven.captureBreadcrumb({
category: 'postgres',
message: text
});
oldQuery.call(this, text);
};
}

// http.get = function (url, callback) {
// // Raven.captureBreadcrumb({
// // type: 'http',
// // category: 'http.get',
// // message: 'you sent an http request to ' + url
// // });
// oldRequest(url, function (response) {
// var reqData = {
// method: 'GET',
// url: url,
// status_code: response.statusCode
// };
// Raven.captureBreadcrumb({
// type: 'http',
// category: 'http.get',
// data: reqData
// });
// callback(response);
// });
// };
// }

0 comments on commit 461ec68

Please sign in to comment.