Skip to content

Commit

Permalink
Attempt to publish traces on unhandled exception
Browse files Browse the repository at this point in the history
Fixes #232
  • Loading branch information
Matt Loring committed Apr 14, 2016
1 parent ec92481 commit 4903c64
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
9 changes: 8 additions & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ module.exports = {

// The number of transactions we buffer before we publish to the trace
// API, unless we hit `flushDelaySeconds` first.
bufferSize: 1000
bufferSize: 1000,

// Specifies the behavior of the trace agent in the case of an uncaught exception.
// Possible values are:
// `ignore`: Take no action.
// `flush`: Attempt to publish traces silence the exception.
// `flushAndExit`: Attempt to publish traces and exit the process.
onUncaughtException: 'ignore'
}
};
8 changes: 8 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ for (var i = 0; i < filesLoadedBeforeTrace.length; i++) {
}
}

var onUncaughtExceptionValues = ['ignore', 'flush', 'flushAndExit'];

/**
* Phantom implementation of the trace agent. This allows API users to decouple
* the enable/disable logic from the calls to the tracing API. The phantom API
Expand Down Expand Up @@ -133,6 +135,12 @@ var publicAgent = {
logger.info('Locally provided ProjectId: ' + config.projectId);
}

if (onUncaughtExceptionValues.indexOf(config.onUncaughtException) === -1) {
logger.error('The value of onUncaughtException should be one of ',
onUncaughtExceptionValues);
throw new Error('Invalid value for onUncaughtException configuration.');
}

var headers = {};
headers[constants.TRACE_AGENT_REQUEST_HEADER] = 1;

Expand Down
15 changes: 15 additions & 0 deletions lib/trace-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ function TraceAgent(config, logger) {

this.policy = tracingPolicy.createTracePolicy(config);

if (config.onUncaughtException !== 'disregard') {

This comment has been minimized.

Copy link
@ofrobots

ofrobots Apr 18, 2016

Contributor

typo

this.unhandledException = function() {
traceAgent.traceWriter.flushBuffer_(traceAgent.config_.projectId);
if (config.onUncaughtException === 'flushAndExit') {
setTimeout(function() {
process.exit(1);
}, 2000);
}
};
process.on('uncaughtException', this.unhandledException);
}

logger.info('trace agent activated');
}

Expand All @@ -55,6 +67,9 @@ TraceAgent.prototype.stop = function() {
this.traceWriter.stop();
this.namespace = null;
traceAgent = null;
if (this.config_.onUncaughtException !== 'disregard') {

This comment has been minimized.

Copy link
@ofrobots

ofrobots Apr 18, 2016

Contributor

typo

process.removeListener('uncaughtException', this.unhandledException);
}
this.logger.info('trace agent deactivated');
};

Expand Down
79 changes: 79 additions & 0 deletions test/standalone/test-trace-uncaught-exception.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

var assert = require('assert');
var nock = require('nock');
var cls = require('../../lib/cls.js');
var agent = require('../..');
var request = require('request');

nock.disableNetConnect();

var uri = 'https://cloudtrace.googleapis.com';
var path = '/v1/projects/0/traces';

process.env.GCLOUD_PROJECT = 0;

var queueSpans = function(n, privateAgent) {
for (var i = 0; i < n; i++) {
privateAgent.createRootSpanData('name', 1, 0).close();
}
};

var formatBuffer = function(buffer) {
return {
traces: buffer.map(function(e) { return JSON.parse(e); })
};
};

describe('tracewriter publishing', function() {

it('should publish on unhandled exception', function(done) {
process.removeAllListeners('uncaughtException'); // Remove mocha handler
var buf;
var scope = nock(uri)
.intercept(path, 'PATCH', function(body) {
var parsedOriginal = formatBuffer(buf);
assert.equal(JSON.stringify(body), JSON.stringify(parsedOriginal));
return true;
}).reply(200);
process.on('uncaughtException', function() {
setTimeout(function() {
assert.equal(process.listeners('uncaughtException').length, 2);
agent.stop();
assert.equal(process.listeners('uncaughtException').length, 1);
scope.done();
done();
}, 20);
});
process.nextTick(function() {
var privateAgent = agent.start({
bufferSize: 1000,
samplingRate: 0,
onUncaughtException: 'flush'
}).private_();
privateAgent.traceWriter.request_ = request; // Avoid authing
cls.getNamespace().run(function() {
queueSpans(2, privateAgent);
buf = privateAgent.traceWriter.buffer_;
throw new Error(':(');
});
});
});

});

0 comments on commit 4903c64

Please sign in to comment.