Socket
Socket
Sign inDemoInstall

@google-cloud/trace-agent

Package Overview
Dependencies
Maintainers
13
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@google-cloud/trace-agent - npm Package Compare versions

Comparing version 2.1.0 to 2.1.1

.vscode/settings.json

16

CHANGELOG.md
# Node.js Agent for Google Cloud Trace ChangeLog
## 2017-07-17, Version 2.1.1 (Beta), @ofrobots
This module is now in Beta.
### Commits
* [[`17555e2071`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/17555e2071)] - beta (#524) (Ali Ijaz Sheikh) [#524](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/524)
* [[`e6671790c1`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/e6671790c1)] - Deduplicate internal code (#511) (Kelvin Jin) [#511](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/511)
* [[`eaab39ac1e`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/eaab39ac1e)] - **test**: omit agent argument from all test-common helper functions (#518) (Kelvin Jin) [#518](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/518)
* [[`ca72dd7f44`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/ca72dd7f44)] - warn when creating a child of a closed span (#520) (Ali Ijaz Sheikh)
* [[`be0b006b35`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/be0b006b35)] - make TraceWriter a singleton (#517) (Kelvin Jin) [#517](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/517)
* [[`eb0a11be23`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/eb0a11be23)] - increase severity of module order log message (#519) (Ali Ijaz Sheikh)
* [[`300dc4fc34`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/300dc4fc34)] - Fix document source link (#514) (Oleg Shalygin) [#514](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/514)
* [[`8561232a04`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/8561232a04)] - Fix typos (#513) (Oleg Shalygin) [#513](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/513)
* [[`a9e46cb1c8`](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/commit/a9e46cb1c8)] - Update datastore test to use datastore module (#509) (Matthew Loring) [#509](https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/pull/509)
## 2017-06-12, Version 2.1.0 (Experimental), @matthewloring

@@ -4,0 +20,0 @@

126

index.js

@@ -25,12 +25,15 @@ /**

var cls = require('./src/cls.js');
var common = require('@google-cloud/common');
var extend = require('extend');
var constants = require('./src/constants.js');
var gcpMetadata = require('gcp-metadata');
var traceUtil = require('./src/util.js');
var TraceApi = require('./src/trace-api.js');
var TraceAgent = require('./src/trace-api.js');
var pluginLoader = require('./src/trace-plugin-loader.js');
var TraceWriter = require('./src/trace-writer.js');
var modulesLoadedBeforeTrace = [];
var traceAgent;
for (var i = 0; i < filesLoadedBeforeTrace.length; i++) {

@@ -44,5 +47,10 @@ var moduleName = traceUtil.packageNameFromPath(filesLoadedBeforeTrace[i]);

var onUncaughtExceptionValues = ['ignore', 'flush', 'flushAndExit'];
var initConfig = function(projectConfig) {
/**
* Normalizes the user-provided configuration object by adding default values
* and overriding with env variables when they are provided.
* @param {*} projectConfig The user-provided configuration object. It will not
* be modified.
* @return A normalized configuration object.
*/
function initConfig(projectConfig) {
var envConfig = {

@@ -58,10 +66,28 @@ logLevel: process.env.GCLOUD_TRACE_LOGLEVEL,

var config = extend(true, {}, require('./config.js'), projectConfig, envConfig);
// Enforce the upper limit for the label value size.
if (config.maximumLabelValueSize > constants.TRACE_SERVICE_LABEL_VALUE_LIMIT) {
config.maximumLabelValueSize = constants.TRACE_SERVICE_LABEL_VALUE_LIMIT;
}
// Clamp the logger level.
if (config.logLevel < 0) {
config.logLevel = 0;
} else if (config.logLevel >= common.logger.LEVELS.length) {
config.logLevel = common.logger.LEVELS.length - 1;
}
return config;
};
}
var traceApi = new TraceApi('Custom Span API');
var agent;
/**
* Stops the Trace Agent. This disables the publicly exposed agent instance,
* as well as any instances passed to plugins. This also prevents the Trace
* Writer from publishing additional traces.
*/
function stop() {
if (traceAgent && traceAgent.isActive()) {
TraceWriter.get().stop();
traceAgent.disable();
pluginLoader.deactivate();
cls.destroyNamespace();
}
}

@@ -83,15 +109,14 @@ /**

if (traceApi.isActive() && !config.forceNewAgent_) { // already started.
if (traceAgent && !config.forceNewAgent_) { // already started.
throw new Error('Cannot call start on an already started agent.');
} else if (traceAgent) {
// For unit tests only.
// Undoes initialization that occurred last time start() was called.
stop();
}
if (!config.enabled) {
return traceApi;
return traceAgent;
}
if (config.logLevel < 0) {
config.logLevel = 0;
} else if (config.logLevel >= common.logger.LEVELS.length) {
config.logLevel = common.logger.LEVELS.length - 1;
}
var logger = common.logger({

@@ -102,69 +127,36 @@ level: common.logger.LEVELS[config.logLevel],

if (config.projectId) {
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;
if (modulesLoadedBeforeTrace.length > 0) {
logger.warn('Tracing might not work as the following modules ' +
logger.error('Tracing might not work as the following modules ' +
'were loaded before the trace agent was initialized: ' +
JSON.stringify(modulesLoadedBeforeTrace));
}
// CLS namespace for context propagation
cls.createNamespace();
TraceWriter.create(logger, config, function(err) {
if (err) {
stop();
}
});
if (typeof config.projectId === 'undefined') {
// Queue the work to acquire the projectId (potentially from the
// network.)
gcpMetadata.project({
property: 'project-id',
headers: headers
}, function(err, response, projectId) {
if (response && response.statusCode !== 200) {
if (response.statusCode === 503) {
err = new Error('Metadata service responded with a 503 status ' +
'code. This may be due to a temporary server error; please try ' +
'again later.');
} else {
err = new Error('Metadata service responded with the following ' +
'status code: ' + response.statusCode);
}
}
if (err) {
logger.error('Unable to acquire the project number from metadata ' +
'service. Please provide a valid project number as an env. ' +
'variable, or through config.projectId passed to start(). ' + err);
if (traceApi.isActive()) {
agent.stop();
traceApi.disable_();
pluginLoader.deactivate();
}
return;
}
config.projectId = projectId;
});
} else if (typeof config.projectId !== 'string') {
traceAgent = new TraceAgent('Custom Span API', logger, config);
pluginLoader.activate(logger, config);
if (typeof config.projectId !== 'string' && typeof config.projectId !== 'undefined') {
logger.error('config.projectId, if provided, must be a string. ' +
'Disabling trace agent.');
return traceApi;
stop();
return traceAgent;
}
agent = require('./src/trace-agent.js').get(config, logger);
traceApi.enable_(agent);
pluginLoader.activate(agent);
// Make trace agent available globally without requiring package
global._google_trace_agent = traceAgent;
return traceApi;
logger.info('trace agent activated');
return traceAgent;
}
function get() {
return traceApi;
return traceAgent;
}
global._google_trace_agent = traceApi;
module.exports = {

@@ -171,0 +163,0 @@ start: start,

{
"name": "@google-cloud/trace-agent",
"version": "2.1.0",
"version": "2.1.1",
"description": "Node.js Support for StackDriver Trace",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -10,3 +10,3 @@ # Stackdriver Trace for Node.js

> *This module is experimental, and should be used by early adopters. This module uses APIs that may be undocumented and subject to change without notice.*
> **Beta**. *This is a Beta release of the Stackdriver Trace agent for Node.js. These libraries might be changed in backward-incompatible ways and are not subject to any SLA or deprecation policy.*

@@ -13,0 +13,0 @@ This module provides Stackdriver Trace support for Node.js applications. [Stackdriver Trace](https://cloud.google.com/cloud-trace/) is a feature of [Google Cloud Platform](https://cloud.google.com/) that collects latency data (traces) from your applications and displays it in near real-time in the [Google Cloud Console][cloud-console].

@@ -20,3 +20,2 @@ /**

var constants = require('./constants.js');
var is = require('is');
var TraceSpan = require('./trace-span.js');

@@ -26,2 +25,3 @@ var TraceLabels = require('./trace-labels.js');

var util = require('util');
var TraceWriter = require('./trace-writer.js');

@@ -40,5 +40,4 @@ // Auto-incrementing integer

*/
function SpanData(agent, trace, name, parentSpanId, isRoot, skipFrames) {
function SpanData(trace, name, parentSpanId, isRoot, skipFrames) {
var spanId = uid++;
this.agent = agent;
var spanName = traceUtil.truncate(name, constants.TRACE_SERVICE_SPAN_NAME_LIMIT);

@@ -48,4 +47,9 @@ this.span = new TraceSpan(spanName, spanId, parentSpanId);

this.isRoot = isRoot;
this.serializedTraceContext = traceUtil.generateTraceContext({
traceId: this.trace.traceId,
spanId: this.span.spanId,
options: 1 // always traced
});
trace.spans.push(this.span);
if (agent.config().stackTraceLimit > 0) {
if (TraceWriter.get().config().stackTraceLimit > 0) {
// This is a mechanism to get the structured stack trace out of V8.

@@ -59,3 +63,3 @@ // prepareStackTrace is called th first time the Error#stack property is

var origLimit = Error.stackTraceLimit;
Error.stackTraceLimit = agent.config().stackTraceLimit + skipFrames;
Error.stackTraceLimit = TraceWriter.get().config().stackTraceLimit + skipFrames;

@@ -94,12 +98,4 @@ var origPrepare = Error.prepareStackTrace;

/**
* Creates a child span of this span.
* @param name The name of the child span.
* @param {number} skipFrames The number of caller frames to eliminate from
* stack traces.
* @returns {SpanData} The new child trace span data.
*/
SpanData.prototype.createChildSpanData = function(name, skipFrames) {
return new SpanData(this.agent, this.trace, name, this.span.spanId, false,
skipFrames + 1);
SpanData.prototype.getTraceContext = function() {
return this.serializedTraceContext;
};

@@ -110,3 +106,3 @@

var string_val = typeof value === 'string' ? value : util.inspect(value);
var v = traceUtil.truncate(string_val, this.agent.config().maximumLabelValueSize);
var v = traceUtil.truncate(string_val, TraceWriter.get().config().maximumLabelValueSize);
this.span.setLabel(k, v);

@@ -116,24 +112,8 @@ };

/**
* Add properties from provided `labels` param to the span as labels.
*
* @param {Object<string, string}>=} labels Labels to be attached to the newly
* created span. Non-object data types are silently ignored.
* Closes the span.
*/
SpanData.prototype.addLabels = function(labels) {
var that = this;
if (is.object(labels)) {
Object.keys(labels).forEach(function(key) {
that.addLabel(key, labels[key]);
});
}
};
/**
* Closes the span and queues it for publishing if it is a root.
*/
SpanData.prototype.close = function() {
SpanData.prototype.endSpan = function() {
this.span.close();
if (this.isRoot) {
this.agent.logger.info('Writing root span');
this.agent.traceWriter.writeSpan(this);
TraceWriter.get().writeSpan(this);
}

@@ -140,0 +120,0 @@ };

@@ -20,98 +20,74 @@ /**

var constants = require('./constants.js');
var extend = require('extend');
var is = require('is');
var TraceLabels = require('./trace-labels.js');
var util = require('./util.js');
var Trace = require('./trace.js');
var SpanData = require('./span-data.js');
var uuid = require('uuid');
var TracingPolicy = require('./tracing-policy.js');
/**
* This file describes an interface for third-party plugins to enable tracing
* for arbitrary modules.
* Phantom implementation of the trace api. When disabled, a TraceAgent instance
* will have its public method implementations replaced with corresponding
* no-op implementations in this object.
*/
/**
* An object that represents a single child span. It exposes functions for
* adding labels to or closing the span.
* @param {TraceAgent} agent The underlying trace agent object.
* @param {SpanData} span The internal data structure backing the child span.
*/
function ChildSpan(agent, span) {
this.agent_ = agent;
this.span_ = span;
this.serializedTraceContext_ = agent.generateTraceContext(span, true);
}
/**
* Adds a label to the child span.
* @param {string} key The name of the label to add.
* @param {*} value The value of the label to add.
*/
ChildSpan.prototype.addLabel = function(key, value) {
this.span_.addLabel(key, value);
var phantomApiImpl = {
enhancedDatabaseReportingEnabled: function() { return false; },
runInRootSpan: function(opts, fn) { return fn(null); },
createChildSpan: function(opts) { return null; },
getResponseTraceContext: function(context, traced) { return ''; },
wrap: function(fn) { return fn; },
wrapEmitter: function(ee) {},
};
/**
* Ends the child span. This function should only be called once.
*/
ChildSpan.prototype.endSpan = function() {
this.span_.close();
};
// A sentinal stored in CLS to indicate that the current request was not sampled.
var nullSpan = {};
/**
* Gets the trace context serialized as a string. This string can be set as the
* 'x-cloud-trace-context' field in an HTTP request header to support
* distributed tracing.
* TraceAgent exposes a number of methods to create trace spans and propagate
* trace context across asynchronous boundaries. Trace spans are published
* in the background with a separate TraceWriter instance, which should be
* initialized beforehand.
* @constructor
* @param {String} name A string identifying this TraceAgent instance in logs.
* @param {common.logger} logger A logger object.
* @param {Configuration} config An object specifying how this instance should
* be configured.
*/
ChildSpan.prototype.getTraceContext = function() {
return this.serializedTraceContext_;
};
/**
* An object that represents a single root span. It exposes functions for adding
* labels to or closing the span.
* @param {TraceAgent} agent The underlying trace agent object.
* @param {SpanData} span The internal data structure backing the root span.
*/
function RootSpan(agent, span) {
this.agent_ = agent;
this.span_ = span;
this.serializedTraceContext_ = agent.generateTraceContext(span, true);
function TraceAgent(name, logger, config) {
this.pluginName_ = name;
this.logger_ = logger;
this.namespace_ = cls.getNamespace();
this.policy_ = TracingPolicy.createTracePolicy(config);
this.config_ = config;
}
/**
* Adds a label to the span.
* @param {string} key The name of the label to add.
* @param {*} value The value of the label to add.
* Disable this TraceAgent instance. This function is only for internal use and
* unit tests.
* @private
*/
RootSpan.prototype.addLabel = function(key, value) {
this.span_.addLabel(key, value);
TraceAgent.prototype.disable = function() {
// Even though plugins should be unpatched, setting a new policy that
// never generates traces allows persisting wrapped methods (either because
// they are already instantiated or the plugin doesn't unpatch them) to
// short-circuit out of trace generation logic.
this.policy_ = new TracingPolicy.TraceNonePolicy();
this.namespace_ = null;
for (var memberName in phantomApiImpl) {
this[memberName] = phantomApiImpl[memberName];
}
};
/**
* Ends the span. This function should only be called once.
* Returns whether the TraceAgent instance is active. This function is only for
* internal use and unit tests; under normal circumstances it will always return
* true.
* @private
*/
RootSpan.prototype.endSpan = function() {
this.span_.close();
TraceAgent.prototype.isActive = function() {
return !!this.namespace_;
};
/**
* Gets the trace context serialized as a string. This string can be set as the
* 'x-cloud-trace-context' field in an HTTP request header to support
* distributed tracing.
*/
RootSpan.prototype.getTraceContext = function() {
return this.serializedTraceContext_;
};
// A sentinal stored in CLS to indicate that the current request was not sampled.
var nullSpan = {};
/**
* The functional implementation of the Trace API
*/
function TraceApiImplementation(agent, pluginName) {
this.agent_ = agent;
this.logger_ = agent.logger;
this.pluginName_ = pluginName;
}
/**
* Gets the value of enhancedDatabaseReporting in the trace agent's

@@ -122,4 +98,4 @@ * configuration object.

*/
TraceApiImplementation.prototype.enhancedDatabaseReportingEnabled = function() {
return this.agent_.config_.enhancedDatabaseReporting;
TraceAgent.prototype.enhancedDatabaseReportingEnabled = function() {
return this.config_.enhancedDatabaseReporting;
};

@@ -148,5 +124,8 @@

*/
TraceApiImplementation.prototype.runInRootSpan = function(options, fn) {
TraceAgent.prototype.runInRootSpan = function(options, fn) {
var that = this;
if (!this.agent_.namespace) {
// TODO validate options
// Don't create a root span if the required namespace doesn't exist, or we
// are already in a root span
if (!this.namespace_) {
this.logger_.warn(this.pluginName_ + ': CLS namespace not present; not ' +

@@ -160,6 +139,32 @@ 'running in root span.');

}
return this.agent_.namespace.runAndReturn(function() {
var skipFrames = options.skipFrames ? options.skipFrames + 3 : 3;
var rootSpan = createRootSpan_(that, options, skipFrames);
return fn(rootSpan);
return this.namespace_.runAndReturn(function() {
// Attempt to read incoming trace context.
var incomingTraceContext;
if (is.string(options.traceContext) && !that.config_.ignoreContextHeader) {
incomingTraceContext = util.parseContextFromHeader(options.traceContext);
}
incomingTraceContext = incomingTraceContext || {};
// Consult the trace policy, and don't create a root span if the trace
// policy disallows it.
var locallyAllowed = that.policy_.shouldTrace(Date.now(), options.url || '');
var remotelyAllowed = isNaN(incomingTraceContext.options) ||
(incomingTraceContext.options & constants.TRACE_OPTIONS_TRACE_ENABLED);
if (!locallyAllowed || !remotelyAllowed) {
cls.setRootContext(nullSpan);
return fn(null);
}
// Create a new root span, and invoke fn with it.
var traceId = incomingTraceContext.traceId || (uuid.v4().split('-').join(''));
var parentId = incomingTraceContext.spanId || '0';
var rootContext = new SpanData(new Trace(0, traceId), /* Trace object */
options.name, /* Span name */
parentId, /* Parent's span ID */
true, /* Is root span */
options.skipFrames ? options.skipFrames + 2 : 2);
rootContext.span.kind = 'RPC_SERVER';
cls.setRootContext(rootContext);
return fn(rootContext);
});

@@ -172,3 +177,3 @@ };

* @param {object} options An object that specifies options for how the child
* span is created and propogated.
* span is created and propagated.
* @param {string} options.name The name to apply to the child span.

@@ -180,6 +185,6 @@ * @param {?number} options.skipFrames The number of stack frames to skip when

*/
TraceApiImplementation.prototype.createChildSpan = function(options) {
TraceAgent.prototype.createChildSpan = function(options) {
var rootSpan = cls.getRootContext();
if (!rootSpan) {
// Lost context
// Context was lost.
this.logger_.warn(this.pluginName_ + ': Attempted to create child span ' +

@@ -189,9 +194,19 @@ 'without root');

} else if (rootSpan === nullSpan) {
// Chose not to sample
// Context wasn't lost, but there's no root span, indicating that this
// request should not be traced.
return null;
} else {
if (rootSpan.span.isClosed()) {
this.logger_.warn(this.pluginName_ + ': creating child for an already closed span',
options.name, rootSpan.span.name);
}
// Create a new child span and return it.
options = options || {};
var childContext = this.agent_.startSpan(options.name, {},
options.skipFrames ? options.skipFrames + 2 : 2);
return new ChildSpan(this.agent_, childContext);
var skipFrames = options.skipFrames ? options.skipFrames + 1 : 1;
var childContext = new SpanData(rootSpan.trace, /* Trace object */
options.name, /* Span name */
rootSpan.span.spanId, /* Parent's span ID */
false, /* Is root span */
skipFrames); /* # of frames to skip in stack trace */
return childContext;
}

@@ -214,5 +229,5 @@ };

*/
TraceApiImplementation.prototype.getResponseTraceContext = function(
TraceAgent.prototype.getResponseTraceContext = function(
incomingTraceContext, isTraced) {
var traceContext = this.agent_.parseContextFromHeader(incomingTraceContext);
var traceContext = util.parseContextFromHeader(incomingTraceContext);
if (!traceContext) {

@@ -222,4 +237,3 @@ return '';

traceContext.options = traceContext.options & isTraced;
return traceContext.traceId + '/' + traceContext.spanId + ';o=' +
traceContext.options;
return util.generateTraceContext(traceContext);
};

@@ -233,4 +247,4 @@

*/
TraceApiImplementation.prototype.wrap = function(fn) {
if (!this.agent_.namespace) {
TraceAgent.prototype.wrap = function(fn) {
if (!this.namespace_) {
this.logger_.warn(this.pluginName_ + ': No CLS namespace to bind ' +

@@ -240,3 +254,3 @@ 'function');

}
return this.agent_.namespace.bind(fn);
return this.namespace_.bind(fn);
};

@@ -250,104 +264,14 @@

*/
TraceApiImplementation.prototype.wrapEmitter = function(emitter) {
if (!this.agent_.namespace) {
TraceAgent.prototype.wrapEmitter = function(emitter) {
if (!this.namespace_) {
this.logger_.warn(this.pluginName_ + ': No CLS namespace to bind ' +
'emitter to');
}
this.agent_.namespace.bindEmitter(emitter);
this.namespace_.bindEmitter(emitter);
};
TraceApiImplementation.prototype.constants = constants;
TraceAgent.prototype.constants = constants;
TraceApiImplementation.prototype.labels = TraceLabels;
TraceAgent.prototype.labels = TraceLabels;
/**
* 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 phantomApiImpl = {
enhancedDatabaseReportingEnabled: function() { return false; },
runInRootSpan: function(opts, fn) { return fn(null); },
createChildSpan: function(opts) { return null; },
getResponseTraceContext: function(context, traced) { return ''; },
wrap: function(fn) { return fn; },
wrapEmitter: function(ee) {},
constants: constants,
labels: TraceLabels
};
/**
* Creates an object that provides an interface to the trace agent
* implementation.
* Upon creation, the object is in an "uninitialized" state, corresponding
* to its intended (no-op) behavior before the trace agent is started.
* When the trace agent is started, the interface object becomes
* "initialized", and its underlying implementation is switched to that of
* the actual agent implementation.
* Finally, when the trace agent is stopped, this object enters the "disabled"
* state, and its underlying implementation is switched back to no-op.
* Currently, this only happens when the application's GCP project ID could
* not be determined from the GCP metadata service.
* This object's state changes strictly from uninitialized to initialized,
* and from initialized to disabled.
*/
module.exports = function TraceApi(pluginName) {
var impl = phantomApiImpl;
extend(this, {
enhancedDatabaseReportingEnabled: function() {
return impl.enhancedDatabaseReportingEnabled();
},
runInRootSpan: function(opts, fn) {
return impl.runInRootSpan(opts, fn);
},
createChildSpan: function(opts) {
return impl.createChildSpan(opts);
},
getResponseTraceContext: function(incomingTraceContext, isTraced) {
return impl.getResponseTraceContext(incomingTraceContext, isTraced);
},
wrap: function(fn) {
return impl.wrap(fn);
},
wrapEmitter: function(ee) {
return impl.wrapEmitter(ee);
},
constants: impl.constants,
labels: impl.labels,
isActive: function() {
return impl !== phantomApiImpl;
},
enable_: function(agent) {
impl = new TraceApiImplementation(agent, pluginName);
},
disable_: function() {
impl = phantomApiImpl;
},
private_: function() { return impl.agent_; }
});
return this;
};
// Module-private functions
function createRootSpan_(api, options, skipFrames) {
options = options || {};
// If the options object passed in has the getTraceContext field set,
// try to retrieve the header field containing incoming trace metadata.
var incomingTraceContext;
if (is.string(options.traceContext)) {
incomingTraceContext = api.agent_.parseContextFromHeader(options.traceContext);
}
incomingTraceContext = incomingTraceContext || {};
if (!api.agent_.shouldTrace(options.url || '',
incomingTraceContext.options)) {
cls.setRootContext(nullSpan);
return null;
}
var rootContext = api.agent_.createRootSpanData(options.name,
incomingTraceContext.traceId,
incomingTraceContext.spanId,
skipFrames + 1);
return new RootSpan(api.agent_, rootContext);
}
module.exports = TraceAgent;

@@ -23,3 +23,3 @@ /**

var util = require('./util.js');
var TraceApi = require('./trace-api.js');
var TraceAgent = require('./trace-api.js');

@@ -30,3 +30,3 @@ var plugins = Object.create(null);

var logger;
var logger_;

@@ -40,3 +40,3 @@ function checkLoadedModules() {

if (file.match(regex)) {
logger.error(moduleName + ' tracing might not work as ' + file +
logger_.error(moduleName + ' tracing might not work as ' + file +
' was loaded before the trace agent was initialized.');

@@ -50,3 +50,3 @@ break;

if (first !== '@google-cloud/trace-agent') {
logger.error('Tracing might not work as ' + first +
logger_.error('Tracing might not work as ' + first +
' was loaded with --require before the trace agent was initialized.');

@@ -65,6 +65,6 @@ }

} else if (patch.unpatch && patch.intercept) {
logger.warn('Plugin for ' + patch.file + ': unpatch is not compatible ' +
logger_.warn('Plugin for ' + patch.file + ': unpatch is not compatible ' +
'with intercept.');
} else if (patch.patch && !patch.unpatch) {
logger.warn('Plugin for ' + patch.file + ': patch method given without ' +
logger_.warn('Plugin for ' + patch.file + ': patch method given without ' +
'accompanying unpatch.');

@@ -74,11 +74,12 @@ }

function activate(agent) {
function activate(logger, config) {
if (activated) {
logger.error('Plugins activated more than once.');
logger_.error('Plugins activated more than once.');
return;
}
activated = true;
logger = agent.logger;
var pluginConfig = agent.config().plugins;
logger_ = logger;
var pluginConfig = config.plugins;
for (var moduleName in pluginConfig) {

@@ -88,11 +89,6 @@ if (!pluginConfig[moduleName]) {

}
// Create a new object exposing functions to create trace spans and
// propagate context. This relies on functions currently exposed by the
// agent.
var api = new TraceApi(moduleName);
api.enable_(agent);
plugins[moduleName] = {
file: pluginConfig[moduleName],
patches: {},
api: api
agent: new TraceAgent(moduleName, logger_, config)
};

@@ -124,3 +120,3 @@ }

if (Object.keys(patchSet).length === 0) {
logger.warn(moduleRoot + ': version ' + version + ' not supported ' +
logger_.warn(moduleRoot + ': version ' + version + ' not supported ' +
'by plugin.');

@@ -138,6 +134,6 @@ }

if (patch.patch) {
patch.patch(patch.module, instrumentation.api);
patch.patch(patch.module, instrumentation.agent);
}
if (patch.intercept) {
patch.module = patch.intercept(patch.module, instrumentation.api);
patch.module = patch.intercept(patch.module, instrumentation.agent);
intercepts[loadPath] = {

@@ -169,3 +165,3 @@ interceptedValue: patch.module

}
logger.info('Patching ' + request + ' at version ' + moduleVersion);
logger_.info('Patching ' + request + ' at version ' + moduleVersion);
var patchedRoot = loadAndPatch(instrumentation, moduleRoot,

@@ -192,3 +188,3 @@ moduleVersion);

var instrumentation = plugins[moduleName];
instrumentation.api.disable_();
instrumentation.agent.disable();
for (var moduleRoot in instrumentation.patches) {

@@ -199,3 +195,3 @@ var patchSet = instrumentation.patches[moduleRoot];

if (patch.unpatch !== undefined) {
logger.info('Unpatching' + moduleName);
logger_.info('Unpatching ' + moduleName);
patch.unpatch(patch.module);

@@ -202,0 +198,0 @@ }

@@ -19,4 +19,4 @@ /**

var common = require('@google-cloud/common');
var gcpMetadata = require('gcp-metadata');
var common = require('@google-cloud/common');
var util = require('util');

@@ -27,4 +27,3 @@ var traceLabels = require('./trace-labels.js');

/* @const {Array<string>} list of scopes needed to operate with the trace API */
var SCOPES = ['https://www.googleapis.com/auth/trace.append'];
var onUncaughtExceptionValues = ['ignore', 'flush', 'flushAndExit'];

@@ -34,2 +33,5 @@ var headers = {};

/* @const {Array<string>} list of scopes needed to operate with the trace API */
var SCOPES = ['https://www.googleapis.com/auth/trace.append'];
/**

@@ -42,4 +44,4 @@ * Creates a basic trace writer.

*/
function TraceWriter(logger, options) {
options = options || {};
function TraceWriter(logger, config) {
config = config || {};

@@ -52,3 +54,3 @@ var serviceOptions = {

};
common.Service.call(this, serviceOptions, options);
common.Service.call(this, serviceOptions, config);

@@ -59,3 +61,3 @@ /** @private */

/** @private */
this.config_ = options;
this.config_ = config;

@@ -71,8 +73,47 @@ /** @private {Array<string>} stringified traces to be published */

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 onUncaughtException = config.onUncaughtException;
if (onUncaughtException !== 'ignore') {
var that = this;
this.unhandledException_ = function() {
that.flushBuffer_();
if (onUncaughtException === 'flushAndExit') {
setTimeout(function() {
process.exit(1);
}, 2000);
}
};
process.on('uncaughtException', this.unhandledException_);
}
}
util.inherits(TraceWriter, common.Service);
TraceWriter.prototype.stop = function() {
this.isActive = false;
};
TraceWriter.prototype.initialize = function(cb) {
var that = this;
// Ensure that cb is called only once.
var pendingOperations = 2;
// Schedule periodic flushing of the buffer, but only if we are able to get
// the project number (potentially from the network.)
var that = this;
that.getProjectId(function(err, project) {
if (err) { return; } // ignore as index.js takes care of this.
that.scheduleFlush_(project);
if (err) {
that.logger_.error('Unable to acquire the project number from metadata ' +
'service. Please provide a valid project number as an env. ' +
'variable, or through config.projectId passed to start(). ' + err);
cb(err);
} else {
that.config_.projectId = project;
that.scheduleFlush_();
if (--pendingOperations === 0) {
cb();
}
}
});

@@ -106,9 +147,11 @@

that.defaultLabels_ = labels;
if (--pendingOperations === 0) {
cb();
}
});
});
}
util.inherits(TraceWriter, common.Service);
};
TraceWriter.prototype.stop = function() {
this.isActive = false;
TraceWriter.prototype.config = function() {
return this.config_;
};

@@ -161,2 +204,12 @@

}, function(err, response, projectId) {
if (response && response.statusCode !== 200) {
if (response.statusCode === 503) {
err = new Error('Metadata service responded with a 503 status ' +
'code. This may be due to a temporary server error; please try ' +
'again later.');
} else {
err = new Error('Metadata service responded with the following ' +
'status code: ' + response.statusCode);
}
}
if (err) {

@@ -206,3 +259,3 @@ callback(err);

that.logger_.info('No project number, dropping trace.');
return; // ignore as index.js takes care of this.
return; // if we even reach this point, disabling traces is already imminent.
}

@@ -217,3 +270,3 @@

that.logger_.info('Flushing: trace buffer full');
setImmediate(function() { that.flushBuffer_(project); });
setImmediate(function() { that.flushBuffer_(); });
}

@@ -228,9 +281,9 @@ });

*/
TraceWriter.prototype.scheduleFlush_ = function(project) {
TraceWriter.prototype.scheduleFlush_ = function() {
this.logger_.info('Flushing: performing periodic flush');
this.flushBuffer_(project);
this.flushBuffer_();
// Do it again after delay
if (this.isActive) {
setTimeout(this.scheduleFlush_.bind(this, project),
setTimeout(this.scheduleFlush_.bind(this),
this.config_.flushDelaySeconds * 1000).unref();

@@ -245,3 +298,3 @@ }

*/
TraceWriter.prototype.flushBuffer_ = function(projectId) {
TraceWriter.prototype.flushBuffer_ = function() {
if (this.buffer_.length === 0) {

@@ -255,3 +308,3 @@ return;

this.logger_.debug('Flushing traces', buffer);
this.publish_(projectId, '{"traces":[' + buffer.join() + ']}');
this.publish_('{"traces":[' + buffer.join() + ']}');
};

@@ -265,8 +318,6 @@

*/
TraceWriter.prototype.publish_ = function(projectId, json) {
// TODO(ofrobots): assert.ok(this.config_.project), and stop accepting
// projectId as an argument.
TraceWriter.prototype.publish_ = function(json) {
var that = this;
var uri = 'https://cloudtrace.googleapis.com/v1/projects/' +
projectId + '/traces';
this.config_.projectId + '/traces';

@@ -279,2 +330,3 @@ var options = {

};
that.logger_.debug('TraceWriter: publishing to ' + uri);
that.request(options, function(err, body, response) {

@@ -290,7 +342,22 @@ if (err) {

/**
* Export TraceWriter.
* FIXME(ofrobots): TraceWriter should be a singleton. We should export
* a get function that returns the instance instead.
*/
module.exports = TraceWriter;
// Singleton
var traceWriter;
module.exports = {
create: function(logger, config, cb) {
if (!cb) {
cb = function() {};
}
if (!traceWriter || config.forceNewAgent_) {
traceWriter = new TraceWriter(logger, config);
traceWriter.initialize(cb);
}
return traceWriter;
},
get: function() {
if (!traceWriter) {
throw new Error('TraceWriter singleton was not initialized.');
}
return traceWriter;
}
};

@@ -81,2 +81,21 @@ /**

/**
* Generates a trace context header value that can be used
* to follow the associated request through other Google services.
*
* @param {?{traceId: string, spanId: string, options: number}} traceContext
* An object with information sufficient for creating a serialized trace
* context.
*/
function generateTraceContext(traceContext) {
if (!traceContext) {
return '';
}
var header = traceContext.traceId + '/' + traceContext.spanId;
if (typeof traceContext.options !== 'undefined') {
header += (';o=' + traceContext.options);
}
return header;
}
/**
* Retrieves a package name from the full import path.

@@ -132,2 +151,3 @@ * For example:

parseContextFromHeader: parseContextFromHeader,
generateTraceContext: generateTraceContext,
packageNameFromPath: packageNameFromPath,

@@ -134,0 +154,0 @@ findModulePath: findModulePath,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc