-
Couldn't load subscription status.
- Fork 39
Send Custom Loggers to Splunk #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
bc66491
471463f
278c81d
dc6758a
75456c0
7f023ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<CustomLoggerItem> { | ||
| 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<CustomLoggerItem> getDescriptor() { | ||
| return Jenkins.getInstance().getDescriptor(getClass()); | ||
| } | ||
|
|
||
|
|
||
| @Extension | ||
| public static final class DescriptorImpl extends Descriptor<CustomLoggerItem> { | ||
| @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; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<CustomLoggerItem> 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<CustomLoggerItem> customLoggers) { | ||
| if (customLoggers == null) { | ||
| this.customLoggers = Collections.emptyList(); | ||
| } else { | ||
| this.customLoggers = customLoggers; | ||
| } | ||
| this.save(); | ||
| } | ||
|
|
||
| public List<CustomLoggerItem> getCustomLoggers() { | ||
| return customLoggers; | ||
| } | ||
|
|
||
| @DataBoundSetter | ||
| public void setCustomLoggers(List<CustomLoggerItem> 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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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()) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Existing logic https://github.com/jenkinsci/splunk-devops-plugin/blob/master/splunk-devops/src/main/java/com/splunk/splunkjenkins/LoggingInitStep.java#L40 |
||
| 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 { | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?jelly escape-by-default='true'?> | ||
| <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" | ||
| xmlns:t="/lib/hudson" xmlns:f="/lib/form"> | ||
| <table width="100%"> | ||
| <f:entry title="${%CustomLoggerName}" field="customLoggerName"> | ||
| <f:select /> | ||
| </f:entry> | ||
| <f:entry> | ||
| <div align="right"> | ||
| <f:repeatableDeleteButton /> | ||
| </div> | ||
| </f:entry> | ||
| </table> | ||
| </j:jelly> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <?jelly escape-by-default='true'?> | ||
| <j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" | ||
| xmlns:t="/lib/hudson" xmlns:f="/lib/form"> | ||
| <f:section title="Splunk Custom Loggers Configuration"> | ||
| <f:entry title="Custom Log Recorders to Send to Splunk"> | ||
| <f:repeatable field="customLoggers" minimum="0" default=""> | ||
| <st:include page="config.jelly" class="${descriptor.clazz}" /> | ||
| </f:repeatable> | ||
| </f:entry> | ||
| </f:section> | ||
| </j:jelly> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this depend on LogRecorderManager, which adds a https://github.com/jenkinsci/jenkins/blob/75b4b31efd3f3340805497b6b23ff4b0cb32d2f3/core/src/main/java/hudson/util/RingBufferLogHandler.java automatically, that means if you want to sent to splunk verbose level log, it has to be logged to RingBufferLogHandler too, unnecessary performance burden.