diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md
index f7cad216..5bad98aa 100644
--- a/doc/CHANGELOG.md
+++ b/doc/CHANGELOG.md
@@ -1,3 +1,5 @@
+### 1.10.1 (July 11, 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.10.0 (July 4, 2022)
- SECURITY-2128 plugin logs unmasked credentials, thanks to [Pierson Yieh](https://github.com/pyieh)
- JENKINS-68775 pipeline build step is considered a stage in splunk plugin, thanks to [Guilherme Mota](https://github.com/guilhermemotadock)
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 bf5363a6..255cc1a2 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
@@ -83,6 +83,8 @@ public void read(String xml) throws XMLStreamException {
case "label":
label = attr.getValue();
break;
+ default:
+ break;
}
}
break;
diff --git a/splunk-devops/pom.xml b/splunk-devops/pom.xml
index 0dea4248..d60fb240 100644
--- a/splunk-devops/pom.xml
+++ b/splunk-devops/pom.xml
@@ -223,6 +223,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..7face5b1
--- /dev/null
+++ b/splunk-devops/src/main/java/com/splunk/splunkjenkins/CustomLoggerItem.java
@@ -0,0 +1,80 @@
+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;
+
+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.getInstance().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 (LogRecorder r : logRecorderManager.getRecorders()) {
+ items.add(r.getName(), r.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..d722febc 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.getLoggers() != null) {
+ for (LogRecorder.Target target : logRecorder.getLoggers()) {
+ 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/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