diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DefaultWidgetRenderer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DefaultWidgetRenderer.java new file mode 100644 index 00000000..d42291e6 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/DefaultWidgetRenderer.java @@ -0,0 +1,92 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.actions.dashboard.widgets; + + +import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.QWidgetData; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class DefaultWidgetRenderer extends AbstractWidgetRenderer +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public RenderWidgetOutput render(RenderWidgetInput input) throws QException + { + return new RenderWidgetOutput(new DefaultWidgetData(input)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static class DefaultWidgetData extends QWidgetData + { + private final String type; + private final Map queryParams; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public DefaultWidgetData(RenderWidgetInput renderWidgetInput) + { + this.type = renderWidgetInput.getWidgetMetaData().getType(); + this.queryParams = renderWidgetInput.getQueryParams(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public String getType() + { + return (type); + } + + + + /******************************************************************************* + ** Getter for queryParams + ** + *******************************************************************************/ + public Map getQueryParams() + { + return queryParams; + } + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/logging/LogPair.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/logging/LogPair.java new file mode 100644 index 00000000..f819217c --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/logging/LogPair.java @@ -0,0 +1,106 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.logging; + + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + + +/******************************************************************************* + ** + *******************************************************************************/ +public 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 LogUtils.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; + } +} 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/logging/LogUtils.java similarity index 55% rename from qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/LogUtils.java rename to qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/logging/LogUtils.java index 01ba9009..a03246df 100644 --- 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/logging/LogUtils.java @@ -1,6 +1,6 @@ /* * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2022. Kingsrook, LLC + * Copyright (C) 2021-2023. Kingsrook, LLC * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States * contact@kingsrook.com * https://github.com/Kingsrook/ @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -package com.kingsrook.qqq.backend.core.utils; +package com.kingsrook.qqq.backend.core.logging; import java.util.Arrays; @@ -34,6 +34,22 @@ import java.util.stream.Collectors; public class LogUtils { + /******************************************************************************* + ** + *******************************************************************************/ + public static String jsonLog(List logPairs) + { + List filteredList = logPairs.stream().filter(Objects::nonNull).toList(); + if(filteredList.isEmpty()) + { + return ("{}"); + } + + return ('{' + filteredList.stream().map(LogPair::toString).collect(Collectors.joining(",")) + '}'); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -44,13 +60,7 @@ public class LogUtils return ("{}"); } - List filteredList = Arrays.stream(logPairs).filter(Objects::nonNull).toList(); - if(filteredList.isEmpty()) - { - return ("{}"); - } - - return ('{' + filteredList.stream().map(LogPair::toString).collect(Collectors.joining(",")) + '}'); + return (jsonLog(Arrays.asList(logPairs))); } @@ -75,86 +85,6 @@ public class LogUtils - /******************************************************************************* - ** - *******************************************************************************/ - 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; - } - } - - - /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java index 4a3f8d38..00723c2b 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java @@ -47,7 +47,8 @@ public enum WidgetType SIMPLE_STATISTICS("simpleStatistics"), STEPPER("stepper"), TABLE("table"), - USA_MAP("usaMap"); + USA_MAP("usaMap"), + DATA_BAG_VIEWER("dataBagViewer"); private final String type; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/QLogger.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/QLogger.java index db6c34b8..906156cd 100755 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/QLogger.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/utils/QLogger.java @@ -22,13 +22,20 @@ package com.kingsrook.qqq.backend.core.utils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.kingsrook.qqq.backend.core.context.QContext; +import com.kingsrook.qqq.backend.core.logging.LogPair; +import com.kingsrook.qqq.backend.core.logging.LogUtils; import com.kingsrook.qqq.backend.core.model.session.QSession; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; /******************************************************************************* @@ -37,10 +44,20 @@ import org.apache.logging.log4j.Logger; *******************************************************************************/ public class QLogger { - private static Map loggerMap = new HashMap<>(); - private Logger logger; + private static Map loggerMap = Collections.synchronizedMap(new HashMap<>()); + private static boolean logSessionIdEnabled = true; + private Logger logger; + static + { + String propertyName = "qqq.logger.logSessionId.disabled"; + String propertyValue = System.getProperty(propertyName, ""); + if(propertyValue.equals("true")) + { + logSessionIdEnabled = false; + } + } /******************************************************************************* ** @@ -67,7 +84,7 @@ public class QLogger *******************************************************************************/ public void log(Level level, String message) { - logger.log(level, wrapMessage(message)); + logger.log(level, messageToJsonString(message)); } @@ -77,7 +94,7 @@ public class QLogger *******************************************************************************/ public void log(Level level, String message, Throwable t) { - logger.log(level, wrapMessage(message), t); + logger.log(level, messageToJsonString(message), t); } @@ -97,7 +114,7 @@ public class QLogger *******************************************************************************/ public void trace(String message) { - logger.trace(wrapMessage(message)); + logger.trace(messageToJsonString(message)); } @@ -107,7 +124,7 @@ public class QLogger *******************************************************************************/ public void trace(String message, Object... values) { - logger.trace(wrapMessage(message), values); + logger.trace(messageToJsonString(message), values); } @@ -117,7 +134,7 @@ public class QLogger *******************************************************************************/ public void trace(String message, Throwable t) { - logger.trace(wrapMessage(message), t); + logger.trace(messageToJsonString(message), t); } @@ -137,7 +154,7 @@ public class QLogger *******************************************************************************/ public void debug(String message) { - logger.debug(wrapMessage(message)); + logger.debug(messageToJsonString(message)); } @@ -147,7 +164,7 @@ public class QLogger *******************************************************************************/ public void debug(String message, Object... values) { - logger.debug(wrapMessage(message), values); + logger.debug(messageToJsonString(message), values); } @@ -157,7 +174,7 @@ public class QLogger *******************************************************************************/ public void debug(String message, Throwable t) { - logger.debug(wrapMessage(message), t); + logger.debug(messageToJsonString(message), t); } @@ -177,7 +194,27 @@ public class QLogger *******************************************************************************/ public void info(String message) { - logger.info(wrapMessage(message)); + logger.info(messageToJsonString(message)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void info(LogPair... logPairs) + { + logger.info(LogUtils.jsonLog(addSessionLogPair(Arrays.asList(logPairs)))); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void info(List logPairList) + { + logger.info(LogUtils.jsonLog(addSessionLogPair(logPairList))); } @@ -187,7 +224,7 @@ public class QLogger *******************************************************************************/ public void info(String message, Object... values) { - logger.info(wrapMessage(message), values); + logger.info(messageToJsonString(message), values); } @@ -197,7 +234,7 @@ public class QLogger *******************************************************************************/ public void info(String message, Throwable t) { - logger.info(wrapMessage(message), t); + logger.info(messageToJsonString(message), t); } @@ -217,7 +254,7 @@ public class QLogger *******************************************************************************/ public void warn(String message) { - logger.warn(wrapMessage(message)); + logger.warn(messageToJsonString(message)); } @@ -227,7 +264,7 @@ public class QLogger *******************************************************************************/ public void warn(String message, Object... values) { - logger.warn(wrapMessage(message), values); + logger.warn(messageToJsonString(message), values); } @@ -237,7 +274,7 @@ public class QLogger *******************************************************************************/ public void warn(String message, Throwable t) { - logger.warn(wrapMessage(message), t); + logger.warn(messageToJsonString(message), t); } @@ -257,7 +294,7 @@ public class QLogger *******************************************************************************/ public void error(String message) { - logger.error(wrapMessage(message)); + logger.error(messageToJsonString(message)); } @@ -267,7 +304,7 @@ public class QLogger *******************************************************************************/ public void error(String message, Object... values) { - logger.error(wrapMessage(message), values); + logger.error(messageToJsonString(message), values); } @@ -277,7 +314,7 @@ public class QLogger *******************************************************************************/ public void error(String message, Throwable t) { - logger.error(wrapMessage(message), t); + logger.error(messageToJsonString(message), t); } @@ -295,17 +332,54 @@ public class QLogger /******************************************************************************* ** *******************************************************************************/ - private String wrapMessage(String message) + private String messageToJsonString(String message) { - String propertyName = "qqq.logger.logSessionId.disabled"; - String propertyValue = System.getProperty(propertyName, ""); - if(propertyValue.equals("true")) - { - return (message); - } + List logPairList = new ArrayList<>(); + logPairList.add(logPair("message", message)); + addSessionLogPair(logPairList); - QSession session = QContext.getQSession(); - String sessionString = (session != null) ? session.getUuid() : "Not provided"; - return ("Session [" + sessionString + "] | " + message); + return (LogUtils.jsonLog(logPairList)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static List addSessionLogPair(List logPairList) + { + if(logSessionIdEnabled) + { + QSession session = QContext.getQSession(); + LogPair sessionLogPair; + + if(session == null) + { + sessionLogPair = logPair("session", "unknown"); + } + else + { + String user = "unknown"; + if(session.getUser() != null) + { + user = session.getUser().getIdReference(); + } + sessionLogPair = logPair("session", logPair("id", session.getUuid()), logPair("user", user)); + } + + try + { + logPairList.add(sessionLogPair); + } + catch(Exception e) + { + ////////////////////////////////////// + // deal with not-modifiable list... // + ////////////////////////////////////// + logPairList = new ArrayList<>(logPairList); + logPairList.add(sessionLogPair); + } + } + return (logPairList); } } 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 index 7c581319..67457371 100644 --- 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 @@ -24,14 +24,15 @@ package com.kingsrook.qqq.backend.core.utils; import java.math.BigDecimal; import com.kingsrook.qqq.backend.core.BaseTest; +import com.kingsrook.qqq.backend.core.logging.LogPair; 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 com.kingsrook.qqq.backend.core.logging.LogUtils.jsonLog; +import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import static org.junit.jupiter.api.Assertions.assertEquals; /******************************************************************************* - ** Unit test for com.kingsrook.qqq.backend.core.utils.LogUtils + ** Unit test for com.kingsrook.qqq.backend.core.logging.LogUtils *******************************************************************************/ class LogUtilsTest extends BaseTest { @@ -49,12 +50,12 @@ class LogUtilsTest extends BaseTest // null cases // //////////////// assertEquals("{}", jsonLog()); - assertEquals("{}", jsonLog((LogUtils.LogPair) null)); - assertEquals("{}", jsonLog((LogUtils.LogPair[]) null)); + assertEquals("{}", jsonLog((LogPair) null)); + assertEquals("{}", jsonLog((LogPair[]) null)); assertEquals(""" - {"null":null}""", jsonLog(logPair(null, (LogUtils.LogPair) null))); + {"null":null}""", jsonLog(logPair(null, (LogPair) null))); assertEquals(""" - {"null":null}""", jsonLog(logPair(null, (LogUtils.LogPair[]) null))); + {"null":null}""", jsonLog(logPair(null, (LogPair[]) null))); ////////////// // escaping // diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinAccessLogger.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinAccessLogger.java new file mode 100644 index 00000000..bb943e62 --- /dev/null +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinAccessLogger.java @@ -0,0 +1,109 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.javalin; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.kingsrook.qqq.backend.core.logging.LogPair; +import com.kingsrook.qqq.backend.core.utils.QLogger; +import com.kingsrook.qqq.backend.core.utils.StringUtils; +import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class QJavalinAccessLogger +{ + private static final QLogger LOG = QLogger.getLogger(QJavalinAccessLogger.class); + + private static ThreadLocal requestStartTime = new ThreadLocal<>(); + private static ThreadLocal requestActionName = new ThreadLocal<>(); + + + + /******************************************************************************* + ** + *******************************************************************************/ + static void logStart(String actionName, LogPair... logPairs) + { + requestStartTime.set(System.currentTimeMillis()); + requestActionName.set(actionName); + List pairList = new ArrayList<>(Arrays.asList(logPairs)); + pairList.add(0, logPair("access", "start")); + pairList.add(1, logPair("action", actionName)); + LOG.info(pairList); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + static void logEndSuccess(LogPair... logPairs) + { + List pairList = new ArrayList<>(Arrays.asList(logPairs)); + pairList.add(0, logPair("access", "end-ok")); + accessLogEnd(pairList); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + static void logEndFail(Throwable t, LogPair... logPairs) + { + List pairList = new ArrayList<>(Arrays.asList(logPairs)); + pairList.add(0, logPair("access", "end-fail")); + pairList.add(1, logPair("exceptionType", t.getClass().getSimpleName())); + pairList.add(2, logPair("exceptionMessage", t.getMessage())); + accessLogEnd(pairList); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static void accessLogEnd(List pairList) + { + String actionName = requestActionName.get(); + requestActionName.remove(); + if(StringUtils.hasContent(actionName)) + { + pairList.add(1, logPair("action", actionName)); + } + + Long startTime = requestStartTime.get(); + requestStartTime.remove(); + if(startTime != null) + { + long endTime = System.currentTimeMillis(); + pairList.add(logPair("millis", (endTime - startTime))); + } + LOG.info(pairList); + } + +} diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index e3f6f98b..ee12f229 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -107,6 +107,7 @@ import io.javalin.apibuilder.EndpointGroup; import io.javalin.http.Context; import org.apache.commons.io.FileUtils; import org.eclipse.jetty.http.HttpStatus; +import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import static io.javalin.apibuilder.ApiBuilder.delete; import static io.javalin.apibuilder.ApiBuilder.get; import static io.javalin.apibuilder.ApiBuilder.patch; @@ -708,11 +709,15 @@ public class QJavalinImplementation *******************************************************************************/ static void dataQuery(Context context) { + String table = context.pathParam("table"); + try { QueryInput queryInput = new QueryInput(); setupSession(context, queryInput); - queryInput.setTableName(context.pathParam("table")); + QJavalinAccessLogger.logStart("query", logPair("table", table)); + + queryInput.setTableName(table); queryInput.setShouldGenerateDisplayValues(true); queryInput.setShouldTranslatePossibleValues(true); queryInput.setSkip(integerQueryParam(context, "skip")); @@ -733,10 +738,13 @@ public class QJavalinImplementation QueryAction queryAction = new QueryAction(); QueryOutput queryOutput = queryAction.execute(queryInput); + QJavalinAccessLogger.logEndSuccess(logPair("table", table), logPair("recordCount", queryOutput.getRecords().size())); + context.result(JsonUtils.toJson(queryOutput)); } catch(Exception e) { + QJavalinAccessLogger.logEndFail(e, logPair("table", table)); handleException(context, e); } }