Skip to content

Commit e37b244

Browse files
elicwhitebenvinegar
authored andcommitted
Adding a breadcrumbCallback for filtering and cleaning (#788)
* Adding a shouldSendBreadcrumbCallback * Change to breadcrumbCallback * Add docs for breadcrumbCallback * Updating breadcrumb return type checking
1 parent 21b2007 commit e37b244

File tree

4 files changed

+268
-0
lines changed

4 files changed

+268
-0
lines changed

docs/config.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,23 @@ Those configuration options are documented below:
159159
}
160160
}
161161
162+
.. describe:: breadcrumbCallback
163+
164+
A function that allows filtering or mutating breadcrumb payloads.
165+
Return false to throw away the breadcrumb.
166+
167+
.. code-block:: javascript
168+
169+
{
170+
breadcrumbCallback: function(crumb) {
171+
if (crumb.type === 'http') {
172+
return crumb;
173+
}
174+
175+
return false;
176+
}
177+
}
178+
162179
.. describe:: shouldSendCallback
163180

164181
A callback function that allows you to apply your own filters to

src/raven.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,16 @@ Raven.prototype = {
413413
timestamp: now() / 1000
414414
}, obj);
415415

416+
if (isFunction(this._globalOptions.breadcrumbCallback)) {
417+
var result = this._globalOptions.breadcrumbCallback(crumb);
418+
419+
if (isObject(result) && !isEmptyObject(result)) {
420+
crumb = result;
421+
} else if (result === false) {
422+
return this;
423+
}
424+
}
425+
416426
this._breadcrumbs.push(crumb);
417427
if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) {
418428
this._breadcrumbs.shift();
@@ -530,6 +540,22 @@ Raven.prototype = {
530540
return this;
531541
},
532542

543+
/*
544+
* Set the breadcrumbCallback option
545+
*
546+
* @param {function} callback The callback to run which allows filtering
547+
* or mutating breadcrumbs
548+
* @return {Raven}
549+
*/
550+
setBreadcrumbCallback: function(callback) {
551+
var original = this._globalOptions.breadcrumbCallback;
552+
this._globalOptions.breadcrumbCallback = isFunction(callback)
553+
? function (data) { return callback(data, original); }
554+
: callback;
555+
556+
return this;
557+
},
558+
533559
/*
534560
* Set the shouldSendCallback option
535561
*

test/raven.test.js

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,41 @@ describe('Raven (public API)', function() {
19611961
});
19621962
});
19631963

1964+
describe('.setBreadcrumbCallback', function() {
1965+
it('should set the globalOptions.breadcrumbCallback attribute', function() {
1966+
var foo = sinon.stub();
1967+
Raven.setBreadcrumbCallback(foo);
1968+
1969+
// note that breadcrumbCallback creates a callback/closure around
1970+
// foo, so can't test for equality - just verify that calling the wrapper
1971+
// also calls foo
1972+
Raven._globalOptions.breadcrumbCallback();
1973+
assert.isTrue(foo.calledOnce);
1974+
});
1975+
1976+
it('should clear globalOptions.breadcrumbCallback with no arguments', function() {
1977+
var foo = function(){};
1978+
Raven._globalOptions.breadcrumbCallback = foo;
1979+
Raven.setBreadcrumbCallback();
1980+
assert.isUndefined(Raven._globalOptions.breadcrumbCallback);
1981+
});
1982+
1983+
it('should generate a wrapper that passes the prior callback as the 2nd argument', function () {
1984+
var foo = sinon.stub();
1985+
var bar = sinon.spy(function(data, orig) {
1986+
assert.equal(orig, foo);
1987+
foo();
1988+
});
1989+
Raven._globalOptions.breadcrumbCallback = foo;
1990+
Raven.setBreadcrumbCallback(bar);
1991+
Raven._globalOptions.breadcrumbCallback({
1992+
'a': 1 // "data"
1993+
});
1994+
assert.isTrue(bar.calledOnce);
1995+
assert.isTrue(foo.calledOnce);
1996+
});
1997+
});
1998+
19641999
describe('.setShouldSendCallback', function() {
19652000
it('should set the globalOptions.shouldSendCallback attribute', function() {
19662001
var foo = sinon.stub();
@@ -2216,6 +2251,193 @@ describe('Raven (public API)', function() {
22162251
{ message: 'lol', timestamp: 0.1 }
22172252
]);
22182253
});
2254+
2255+
describe('breadcrumbCallback', function() {
2256+
it('should filter the breadcrumb if it returns false', function() {
2257+
Raven.setBreadcrumbCallback(function() {
2258+
return false;
2259+
});
2260+
2261+
Raven.captureBreadcrumb({
2262+
type: 'http',
2263+
data: {
2264+
url: 'http://example.org/api/0/auth/',
2265+
status_code: 200
2266+
}
2267+
});
2268+
2269+
assert.strictEqual(Raven._breadcrumbs.length, 0);
2270+
});
2271+
2272+
it('should not filter the breadcrumb if callback returns undefined', function() {
2273+
Raven.setBreadcrumbCallback(function() {});
2274+
2275+
Raven.captureBreadcrumb({
2276+
type: 'http',
2277+
data: {
2278+
url: 'http://example.org/api/0/auth/',
2279+
status_code: 200
2280+
}
2281+
});
2282+
2283+
assert.deepEqual(Raven._breadcrumbs, [{
2284+
type: 'http',
2285+
timestamp: 0.1,
2286+
data: {
2287+
url: 'http://example.org/api/0/auth/',
2288+
status_code: 200
2289+
}
2290+
}]);
2291+
});
2292+
2293+
it('should mutate the breadcrumb if it returns an object', function() {
2294+
Raven.setBreadcrumbCallback(function(crumb) {
2295+
crumb.type = 'whee';
2296+
return crumb;
2297+
});
2298+
2299+
Raven.captureBreadcrumb({
2300+
type: 'http',
2301+
data: {
2302+
url: 'http://example.org/api/0/auth/',
2303+
status_code: 200
2304+
}
2305+
});
2306+
2307+
assert.deepEqual(Raven._breadcrumbs, [{
2308+
type: 'whee',
2309+
timestamp: 0.1,
2310+
data: {
2311+
url: 'http://example.org/api/0/auth/',
2312+
status_code: 200
2313+
}
2314+
}]);
2315+
});
2316+
2317+
it('should enable replacing the breadcrumb', function() {
2318+
Raven.setBreadcrumbCallback(function() {
2319+
return {
2320+
foo: 'bar'
2321+
}
2322+
});
2323+
2324+
Raven.captureBreadcrumb({
2325+
type: 'http',
2326+
data: {
2327+
url: 'http://example.org/api/0/auth/',
2328+
status_code: 200
2329+
}
2330+
});
2331+
2332+
assert.deepEqual(Raven._breadcrumbs[0], {
2333+
foo: 'bar'
2334+
});
2335+
});
2336+
2337+
it('should not replace the breadcrumb if a non object is returned', function() {
2338+
Raven.setBreadcrumbCallback(function() {
2339+
return 'foo';
2340+
});
2341+
2342+
Raven.captureBreadcrumb({
2343+
type: 'http',
2344+
data: {
2345+
url: 'http://example.org/api/0/auth/',
2346+
status_code: 200
2347+
}
2348+
});
2349+
2350+
assert.deepEqual(Raven._breadcrumbs, [{
2351+
type: 'http',
2352+
timestamp: 0.1,
2353+
data: {
2354+
url: 'http://example.org/api/0/auth/',
2355+
status_code: 200
2356+
}
2357+
}]);
2358+
});
2359+
2360+
it('should not replace the breadcrumb if an empty object is returned', function() {
2361+
Raven.setBreadcrumbCallback(function() {
2362+
return {};
2363+
});
2364+
2365+
Raven.captureBreadcrumb({
2366+
type: 'http',
2367+
data: {
2368+
url: 'http://example.org/api/0/auth/',
2369+
status_code: 200
2370+
}
2371+
});
2372+
2373+
assert.deepEqual(Raven._breadcrumbs, [{
2374+
type: 'http',
2375+
timestamp: 0.1,
2376+
data: {
2377+
url: 'http://example.org/api/0/auth/',
2378+
status_code: 200
2379+
}
2380+
}]);
2381+
});
2382+
2383+
it('should call the callback with the breadcrumb object', function() {
2384+
var callback = this.sinon.stub().returnsArg(0);
2385+
Raven.setBreadcrumbCallback(callback);
2386+
2387+
Raven.captureBreadcrumb({
2388+
type: 'http',
2389+
data: {
2390+
url: 'http://example.org/api/0/auth/',
2391+
status_code: 200
2392+
}
2393+
});
2394+
2395+
assert.isTrue(callback.calledWith({
2396+
type: 'http',
2397+
timestamp: 0.1,
2398+
data: {
2399+
url: 'http://example.org/api/0/auth/',
2400+
status_code: 200
2401+
}
2402+
}));
2403+
});
2404+
2405+
it('should enable filtering one breadcrumb but not another', function() {
2406+
function callback(data) {
2407+
if (data.data.url === 'example') {
2408+
return false;
2409+
}
2410+
2411+
return data;
2412+
}
2413+
2414+
Raven.setBreadcrumbCallback(callback);
2415+
Raven.captureBreadcrumb({
2416+
type: 'http',
2417+
data: {
2418+
url: 'example',
2419+
status_code: 200
2420+
}
2421+
});
2422+
2423+
Raven.captureBreadcrumb({
2424+
type: 'http',
2425+
data: {
2426+
url: 'foo',
2427+
status_code: 301
2428+
}
2429+
});
2430+
2431+
assert.deepEqual(Raven._breadcrumbs, [{
2432+
type: 'http',
2433+
timestamp: 0.1,
2434+
data: {
2435+
url: 'foo',
2436+
status_code: 301
2437+
}
2438+
}]);
2439+
});
2440+
});
22192441
});
22202442

22212443
describe('._captureUrlChange', function () {

typescript/raven.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ interface RavenStatic {
210210
/** Specify a function that allows mutation of the data payload right before being sent to Sentry. */
211211
setDataCallback(data: any, orig?: any): RavenStatic;
212212

213+
/** Specify a callback function that allows you to mutate or filter breadcrumbs when they are captured. */
214+
setBreadcrumbCallback(data: any, orig?: any): RavenStatic;
215+
213216
/** Specify a callback function that allows you to apply your own filters to determine if the message should be sent to Sentry. */
214217
setShouldSendCallback(data: any, orig?: any): RavenStatic;
215218

0 commit comments

Comments
 (0)