From bc6649103acca238cf906373f8e70447b628dc74 Mon Sep 17 00:00:00 2001 From: Pierson Yieh Date: Tue, 5 Apr 2022 19:23:07 -0700 Subject: [PATCH 1/5] Add a CustomLoggersConfig that pulls from LogRecordManager, JdkSplunkHandler.isLoggable() will forward any logs from those specified sources to splunk regardless of level --- splunk-devops/pom.xml | 7 ++ .../splunkjenkins/CustomLoggerItem.java | 82 +++++++++++++++++++ .../splunkjenkins/CustomLoggersConfig.java | 66 +++++++++++++++ .../splunkjenkins/JdkSplunkLogHandler.java | 34 +++++++- .../CustomLoggerItem/config.jelly | 14 ++++ .../CustomLoggersConfig/config.jelly | 11 +++ .../CustomLoggersConfigTest.java | 31 +++++++ .../JdkSplunkLogHandlerTest.java | 4 + 8 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java create mode 100644 splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggersConfig.java create mode 100644 splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggerItem/config.jelly create mode 100644 splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggersConfig/config.jelly create mode 100644 splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java diff --git a/splunk-devops/pom.xml b/splunk-devops/pom.xml index 462b21e3..1f7eec0f 100644 --- a/splunk-devops/pom.xml +++ b/splunk-devops/pom.xml @@ -221,6 +221,13 @@ + + org.kohsuke.stapler + stapler + 1.259 + compile + + diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java new file mode 100644 index 00000000..fc691907 --- /dev/null +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java @@ -0,0 +1,82 @@ +package com.splunk.splunkjenkins; + +import hudson.Extension; +import hudson.logging.LogRecorder; +import hudson.logging.LogRecorderManager; +import hudson.model.Describable; +import hudson.model.Descriptor; +import hudson.util.FormValidation; +import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import java.util.Map; + +public class CustomLoggerItem implements Describable { + private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CustomLoggerItem.class.getName()); + + private static transient final LogRecorderManager logRecorderManager = Jenkins.getInstanceOrNull().getLog(); + + String customLoggerName; + LogRecorder logRecorder; + + @DataBoundConstructor + public CustomLoggerItem(String customLoggerName) { + this.customLoggerName = customLoggerName; + logRecorder = logRecorderManager.getLogRecorder(customLoggerName); + if (logRecorder == null) { + LOG.warning("CustomLoggerItem created for non-existent custom logger with name: " + customLoggerName); + } + } + + public String getCustomLoggerName() { + return customLoggerName; + } + + public void setCustomLoggerName(String customLoggerName) { + this.customLoggerName = customLoggerName; + } + + public LogRecorder getLogRecorder() { + return logRecorder; + } + + public void setLogRecorder(LogRecorder logRecorder) { + this.logRecorder = logRecorder; + } + + public String toString() { + return "CustomLoggerName: " + customLoggerName; + } + + public Descriptor getDescriptor() { + return Jenkins.getInstance().getDescriptor(getClass()); + } + + + @Extension + public static final class DescriptorImpl extends Descriptor { + @Override + public String getDisplayName() { + return "CustomLoggerItem"; + } + + public FormValidation doCheckName(@QueryParameter String value) { + if (value == null || value.isEmpty()) { + return FormValidation.error("Name cannot be empty"); + } else { + return FormValidation.ok(); + } + } + + public static ListBoxModel doFillCustomLoggerNameItems() { + ListBoxModel items = new ListBoxModel(); + + for (Map.Entry e : logRecorderManager.logRecorders.entrySet()) { + items.add(e.getKey(), e.getValue().getDisplayName()); + } + return items; + } + } +} diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggersConfig.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggersConfig.java new file mode 100644 index 00000000..76b36ba2 --- /dev/null +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggersConfig.java @@ -0,0 +1,66 @@ +package com.splunk.splunkjenkins; + +import hudson.Extension; +import jenkins.model.GlobalConfiguration; +import jenkins.model.Jenkins; +import net.sf.json.JSONArray; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.StaplerRequest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Extension +public class CustomLoggersConfig extends GlobalConfiguration { + private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CustomLoggersConfig.class.getName()); + + List customLoggers; + + public static CustomLoggersConfig get() { + return (CustomLoggersConfig) Jenkins.getInstance().getDescriptor(CustomLoggersConfig.class); + } + + public CustomLoggersConfig() { + super.load(); + if (customLoggers == null) { + this.customLoggers = new ArrayList<>(); + } + } + + @DataBoundConstructor + public CustomLoggersConfig(List customLoggers) { + if (customLoggers == null) { + this.customLoggers = Collections.emptyList(); + } else { + this.customLoggers = customLoggers; + } + this.save(); + } + + public List getCustomLoggers() { + return customLoggers; + } + + @DataBoundSetter + public void setCustomLoggers(List customLoggers) { + this.customLoggers = customLoggers; + } + + public void addCustomLogger(CustomLoggerItem customLoggerItem) { + this.customLoggers.add(customLoggerItem); + this.save(); + } + + @Override + public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + if (!json.has("customLoggers")) { + json.put("customLoggers", new JSONArray()); + } + req.bindJSON(this, json); + save(); + return true; + } +} diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java index 51964d46..55025fde 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java @@ -4,17 +4,25 @@ import com.splunk.splunkjenkins.model.EventType; import com.splunk.splunkjenkins.utils.LogConsumer; import com.splunk.splunkjenkins.utils.SplunkLogService; +import hudson.logging.LogRecorder; import hudson.model.Computer; import jenkins.model.Jenkins; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.*; +import java.util.logging.Filter; import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; import static com.splunk.splunkjenkins.Constants.JDK_FINE_LOG_BATCH; @@ -77,6 +85,28 @@ public void flush() { SplunkLogService.getInstance().sendBatch(copyList, EventType.LOG); } + @Override + public boolean isLoggable(LogRecord record) { + if (!record.getLoggerName().contains(JdkSplunkLogHandler.class.getName()) + && !record.getLoggerName().contains(CustomLoggersConfig.class.getName())) { // ignores self references + CustomLoggersConfig clConfig = CustomLoggersConfig.get(); + if (clConfig != null) { + for (CustomLoggerItem clItem : clConfig.getCustomLoggers()) { + LogRecorder logRecorder = clItem.getLogRecorder(); + if (logRecorder != null && logRecorder.targets != null) { + for (LogRecorder.Target target : logRecorder.targets) { + if (Boolean.TRUE.equals(target.matches(record))) { + return true; + } + } + } + } + } + } + return super.isLoggable(record); + } + + @Override public void close() throws SecurityException { diff --git a/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggerItem/config.jelly b/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggerItem/config.jelly new file mode 100644 index 00000000..4bbde769 --- /dev/null +++ b/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggerItem/config.jelly @@ -0,0 +1,14 @@ + + + + + + + +
+ +
+
+
+
diff --git a/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggersConfig/config.jelly b/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggersConfig/config.jelly new file mode 100644 index 00000000..467000fb --- /dev/null +++ b/splunk-devops/src/main/resources/com/splunk/splunkjenkins/CustomLoggersConfig/config.jelly @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java b/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java new file mode 100644 index 00000000..68f5c950 --- /dev/null +++ b/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java @@ -0,0 +1,31 @@ +package com.splunk.splunkjenkins; + +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class CustomLoggersConfigTest { + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void roundTripTest() throws Exception { + CustomLoggerItem cli1 = new CustomLoggerItem("logger1"); + CustomLoggerItem cli2 = new CustomLoggerItem("logger2"); + List loggerItems = Arrays.asList(cli1, cli2); + + CustomLoggersConfig config = CustomLoggersConfig.get(); + + config.addCustomLogger(cli1); + config.addCustomLogger(cli2); + + r.configRoundtrip(); + + assertEquals(loggerItems, config.getCustomLoggers()); + } +} diff --git a/splunk-devops/src/test/java/com/splunk/splunkjenkins/JdkSplunkLogHandlerTest.java b/splunk-devops/src/test/java/com/splunk/splunkjenkins/JdkSplunkLogHandlerTest.java index 03a0f319..79e86368 100644 --- a/splunk-devops/src/test/java/com/splunk/splunkjenkins/JdkSplunkLogHandlerTest.java +++ b/splunk-devops/src/test/java/com/splunk/splunkjenkins/JdkSplunkLogHandlerTest.java @@ -36,4 +36,8 @@ public void publish() throws Exception { verifySplunkSearchResult(message + " level=INFO", 1); } + @Test + public void publishCustomLogger() throws Exception { + + } } \ No newline at end of file From 471463fc31202a3fe1351b62fa653915a6ae2c48 Mon Sep 17 00:00:00 2001 From: Pierson Yieh Date: Tue, 5 Apr 2022 19:35:29 -0700 Subject: [PATCH 2/5] update CHANGELOG --- doc/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 4e0f4484..ada37182 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,3 +1,6 @@ +### 1.9.10 (April 5, 2022) +- Add CustomLoggersConfig global configuration that allows for specificying custom loggers to always send to Splunk regardless of log level, thanks to [Pierson Yieh](https://github.com/pyieh) + ### 1.9.9 (January 8, 2022) - JENKINS-67492 fix xml entity not declared error when parsing console node ### 1.9.8 (December 18, 2021) From 278c81da060ed8ff60567e574a6060c566a1cfbd Mon Sep 17 00:00:00 2001 From: Pierson Yieh Date: Wed, 6 Apr 2022 15:18:07 -0700 Subject: [PATCH 3/5] Rmv config round trip test --- .../CustomLoggersConfigTest.java | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java diff --git a/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java b/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java deleted file mode 100644 index 68f5c950..00000000 --- a/splunk-devops/src/test/java/com/splunk/splunkjenkins/CustomLoggersConfigTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.splunk.splunkjenkins; - -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -public class CustomLoggersConfigTest { - @Rule - public JenkinsRule r = new JenkinsRule(); - - @Test - public void roundTripTest() throws Exception { - CustomLoggerItem cli1 = new CustomLoggerItem("logger1"); - CustomLoggerItem cli2 = new CustomLoggerItem("logger2"); - List loggerItems = Arrays.asList(cli1, cli2); - - CustomLoggersConfig config = CustomLoggersConfig.get(); - - config.addCustomLogger(cli1); - config.addCustomLogger(cli2); - - r.configRoundtrip(); - - assertEquals(loggerItems, config.getCustomLoggers()); - } -} From dc6758a51ef89031f34de9400efaf5e17cf04ab5 Mon Sep 17 00:00:00 2001 From: Pierson Yieh Date: Wed, 13 Apr 2022 17:19:33 -0700 Subject: [PATCH 4/5] Address SpotBugs --- .../com/splunk/splunkjenkins/console/ConsoleNoteHandler.java | 2 ++ .../main/java/com/splunk/splunkjenkins/CustomLoggerItem.java | 2 +- .../splunk/splunkjenkins/listeners/LoggingQueueListener.java | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleNoteHandler.java b/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleNoteHandler.java index 0e4e6717..50032af2 100644 --- a/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleNoteHandler.java +++ b/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleNoteHandler.java @@ -81,6 +81,8 @@ public void read(String xml) throws XMLStreamException { case "label": label = attr.getValue(); break; + default: + break; } } break; diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java index fc691907..e534bbe2 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java @@ -16,7 +16,7 @@ public class CustomLoggerItem implements Describable { private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CustomLoggerItem.class.getName()); - private static transient final LogRecorderManager logRecorderManager = Jenkins.getInstanceOrNull().getLog(); + private static transient final LogRecorderManager logRecorderManager = Jenkins.getInstance().getLog(); String customLoggerName; LogRecorder logRecorder; diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingQueueListener.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingQueueListener.java index 24d768b2..99c8cf9d 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingQueueListener.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingQueueListener.java @@ -38,7 +38,7 @@ public class LoggingQueueListener extends QueueListener { private final static ConcurrentHashMap queuePhase = new ConcurrentHashMap(); // default is 3 question masks ???, see also hudson/model/Messages // due to access policy hudson/model/Messages must not be used, hard code it here - private final String QUEUE_UNKNOWN_MSG = "???"; + private final static String QUEUE_UNKNOWN_MSG = "???"; @Override public void onEnterWaiting(Queue.WaitingItem wi) { From 7f023efb80981e6003c07c18763aa9008475c76a Mon Sep 17 00:00:00 2001 From: Pierson Yieh Date: Mon, 11 Jul 2022 16:27:20 -0700 Subject: [PATCH 5/5] Fixed deprecated calls --- .../java/com/splunk/splunkjenkins/CustomLoggerItem.java | 6 ++---- .../java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java index e534bbe2..7face5b1 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java @@ -11,8 +11,6 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; -import java.util.Map; - public class CustomLoggerItem implements Describable { private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CustomLoggerItem.class.getName()); @@ -73,8 +71,8 @@ public FormValidation doCheckName(@QueryParameter String value) { public static ListBoxModel doFillCustomLoggerNameItems() { ListBoxModel items = new ListBoxModel(); - for (Map.Entry e : logRecorderManager.logRecorders.entrySet()) { - items.add(e.getKey(), e.getValue().getDisplayName()); + for (LogRecorder r : logRecorderManager.getRecorders()) { + items.add(r.getName(), r.getDisplayName()); } return items; } diff --git a/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java b/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java index 55025fde..d722febc 100644 --- a/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java +++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/JdkSplunkLogHandler.java @@ -93,8 +93,8 @@ public boolean isLoggable(LogRecord record) { if (clConfig != null) { for (CustomLoggerItem clItem : clConfig.getCustomLoggers()) { LogRecorder logRecorder = clItem.getLogRecorder(); - if (logRecorder != null && logRecorder.targets != null) { - for (LogRecorder.Target target : logRecorder.targets) { + if (logRecorder != null && logRecorder.getLoggers() != null) { + for (LogRecorder.Target target : logRecorder.getLoggers()) { if (Boolean.TRUE.equals(target.matches(record))) { return true; }