Skip to content

Commit

Permalink
Refactor public api to match plugin api (#393)
Browse files Browse the repository at this point in the history
PR-URL: #393

Fixes #330
  • Loading branch information
matthewloring authored Feb 22, 2017
1 parent 58b3dd4 commit f641802
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 342 deletions.
63 changes: 28 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ require('continuation-local-storage');

var common = require('@google-cloud/common');
var constants = require('./src/constants.js');
var TraceLabels = require('./src/trace-labels.js');
var gcpMetadata = require('gcp-metadata');
var semver = require('semver');
var traceUtil = require('./src/util.js');
var SpanData = require('./src/span-data.js');
var util = require('util');
var PluginApi = require('./src/trace-plugin-interface.js');

var modulesLoadedBeforeTrace = [];

Expand All @@ -43,31 +44,23 @@ 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
* Phantom implementation of the trace api. This allows API users to decouple
* the enable/disable logic from the calls to the tracing API. The phantom API
* has a lower overhead than isEnabled checks inside the API functions.
* @private
*/
var phantomTraceAgent = {
startSpan: function() { return SpanData.nullSpan; },
endSpan: function(spanData) { spanData.close(); },
runInSpan: function(name, labels, fn) {
if (typeof(labels) === 'function') {
fn = labels;
}
fn(function() {});
},
runInRootSpan: function(name, labels, fn) {
if (typeof(labels) === 'function') {
fn = labels;
}
fn(function() {});
},
setTransactionName: function() {},
addTransactionLabel: function() {}
enhancedDatabaseReportingEnabled: function() { return false; },
runInRootSpan: function(opts, fn) { return fn(PluginApi.nullSpan); },
createChildSpan: function(opts) { return PluginApi.nullSpan; },
wrap: function(fn) { return fn; },
wrapEmitter: function(ee) { return ee; },
constants: constants,
labels: TraceLabels
};

/** @private */
// This can be either a PhantomAgent or PluginAPI object.
var agent = phantomTraceAgent;

var initConfig = function(projectConfig) {
Expand All @@ -91,36 +84,36 @@ var initConfig = function(projectConfig) {
};

/**
* The singleton public agent. This is the public API of the module.
* The singleton public api. This is the public API of the module.
*/
var publicAgent = {
isActive: function() {
return agent !== phantomTraceAgent;
},

startSpan: function(name, labels) {
return agent.startSpan(name, labels);
enhancedDatabaseReportingEnabled: function() {
return agent.enhancedDatabaseReportingEnabled();
},

endSpan: function(spanData, labels) {
return agent.endSpan(spanData, labels);
runInRootSpan: function(opts, fn) {
return agent.runInRootSpan(opts, fn);
},

runInSpan: function(name, labels, fn) {
return agent.runInSpan(name, labels, fn);
createChildSpan: function(opts) {
return agent.createChildSpan(opts);
},

runInRootSpan: function(name, labels, fn) {
return agent.runInRootSpan(name, labels, fn);
wrap: function(fn) {
return agent.wrap(fn);
},

setTransactionName: function(name) {
return agent.setTransactionName(name);
wrapEmitter: function(ee) {
return agent.wrapEmitter(ee);
},

addTransactionLabel: function(key, value) {
return agent.addTransactionLabel(key, value);
},
constants: constants,

labels: TraceLabels,

start: function(projectConfig) {
var config = initConfig(projectConfig);
Expand Down Expand Up @@ -191,7 +184,7 @@ var publicAgent = {
'service. Please provide a valid project number as an env. ' +
'variable, or through config.projectId passed to start(). ' + err);
if (that.isActive()) {
agent.stop();
agent.agent_.stop();
agent = phantomTraceAgent;
}
return;
Expand All @@ -204,7 +197,7 @@ var publicAgent = {
return this;
}

agent = require('./src/trace-agent.js').get(config, logger);
agent = new PluginApi(require('./src/trace-agent.js').get(config, logger));
return this; // for chaining
},

Expand All @@ -219,7 +212,7 @@ var publicAgent = {
* For use in tests only.
* @private
*/
private_: function() { return agent; }
private_: function() { return agent.agent_; }
};

/**
Expand Down
7 changes: 0 additions & 7 deletions src/span-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,6 @@ function StackFrame(className, methodName, fileName, lineNumber, columnNumber) {
}
}

SpanData.nullSpan = {
createChildSpanData: function() { return SpanData.nullSpan; },
addLabel: function() {},
addLabels: function() {},
close: function() {}
};

/**
* Export SpanData.
*/
Expand Down
88 changes: 0 additions & 88 deletions src/trace-agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,94 +126,6 @@ TraceAgent.prototype.endSpan = function(spanData, labels) {
spanData.close();
};

/**
* Run the provided function in a new span with the provided name.
* If the provided function accepts a parameter, it is assumed to be
* async and is given a continuation to terminate the span after its
* work is completed.
* @param {string} name The name of the resulting span.
* @param {Object<string, string}>=} labels Labels to be attached
* to the resulting span. Non-object data types are silently ignored.
* @param {function(function()=)} fn The function to trace.
*/
TraceAgent.prototype.runInSpan = function(name, labels, fn) {
if (typeof(labels) === 'function') {
fn = labels;
labels = undefined;
}
var span = this.startSpan(name, labels, 1);
if (fn.length === 0) {
fn();
this.endSpan(span);
} else {
fn(this.endSpan.bind(this, span));
}
};

/**
* Run the provided function in a new root span with the provided
* name. As with runInSpan, if the provided function accepts a parameter,
* it is assumed to be asynchronous, and a callback will be passed
* to the function to terminate the root span.
* @param {string} name The name of the resulting root span.
* @param {Object<string, string}>=} labels Labels to be attached
* to the resulting span.
* @param {function(function()=)} fn The function to trace.
*/
TraceAgent.prototype.runInRootSpan = function(name, labels, fn) {
if (typeof(labels) === 'function') {
fn = labels;
labels = undefined;
}
var self = this;
this.namespace.run(function () {
if (cls.getRootContext()) {
self.logger.error('Can\'t nest root spans');
return;
}
var span = self.createRootSpanData(name, null, null, 3, 'SPAN_KIND_UNSPECIFIED');
span.addLabels(labels);
if (fn.length === 0) {
fn();
span.close();
} else {
fn(function () {
span.close();
});
}
});
};

/**
* Set the name of the root transaction.
* @param {string} name The new name for the current root transaction.
*/
TraceAgent.prototype.setTransactionName = function(name) {
var rootSpan = cls.getRootContext();
if (rootSpan === SpanData.nullSpan) {
return;
}
if (rootSpan) {
rootSpan.span.name = name;
} else {
this.logger.error('Cannot set transaction name without an active transaction');
}
};

/**
* Add a new key value label to the root transaction.
* @param {string} key The key for the new label.
* @param {string} value The value for the new label.
*/
TraceAgent.prototype.addTransactionLabel = function(key, value) {
var rootSpan = cls.getRootContext();
if (rootSpan) {
rootSpan.addLabel(key, value);
} else {
this.logger.error('Cannot add label without an active transaction');
}
};

/**
* Determines whether a trace of the given name should be recorded based
* on the current tracing policy.
Expand Down
3 changes: 1 addition & 2 deletions src/trace-labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
/**
* Well-known trace span label values.
*/
function TraceLabels() {
}
var TraceLabels = {};


/**
Expand Down
66 changes: 25 additions & 41 deletions src/trace-plugin-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ RootSpan.prototype.getTraceContext = function() {
return this.serializedTraceContext_;
};

var nullSpan = {
addLabel: function(k, v) {},
endSpan: function() {},
getTraceContext: function() { return ''; }
};

/**
* PluginAPI constructor. Don't call directly - a plugin object will be passed to
* plugin themselves
Expand Down Expand Up @@ -130,43 +136,6 @@ PluginAPI.prototype.summarizeDatabaseResults = function(res) {
this.agent_.config_.databaseResultReportingSize);
};

/**
* Creates and returns a new RootSpan object corresponding to an incoming
* request.
* @param {object} options An object that specifies options for how the root
* span is created and propogated.
* @param {string} options.name The name to apply to the root span.
* @param {?string} options.url A URL associated with the root span, if
* applicable.
* @param {?string} options.traceContext The serialized form of an object that
* contains information about an existing trace context.
* @param {?number} options.skipFrames The number of stack frames to skip when
* collecting call stack information for the root span, starting from the top;
* this should be set to avoid including frames in the plugin. Defaults to 0.
* @returns A new RootSpan object, or null if the trace agent's policy has
* disabled tracing for the given set of options.
*/
PluginAPI.prototype.createRootSpan = function(options) {
var skipFrames = options.skipFrames ? options.skipFrames + 1 : 1;
return createRootSpan_(this, options, skipFrames);
};

/**
* Returns a RootSpan object that corresponds to a root span started earlier
* in the same context, or null if one doesn't exist.
* @returns A new RootSpan object, or null if a root span doesn't exist in
* the current context.
*/
PluginAPI.prototype.getRootSpan = function() {
if (cls.getRootContext()) {
return new RootSpan(this.agent_, cls.getRootContext());
} else {
this.logger_.warn('Attempted to get root span when it doesn\'t' +
' exist');
return null;
}
};

/**
* Runs the given function in a root span corresponding to an incoming request,
* possibly passing it an object that exposes an interface for adding labels
Expand All @@ -187,6 +156,10 @@ PluginAPI.prototype.runInRootSpan = function(options, fn) {
'root span.');
return fn(null);
}
if (cls.getRootContext()) {
this.logger_.warn('Trace agent: Cannot create nested root spans.');
return fn(null);
}
return this.agent_.namespace.runAndReturn(function() {
var skipFrames = options.skipFrames ? options.skipFrames + 2 : 2;
var rootSpan = createRootSpan_(that, options, skipFrames);
Expand All @@ -195,15 +168,14 @@ PluginAPI.prototype.runInRootSpan = function(options, fn) {
};

/**
* Creates and returns a new ChildSpan object nested within the root span object
* returned by getRootSpan. If there is no current RootSpan object, this
* function returns null.
* Creates and returns a new ChildSpan object nested within the root span. If
* there is no current RootSpan object, this function returns null.
* @param {object} options An object that specifies options for how the child
* span is created and propogated.
* @returns A new ChildSpan object, or null if there is no active root span.
*/
PluginAPI.prototype.createChildSpan = function(options) {
var rootSpan = this.getRootSpan();
var rootSpan = getRootSpan_(this);
if (rootSpan) {
options = options || {};
var childContext = this.agent_.startSpan(options.name, {},
Expand Down Expand Up @@ -247,6 +219,8 @@ PluginAPI.prototype.constants = constants;

PluginAPI.prototype.labels = TraceLabels;

PluginAPI.nullSpan = nullSpan;

module.exports = PluginAPI;

// Module-private functions
Expand All @@ -270,3 +244,13 @@ function createRootSpan_(api, options, skipFrames) {
skipFrames + 1);
return new RootSpan(api.agent_, rootContext);
}

function getRootSpan_(api) {
if (cls.getRootContext()) {
return new RootSpan(api.agent_, cls.getRootContext());
} else {
api.logger_.warn('Attempted to get root span when it doesn\'t' +
' exist');
return null;
}
}
16 changes: 12 additions & 4 deletions test/hooks/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,18 @@ function init(agent) {
};
}

function replaceFunction(target, prop, fn) {
var old = target[prop];
target[prop] = fn;
return old;
}

function replaceDebugLogger(agent, fn) {
var privateAgent = agent.private_();
var oldDebug = privateAgent.logger.debug;
privateAgent.logger.debug = fn;
return oldDebug;
return replaceFunction(agent.private_().logger, 'debug', fn);
}

function replaceTracingPolicy(agent, fn) {
return replaceFunction(agent.private_(), 'policy', fn);
}

/**
Expand Down Expand Up @@ -248,6 +255,7 @@ module.exports = {
runInTransaction: runInTransaction,
getShouldTraceArgs: getShouldTraceArgs,
replaceDebugLogger: replaceDebugLogger,
replaceTracingPolicy: replaceTracingPolicy,
createRootSpanData: createRootSpanData,
clearNamespace: clearNamespace,
getConfig: getConfig,
Expand Down
Loading

0 comments on commit f641802

Please sign in to comment.