diff --git a/plugins/tasks/log/pom.xml b/plugins/tasks/log/pom.xml index 64e47352a2..e69802916c 100644 --- a/plugins/tasks/log/pom.xml +++ b/plugins/tasks/log/pom.xml @@ -24,6 +24,27 @@ provided + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + javax.inject javax.inject diff --git a/plugins/tasks/log/src/main/java/com/walmartlabs/concord/plugins/log/LoggingTaskV2.java b/plugins/tasks/log/src/main/java/com/walmartlabs/concord/plugins/log/LoggingTaskV2.java index 87b2e54ca1..3884f0473d 100644 --- a/plugins/tasks/log/src/main/java/com/walmartlabs/concord/plugins/log/LoggingTaskV2.java +++ b/plugins/tasks/log/src/main/java/com/walmartlabs/concord/plugins/log/LoggingTaskV2.java @@ -20,39 +20,48 @@ * ===== */ +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.walmartlabs.concord.runtime.v2.sdk.Task; import com.walmartlabs.concord.runtime.v2.sdk.TaskResult; import com.walmartlabs.concord.runtime.v2.sdk.Variables; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import javax.inject.Named; @Named("log") public class LoggingTaskV2 implements Task { + private static final ObjectMapper yamlObjectMapper = createYamlObjectMapper(); + @Override public TaskResult execute(Variables variables) { Object msg = variables.get("msg"); + String format = variables.getString("format"); String logLevel = variables.getString("level", "INFO"); switch (logLevel.toUpperCase()) { case "DEBUG": { - LogUtils.debug(msg); + LogUtils.debug(formatMessage(format, msg)); break; } case "INFO": { - LogUtils.info(msg); + LogUtils.info(formatMessage(format, msg)); break; } case "WARN": { - LogUtils.warn(msg); + LogUtils.warn(formatMessage(format, msg)); break; } case "ERROR": { - LogUtils.error(msg); + LogUtils.error(formatMessage(format, msg)); break; } default: - LogUtils.info(msg); + LogUtils.info(formatMessage(format, msg)); } return TaskResult.success(); @@ -93,4 +102,34 @@ public static void error(Object o) { public void call(String s) { LogUtils.info(s); } + + private static Object formatMessage(String format, Object msg) { + if (format == null || format.trim().isEmpty()) { + return msg; + } + + if ("yaml".equalsIgnoreCase(format)) { + try { + return "\n" + yamlObjectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(msg); + } catch (Exception e) { + throw new RuntimeException("Invalid yaml:" + e.getMessage()); + } + } + + throw new IllegalArgumentException("Unknown format '" + format + "'"); + } + + private static ObjectMapper createYamlObjectMapper() { + return defaultObjectMapper(new YAMLFactory() + .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) + .disable(YAMLGenerator.Feature.SPLIT_LINES)); + } + + private static ObjectMapper defaultObjectMapper(JsonFactory jf) { + ObjectMapper om = new ObjectMapper(jf); + om.registerModule(new Jdk8Module()); + om.registerModule(new JavaTimeModule()); + return om; + } } diff --git a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/GrammarV2.java b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/GrammarV2.java index 78d2ece4d9..96e9850957 100644 --- a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/GrammarV2.java +++ b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/GrammarV2.java @@ -47,6 +47,7 @@ import static com.walmartlabs.concord.runtime.v2.parser.GrammarMisc.*; import static com.walmartlabs.concord.runtime.v2.parser.GroupOfStepsGrammar.group; import static com.walmartlabs.concord.runtime.v2.parser.LogGrammar.logStep; +import static com.walmartlabs.concord.runtime.v2.parser.LogGrammar.logYamlStep; import static com.walmartlabs.concord.runtime.v2.parser.ParallelGrammar.parallelBlock; import static com.walmartlabs.concord.runtime.v2.parser.ReturnGrammar.returnStep; import static com.walmartlabs.concord.runtime.v2.parser.ScriptGrammar.script; @@ -181,7 +182,7 @@ private static Parser _val(JsonToken t) { private static final Parser stepObject = betweenTokens(JsonToken.START_OBJECT, JsonToken.END_OBJECT, choice(choice(parallelBlock, group, exprFull), choice(taskFull, script, callFull, callForm), - choice(checkpoint, ifExpr, switchExpr), setVars, logStep, throwStep, suspendStep)); + choice(checkpoint, ifExpr, switchExpr, setVars), logStep, logYamlStep, throwStep, suspendStep)); // step := exit | exprShort | parallelBlock | stepObject private static final Parser step = orError(choice(exit, returnStep, exprShort, stepObject), YamlValueType.STEP); diff --git a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/LogGrammar.java b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/LogGrammar.java index 5350bf4aae..75b5f872fb 100644 --- a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/LogGrammar.java +++ b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/parser/LogGrammar.java @@ -26,18 +26,26 @@ import static com.walmartlabs.concord.runtime.v2.parser.GrammarMisc.namedStep; import static com.walmartlabs.concord.runtime.v2.parser.GrammarOptions.namedOptions; import static com.walmartlabs.concord.runtime.v2.parser.GrammarV2.anyVal; -import static com.walmartlabs.concord.runtime.v2.parser.TaskGrammar.optionsWithStepName; public final class LogGrammar { public static final Parser logStep = namedStep("log", YamlValueType.TASK, (stepName, a) -> anyVal.bind(msg -> - namedOptions.map(options -> new TaskCall(a.location, "log", optionsWithStepName(stepName) + namedOptions.map(options -> new TaskCall(a.location, "log", TaskGrammar.optionsWithStepName(stepName) .putInput("msg", msg) .putAllMeta(options.meta()) .build())))); + public static final Parser logYamlStep = + namedStep("logYaml", YamlValueType.TASK, (stepName, a) -> + anyVal.bind(msg -> + namedOptions.map(options -> new TaskCall(a.location, "log", TaskGrammar.optionsWithStepName(stepName) + .putInput("msg", msg) + .putInput("format", "yaml") + .putAllMeta(options.meta()) + .build())))); + private LogGrammar() { } } diff --git a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/LogYamlStepMixIn.java b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/LogYamlStepMixIn.java new file mode 100644 index 0000000000..d77018eccc --- /dev/null +++ b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/LogYamlStepMixIn.java @@ -0,0 +1,38 @@ +package com.walmartlabs.concord.runtime.v2.schema; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2020 Walmart Inc. + * ----- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ===== + */ + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.kjetland.jackson.jsonSchema.annotations.JsonSchemaTitle; + +import java.util.Map; + +@JsonTypeName("LogYamlStep") +public interface LogYamlStepMixIn extends NamedStep { + + @JsonProperty(value = "logYaml", required = true) + @JsonSchemaTitle("Log yaml step") + String logYaml(); + + @JsonProperty("meta") + Map meta(); +} diff --git a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/StepMixIn.java b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/StepMixIn.java index 8e475bc430..d8100fba99 100644 --- a/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/StepMixIn.java +++ b/runtime/v2/model/src/main/java/com/walmartlabs/concord/runtime/v2/schema/StepMixIn.java @@ -36,6 +36,7 @@ @JsonSubTypes.Type(value = ExpressionShortMixIn.class, name = "Expression (short form)"), @JsonSubTypes.Type(value = ExpressionFullMixIn.class, name = "Expression (full form)"), @JsonSubTypes.Type(value = LogStepMixIn.class, name = "Log"), + @JsonSubTypes.Type(value = LogYamlStepMixIn.class, name = "LogYaml"), @JsonSubTypes.Type(value = FlowCallStepMixIn.class, name = "Flow call"), @JsonSubTypes.Type(value = FormCallStepMixIn.class, name = "Form call"), @JsonSubTypes.Type(value = IfStepMixIn.class, name = "IF step"), diff --git a/runtime/v2/runner/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java b/runtime/v2/runner/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java index 61db3ca3a1..c93fd6e595 100644 --- a/runtime/v2/runner/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java +++ b/runtime/v2/runner/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java @@ -229,6 +229,7 @@ public void test() throws Exception { byte[] log = run(); assertLog(log, ".*Hello, Concord!.*"); assertLog(log, ".*" + Pattern.quote("defaultsMap:{a=a-value}") + ".*"); + assertLog(log, ".*k: \"value\".*"); verify(processStatusCallback, times(1)).onRunning(instanceId); } diff --git a/runtime/v2/runner/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/hello/concord.yml b/runtime/v2/runner/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/hello/concord.yml index b3237fdb4b..f3a35ae0a0 100644 --- a/runtime/v2/runner/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/hello/concord.yml +++ b/runtime/v2/runner/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/hello/concord.yml @@ -1,4 +1,6 @@ flows: default: - log: "Hello, ${name}!" - - task: testDefaults \ No newline at end of file + - task: testDefaults + - logYaml: + k: "value" \ No newline at end of file