/*
* Copyright 2005-2014 Red Hat, Inc.
* Red Hat 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.
*/
package io.fabric8.apmagent;
import io.fabric8.apmagent.metrics.ApmAgentContext;
import io.fabric8.apmagent.metrics.ThreadMetrics;
import io.fabric8.apmagent.strategy.sampling.SamplingStrategy;
import io.fabric8.apmagent.strategy.trace.TraceStrategy;
import io.fabric8.apmagent.utils.PropertyUtils;
import org.jolokia.jvmagent.JvmAgent;
import org.slf4j.LoggerFactory;
import java.lang.instrument.Instrumentation;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class ApmAgent implements ApmAgentMBean, ApmConfigurationChangeListener {
public static final ApmAgent INSTANCE = new ApmAgent();
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ApmAgent.class);
final ApmConfiguration configuration = new ApmConfiguration();
private AtomicBoolean initialized = new AtomicBoolean();
private AtomicBoolean started = new AtomicBoolean();
private final ApmAgentContext apmAgentContext;
private Instrumentation instrumentation;
private Strategy strategy;
// The following is the entry point when loaded dynamically to inject
// recorders from the target process.
private ApmAgent() {
this.apmAgentContext = new ApmAgentContext(this);
}
// The following is the entry point when loaded as a java agent along with
// the target process on the java command line.
public static void agentmain(final String args,
final Instrumentation instrumentation) throws Exception {
try {
ApmAgent agent = ApmAgent.INSTANCE;
if (agent.initialize(instrumentation, args)) {
if (agent.getConfiguration().isStartJolokiaAgent()) {
JvmAgent.agentmain(args);
}
}
} catch (Exception e) {
LOG.info("Failed in agentmain", e);
throw e;
}
}
public static void premain(String args, Instrumentation instrumentation) throws Exception {
try {
ApmAgent agent = ApmAgent.INSTANCE;
if (agent.initialize(instrumentation, args)) {
if (agent.getConfiguration().isStartJolokiaAgent()) {
JvmAgent.premain(args);
}
if (agent.getConfiguration().isAutoStartMetrics()) {
agent.startMetrics();
}
}
} catch (Exception e) {
LOG.error("Failed in premain", e);
throw e;
}
}
public static void enterMethod(String methodName) {
if (INSTANCE.started.get()) {
INSTANCE.apmAgentContext.enterMethod(Thread.currentThread(), methodName, false);
}
}
public static void exitMethod(String methodName) {
if (INSTANCE.started.get()) {
INSTANCE.apmAgentContext.exitMethod(Thread.currentThread(), methodName, false);
}
}
public List<String> getTransformedMethods() {
if (isInitialized()) {
return apmAgentContext.getTransformedMethods();
}
return Collections.EMPTY_LIST;
}
public List<String> getAllMethods() {
if (isInitialized()) {
return apmAgentContext.getAllMethods();
}
return Collections.EMPTY_LIST;
}
public List<ThreadMetrics> getThreadMetrics() {
if (isInitialized()) {
return apmAgentContext.getThreadMetrics();
}
return Collections.EMPTY_LIST;
}
public boolean isInitialized() {
return initialized.get();
}
public ApmConfiguration getConfiguration() {
return configuration;
}
/**
* @param instrumentation
* @param args
* @return false if already initialized, else true if is actually initialized
*/
public boolean initialize(final Instrumentation instrumentation, String args) throws Exception {
boolean result;
if ((result = initialized.compareAndSet(false, true))) {
this.instrumentation = instrumentation;
PropertyUtils.setProperties(configuration, args);
configuration.addChangeListener(this);
apmAgentContext.initialize();
ApmConfiguration.STRATEGY theStrategy = configuration.getStrategyImpl();
switch (theStrategy) {
case TRACE:
this.strategy = new TraceStrategy(apmAgentContext, instrumentation);
LOG.info("Using Trace strategy");
break;
default:
this.strategy = new SamplingStrategy(apmAgentContext);
LOG.info("Using Sampling strategy");
}
this.strategy.initialize();
//add shutdown hook
Thread cleanup = new Thread() {
@Override
public void run() {
try {
ApmAgent apmAgent = ApmAgent.INSTANCE;
apmAgent.shutDown();
} catch (Exception e) {
LOG.error("Failed to run shutdown hook", e);
}
}
};
Runtime.getRuntime().addShutdownHook(cleanup);
}
return result;
}
public void startMetrics() {
if (isInitialized() && started.compareAndSet(false, true)) {
apmAgentContext.start();
try {
Strategy s = this.strategy;
if (s != null) {
s.start();
}
} catch (Throwable e) {
LOG.error("Failed to start strategy ", e);
}
} else {
System.err.println("STARTMETRICS ALREADY STARTED");
}
}
public void stopMetrics() {
if (started.compareAndSet(true, false)) {
try {
Strategy s = this.strategy;
if (s != null) {
s.stop();
}
} catch (Throwable e) {
LOG.error("Failed to stop strategy", e);
}
apmAgentContext.stop();
}
}
// The following is the implementation for resetting the instrumentation.
public void shutDown() {
if (initialized.compareAndSet(true, false)) {
stopMetrics();
configuration.removeChangeListener(this);
apmAgentContext.shutDown();
try {
//clean up
Strategy s = this.strategy;
if (s != null) {
s.shutDown();
}
} catch (Throwable e) {
LOG.error("Failed to shutdown", e);
}
}
}
@Override
public void configurationChanged() {
if (started.get()) {
if (configuration.isMethodMetricDepthChanged()) {
apmAgentContext.methodMetricsDepthChanged();
}
if (configuration.isThreadMetricDepthChanged()) {
apmAgentContext.threadMetricsDepthChanged();
}
if (configuration.isStrategyChanged()) {
boolean hasStarted = this.started.get();
if (initialized.get()) {
shutDown();
try {
//we need to restart
initialize(this.instrumentation, null);
if (hasStarted) {
startMetrics();
}
} catch (Exception e) {
LOG.error("Could not re-initialize");
}
}
}
}
}
}