diff --git a/CHANGELOG.md b/CHANGELOG.md index a9eba045..f13973e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Changelog ========= # 0.49.2-SNAPSHOT / TBC +* [FEATURE] Added support for emitting logs with thread name [#525][] * [BUGFIX] Telemetry is no longer initialized when telemetry is disabled [#522][] # 0.49.1 / 2024-04-09 @@ -768,7 +769,6 @@ Changelog [#477]: https://github.com/DataDog/jmxfetch/issues/477 [#509]: https://github.com/DataDog/jmxfetch/issues/509 [#512]: https://github.com/DataDog/jmxfetch/pull/512 -[#522]: https://github.com/DataDog/jmxfetch/pull/522 [@alz]: https://github.com/alz [@aoking]: https://github.com/aoking [@arrawatia]: https://github.com/arrawatia diff --git a/src/main/java/org/datadog/jmxfetch/AppConfig.java b/src/main/java/org/datadog/jmxfetch/AppConfig.java index d8888efe..5f736d8a 100644 --- a/src/main/java/org/datadog/jmxfetch/AppConfig.java +++ b/src/main/java/org/datadog/jmxfetch/AppConfig.java @@ -96,6 +96,13 @@ public class AppConfig { @Builder.Default private boolean logFormatRfc3339 = false; + @Parameter( + names = {"--log_thread_name"}, + description = "Logs the thread name with each message", + required = false) + @Builder.Default + private boolean logThreadName = false; + @Parameter( names = {"--conf_directory", "-D"}, description = "Absolute path to the conf.d directory", @@ -446,6 +453,10 @@ public boolean isLogFormatRfc3339() { return logFormatRfc3339; } + public boolean isLogThreadName() { + return logThreadName; + } + /** Returns path to auto-discovery pipe. Deprecated.. */ public String getAutoDiscoveryPipe() { String pipePath; diff --git a/src/main/java/org/datadog/jmxfetch/JmxFetch.java b/src/main/java/org/datadog/jmxfetch/JmxFetch.java index 11ccc711..1981a7e8 100644 --- a/src/main/java/org/datadog/jmxfetch/JmxFetch.java +++ b/src/main/java/org/datadog/jmxfetch/JmxFetch.java @@ -46,8 +46,9 @@ public static void main(String[] args) { // Set up the logger to add file handler CustomLogger.setup(LogLevel.fromString(config.getLogLevel()), - config.getLogLocation(), - config.isLogFormatRfc3339()); + config.getLogLocation(), + config.isLogFormatRfc3339(), + config.isLogThreadName()); // Set up the shutdown hook to properly close resources attachShutdownHook(); diff --git a/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java b/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java index 1bc75da6..5b462814 100644 --- a/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java +++ b/src/main/java/org/datadog/jmxfetch/util/CustomLogger.java @@ -2,22 +2,17 @@ import lombok.extern.slf4j.Slf4j; -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; @@ -46,9 +41,14 @@ public class CustomLogger { 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 String JDK14_LAYOUT = + "%s | JMX | %2$s | %3$s | %4$s%5$s%n"; + private static final String JDK14_WITH_THREADS_LAYOUT = + "%s | JMX | %2$s | %3$s | %4$s | %5$s%6$s%n"; private static final String JDK14_LAYOUT_FILE_LINE = - "%s | JMX | %2$s | %3$s:%4$d | %5$s%6$s%n"; + "%s | JMX | %2$s | %3$s:%4$d | %5$s%6$s%n"; + private static final String JDK14_WITH_THREADS_LAYOUT_FILE_LINE = + "%s | JMX | %2$s | %3$s | %4$s:%5$d | %6$s%7$s%n"; private static final int MAX_FILE_SIZE = 5 * 1024 * 1024; @@ -66,67 +66,8 @@ private static boolean isStdOut(String target) { /** setup and configure the logging. */ public static synchronized void setup(LogLevel level, String logLocation, - boolean logFormatRfc3339) { - String target = "CONSOLE"; - String dateFormat = logFormatRfc3339 ? DATE_JDK14_LAYOUT_RFC3339 : DATE_JDK14_LAYOUT; - if (millisecondLogging) { - dateFormat = dateFormat.replace("ss", "ss.SSS"); - } - 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); - } - - @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(); - } - - if (enableFileLineLogging) { - Throwable throwable = new Throwable(); - StackTraceElement logEmissionFrame = throwable.getStackTrace()[6]; - - return String.format(JDK14_LAYOUT_FILE_LINE, - dateFormatter.format(new Date()).toString(), - LogLevel.fromJulLevel(lr.getLevel()).toString(), - logEmissionFrame.getFileName(), - logEmissionFrame.getLineNumber(), - lr.getMessage(), - exception - ); - - } - - return String.format(format, - dateFormatter.format(new Date()).toString(), - LogLevel.fromJulLevel(lr.getLevel()).toString(), - simpleClassName(lr.getSourceClassName()), - lr.getMessage(), - exception - ); - } - }; + boolean logFormatRfc3339, boolean logThreadName) { + SimpleFormatter formatter = getFormatter(logFormatRfc3339, logThreadName); // log level Level julLevel = level.toJulLevel(); @@ -138,14 +79,14 @@ public synchronized String format(LogRecord lr) { manager.reset(); // prepare the different handlers - ConsoleHandler stdoutHandler = null; + ConsoleHandler stdoutHandler; 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 (logLocation != null && !logLocation.isEmpty()) { if (!isStdOut(logLocation) && !isStdErr(logLocation)) { // file logging try { @@ -178,14 +119,105 @@ public synchronized String format(LogRecord lr) { if (fileHandler != null) { jmxfetchLogger.addHandler(fileHandler); } - if (stdoutHandler != null) { // always non-null but doesn't cost much - jmxfetchLogger.addHandler(stdoutHandler); - } + + jmxfetchLogger.addHandler(stdoutHandler); + if (stderrHandler != null) { jmxfetchLogger.addHandler(stderrHandler); } } + private static SimpleFormatter getFormatter( + final boolean logFormatRfc3339, + final boolean logThreadName) { + String dateFormat = logFormatRfc3339 ? DATE_JDK14_LAYOUT_RFC3339 : DATE_JDK14_LAYOUT; + if (millisecondLogging) { + dateFormat = dateFormat.replace("ss", "ss.SSS"); + } + final SimpleDateFormat dateFormatter = new SimpleDateFormat(dateFormat, + Locale.getDefault()); + + // log format + return new SimpleFormatter() { + private final String formatLayout = + logThreadName ? JDK14_WITH_THREADS_LAYOUT : 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(); + } + return str.substring(start + 1, end); + } + + @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(); + } + + if (enableFileLineLogging) { + Throwable throwable = new Throwable(); + StackTraceElement logEmissionFrame = throwable.getStackTrace()[6]; + + final String logFormat; + if (logThreadName) { + logFormat = JDK14_WITH_THREADS_LAYOUT_FILE_LINE; + } else { + logFormat = JDK14_LAYOUT_FILE_LINE; + } + if (logThreadName) { + return String.format(logFormat, + dateFormatter.format(new Date()), + LogLevel.fromJulLevel(lr.getLevel()).toString(), + Thread.currentThread().getName(), + logEmissionFrame.getFileName(), + logEmissionFrame.getLineNumber(), + lr.getMessage(), + exception + ); + } + return String.format(logFormat, + dateFormatter.format(new Date()), + LogLevel.fromJulLevel(lr.getLevel()).toString(), + logEmissionFrame.getFileName(), + logEmissionFrame.getLineNumber(), + lr.getMessage(), + exception + ); + } + + if (logThreadName) { + return String.format(formatLayout, + dateFormatter.format(new Date()), + LogLevel.fromJulLevel(lr.getLevel()).toString(), + Thread.currentThread().getName(), + simpleClassName(lr.getSourceClassName()), + lr.getMessage(), + exception + ); + } + return String.format(formatLayout, + dateFormatter.format(new Date()), + LogLevel.fromJulLevel(lr.getLevel()).toString(), + simpleClassName(lr.getSourceClassName()), + lr.getMessage(), + exception + ); + } + }; + } + /** closeHandlers closes all opened handlers. */ public static synchronized void shutdown() { for (Handler handler : jmxfetchLogger.getHandlers()) { diff --git a/src/test/java/org/datadog/jmxfetch/TestCommon.java b/src/test/java/org/datadog/jmxfetch/TestCommon.java index 4450c741..031e8a2f 100644 --- a/src/test/java/org/datadog/jmxfetch/TestCommon.java +++ b/src/test/java/org/datadog/jmxfetch/TestCommon.java @@ -73,7 +73,7 @@ public static void init() throws Exception { if (level == null) { level = "ALL"; } - CustomLogger.setup(LogLevel.fromString(level), "/tmp/jmxfetch_test.log", false); + CustomLogger.setup(LogLevel.fromString(level), "/tmp/jmxfetch_test.log", false, true); } /** diff --git a/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java b/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java index 262661a7..0b71f0a3 100644 --- a/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java +++ b/src/test/java/org/datadog/jmxfetch/util/CustomLoggerPerfTest.java @@ -34,6 +34,7 @@ public final class CustomLoggerPerfTest { 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 final boolean logThreadName; private AtomicBoolean running; private final ExecutorService executor; @@ -42,29 +43,37 @@ public final class 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 - }); + return Arrays.asList(new Object[][]{ + {90, 100, 1, 128, false, false}, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, false, false + {90, 100, 1, 512, false, false}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false, false + {90, 100, 1, 1024, false, false}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false, false + {90, 100, 1, 128, false, true}, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, false, true + {90, 100, 1, 512, false, true}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false, true + {90, 100, 1, 1024, false, true}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, false, true + {90, 100, 1, 128, true, false}, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, true, false + {90, 100, 1, 512, true, false}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true, false + {90, 100, 1, 1024, true, false}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true, false + {90, 100, 1, 128, true, true}, // 90 seconds, 100 microsecond pause, 1 worker, 128 byte string, true, true + {90, 100, 1, 512, true, true}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true, true + {90, 100, 1, 1024, true, true}, // 90 seconds, 100 microsecond pause, 1 worker, 512 byte string, true, true + }); } - public CustomLoggerPerfTest(int duration, int uPause, int testWorkers, int msgSize, boolean rfc3339) throws IOException { + public CustomLoggerPerfTest(int duration, int uPause, int testWorkers, int msgSize, boolean rfc3339, boolean logThreadName) throws IOException { this.duration = duration; this.testWorkers = testWorkers; this.msgSize = msgSize; this.uPause = uPause; this.rfc3339 = rfc3339; + this.logThreadName =logThreadName; this.executor = Executors.newFixedThreadPool(testWorkers); this.running = new AtomicBoolean(true); CustomLogger.setup(LogLevel.fromString("INFO"), - null, //stdout - rfc3339); + null, //stdout + rfc3339, + logThreadName); } /** @@ -117,11 +126,13 @@ public void run() { .append(this.msgSize) .append(" and RFC 3339 mode set to ") .append(this.rfc3339) + .append(" and log thread name set to ") + .append(this.logThreadName) .append(" logged ") .append(count.get()) .append(" messsages."); - System.out.println(sb.toString()); + System.out.println(sb); } private String getRandomString(int length) { @@ -135,8 +146,7 @@ private String getRandomString(int length) { (random.nextFloat() * (rightLimit - leftLimit + 1)); buffer.append((char) randomLimitedInt); } - String generatedString = buffer.toString(); - return generatedString; + return buffer.toString(); } }