diff --git a/.gitignore b/.gitignore index 8bdb411fa..daceac0d8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ target/* # jenv .java_version +.java-version diff --git a/pom.xml b/pom.xml index 0d79f3b1b..12348c986 100644 --- a/pom.xml +++ b/pom.xml @@ -39,9 +39,7 @@ 3.5 2.10.5 1.35 - - 2.12.2 - 1.7.26 + 1.7.32 2.12.3 1.26 2.8.2 @@ -167,28 +165,10 @@ ${slf4j.version} - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - com.sun.jmx - jmxri - - - com.sun.jdmk - jmxtools - - - javax.jms - jms - - - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} + org.slf4j + slf4j-jdk14 + + ${slf4j.version} org.yaml diff --git a/src/main/java/org/datadog/jmxfetch/App.java b/src/main/java/org/datadog/jmxfetch/App.java index 3b5079d2b..5a34fae77 100644 --- a/src/main/java/org/datadog/jmxfetch/App.java +++ b/src/main/java/org/datadog/jmxfetch/App.java @@ -6,8 +6,6 @@ import com.beust.jcommander.ParameterException; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; import org.datadog.jmxfetch.reporter.Reporter; import org.datadog.jmxfetch.tasks.TaskMethod; @@ -17,6 +15,7 @@ import org.datadog.jmxfetch.util.ByteArraySearcher; import org.datadog.jmxfetch.util.CustomLogger; import org.datadog.jmxfetch.util.FileHelper; +import org.datadog.jmxfetch.util.LogLevel; import org.datadog.jmxfetch.util.MetadataHelper; import org.datadog.jmxfetch.util.ServiceCheckHelper; import org.slf4j.Marker; @@ -144,7 +143,7 @@ public static void main(String[] args) { // not needed in dd-java-agent, which calls run directly. // Set up the logger to add file handler - CustomLogger.setup(Level.toLevel(config.getLogLevel()), + CustomLogger.setup(LogLevel.fromString(config.getLogLevel()), config.getLogLocation(), config.isLogFormatRfc3339()); @@ -159,21 +158,18 @@ public static void main(String[] args) { * System#exit}. */ public static int run(AppConfig config) { - Marker fatal = MarkerFactory.getMarker("FATAL"); String action = config.getAction(); // The specified action is unknown if (!AppConfig.ACTIONS.contains(action)) { - log.error(fatal, - action + " is not in " + AppConfig.ACTIONS + ". Exiting."); + log.error(action + " is not in " + AppConfig.ACTIONS + ". Exiting."); return 1; } if (!action.equals(AppConfig.ACTION_COLLECT) && !(config.isConsoleReporter() || config.isJsonReporter())) { // The "list_*" actions can not be used with the statsd reporter - log.error(fatal, - action + log.error(action + " argument can only be used with the console or json reporter. Exiting."); return 1; } @@ -230,8 +226,8 @@ private static void attachShutdownHook() { @Override public void run() { log.info("JMXFetch is closing"); - // Properly close log handlers - LogManager.shutdown(); + // make sure log handlers are properly closed + CustomLogger.shutdown(); } } ); @@ -1192,7 +1188,7 @@ private void processCollectionStatus( + " metrics."; instanceStatus = Status.STATUS_WARNING; - CustomLogger.laconic(log, Level.WARN, instanceMessage, 0); + CustomLogger.laconic(log, LogLevel.WARN, instanceMessage, 0); } if (numberOfMetrics > 0) { diff --git a/src/main/java/org/datadog/jmxfetch/AppConfig.java b/src/main/java/org/datadog/jmxfetch/AppConfig.java index 9c2b9df26..5f399feeb 100644 --- a/src/main/java/org/datadog/jmxfetch/AppConfig.java +++ b/src/main/java/org/datadog/jmxfetch/AppConfig.java @@ -10,7 +10,7 @@ import org.datadog.jmxfetch.reporter.ConsoleReporter; import org.datadog.jmxfetch.reporter.JsonReporter; import org.datadog.jmxfetch.reporter.Reporter; -import org.datadog.jmxfetch.validator.Log4JLevelValidator; +import org.datadog.jmxfetch.validator.LogLevelValidator; import org.datadog.jmxfetch.validator.PositiveIntegerValidator; import org.datadog.jmxfetch.validator.ReporterValidator; @@ -72,7 +72,7 @@ public class AppConfig { @Parameter( names = {"--log_level", "-L"}, description = "Level of verbosity", - validateWith = Log4JLevelValidator.class, + validateWith = LogLevelValidator.class, required = false) @Builder.Default private String logLevel = "INFO"; diff --git a/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java b/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java index 384654b52..7b379ed34 100644 --- a/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java +++ b/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java @@ -1,123 +1,180 @@ package org.datadog.jmxfetch.util; import lombok.extern.slf4j.Slf4j; -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.appender.ConsoleAppender; -import org.apache.logging.log4j.core.appender.RollingFileAppender; -import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy; -import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy; -import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.Configurator; -import org.apache.logging.log4j.core.config.LoggerConfig; -import org.apache.logging.log4j.core.layout.PatternLayout; +import org.datadog.jmxfetch.util.LogLevel; +import org.datadog.jmxfetch.util.StdoutConsoleHandler; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Filter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; @Slf4j public class CustomLogger { + // Keep a reference to our logger so it doesn't get GC'd + private static Logger jmxfetchLogger; + private static final ConcurrentHashMap messageCount = new ConcurrentHashMap(); - private static final String LAYOUT = "%d{yyyy-MM-dd HH:mm:ss z} | JMX | %-5p | %c{1} | %m%n"; - private static final String LAYOUT_RFC3339 = - "%d{yyyy-MM-dd'T'HH:mm:ss'Z'} | JMX | %-5p | %c{1} | %m%n"; + private static final String DATE_JDK14_LAYOUT = "yyyy-MM-dd HH:mm:ss z"; + private static final String DATE_JDK14_LAYOUT_RFC3339 = "yyyy-MM-dd'T'HH:mm:ssXXX"; + private static final String JDK14_LAYOUT = "%s | JMX | %2$s | %3$s | %4$s%5$s%n"; + + private static final int MAX_FILE_SIZE = 5 * 1024 * 1024; - // log4j2 uses SYSTEM_OUT and SYSTEM_ERR - support both - private static final String SYSTEM_OUT_ALT = "STDOUT"; - private static final String SYSTEM_ERR_ALT = "STDERR"; + private static final int FILE_COUNT = 2; + + private static boolean isStdErr(String target) { + List stderrs = Arrays.asList("SYSTEM.ERR", "SYSTEM_ERR", "STDERR"); + return stderrs.contains(target.toUpperCase()); + } - /** Sets up the custom logger to the specified level and location. */ - public static void setup(Level level, String logLocation, boolean logFormatRfc3339) { - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Configuration config = ctx.getConfiguration(); + private static boolean isStdOut(String target) { + List stdouts = Arrays.asList("SYSTEM.OUT", "SYSTEM_OUT", "STDOUT"); + return stdouts.contains(target.toUpperCase()); + } + + /** setup and configure the logging. */ + public static synchronized void setup(LogLevel level, String logLocation, + boolean logFormatRfc3339) { String target = "CONSOLE"; + final String dateFormat = logFormatRfc3339 ? DATE_JDK14_LAYOUT_RFC3339 : DATE_JDK14_LAYOUT; + final SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat, + Locale.getDefault()); + + // log format + SimpleFormatter formatter = new SimpleFormatter() { + private static final String format = JDK14_LAYOUT; + + private String simpleClassName(String str) { + int start = str.lastIndexOf('.'); + int end = str.indexOf('$'); + if (start == -1 || start + 1 == str.length()) { + return str; + } + if (end == -1 || end <= start || end > str.length()) { + end = str.length(); + } + return str.substring(start + 1, end); + } - String logPattern = logFormatRfc3339 ? LAYOUT_RFC3339 : LAYOUT; - - PatternLayout layout = PatternLayout.newBuilder() - .withConfiguration(config) - .withPattern(logPattern) - .build(); - - if (logLocation != null - && !ConsoleAppender.Target.SYSTEM_ERR.toString().equals(logLocation) - && !SYSTEM_ERR_ALT.equals(logLocation) - && !ConsoleAppender.Target.SYSTEM_OUT.toString().equals(logLocation) - && !SYSTEM_OUT_ALT.equals(logLocation)) { - - target = "FileLogger"; - - RollingFileAppender fa = RollingFileAppender.newBuilder() - .setConfiguration(config) - .withName(target) - .withLayout(layout) - .withFileName(logLocation) - .withFilePattern(logLocation + ".%d") - .withPolicy(SizeBasedTriggeringPolicy.createPolicy("5MB")) - .withStrategy(DefaultRolloverStrategy.newBuilder().withMax("1").build()) - .build(); - - fa.start(); - config.addAppender(fa); - ctx.getRootLogger().addAppender(config.getAppender(fa.getName())); - - log.info("File Handler set"); - } else { - - if (logLocation != null - && (ConsoleAppender.Target.SYSTEM_ERR.toString().equals(logLocation) - || SYSTEM_ERR_ALT.equals(logLocation))) { - - ConsoleAppender console = (ConsoleAppender)config.getAppender("CONSOLE"); - console.stop(); - config.getRootLogger().removeAppender("CONSOLE"); - ctx.updateLoggers(); - - ConsoleAppender ca = ConsoleAppender.newBuilder() - .setConfiguration(config) - .withName(logLocation) - .setTarget(ConsoleAppender.Target.SYSTEM_ERR) - .withLayout(layout) - .build(); - - ca.start(); - config.addAppender(ca); - ctx.getRootLogger().addAppender(config.getAppender(ca.getName())); + @Override + public synchronized String format(LogRecord lr) { + String exception = ""; + if (lr.getThrown() != null) { + StringWriter writer = new StringWriter(); + PrintWriter stream = new PrintWriter(writer); + stream.println(); + lr.getThrown().printStackTrace(stream); + stream.close(); + exception = writer.toString(); + } + return String.format(format, + dateFormatter.format(new Date()).toString(), + LogLevel.fromJulLevel(lr.getLevel()).toString(), + simpleClassName(lr.getSourceClassName()), + lr.getMessage(), + exception + ); + } + }; + + // log level + Level julLevel = level.toJulLevel(); + + // Reset logging (removes all existing handlers, including on the root logger) + // Note: at some point we'd likely want to be more fine-grained and allow + // log handlers on other loggers instead, with some control on their log level + final LogManager manager = LogManager.getLogManager(); + manager.reset(); + + // prepare the different handlers + ConsoleHandler stdoutHandler = null; + ConsoleHandler stderrHandler = null; + FileHandler fileHandler = null; + + // the logLocation isn't always containing a file, it is sometimes + // referring to a standard output. We want to create a FileHandler only + // if the logLocation is a file on the FS. + if (logLocation != null && logLocation.length() > 0) { + if (!isStdOut(logLocation) && !isStdErr(logLocation)) { + // file logging + try { + // maximum one 5MB file + fileHandler = new FileHandler(logLocation, MAX_FILE_SIZE, FILE_COUNT); + fileHandler.setFormatter(formatter); + // note: fileHandler defaults to Level.ALL, so no need to set its log level + } catch (Exception e) { + fileHandler = null; + log.error("can't open the file handler:", e); + } + } else if (isStdErr(logLocation)) { + // console handler sending on stderr + // note that ConsoleHandler is sending on System.err + stderrHandler = new ConsoleHandler(); + stderrHandler.setFormatter(formatter); + stderrHandler.setLevel(julLevel); } } - // replace default appender with the correct layout - LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME); - loggerConfig.removeAppender(target); + // always have a console handler sending the logs to stdout + stdoutHandler = new StdoutConsoleHandler(); + stdoutHandler.setFormatter(formatter); + stdoutHandler.setLevel(julLevel); - Appender appender = ConsoleAppender.newBuilder() - .setConfiguration(config) - .withName(target) - .withLayout(layout) - .build(); - appender.start(); + // Create our Logger, and set our configured handlers on it + jmxfetchLogger = Logger.getLogger("org.datadog.jmxfetch"); + jmxfetchLogger.setLevel(julLevel); - loggerConfig.addAppender(appender, null, null); - loggerConfig.setLevel(level); + if (fileHandler != null) { + jmxfetchLogger.addHandler(fileHandler); + } + if (stdoutHandler != null) { // always non-null but doesn't cost much + jmxfetchLogger.addHandler(stdoutHandler); + } + if (stderrHandler != null) { + jmxfetchLogger.addHandler(stderrHandler); + } + } - ctx.updateLoggers(); + /** closeHandlers closes all opened handlers. */ + public static synchronized void shutdown() { + for (Handler handler : jmxfetchLogger.getHandlers()) { + if (handler != null) { + handler.close(); + jmxfetchLogger.removeHandler(handler); + } + } } /** Laconic logging for reduced verbosity. */ - public static void laconic(org.slf4j.Logger logger, Level level, String message, int max) { + public static void laconic(org.slf4j.Logger logger, LogLevel level, String message, int max) { if (shouldLog(message, max)) { - if (level.isInRange(Level.ERROR, Level.ALL)) { + if (level == LogLevel.ERROR) { logger.error(message); - } else if (level == Level.WARN) { + } else if (level == LogLevel.WARN) { logger.warn(message); - } else if (level == Level.INFO) { + } else if (level == LogLevel.INFO) { logger.info(message); - } else if (level == Level.DEBUG) { + } else if (level == LogLevel.DEBUG) { logger.debug(message); } } diff --git a/src/main/java/org/datadog/jmxfetch/util/LogLevel.java b/src/main/java/org/datadog/jmxfetch/util/LogLevel.java new file mode 100644 index 000000000..0f9c77418 --- /dev/null +++ b/src/main/java/org/datadog/jmxfetch/util/LogLevel.java @@ -0,0 +1,110 @@ +package org.datadog.jmxfetch.util; + +import java.util.logging.Level; + +/** + * LogLevel used for internal logging to match Datadog Agent levels. + * Comparison table with java.util.logging: + *
+ *  JUL     |  JMXFetch LogLevel
+ * ----------------------------
+ *  OFF     |  OFF
+ *  SEVERE  |  ERROR
+ *  WARNING |  WARN
+ *  INFO    |  INFO
+ *  CONFIG  |  DEBUG
+ *  FINE    |  DEBUG
+ *  FINER   |  TRACE
+ *  FINEST  |  TRACE
+ *  ALL     |  ALL
+ * 
+ *

+ * `FATAL` from previous bindings used by JMXFetch is now converted + * to `LogLevel.ERROR`. + *

+ */ +public enum LogLevel { + OFF(0, "OFF"), + ERROR(1, "ERROR"), + WARN(2, "WARN"), + INFO(3, "INFO"), + DEBUG(4, "DEBUG"), + TRACE(5, "TRACE"), + ALL(6, "ALL"); + + private int level; + private String label; + private LogLevel(int level, String label) { + this.level = level; + this.label = label; + } + + // -- + + /** fromJulLevel converts a java.util.logging.Level into a LogLevel. */ + public static LogLevel fromJulLevel(Level julLevel) { + if (julLevel == Level.ALL) { + return ALL; + } else if (julLevel == Level.SEVERE) { + return ERROR; + } else if (julLevel == Level.WARNING) { + return WARN; + } else if (julLevel == Level.INFO) { + return INFO; + } else if (julLevel == Level.CONFIG) { + return DEBUG; + } else if (julLevel == Level.FINE) { + return DEBUG; + } else if (julLevel == Level.FINER) { + return TRACE; + } else if (julLevel == Level.FINEST) { + return TRACE; + } else if (julLevel == Level.OFF) { + return OFF; + } + + // should never happen but defaults to INFO + return INFO; + } + + /** fromString converts a string into a LogLevel, when not possible, it returns `INFO`. */ + public static LogLevel fromString(String str) { + // compatibility + if (str.toUpperCase().equals("FATAL")) { + return ERROR; + } + for (LogLevel l : LogLevel.class.getEnumConstants()) { + if (str.toUpperCase().equals(l.toString().toUpperCase())) { + return l; + } + } + + // default to INFO + return INFO; + } + + /** + * toJulLevel converts a LogLevel to a `java.util.logging.Level`. + * This mapping needs to match http://slf4j.org/api/org/slf4j/impl/JDK14LoggerAdapter.html + **/ + public Level toJulLevel() { + switch (this) { + case ALL: + return Level.ALL; + case ERROR: + return Level.SEVERE; + case WARN: + return Level.WARNING; + case INFO: + return Level.INFO; + case DEBUG: + return Level.FINE; + case TRACE: + return Level.FINEST; + case OFF: + return Level.OFF; + default: + return Level.INFO; + } + } +} diff --git a/src/main/java/org/datadog/jmxfetch/util/StdoutConsoleHandler.java b/src/main/java/org/datadog/jmxfetch/util/StdoutConsoleHandler.java new file mode 100644 index 000000000..2ff880bc5 --- /dev/null +++ b/src/main/java/org/datadog/jmxfetch/util/StdoutConsoleHandler.java @@ -0,0 +1,13 @@ +package org.datadog.jmxfetch.util; + +import java.io.OutputStream; +import java.lang.SecurityException; +import java.util.logging.ConsoleHandler; + +public class StdoutConsoleHandler extends ConsoleHandler { + protected void setOutputStream(OutputStream out) throws SecurityException { + // force ConsoleHandler to set its output to System.out + // instead of System.err + super.setOutputStream(System.out); + } +} \ No newline at end of file diff --git a/src/main/java/org/datadog/jmxfetch/validator/Log4JLevelValidator.java b/src/main/java/org/datadog/jmxfetch/validator/LogLevelValidator.java similarity index 50% rename from src/main/java/org/datadog/jmxfetch/validator/Log4JLevelValidator.java rename to src/main/java/org/datadog/jmxfetch/validator/LogLevelValidator.java index c92cc5330..3ecdac033 100644 --- a/src/main/java/org/datadog/jmxfetch/validator/Log4JLevelValidator.java +++ b/src/main/java/org/datadog/jmxfetch/validator/LogLevelValidator.java @@ -8,20 +8,26 @@ import java.util.Arrays; import java.util.List; -public class Log4JLevelValidator implements IParameterValidator { - public static final List LOG4J_LEVELS = +public class LogLevelValidator implements IParameterValidator { + // for history, there is a `FATAL` log level supported here as we were supporting it + // before moving to the `java.util.logging` log system. + // we keep it here since we still want it to be valid, but we consider it as ERROR + // in jmxfetch + // FIXME: remove the "LEVEL" log level, which was introduced by mistake early in JMXFetch's + // development (currently defaults to INFO). + public static final List LOGLEVELS = Arrays.asList( "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE", "LEVEL", "WARN"); - /** Validates a string as a valid Log4J logging level. */ + /** Validates a string as a valid logging level. */ public void validate(String name, String value) throws ParameterException { - if (!LOG4J_LEVELS.contains(value)) { + if (!LOGLEVELS.contains(value.toUpperCase())) { String message = "Parameter " + name + " should be in (" - + StringUtils.join(",", LOG4J_LEVELS) + + StringUtils.join(",", LOGLEVELS) + ")"; throw new ParameterException(message); } diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties deleted file mode 100644 index 03bbf5f21..000000000 --- a/src/main/resources/log4j2.properties +++ /dev/null @@ -1,10 +0,0 @@ -rootLogger.level = all -rootLogger.appenderRef.stdout.ref = CONSOLE - -appender.console.type = Console -appender.console.name = CONSOLE -appender.console.target = SYSTEM_OUT -appender.console.layout.type = PatternLayout -appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss z} | JMX | %-5p | %c{1} | %m%n -appender.console.filter.threshold.type = ThresholdFilter -appender.console.filter.threshold.level = all \ No newline at end of file diff --git a/src/test/java/org/datadog/jmxfetch/TestCommon.java b/src/test/java/org/datadog/jmxfetch/TestCommon.java index 535b81c83..ab9416ac9 100644 --- a/src/test/java/org/datadog/jmxfetch/TestCommon.java +++ b/src/test/java/org/datadog/jmxfetch/TestCommon.java @@ -24,10 +24,10 @@ import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; -import org.apache.logging.log4j.Level; import org.datadog.jmxfetch.reporter.ConsoleReporter; import org.datadog.jmxfetch.reporter.Reporter; import org.datadog.jmxfetch.util.CustomLogger; +import org.datadog.jmxfetch.util.LogLevel; import org.junit.After; import org.junit.BeforeClass; @@ -46,7 +46,7 @@ public static void init() throws Exception { if (level == null) { level = "ALL"; } - CustomLogger.setup(Level.toLevel(level), "/tmp/jmxfetch_test.log", false); + CustomLogger.setup(LogLevel.ALL, "/tmp/jmxfetch_test.log", false); } /** diff --git a/src/test/java/org/datadog/jmxfetch/TestInstance.java b/src/test/java/org/datadog/jmxfetch/TestInstance.java index fcf303415..ccb9489e8 100644 --- a/src/test/java/org/datadog/jmxfetch/TestInstance.java +++ b/src/test/java/org/datadog/jmxfetch/TestInstance.java @@ -14,12 +14,10 @@ import java.util.List; import java.util.Map; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; import org.junit.Test; public class TestInstance extends TestCommon { - private static final Logger log = LogManager.getLogger("Test Instance"); + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("Test Instance"); @Test public void testMinCollectionInterval() throws Exception { diff --git a/src/test/java/org/datadog/jmxfetch/TestParsingJCommander.java b/src/test/java/org/datadog/jmxfetch/TestParsingJCommander.java index 92999d251..42bb133ef 100644 --- a/src/test/java/org/datadog/jmxfetch/TestParsingJCommander.java +++ b/src/test/java/org/datadog/jmxfetch/TestParsingJCommander.java @@ -12,7 +12,7 @@ import org.datadog.jmxfetch.reporter.ConsoleReporter; import org.datadog.jmxfetch.reporter.StatsdReporter; import org.datadog.jmxfetch.util.StringUtils; -import org.datadog.jmxfetch.validator.Log4JLevelValidator; +import org.datadog.jmxfetch.validator.LogLevelValidator; import org.junit.Test; public class TestParsingJCommander { @@ -53,7 +53,7 @@ public void testParsingHelp() { @Test public void testParsingLogLevel() { - for (String logLevel : Log4JLevelValidator.LOG4J_LEVELS) { + for (String logLevel : LogLevelValidator.LOGLEVELS) { String[] params = new String[] { "--reporter", diff --git a/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java b/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java new file mode 100644 index 000000000..876d4f664 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java @@ -0,0 +1,142 @@ +package com.timgroup.statsd; + +import lombok.extern.slf4j.Slf4j; + +import org.datadog.jmxfetch.util.CustomLogger; +import org.datadog.jmxfetch.util.LogLevel; + +import java.io.IOException; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Random; +import java.util.logging.Logger; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + + +@Slf4j +@RunWith(Parameterized.class) +public final class CustomLoggerPerfTest { + + private final int duration; // Duration in secs + private final int testWorkers; + private final int msgSize; // length of log message in bytes + private final int uPause; // length of log message in bytes + private final boolean rfc3339; // length of log message in bytes + + private AtomicBoolean running; + private final ExecutorService executor; + + private static Logger log = Logger.getLogger("CustomLoggerPerfTest"); + + @Parameters + public static Collection data() { + return Arrays.asList(new Object[][] { + { 90, 100, 1, 128, false }, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, false + { 90, 100, 1, 512, false }, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false + { 90, 100, 1, 1024, false }, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false + { 90, 100, 1, 128, true }, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, true + { 90, 100, 1, 512, true }, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true + { 90, 100, 1, 1024, true }, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true + }); + } + + public CustomLoggerPerfTest(int duration, int uPause, int testWorkers, int msgSize, boolean rfc3339) throws IOException { + this.duration = duration; + this.testWorkers = testWorkers; + this.msgSize = msgSize; + this.uPause = uPause; + this.rfc3339 = rfc3339; + + this.executor = Executors.newFixedThreadPool(testWorkers); + this.running = new AtomicBoolean(true); + + CustomLogger.setup(LogLevel.fromString("INFO"), + null, //stdout + rfc3339); + } + + /** + * Run with -Dtest_perf=true if you wish the performance tests defined here to run + */ + @Before + public void shouldRun() { + boolean run = false; + try { + run = (System.getProperty("test_perf").compareToIgnoreCase("true") == 0); + } catch (Exception ex) { + // NADA + } + Assume.assumeTrue(run); + } + + @Test + public void perfTest() throws Exception { + + final String msg = getRandomString(msgSize); + final AtomicInteger count = new AtomicInteger(0); + final int pause =(int) TimeUnit.MICROSECONDS.toNanos(this.uPause); + + for(int i=0 ; i < this.testWorkers ; i++) { + executor.submit(new Runnable() { + public void run() { + while (running.get()) { + log.info(msg); + count.incrementAndGet(); + try { + Thread.sleep(0, pause); + } catch (InterruptedException e) { + // pass + } + } + } + }); + } + + Thread.sleep(TimeUnit.SECONDS.toMillis(this.duration)); + running.set(false); + + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.SECONDS); + + StringBuilder sb = new StringBuilder() + .append("For ") + .append(this.testWorkers) + .append(" workers with msg size ") + .append(this.msgSize) + .append(" and RFC 3339 mode set to ") + .append(this.rfc3339) + .append(" logged ") + .append(count.get()) + .append(" messsages."); + + System.out.println(sb.toString()); + } + + private String getRandomString(int length) { + + int leftLimit = 97; // letter 'a' + int rightLimit = 122; // letter 'z' + Random random = new Random(); + StringBuilder buffer = new StringBuilder(length); + for (int i = 0; i < length; i++) { + int randomLimitedInt = leftLimit + (int) + (random.nextFloat() * (rightLimit - leftLimit + 1)); + buffer.append((char) randomLimitedInt); + } + String generatedString = buffer.toString(); + + return generatedString; + } +} diff --git a/src/test/java/org/datadog/jmxfetch/util/LogLevelTest.java b/src/test/java/org/datadog/jmxfetch/util/LogLevelTest.java new file mode 100644 index 000000000..a1315a881 --- /dev/null +++ b/src/test/java/org/datadog/jmxfetch/util/LogLevelTest.java @@ -0,0 +1,49 @@ +package org.datadog.jmxfetch.util; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.logging.Level; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(Parameterized.class) +public class LogLevelTest { + + @Parameterized.Parameters + public static Iterable testCases() { + return Arrays.asList(new Object[][] { + {"ALL", LogLevel.ALL, Level.ALL}, + {"DEBUG", LogLevel.DEBUG, Level.FINE}, + {"TRACE", LogLevel.TRACE, Level.FINEST}, + {"INFO", LogLevel.INFO, Level.INFO}, + {"WARN", LogLevel.WARN, Level.WARNING}, + {"ERROR", LogLevel.ERROR, Level.SEVERE}, + {"FATAL", LogLevel.ERROR, Level.SEVERE}, + {"OFF", LogLevel.OFF, Level.OFF}, + {"LEVEL", LogLevel.INFO, Level.INFO}, + {"some_unknown_level", LogLevel.INFO, Level.INFO}, + }); + } + + private final String configLogLevel; + private final LogLevel expectedLogLevel; + private final Level expectedJulLevel; + + public LogLevelTest(String configLogLevel, LogLevel expectedLogLevel, Level expectedJulLevel) { + this.configLogLevel = configLogLevel; + this.expectedLogLevel = expectedLogLevel; + this.expectedJulLevel = expectedJulLevel; + } + + @Test + public void testFromStringToJulLevel() { + LogLevel logLevel = LogLevel.fromString(configLogLevel); + assertEquals(expectedLogLevel, logLevel); + assertEquals(expectedJulLevel, logLevel.toJulLevel()); + } +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties deleted file mode 100644 index bc79401e0..000000000 --- a/src/test/resources/log4j.properties +++ /dev/null @@ -1,6 +0,0 @@ -log4j.rootLogger=all, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.Threshold=ALL -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d | %-5p| %c{1} | %m%n