You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/02/13 17:33:12 UTC
[camel] 02/03: CAMEL-19033: camel-jbang - get trace in color output of json so its easier to read
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 34a1bf1ffc08d005ff095a1883da467b4aa2e786
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Feb 13 16:29:23 2023 +0100
CAMEL-19033: camel-jbang - get trace in color output of json so its easier to read
---
.../dsl/jbang/core/commands/process/ListTrace.java | 38 ++++++-
.../camel/dsl/jbang/core/common/JSonHelper.java | 54 ++++++++++
.../java/org/apache/camel/util/json/Jsoner.java | 111 +++++++++++++++++++++
.../java/org/apache/camel/util/json/Yytoken.java | 8 +-
.../camel/util/json/JSonerColorPrintTest.java | 83 +++++++++++++++
5 files changed, 287 insertions(+), 7 deletions(-)
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListTrace.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListTrace.java
index b9aaf09c32a..a77ea0caf96 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListTrace.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListTrace.java
@@ -27,6 +27,7 @@ import com.github.freva.asciitable.Column;
import com.github.freva.asciitable.HorizontalAlign;
import com.github.freva.asciitable.OverflowBehaviour;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.JSonHelper;
import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.TimeUtils;
@@ -92,7 +93,7 @@ public class ListTrace extends ProcessWatchCommand {
rows.sort(this::sortRow);
if (!rows.isEmpty()) {
- System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
+ String data = AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
new Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
new Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(30, OverflowBehaviour.ELLIPSIS_RIGHT)
.with(r -> r.name),
@@ -103,8 +104,23 @@ public class ListTrace extends ProcessWatchCommand {
new Column().header("ID").dataAlign(HorizontalAlign.LEFT).maxWidth(25, OverflowBehaviour.ELLIPSIS_RIGHT)
.with(this::getId),
new Column().header("AGE").dataAlign(HorizontalAlign.RIGHT).with(this::getTimestamp),
- new Column().header("MESSAGE").dataAlign(HorizontalAlign.LEFT).maxWidth(110, OverflowBehaviour.NEWLINE)
- .with(this::getMessage))));
+ new Column().header("ELAPSED").dataAlign(HorizontalAlign.RIGHT).with(this::getElapsed),
+ new Column().header("FAILED").dataAlign(HorizontalAlign.RIGHT).with(this::getFailed)));
+ String[] arr = data.split(System.lineSeparator());
+ // print header
+ System.out.println(arr[0]);
+ // mix column and message (master/detail) mode
+ for (int i = 0; i < rows.size(); i++) {
+ String s = arr[i + 1];
+ System.out.println(s);
+ String json = getMessage(rows.get(i));
+ // pad with 8 spaces to indent json data
+ String[] lines = json.split(System.lineSeparator());
+ for (String line : lines) {
+ System.out.print(" ");
+ System.out.println(line);
+ }
+ }
}
return 0;
@@ -173,6 +189,20 @@ public class ListTrace extends ProcessWatchCommand {
return "";
}
+ private String getElapsed(Row r) {
+ if (r.elapsed > 0) {
+ return TimeUtils.printDuration(r.elapsed, true);
+ }
+ return "";
+ }
+
+ private String getFailed(Row r) {
+ if (r.failed) {
+ return "1";
+ }
+ return "0";
+ }
+
private String getUid(Row r) {
return "" + r.uid;
}
@@ -194,7 +224,7 @@ public class ListTrace extends ProcessWatchCommand {
private String getMessage(Row r) {
String s = r.message.toJson();
if (pretty) {
- s = Jsoner.prettyPrint(s, 2);
+ s = JSonHelper.colorPrint(s, 2);
}
return s;
}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/JSonHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/JSonHelper.java
new file mode 100644
index 00000000000..7d1b06f2d35
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/JSonHelper.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.camel.dsl.jbang.core.common;
+
+import org.apache.camel.util.json.Jsoner;
+import org.apache.camel.util.json.Yytoken;
+import org.fusesource.jansi.Ansi;
+
+public final class JSonHelper {
+
+ private JSonHelper() {
+ }
+
+ /**
+ * Prints the JSon in ANSi color (similar to jq)
+ */
+ public static String colorPrint(String json, int spaces) {
+ return Jsoner.colorPrint(json, spaces, new Jsoner.ColorPrintElement() {
+ Yytoken.Types prev;
+
+ @Override
+ public String color(Yytoken.Types type, Object value) {
+ String s = value.toString();
+ switch (type) {
+ case COLON, COMMA, LEFT_SQUARE, RIGHT_SQUARE, LEFT_BRACE, RIGHT_BRACE ->
+ s = Ansi.ansi().fg(Ansi.Color.WHITE).bold().a(s).reset().toString();
+ case VALUE -> {
+ if (Yytoken.Types.COLON == prev) {
+ s = Ansi.ansi().fg(Ansi.Color.GREEN).a(s).reset().toString();
+ } else {
+ s = Ansi.ansi().fgBright(Ansi.Color.BLUE).a(s).reset().toString();
+ }
+ }
+ }
+ prev = type;
+ return s;
+ }
+ });
+ }
+}
diff --git a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Jsoner.java b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Jsoner.java
index a2624a74478..573fe7eb484 100644
--- a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Jsoner.java
+++ b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Jsoner.java
@@ -732,6 +732,117 @@ public final class Jsoner {
return returnable.toString();
}
+ @FunctionalInterface
+ public interface ColorPrintElement {
+ String color(Yytoken.Types type, Object value);
+ }
+
+ public static String colorPrint(final String printable, final ColorPrintElement color) {
+ return Jsoner.colorPrint(printable, "\t", Integer.MAX_VALUE, color);
+ }
+
+ public static String colorPrint(final String printable, final int spaces, final ColorPrintElement color) {
+ if (spaces > 10 || spaces < 2) {
+ throw new IllegalArgumentException("Indentation with spaces must be between 2 and 10.");
+ }
+ final StringBuilder indentation = new StringBuilder("");
+ for (int i = 0; i < spaces; i++) {
+ indentation.append(" ");
+ }
+ return Jsoner.colorPrint(printable, indentation.toString(), Integer.MAX_VALUE, color);
+ }
+
+ public static String colorPrint(
+ final String printable, final String indentation, final int depth, ColorPrintElement color) {
+ final Yylex lexer = new Yylex(new StringReader(printable));
+ Yytoken lexed;
+ final StringBuilder returnable = new StringBuilder();
+ int level = 0;
+ try {
+ do {
+ lexed = Jsoner.lexNextToken(lexer);
+ switch (lexed.getType()) {
+ case COLON:
+ returnable.append(color.color(Yytoken.Types.COLON, ":")).append(" ");
+ break;
+ case COMMA:
+ returnable.append(color.color(Yytoken.Types.COMMA, lexed.getValue()));
+ if (level <= depth) {
+ returnable.append("\n");
+ for (int i = 0; i < level; i++) {
+ returnable.append(indentation);
+ }
+ } else {
+ returnable.append(" ");
+ }
+ break;
+ case END:
+ returnable.append("\n");
+ break;
+ case LEFT_BRACE:
+ returnable.append(color.color(Yytoken.Types.LEFT_BRACE, lexed.getValue()));
+ if (++level <= depth) {
+ returnable.append("\n");
+ for (int i = 0; i < level; i++) {
+ returnable.append(indentation);
+ }
+ } else {
+ returnable.append(" ");
+ }
+ break;
+ case LEFT_SQUARE:
+ returnable.append(color.color(Yytoken.Types.LEFT_SQUARE, lexed.getValue()));
+ if (++level <= depth) {
+ returnable.append("\n");
+ for (int i = 0; i < level; i++) {
+ returnable.append(indentation);
+ }
+ } else {
+ returnable.append(" ");
+ }
+ break;
+ case RIGHT_BRACE:
+ if (level-- <= depth) {
+ returnable.append("\n");
+ for (int i = 0; i < level; i++) {
+ returnable.append(indentation);
+ }
+ } else {
+ returnable.append(" ");
+ }
+ returnable.append(color.color(Yytoken.Types.RIGHT_BRACE, lexed.getValue()));
+ break;
+ case RIGHT_SQUARE:
+ if (level-- <= depth) {
+ returnable.append("\n");
+ for (int i = 0; i < level; i++) {
+ returnable.append(indentation);
+ }
+ } else {
+ returnable.append(" ");
+ }
+ returnable.append(color.color(Yytoken.Types.RIGHT_SQUARE, lexed.getValue()));
+ break;
+ default:
+ if (lexed.getValue() instanceof String) {
+ String s = "\"" + Jsoner.escape((String) lexed.getValue()) + "\"";
+ returnable.append(color.color(Yytoken.Types.VALUE, s));
+ } else {
+ returnable.append(color.color(Yytoken.Types.VALUE, lexed.getValue()));
+ }
+ break;
+ }
+ } while (!lexed.getType().equals(Yytoken.Types.END));
+ } catch (final DeserializationException caught) {
+ /* This is according to the method's contract. */
+ return null;
+ } catch (final IOException caught) {
+ /* See StringReader. */
+ return null;
+ }
+ return returnable.toString();
+ }
+
/**
* A convenience method that assumes a StringWriter.
*
diff --git a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Yytoken.java b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Yytoken.java
index 5d1a583969f..5ef989f7233 100644
--- a/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Yytoken.java
+++ b/tooling/camel-util-json/src/main/java/org/apache/camel/util/json/Yytoken.java
@@ -21,9 +21,9 @@ package org.apache.camel.util.json;
*
* @since 2.0.0
*/
-class Yytoken {
+public class Yytoken {
/** Represents the different kinds of tokens. */
- enum Types {
+ public enum Types {
/** Tokens of this type will always have a value of ":" */
COLON,
/** Tokens of this type will always have a value of "," */
@@ -41,7 +41,9 @@ class Yytoken {
/** Tokens of this type will always have a value of "}" */
RIGHT_BRACE,
/** Tokens of this type will always have a value of "]" */
- RIGHT_SQUARE;
+ RIGHT_SQUARE,
+ /** Represent the value (not a parsing token but used during color print) */
+ VALUE;
}
private final Types type;
diff --git a/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JSonerColorPrintTest.java b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JSonerColorPrintTest.java
new file mode 100644
index 00000000000..436835072f1
--- /dev/null
+++ b/tooling/camel-util-json/src/test/java/org/apache/camel/util/json/JSonerColorPrintTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.camel.util.json;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class JSonerColorPrintTest {
+
+ @Test
+ public void testColor() throws Exception {
+ InputStream is = new FileInputStream("src/test/resources/bean.json");
+ String json = loadText(is);
+
+ String color = Jsoner.colorPrint(json, new Jsoner.ColorPrintElement() {
+ Yytoken.Types prev;
+
+ @Override
+ public String color(Yytoken.Types type, Object value) {
+ String answer;
+ if (Yytoken.Types.VALUE == type) {
+ if (Yytoken.Types.COLON == prev) {
+ // value
+ answer = "GREEN" + value.toString();
+ } else {
+ // value
+ answer = "BLUE" + value.toString();
+ }
+ } else {
+ answer = value.toString();
+ }
+ prev = type;
+ return answer;
+ }
+ });
+
+ Assertions.assertTrue(color.contains("BLUE\"title\": GREEN\"Bean\""));
+ }
+
+ public static String loadText(InputStream in) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ InputStreamReader isr = new InputStreamReader(in);
+
+ try {
+ BufferedReader reader = new BufferedReader(isr);
+
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) {
+ line = builder.toString();
+ return line;
+ }
+
+ builder.append(line);
+ builder.append("\n");
+ }
+ } finally {
+ isr.close();
+ in.close();
+ }
+ }
+
+}