diff --git a/apm-collector/apm-collector-core/pom.xml b/apm-collector/apm-collector-core/pom.xml index 4d35a3a00..5c19df2da 100644 --- a/apm-collector/apm-collector-core/pom.xml +++ b/apm-collector/apm-collector-core/pom.xml @@ -23,5 +23,10 @@ gson 2.8.1 + + net.bytebuddy + byte-buddy + 1.7.8 + \ No newline at end of file diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java index 2cde24246..838b564e5 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleManager.java @@ -31,6 +31,7 @@ import java.util.ServiceLoader; */ public class ModuleManager { private Map loadedModules = new HashMap<>(); + private boolean isServiceInstrument = false; /** * Init the given modules @@ -83,4 +84,12 @@ public class ModuleManager { return module; throw new ModuleNotFoundException(moduleName + " missing."); } + + public boolean isServiceInstrument() { + return isServiceInstrument; + } + + public void setServiceInstrument(boolean serviceInstrument) { + isServiceInstrument = serviceInstrument; + } } diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java index 7c693de2c..05d498879 100644 --- a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/ModuleProvider.java @@ -21,6 +21,7 @@ package org.skywalking.apm.collector.core.module; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import org.skywalking.apm.collector.core.module.instrument.ServiceInstrumentation; /** * The ModuleProvider is an implementation of a {@link Module}. @@ -90,6 +91,9 @@ public abstract class ModuleProvider { protected final void registerServiceImplementation(Class serviceType, Service service) throws ServiceNotProvidedException { if (serviceType.isInstance(service)) { + if (manager.isServiceInstrument()) { + service = ServiceInstrumentation.INSTANCE.buildServiceUnderMonitor(service); + } this.services.put(serviceType, service); } else { throw new ServiceNotProvidedException(serviceType + " is not implemented by " + service); diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceInstrumentation.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceInstrumentation.java new file mode 100644 index 000000000..1b85a8b6c --- /dev/null +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceInstrumentation.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017, OpenSkywalking Organization 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. + * + * Project repository: https://github.com/OpenSkywalking/skywalking + */ + +package org.skywalking.apm.collector.core.module.instrument; + +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.matcher.ElementMatcher; +import org.skywalking.apm.collector.core.module.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * @author wu-sheng + */ +public enum ServiceInstrumentation { + INSTANCE; + + private final Logger logger = LoggerFactory.getLogger(ServiceInstrumentation.class); + private ElementMatcher excludeObjectMethodsMatcher; + + public Service buildServiceUnderMonitor(Service implementation) { + if (TracedService.class.isInstance(implementation)) { + // Duplicate service instrument, ignore. + return implementation; + } + try { + return new ByteBuddy().subclass(implementation.getClass()) + .implement(TracedService.class) + .method(getDefaultMatcher()).intercept( + MethodDelegation.withDefaultConfiguration().to(new ServiceMetricCollector()) + ).make().load(getClass().getClassLoader() + ).getLoaded().newInstance(); + } catch (InstantiationException e) { + logger.error("Create instrumented service " + implementation.getClass() + " fail.", e); + } catch (IllegalAccessException e) { + logger.error("Create instrumented service " + implementation.getClass() + " fail.", e); + } + return implementation; + } + + private ElementMatcher getDefaultMatcher() { + if (excludeObjectMethodsMatcher == null) { + excludeObjectMethodsMatcher = not(isStatic().or(named("getClass")).or(named("hashCode")).or(named("equals")).or(named("clone")) + .or(named("toString")).or(named("notify")).or(named("notifyAll")).or(named("wait")).or(named("finalize"))); + } + return excludeObjectMethodsMatcher; + } +} diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceMetricCollector.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceMetricCollector.java new file mode 100644 index 000000000..2da824857 --- /dev/null +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/ServiceMetricCollector.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017, OpenSkywalking Organization 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. + * + * Project repository: https://github.com/OpenSkywalking/skywalking + */ + +package org.skywalking.apm.collector.core.module.instrument; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.SuperCall; +import net.bytebuddy.implementation.bind.annotation.This; + +/** + * @author wu-sheng + */ +public class ServiceMetricCollector { + + @RuntimeType + public Object intercept(@This Object obj, + @AllArguments Object[] allArguments, + @SuperCall Callable zuper, + @Origin Method method + ) throws Throwable { + return zuper.call(); + } +} diff --git a/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/TracedService.java b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/TracedService.java new file mode 100644 index 000000000..7cec836ca --- /dev/null +++ b/apm-collector/apm-collector-core/src/main/java/org/skywalking/apm/collector/core/module/instrument/TracedService.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017, OpenSkywalking Organization 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. + * + * Project repository: https://github.com/OpenSkywalking/skywalking + */ + +package org.skywalking.apm.collector.core.module.instrument; + +import org.skywalking.apm.collector.core.module.ModuleManager; + +/** + * The TracedService implementation are dynamic class, generated by {@link ServiceInstrumentation}, when + * {@link ModuleManager#isServiceInstrument} is true. + * + * By that, all the services metrics are collected, and report in the certain cycle through console. + * + * @author wu-sheng + */ +public interface TracedService { +} diff --git a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleBProvider.java b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleBProvider.java index d84602857..e5de9fd65 100644 --- a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleBProvider.java +++ b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleBProvider.java @@ -19,6 +19,17 @@ package org.skywalking.apm.collector.core.module; import java.util.Properties; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.matcher.ElementMatcher; +import org.junit.Test; +import org.skywalking.apm.collector.core.module.instrument.ServiceMetricCollector; +import org.skywalking.apm.collector.core.module.instrument.TracedService; + +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; /** * @author wu-sheng diff --git a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleManagerTest.java b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleManagerTest.java index fda8a4f34..0874c9569 100644 --- a/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleManagerTest.java +++ b/apm-collector/apm-collector-core/src/test/java/org/skywalking/apm/collector/core/module/ModuleManagerTest.java @@ -18,7 +18,9 @@ package org.skywalking.apm.collector.core.module; +import org.junit.Assert; import org.junit.Test; +import org.skywalking.apm.collector.core.module.instrument.TracedService; /** * @author wu-sheng @@ -37,4 +39,21 @@ public class ModuleManagerTest { BaseModuleA.ServiceABusiness1 serviceABusiness1 = manager.find("BaseA").provider().getService(BaseModuleA.ServiceABusiness1.class); serviceABusiness1.print(); } + + //@Test + public void testInstrument() throws ServiceNotProvidedException, ModuleNotFoundException, ProviderNotFoundException, DuplicateProviderException { + ApplicationConfiguration configuration = new ApplicationConfiguration(); + configuration.addModule("Test").addProviderConfiguration("TestModule-Provider", null); + configuration.addModule("BaseA").addProviderConfiguration("P-A",null); + configuration.addModule("BaseB").addProviderConfiguration("P-B",null); + + ModuleManager manager = new ModuleManager(); + manager.setServiceInstrument(true); + manager.init(configuration); + + BaseModuleA.ServiceABusiness1 serviceABusiness1 = manager.find("BaseA").provider().getService(BaseModuleA.ServiceABusiness1.class); + serviceABusiness1.print(); + + Assert.assertTrue(serviceABusiness1 instanceof TracedService); + } }