diff --git a/changes/add_logging-level.md b/changes/add_logging-level.md new file mode 100644 index 000000000..ad5bd855c --- /dev/null +++ b/changes/add_logging-level.md @@ -0,0 +1,2 @@ +Logging level for Definer log infrastructure (eg Loki plugin) + diff --git a/docs/plugin-loki.md b/docs/plugin-loki.md index a723dfa8c..3352ba7c7 100644 --- a/docs/plugin-loki.md +++ b/docs/plugin-loki.md @@ -7,13 +7,23 @@ ending `.loki` with the following: "url": "http://your.loki.server/loki/api/v1/push", "labels": { "environment": "foo" - } + }, + "level": INFO } The `"url"` property is the URL of the Loki server to push logs into. The optional `"labels"` object will apply static labels to all values logged from this instance. +The `"level"` property is one of: +1. FATAL +2. ERROR +3. WARN +4. INFO +5. DEBUG + +All messages at or above the setting in severity will be logged to Loki. + ## For Plugin Developers The PluginManager passes each `PluginFileType` an implementation of `Definer` which has a method `log(String, Map)`. If the Loki plugin is installed, then the PluginManager will route log diff --git a/plugin-guanyin/src/main/java/ca/on/oicr/gsi/shesmu/guanyin/RunReport.java b/plugin-guanyin/src/main/java/ca/on/oicr/gsi/shesmu/guanyin/RunReport.java index f39a6f18f..1135dc539 100644 --- a/plugin-guanyin/src/main/java/ca/on/oicr/gsi/shesmu/guanyin/RunReport.java +++ b/plugin-guanyin/src/main/java/ca/on/oicr/gsi/shesmu/guanyin/RunReport.java @@ -2,10 +2,7 @@ import ca.on.oicr.gsi.prometheus.LatencyHistogram; import ca.on.oicr.gsi.shesmu.cromwell.WorkflowIdAndStatus; -import ca.on.oicr.gsi.shesmu.plugin.Definer; -import ca.on.oicr.gsi.shesmu.plugin.FrontEndIcon; -import ca.on.oicr.gsi.shesmu.plugin.MultiPartBodyPublisher; -import ca.on.oicr.gsi.shesmu.plugin.Utils; +import ca.on.oicr.gsi.shesmu.plugin.*; import ca.on.oicr.gsi.shesmu.plugin.action.ActionCommand; import ca.on.oicr.gsi.shesmu.plugin.action.ActionCommand.Preference; import ca.on.oicr.gsi.shesmu.plugin.action.ActionServices; @@ -373,7 +370,7 @@ private void showError(HttpResponse response, URI url) final List errors = new ArrayList<>(); final Map labels = new TreeMap<>(); labels.put("url", url.getHost()); - owner.log("HTTP error: " + response.statusCode(), labels); + owner.log("HTTP error: " + response.statusCode(), LogLevel.ERROR, labels); errors.add("HTTP error: " + response.statusCode()); this.errors = errors; } diff --git a/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/IssueVerb.java b/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/IssueVerb.java index f45d8c053..cd4de5fff 100644 --- a/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/IssueVerb.java +++ b/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/IssueVerb.java @@ -1,6 +1,7 @@ package ca.on.oicr.gsi.shesmu.jira; import ca.on.oicr.gsi.shesmu.plugin.Definer; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.action.ActionState; import java.io.IOException; import java.net.URISyntaxException; @@ -61,7 +62,8 @@ public ActionState perform( Map lokiLabels = new HashMap<>(); lokiLabels.put("issue", issue.getKey()); lokiLabels.put("verb", "close"); - ((Definer) definer).log(errorBuilder.toString(), lokiLabels); + ((Definer) definer) + .log(errorBuilder.toString(), LogLevel.ERROR, lokiLabels); return ActionState.FAILED; } } diff --git a/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/JiraConnection.java b/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/JiraConnection.java index 1ca117c68..b19040f79 100644 --- a/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/JiraConnection.java +++ b/plugin-jira/src/main/java/ca/on/oicr/gsi/shesmu/jira/JiraConnection.java @@ -6,6 +6,7 @@ import ca.on.oicr.gsi.shesmu.jira.Issue.Field; import ca.on.oicr.gsi.shesmu.plugin.Definer; import ca.on.oicr.gsi.shesmu.plugin.FrontEndIcon; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.Tuple; import ca.on.oicr.gsi.shesmu.plugin.action.ActionState; import ca.on.oicr.gsi.shesmu.plugin.action.ShesmuAction; @@ -456,7 +457,8 @@ boolean transition( .append(transitionResult.body()); Map lokiLabels = new HashMap<>(); lokiLabels.put("issue", issue.getKey()); - ((Definer) definer).log(errorBuilder.toString(), lokiLabels); + ((Definer) definer) + .log(errorBuilder.toString(), LogLevel.ERROR, lokiLabels); return false; } else { return true; diff --git a/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/Configuration.java b/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/Configuration.java index 88dd5d774..1e23f12fb 100644 --- a/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/Configuration.java +++ b/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/Configuration.java @@ -1,5 +1,6 @@ package ca.on.oicr.gsi.shesmu.loki; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.Map; @@ -8,6 +9,7 @@ public class Configuration { private Map labels = Map.of(); private String url; + private LogLevel level; public Map getLabels() { return labels; @@ -24,4 +26,12 @@ public void setLabels(Map labels) { public void setUrl(String url) { this.url = url; } + + public LogLevel getLevel() { + return level; + } + + public void setLevel(LogLevel level) { + this.level = level; + } } diff --git a/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/LokiPlugin.java b/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/LokiPlugin.java index 68a597af9..810361bb3 100644 --- a/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/LokiPlugin.java +++ b/plugin-loki/src/main/java/ca/on/oicr/gsi/shesmu/loki/LokiPlugin.java @@ -3,6 +3,7 @@ import ca.on.oicr.gsi.Pair; import ca.on.oicr.gsi.prometheus.LatencyHistogram; import ca.on.oicr.gsi.shesmu.plugin.Definer; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.json.JsonPluginFile; import ca.on.oicr.gsi.status.SectionRenderer; import com.fasterxml.jackson.databind.ObjectMapper; @@ -58,6 +59,7 @@ public void configuration(SectionRenderer renderer) { configuration.ifPresent( configuration -> { renderer.link("Instance", configuration.getUrl(), configuration.getUrl()); + renderer.line("Level", configuration.getLevel().toString()); for (final var entry : configuration.getLabels().entrySet()) { renderer.line("Label: " + entry.getKey(), entry.getValue()); } @@ -160,9 +162,10 @@ protected synchronized Optional update(Configuration configuration) { } @Override - public synchronized void writeLog(String message, Map attributes) { + public synchronized void writeLog( + String message, LogLevel level, Map attributes) { final var now = Instant.now(); buffer.computeIfAbsent(attributes, k -> new ArrayList<>()).add(new Pair<>(now, message)); - flush(now); + if (level.compareTo(this.configuration.get().getLevel()) >= 0) flush(now); } } diff --git a/plugin-sftp/src/main/java/ca/on/oicr/gsi/shesmu/sftp/SftpServer.java b/plugin-sftp/src/main/java/ca/on/oicr/gsi/shesmu/sftp/SftpServer.java index 7b6395054..8b402e91b 100644 --- a/plugin-sftp/src/main/java/ca/on/oicr/gsi/shesmu/sftp/SftpServer.java +++ b/plugin-sftp/src/main/java/ca/on/oicr/gsi/shesmu/sftp/SftpServer.java @@ -4,6 +4,7 @@ import ca.on.oicr.gsi.prometheus.LatencyHistogram; import ca.on.oicr.gsi.shesmu.plugin.AlgebraicValue; import ca.on.oicr.gsi.shesmu.plugin.Definer; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.Tuple; import ca.on.oicr.gsi.shesmu.plugin.action.ActionState; import ca.on.oicr.gsi.shesmu.plugin.action.ShesmuAction; @@ -176,7 +177,7 @@ public Thread drainOutput( labels.put("type", "refiller"); labels.put("stream", stream); final var errorDrainThread = - new Thread(() -> errorReader.lines().forEach(l -> definer.log(l, labels))); + new Thread(() -> errorReader.lines().forEach(l -> definer.log(l, LogLevel.ERROR, labels))); errorDrainThread.start(); return errorDrainThread; } @@ -314,7 +315,7 @@ public boolean refill(String name, String command, ArrayNode data) { labels.put("command", command); labels.put("name", name); labels.put("type", "refiller"); - definer.log("Expected OK or UPDATE, but got no output", labels); + definer.log("Expected OK or UPDATE, but got no output", LogLevel.ERROR, labels); } else { switch (response) { case "UPDATE": @@ -355,7 +356,10 @@ public boolean refill(String name, String command, ArrayNode data) { labels.put("command", command); labels.put("name", name); labels.put("type", "refiller"); - definer.log("Expected OK or UPDATE, but got invalid response: " + response, labels); + definer.log( + "Expected OK or UPDATE, but got invalid response: " + response, + LogLevel.ERROR, + labels); break; } } @@ -509,7 +513,7 @@ protected Optional fetch(Tuple key, Instant lastUpdated) throws Exceptio labels.put("command", command); labels.put("name", name); labels.put("type", "function"); - errorReader.lines().forEach(l -> definer.log(l, labels)); + errorReader.lines().forEach(l -> definer.log(l, LogLevel.ERROR, labels)); process.join(); if (process.getExitStatus() == null || process.getExitStatus() != 0) { return Optional.empty(); diff --git a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/Definer.java b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/Definer.java index d1a21d1a1..6adcaa08c 100644 --- a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/Definer.java +++ b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/Definer.java @@ -296,5 +296,5 @@ String defineStaticSigner( * @param message the log message to write * @param labels the labels associated with this message */ - void log(String message, Map labels); + void log(String message, LogLevel level, Map labels); } diff --git a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/LogLevel.java b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/LogLevel.java new file mode 100644 index 000000000..027bb5192 --- /dev/null +++ b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/LogLevel.java @@ -0,0 +1,9 @@ +package ca.on.oicr.gsi.shesmu.plugin; + +public enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR, + FATAL +} diff --git a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFile.java b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFile.java index a1f7b8991..c6e66bc0a 100644 --- a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFile.java +++ b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFile.java @@ -117,9 +117,10 @@ public void stop() {} * service. To write logs, use {@link Definer#log(String, Map)} * * @param message the log message to write + * @param level the logging level of this message * @param attributes the labels associated with this message */ - public void writeLog(String message, Map attributes) { + public void writeLog(String message, LogLevel level, Map attributes) { // Do nothing } } diff --git a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFileType.java b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFileType.java index dcd2c0412..31abd22c0 100644 --- a/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFileType.java +++ b/shesmu-pluginapi/src/main/java/ca/on/oicr/gsi/shesmu/plugin/PluginFileType.java @@ -170,9 +170,10 @@ public void writeJavaScriptRenderer(PrintStream writer) {} * service. To write logs, use {@link Definer#log(String, Map)} * * @param message the log message to write + * @param level the logging level of this message * @param attributes the labels associated with this message */ - public void writeLog(String message, Map attributes) { + public void writeLog(String message, LogLevel level, Map attributes) { // Do nothing } } diff --git a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/Server.java b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/Server.java index 93754f2ca..78f375a96 100644 --- a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/Server.java +++ b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/Server.java @@ -21,6 +21,7 @@ import ca.on.oicr.gsi.shesmu.core.StandardDefinitions; import ca.on.oicr.gsi.shesmu.plugin.ErrorableStream; import ca.on.oicr.gsi.shesmu.plugin.FrontEndIcon; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.SourceLocation; import ca.on.oicr.gsi.shesmu.plugin.action.Action; import ca.on.oicr.gsi.shesmu.plugin.action.ActionCommand.Preference; @@ -3137,7 +3138,7 @@ public void start() { processor.start(executor, compiler); System.out.println("Starting scheduler..."); master.start(executor); - pluginManager.log("Shesmu started.", Map.of()); + pluginManager.log("Shesmu started.", LogLevel.INFO, Map.of()); } private void storeEntries(ObjectNode entries, LabelledKeyValueCache cache) { diff --git a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/ActionProcessor.java b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/ActionProcessor.java index 2a05c4be8..7dd336201 100644 --- a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/ActionProcessor.java +++ b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/ActionProcessor.java @@ -4,6 +4,7 @@ import ca.on.oicr.gsi.prometheus.LatencyHistogram; import ca.on.oicr.gsi.shesmu.Server; import ca.on.oicr.gsi.shesmu.core.input.shesmu.ShesmuIntrospectionValue; +import ca.on.oicr.gsi.shesmu.plugin.LogLevel; import ca.on.oicr.gsi.shesmu.plugin.SourceLocation; import ca.on.oicr.gsi.shesmu.plugin.SourceLocation.SourceLocationLinker; import ca.on.oicr.gsi.shesmu.plugin.Utils; @@ -769,7 +770,8 @@ public CommandStatistics command( @Override public Boolean accepted() { labels.put("command", c.command()); - pluginManager.log("Performed command", labels); + pluginManager.log( + "Performed command", LogLevel.INFO, labels); return true; } @@ -787,7 +789,8 @@ public Boolean murder(Filter filter) { @Override public Boolean purge() { labels.put("command", c.command()); - pluginManager.log("Performed command", labels); + pluginManager.log( + "Performed command", LogLevel.INFO, labels); purge.add(e.getKey()); stateCount .labels( @@ -800,7 +803,8 @@ public Boolean purge() { @Override public Boolean reset() { labels.put("command", c.command()); - pluginManager.log("Performed command", labels); + pluginManager.log( + "Performed command", LogLevel.INFO, labels); if (e.getValue().lastState != ActionState.UNKNOWN) { stateCount .labels( diff --git a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/plugins/PluginManager.java b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/plugins/PluginManager.java index 4d9fcaa4c..1b125a434 100644 --- a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/plugins/PluginManager.java +++ b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/server/plugins/PluginManager.java @@ -13,15 +13,8 @@ import ca.on.oicr.gsi.shesmu.compiler.definitions.SignatureDefinition; import ca.on.oicr.gsi.shesmu.compiler.definitions.SignatureVariableForDynamicSigner; import ca.on.oicr.gsi.shesmu.compiler.definitions.SignatureVariableForStaticSigner; -import ca.on.oicr.gsi.shesmu.plugin.Definer; -import ca.on.oicr.gsi.shesmu.plugin.ErrorableStream; -import ca.on.oicr.gsi.shesmu.plugin.Parser; -import ca.on.oicr.gsi.shesmu.plugin.PluginFile; -import ca.on.oicr.gsi.shesmu.plugin.PluginFileType; -import ca.on.oicr.gsi.shesmu.plugin.RequiredServices; +import ca.on.oicr.gsi.shesmu.plugin.*; import ca.on.oicr.gsi.shesmu.plugin.SourceLocation.SourceLocationLinker; -import ca.on.oicr.gsi.shesmu.plugin.SupplementaryInformation; -import ca.on.oicr.gsi.shesmu.plugin.Utils; import ca.on.oicr.gsi.shesmu.plugin.action.Action; import ca.on.oicr.gsi.shesmu.plugin.action.ActionState; import ca.on.oicr.gsi.shesmu.plugin.action.CustomActionParameter; @@ -851,11 +844,11 @@ public Stream isOverloaded(Set services) { } @Override - public void log(String message, Map labels) { + public void log(String message, LogLevel level, Map labels) { final Map amendedLabels = new TreeMap<>(labels); amendedLabels.put("plugin", instance.fileName().toString()); amendedLabels.put("plugin_type", FormatTypeWrapper.this.fileFormat.getClass().toString()); - PluginManager.this.log(message, amendedLabels); + PluginManager.this.log(message, level, amendedLabels); } public Stream refillers() { @@ -1210,12 +1203,12 @@ public final Stream listConfiguration() { return configuration.stream().map(FileWrapper::configuration); } - public void log(String message, Map attributes) { - fileFormat.writeLog(message, attributes); + public void log(String message, LogLevel level, Map attributes) { + fileFormat.writeLog(message, level, attributes); for (final var reference : this.wrappers.values()) { final var wrapper = reference.get(); if (wrapper != null) { - wrapper.instance.writeLog(message, attributes); + wrapper.instance.writeLog(message, level, attributes); } } } @@ -2051,14 +2044,14 @@ public Stream listConfiguration() { return formatTypes.stream().flatMap(FormatTypeWrapper::listConfiguration); } - public void log(String message, Map attributes) { + public void log(String message, LogLevel level, Map attributes) { if (LOG_REENTRANT_CHECK.get()) { throw new IllegalStateException("Trying to log while logging."); } LOG_REENTRANT_CHECK.set(true); try { for (final var format : this.formatTypes) { - format.log(message, attributes); + format.log(message, level, attributes); } } finally { LOG_REENTRANT_CHECK.set(false);