From 322efa21021d4f2189c6dc9ffc12441489474421 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 15 Nov 2022 10:47:49 -0600 Subject: [PATCH] Initial checkin --- .../qqq/backend/core/utils/LogUtils.java | 170 ++++++++++++++++++ .../qqq/backend/core/utils/LogUtilsTest.java | 122 +++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/LogUtils.java create mode 100644 qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/LogUtilsTest.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/LogUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/LogUtils.java new file mode 100644 index 00000000..01ba9009 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/LogUtils.java @@ -0,0 +1,170 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.utils; + + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class LogUtils +{ + + /******************************************************************************* + ** + *******************************************************************************/ + public static String jsonLog(LogPair... logPairs) + { + if(logPairs == null || logPairs.length == 0) + { + return ("{}"); + } + + List filteredList = Arrays.stream(logPairs).filter(Objects::nonNull).toList(); + if(filteredList.isEmpty()) + { + return ("{}"); + } + + return ('{' + filteredList.stream().map(LogPair::toString).collect(Collectors.joining(",")) + '}'); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static LogPair logPair(String key, Object value) + { + return (new LogPair(key, value)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static LogPair logPair(String key, LogPair... values) + { + return (new LogPair(key, values)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static class LogPair + { + private String key; + private Object value; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public LogPair(String key, Object value) + { + this.key = key; + this.value = value; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public String toString() + { + String valueString = getValueString(value); + + return "\"" + Objects.requireNonNullElse(key, "null").replace('"', '.') + "\":" + valueString; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private String getValueString(Object value) + { + String valueString; + if(value == null) + { + valueString = "null"; + } + else if(value instanceof LogPair subLogPair) + { + valueString = '{' + subLogPair.toString() + '}'; + } + else if(value instanceof LogPair[] subLogPairs) + { + String subLogPairsString = Arrays.stream(subLogPairs).map(LogPair::toString).collect(Collectors.joining(",")); + valueString = '{' + subLogPairsString + '}'; + } + else if(value instanceof UnsafeSupplier us) + { + try + { + Object o = us.get(); + return getValueString(o); + } + catch(Exception e) + { + valueString = "LogValueError"; + } + } + else if(value instanceof Number n) + { + valueString = String.valueOf(n); + } + else + { + valueString = '"' + String.valueOf(value).replace("\"", "\\\"") + '"'; + } + return valueString; + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @FunctionalInterface + public interface UnsafeSupplier + { + /******************************************************************************* + ** + *******************************************************************************/ + Object get() throws Exception; + } + +} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/LogUtilsTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/LogUtilsTest.java new file mode 100644 index 00000000..63396d3c --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/LogUtilsTest.java @@ -0,0 +1,122 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.utils; + + +import java.math.BigDecimal; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Test; +import static com.kingsrook.qqq.backend.core.utils.LogUtils.jsonLog; +import static com.kingsrook.qqq.backend.core.utils.LogUtils.logPair; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +/******************************************************************************* + ** Unit test for com.kingsrook.qqq.backend.core.utils.LogUtils + *******************************************************************************/ +class LogUtilsTest +{ + private static final Logger LOG = LogManager.getLogger(LogUtilsTest.class); + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void test() throws Exception + { + //////////////// + // null cases // + //////////////// + assertEquals("{}", jsonLog()); + assertEquals("{}", jsonLog((LogUtils.LogPair) null)); + assertEquals("{}", jsonLog((LogUtils.LogPair[]) null)); + assertEquals(""" + {"null":null}""", jsonLog(logPair(null, (LogUtils.LogPair) null))); + assertEquals(""" + {"null":null}""", jsonLog(logPair(null, (LogUtils.LogPair[]) null))); + + ////////////// + // escaping // + ////////////// + assertEquals(""" + {"f.o.o":"b\\"a\\"r"}""", jsonLog(logPair("f\"o\"o", "b\"a\"r"))); + + ////////////////// + // normal stuff // + ////////////////// + assertEquals(""" + {"foo":"bar"}""", jsonLog(logPair("foo", "bar"))); + + assertEquals(""" + {"bar":1}""", jsonLog(logPair("bar", 1))); + + assertEquals(""" + {"baz":3.50}""", jsonLog(logPair("baz", new BigDecimal("3.50")))); + + //////////////// + // many pairs // + //////////////// + assertEquals(""" + {"foo":"bar","bar":1,"baz":3.50}""", jsonLog(logPair("foo", "bar"), logPair("bar", 1), logPair("baz", new BigDecimal("3.50")))); + + ////////////////// + // nested pairs // + ////////////////// + assertEquals(""" + {"foo":{"bar":1,"baz":2}}""", jsonLog(logPair("foo", logPair("bar", 1), logPair("baz", 2)))); + + assertEquals(""" + { + "foo": + { + "bar":1, + "baz":2 + } + }""".replaceAll("\\s", ""), jsonLog(logPair("foo", logPair("bar", 1), logPair("baz", 2)))); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testLog2() + { + LOG.info(jsonLog(logPair("message", "Doing a thing"), logPair("trackingNo", "1Z123123123"), logPair("Order", logPair("id", 89101324), logPair("client", "ACME")))); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testLogging() + { + LOG.info(jsonLog(logPair("message", "Doing a thing"), logPair("trackingNo", "1Z123123123"), logPair("Order", logPair("id", 89101324), logPair("client", "ACME")))); + } + +} \ No newline at end of file