提交 75b9d8f6 编写于 作者: X Xin,Zhang 提交者: wu-sheng

Support multiple version enhancement (#23)

* Support multiple version enhancement

* Change the mysql version check

* Fix CI failed
上级 69977daa
......@@ -45,13 +45,13 @@ Agent.prototype.start = function(agentOptions) {
let _agent = this;
require("./require-module-hook")(_pluginManager.allEnhanceModules(),
function(originModule, moduleName, version, enhanceFile) {
let plugin = _pluginManager.findPlugin(moduleName, version, enhanceFile);
let intercept = _pluginManager.attemptToFindInterceptor(moduleName, version, enhanceFile);
if (plugin == undefined) {
if (intercept == undefined) {
return originModule;
}
return plugin(originModule, _agent._instrumentation,
return intercept(originModule, _agent._instrumentation,
_agent._contextManager);
});
};
......@@ -17,9 +17,9 @@
"use strict";
const onFinished = require("on-finished");
const ContextCarrier = require("../../../trace/context-carrier");
const layerDefine = require("../../../trace/span-layer");
const componentDefine = require("../../../trace/component-define");
const ContextCarrier = require("../../trace/context-carrier");
const layerDefine = require("../../trace/span-layer");
const componentDefine = require("../../trace/component-define");
/**
*
......
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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";
const Plugin = require("../plugin");
module.exports = new Plugin("http-plugin", "http", [{
name: "v1",
description: "",
enhanceModule: ["http"],
canEnhance: function(version, enhanceFile) {
return true;
},
getInterceptor: function(enhanceFile) {
return require("./" + enhanceFile);
},
}]);
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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";
const Plugin = require("../plugin");
const semver = require("semver");
module.exports = new Plugin("mysql-plugin", "mysql", [{
_name: "v1",
_description: "Only enhancements to version 1.x",
_enhanceModules: ["mysql"],
canEnhance: function(version, enhanceFile) {
if (this._enhanceModules.indexOf(enhanceFile) > -1 && semver.satisfies(version, "~1")) {
return true;
}
return false;
},
getInterceptor: function(enhanceFile) {
return require("./" + this._name + "/" + enhanceFile);
},
}, {
_name: "v2",
_description: "Only enhancements to version 2.x",
_enhanceModules: ["mysql"],
canEnhance: function(version, enhanceFile) {
if (this._enhanceModules.indexOf(enhanceFile) > -1 && semver.satisfies(version, "~2")) {
return true;
}
return false;
},
getInterceptor: function(enhanceFile) {
return require("./" + this._name + "/" + enhanceFile);
},
}]);
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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";
const spanLayer = require("../../../trace/span-layer");
const componentDefine = require("../../../trace/component-define");
const Tags = require("../../../trace/tags");
/**
* @param {originModule} originModule
* @param {instrumentation} instrumentation
* @param {contextManager} contextManager
* @return {*}
* @author zhang xin
*/
module.exports = function(originModule, instrumentation, contextManager) {
instrumentation.enhanceMethod(originModule, "createConnection",
wrapCreateConnection);
instrumentation.enhanceMethod(originModule, "createPool", wrapCreatePool);
instrumentation.enhanceMethod(originModule, "createPoolCluster", wrapCreatePoolCluster);
/**
* @this OriginObject
* @param {original} original
* @return {function(): *}
*/
function wrapCreateConnection(original) {
return function() {
let connection = original.apply(this, arguments);
enhanceQueryMethod(connection, instrumentation, contextManager);
return connection;
};
}
/**
*
* @param {origin} origin
* @return {function(): *}
*/
function wrapCreatePool(origin) {
return function createPool() {
let pool = origin.apply(this, arguments);
instrumentation.enhanceMethod(pool.property, "createPool", wrapGetConnection);
return pool;
};
}
/**
*
* @param {origin} origin
* @return {function(): *}
*/
function wrapGetConnection(origin) {
return function getConnection() {
let callBack = arguments[0];
if (typeof callBack === "function") {
arguments[0] = function(err, connection) {
if (connection) enhanceQueryMethod(connection, instrumentation, contextManager);
return callBack.apply(this, arguments);
};
}
return origin.apply(this, arguments);
};
}
/**
*
* @param {origin} origin
* @return {function(): *}
*/
function wrapCreatePoolCluster(origin) {
return function wrappedCreatePoolCluster() {
let cluster = origin.apply(this, arguments);
instrumentation.enhanceMethod(cluster, "of", function wrapOf(original) {
return function wrappedOf() {
let ofCluster = original.apply(this, arguments);
instrumentation.enhanceMethod(ofCluster, "getConnection", wrapGetConnection);
return ofCluster;
};
});
return cluster;
};
}
return originModule;
};
/**
* @param {obj} obj
* @param {instrumentation} instrumentation
* @param {contextManager} contextManager
* @return {wrappedMethod}
*/
function enhanceQueryMethod(obj, instrumentation, contextManager) {
let connection = obj;
return instrumentation.enhanceMethod(obj, "query", queryInterceptor);
/**
*
* @param {original} original
* @return {function(*=, *=, *=): *}
*/
function queryInterceptor(original) {
return function(sql, values, cb) {
let span = contextManager.createExitSpan("Mysql/query", connection.config.host +
":" + connection.config.port);
span.component(componentDefine.Components.MYSQL);
span.spanLayer(spanLayer.Layers.DB);
Tags.DB_TYPE.tag(span, "sql");
Tags.DB_INSTANCE.tag(span, connection.config.database);
Tags.DB_STATEMENT.tag(span, sql);
let enhancedValues = values;
let enhanceCallback = cb;
if (typeof values === "function") {
enhancedValues = instrumentation.enhanceCallback(span.traceContext(),
contextManager, function() {
contextManager.finishSpan(span);
return values.apply(this, arguments);
});
} else if (typeof cb === "function") {
enhanceCallback = instrumentation.enhanceCallback(span.traceContext(),
contextManager, function() {
contextManager.finishSpan(span);
return cb.apply(this, arguments);
});
}
return original.apply(this, [sql, enhancedValues, enhanceCallback]);
};
}
}
......@@ -29,10 +29,20 @@ const OFFICER_SUPPORTED_MODULE = ["mysql", "http"];
*/
function PluginManager(customPlugin) {
this._enhanceModule = OFFICER_SUPPORTED_MODULE;
const plugins = {};
this._enhanceModule.forEach(function(enhanceModule) {
plugins[enhanceModule] = require("./" + enhanceModule);
});
this._plugins = plugins;
}
PluginManager.prototype.findPlugin = function(moduleName, version, enhanceFile) {
return require("./supported/" + moduleName + "/" + enhanceFile);
PluginManager.prototype.attemptToFindInterceptor = function(moduleName, version, enhanceFile) {
if (!this._plugins || !this._plugins[moduleName]) {
return undefined;
}
return this._plugins[moduleName].attemptToFindInterceptor(version, enhanceFile);
};
PluginManager.prototype.allEnhanceModules = function() {
......
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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";
module.exports = Plugin;
/**
*
* @param {name} name
* @param {moduleName} moduleName
* @param {enhanceVersions} enhanceVersions
* @constructor
* @author zhang xin
*/
function Plugin(name, moduleName, enhanceVersions) {
this._name = name;
this._moduleName = moduleName;
this._enhanceVersions = enhanceVersions;
}
Plugin.prototype.getName = function() {
return this._name;
};
Plugin.prototype.attemptToFindInterceptor = function(version, enhanceFile) {
let interceptor = undefined;
this._enhanceVersions.forEach(function(enhanceVersion) {
if (enhanceVersion.canEnhance(version, enhanceFile)) {
interceptor = enhanceVersion.getInterceptor(enhanceFile);
return;
}
});
return interceptor;
};
Plugin.prototype.getModuleName = function() {
return this._moduleName;
};
/*
* Licensed to the OpenSkywalking under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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";
const spanLayer = require("../../../trace/span-layer");
const componentDefine = require("../../../trace/component-define");
const Tags = require("../../../trace/tags");
/**
* @param {originModule} originModule
* @param {instrumentation} instrumentation
* @param {contextManager} contextManager
* @return {*}
* @author zhang xin
*/
module.exports = function(originModule, instrumentation, contextManager) {
instrumentation.enhanceMethod(originModule, "createConnection",
wrapCreateConnection);
/**
* @this OriginObject
* @param {original} original
* @return {function(): *}
*/
function wrapCreateConnection(original) {
return function() {
let connection = original.apply(this, arguments);
enhanceQueryMethod(connection, instrumentation, contextManager);
return connection;
};
}
return originModule;
};
/**
* @param {obj} obj
* @param {instrumentation} instrumentation
* @param {contextManager} contextManager
* @return {wrappedMethod}
*/
function enhanceQueryMethod(obj, instrumentation, contextManager) {
let connection = obj;
return instrumentation.enhanceMethod(obj, "query", queryInterceptor);
/**
*
* @param {original} original
* @return {function(*=, *=, *=): *}
*/
function queryInterceptor(original) {
return function(sql, values, cb) {
let span = contextManager.createExitSpan("Mysql/query", connection.config.host +
":" + connection.config.port);
span.component(componentDefine.Components.MYSQL);
span.spanLayer(spanLayer.Layers.DB);
Tags.DB_TYPE.tag(span, "sql");
Tags.DB_INSTANCE.tag(span, connection.config.database);
Tags.DB_STATEMENT.tag(span, sql);
let enhancedValues = values;
let enhanceCallback = cb;
if (typeof values === "function") {
enhancedValues = instrumentation.enhanceCallback(span.traceContext(),
contextManager, function() {
contextManager.finishSpan(span);
return values.apply(this, arguments);
});
} else if (typeof cb === "function") {
enhanceCallback = instrumentation.enhanceCallback(span.traceContext(),
contextManager, function() {
contextManager.finishSpan(span);
return cb.apply(this, arguments);
});
}
return original.apply(this, [sql, enhancedValues, enhanceCallback]);
};
}
}
......@@ -37,8 +37,7 @@ function hook(modules, requireCallback) {
let filename = Module._resolveFilename(request, this);
let core = filename.indexOf(path.sep) === -1;
let moduleName = "";
let matchModuleName = "";
let enhanceFile = "";
let enhanceFile = undefined;
let version = undefined;
let basedir = undefined;
......@@ -49,7 +48,6 @@ function hook(modules, requireCallback) {
let exports = _origRequire.apply(this, arguments);
if (core) {
moduleName = filename;
matchModuleName = filename;
enhanceFile = filename;
} else {
let pathSegment = filename.split(path.sep);
......@@ -63,10 +61,7 @@ function hook(modules, requireCallback) {
let res = resolve.sync(moduleName, {basedir: basedir});
if (res !== filename) {
enhanceFile = pathSegment[pathSegment.length - 1].split(".")[0];
matchModuleName = moduleName + ":" + pathSegment[pathSegment.length - 1];
} else {
matchModuleName = moduleName;
moduleName = moduleName;
enhanceFile = moduleName;
}
} catch (e) {
......@@ -74,7 +69,7 @@ function hook(modules, requireCallback) {
}
}
if (modules && modules.indexOf(matchModuleName) === -1) return exports;
if (modules && modules.indexOf(moduleName) === -1) return exports;
// get version. Because of we enhance Module.prototype.require, so we can't fetch the version by execute
// `require(basedir + path.sep + "package.json")` method
......
......@@ -49,7 +49,7 @@
"mysql": "^2.15.0",
"require-self": "^0.2.1",
"run-script-os": "^1.0.3",
"semver": "^5.5.0"
"semver": "^5.5.1"
},
"eslintIgnore": [
"lib/network/*.js"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册