File: //home/arjun/.pm2/modules/pm2-logrotate/node_modules/vxx/src/trace-plugin-loader.js
/**
* Copyright 2017 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.
*/
/**
* This file has been modified by Keymetrics
*/
'use strict';
var Module = require('module');
var shimmer = require('shimmer');
var path = require('path');
var semver = require('semver');
var util = require('./util.js');
var TraceApi = require('./trace-api.js');
var plugins = Object.create(null);
var intercepts = Object.create(null);
var activated = false;
var logger;
function checkLoadedModules() {
for (var moduleName in plugins) {
// \\ is benign on unix and escapes \\ on windows
var regex = new RegExp('node_modules\\' + path.sep + moduleName +
'\\' + path.sep);
for (var file in require.cache) {
if (file.match(regex)) {
logger.error(moduleName + ' tracing might not work as ' + file +
' was loaded before the trace agent was initialized.');
break;
}
}
}
if (process._preload_modules && process._preload_modules.length > 0) {
var first = process._preload_modules[0];
if (first !== 'vxx') {
logger.error('Tracing might not work as ' + first +
' was loaded with --require before the trace agent was initialized.');
}
}
}
function checkPatch(patch) {
if (!patch.patch && !patch.intercept) {
throw new Error('Plugin for ' + patch.file + ' doesn\'t patch ' +
'anything.');
} else if (patch.patch && patch.intercept) {
throw new Error('Plugin for ' + patch.file + ' has ' +
'both intercept and patch functions.');
} else if (patch.unpatch && patch.intercept) {
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 ' +
'accompanying unpatch.');
}
}
function activate(agent) {
if (activated) {
logger.error('Plugins activated more than once.');
return;
}
activated = true;
logger = agent.logger;
var pluginConfig = agent.config().plugins;
for (var moduleName in pluginConfig) {
if (!pluginConfig[moduleName]) {
continue;
}
// 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
};
}
checkLoadedModules();
// hook into Module._load so that we can hook into userspace frameworks
shimmer.wrap(Module, '_load', function(originalModuleLoad) {
function loadAndPatch(instrumentation, moduleRoot, version) {
var patchSet = instrumentation.patches[moduleRoot];
if (!patchSet) {
// Load the plugin object
var plugin = originalModuleLoad(instrumentation.file, module, false);
patchSet = {};
plugin.forEach(function(patch) {
if (!patch.versions || semver.satisfies(version, patch.versions)) {
var file = patch.file || '';
patchSet[file] = {
file: file,
patch: patch.patch,
unpatch: patch.unpatch,
intercept: patch.intercept
};
checkPatch(patchSet[file]);
}
});
if (Object.keys(patchSet).length === 0) {
logger.warn(moduleRoot + ': version ' + version + ' not supported ' +
'by plugin.');
}
instrumentation.patches[moduleRoot] = patchSet;
}
for (var file in patchSet) {
var patch = patchSet[file];
var loadPath = moduleRoot ? path.join(moduleRoot, patch.file) : patch.file;
if (!patch.module) {
patch.module = originalModuleLoad(loadPath, module, false);
}
if (patch.patch) {
patch.patch(patch.module, instrumentation.api);
}
if (patch.intercept) {
patch.module = patch.intercept(patch.module, instrumentation.api);
intercepts[loadPath] = {
interceptedValue: patch.module
};
}
}
var rootPatch = patchSet[''];
if (rootPatch && rootPatch.intercept) {
return rootPatch.module;
} else {
return null;
}
}
function moduleAlreadyPatched(instrumentation, moduleRoot, version) {
return instrumentation.patches[moduleRoot];
}
// Future requires get patched as they get loaded.
return function Module_load(request, parent, isMain) {
var instrumentation = plugins[request];
if (instrumentation) {
var moduleRoot = util.findModulePath(request, parent);
var moduleVersion = util.findModuleVersion(moduleRoot, originalModuleLoad);
if (moduleAlreadyPatched(instrumentation, moduleRoot, moduleVersion)) {
return originalModuleLoad.apply(this, arguments);
}
logger.info('Patching ' + request + ' at version ' + moduleVersion);
var patchedRoot = loadAndPatch(instrumentation, moduleRoot,
moduleVersion);
if (patchedRoot !== null) {
return patchedRoot;
}
} else {
var modulePath = Module._resolveFilename(request, parent).replace('/', path.sep);
if (intercepts[modulePath]) {
return intercepts[modulePath].interceptedValue;
}
}
return originalModuleLoad.apply(this, arguments);
};
});
}
function deactivate() {
if (activated) {
activated = false;
for (var moduleName in plugins) {
var instrumentation = plugins[moduleName];
instrumentation.api.disable_();
for (var moduleRoot in instrumentation.patches) {
var patchSet = instrumentation.patches[moduleRoot];
for (var file in patchSet) {
var patch = patchSet[file];
if (patch.unpatch !== undefined) {
logger.info('Unpatching' + moduleName);
patch.unpatch(patch.module);
}
}
}
}
plugins = Object.create(null);
intercepts = Object.create(null);
// unhook module.load
shimmer.unwrap(Module, '_load');
}
}
module.exports = {
activate: activate,
deactivate: deactivate
};