org.jmxtrans.agent.JmxTransExporter Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2013 the original author or authors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package org.jmxtrans.agent;
import java.lang.management.ManagementFactory;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import javax.management.MBeanServer;
import org.jmxtrans.agent.util.GcdCalculator;
import org.jmxtrans.agent.util.logging.Logger;
/**
* @author Cyrille Le Clerc
*/
public class JmxTransExporter {
private final Logger logger = Logger.getLogger(getClass().getName());
private ThreadFactory threadFactory = new ThreadFactory() {
final AtomicInteger counter = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread thread = Executors.defaultThreadFactory().newThread(r);
thread.setDaemon(true);
thread.setName("jmxtrans-agent-" + counter.incrementAndGet());
return thread;
}
};
private ScheduledExecutorService scheduledExecutorService;
private MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
private ScheduledFuture scheduledFuture;
private JmxTransConfigurationLoader configLoader;
private volatile JmxTransExporterConfiguration config;
private volatile List collectors;
private volatile long runIntervalMillis;
public JmxTransExporter(JmxTransConfigurationLoader configLoader) {
this.configLoader = configLoader;
loadNewConfiguration();
}
private void loadNewConfiguration() {
this.config = configLoader.loadConfiguration();
logger.finest("Configuration loaded: " + config);
this.collectors = createTimeTrackingCollectors();
this.runIntervalMillis = calculateRunIntervalMillis();
}
private List createTimeTrackingCollectors() {
List newCollectors = new ArrayList<>();
for (Query q : config.getQueries()) {
TimeTrackingCollector timeTrackingCollector = createTimeTrackingCollector(q, q.getCollectIntervalOverrideOrNull());
newCollectors.add(timeTrackingCollector);
}
for (Invocation i : config.getInvocations()) {
TimeTrackingCollector timeTrackingCollector = createTimeTrackingCollector(i, i.getCollectIntervalOverrideOrNull());
newCollectors.add(timeTrackingCollector);
}
return newCollectors;
}
private TimeTrackingCollector createTimeTrackingCollector(Collector collector, Integer collectIntervalOverride) {
int actualCollectInterval = collectIntervalOverride != null ? collectIntervalOverride : config.getCollectInterval();
TimeTrackingCollector timeTrackingCollector = new TimeTrackingCollector(collector, TimeUnit.MILLISECONDS.convert(actualCollectInterval, config.getCollectIntervalTimeUnit()));
return timeTrackingCollector;
}
private long calculateRunIntervalMillis() {
// Use the greatest common divisor of the collect intervals to get period in which we need to collect
if (collectors.isEmpty()) {
// Ensure that we trigger runs even if there are no queries so that config refresh works.
return config.getCollectInterval();
}
List collectIntervals = new ArrayList<>();
for (TimeTrackingCollector c : collectors) {
collectIntervals.add(c.getCollectIntervalMillis());
}
return GcdCalculator.gcd(collectIntervals);
}
public void start() {
if (logger.isLoggable(Level.FINER)) {
logger.fine("starting " + this.toString() + " ...");
} else {
logger.fine("starting " + getClass().getName() + " ...");
}
if (scheduledExecutorService != null || scheduledFuture != null)
throw new IllegalArgumentException("Exporter is already started: scheduledExecutorService=" + scheduledExecutorService + ", scheduledFuture=" + scheduledFuture);
scheduledExecutorService = Executors.newScheduledThreadPool(1, threadFactory);
if (config.getResultNameStrategy() == null)
throw new IllegalStateException("resultNameStrategy is not defined, jmxTransExporter is not properly initialised");
scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
collectAndExport();
}
}, runIntervalMillis / 2, runIntervalMillis, TimeUnit.MILLISECONDS);
if (config.getConfigReloadInterval() >= 0) {
Runnable runnable = new Runnable() {
private final Logger logger = Logger.getLogger(JmxTransExporter.class.getName() + ".reloader");
private long lastModified = configLoader.lastModified();
@Override
public void run() {
long newLastModified = configLoader.lastModified();
if (newLastModified == 0L) {
if (logger.isLoggable(Level.FINER))
logger.finer("Don't reload lastModified=" + lastModified + " / " + new Timestamp(lastModified) +
", newLastModified=" + newLastModified);
// ignore new config not found
} else if (newLastModified > lastModified) {
logger.info("jmxtrans-agent configuration has changed. Reload " + configLoader);
if (logger.isLoggable(Level.FINER))
logger.finer("Reload lastModified=" + lastModified + " / " + new Timestamp(lastModified) +
", newLastModified=" + newLastModified + " / " + new Timestamp(newLastModified));
lastModified = newLastModified;
stop();
loadNewConfiguration();
start();
} else {
if (logger.isLoggable(Level.FINER))
logger.finer("Don't reload lastModified=" + lastModified + " / " + new Timestamp(lastModified) +
", newLastModified=" + newLastModified + " / " + new Timestamp(newLastModified));
// ignore, config not modified
}
}
};
int configReloadIntervalInSecs = Math.max(config.getConfigReloadInterval(), 5);
if (logger.isLoggable(Level.INFO))
logger.info("Configuration reload interval: " + configReloadIntervalInSecs + "secs");
scheduledExecutorService.scheduleWithFixedDelay(runnable, 0, configReloadIntervalInSecs, TimeUnit.SECONDS);
}
logger.fine(getClass().getName() + " started");
}
public void stop() {
// cancel jobs
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
scheduledFuture = null;
}
scheduledExecutorService.shutdown();
// one last export
collectAndExport();
// wait for stop
try {
scheduledExecutorService.awaitTermination(runIntervalMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
scheduledExecutorService = null;
config.getOutputWriter().preDestroy();
logger.info(getClass().getName() + " stopped.");
}
protected void collectAndExport() {
OutputWriter outputWriter = config.getOutputWriter();
try {
outputWriter.preCollect();
for (TimeTrackingCollector collector : collectors) {
try {
collector.collectIfEnoughTimeHasPassed(mbeanServer, outputWriter);
} catch (Exception e) {
logger.log(Level.WARNING, "Ignore exception collecting with collector " + collector, e);
}
}
outputWriter.postCollect();
} catch (Exception e) {
logger.log(Level.WARNING, "Ignore exception flushing metrics ", e);
}
}
@Override
public String toString() {
return "JmxTransExporter{" +
", configuration=" + config +
'}';
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy