From ef732f13894bd0c0f1577a31e5e870bd26df5eb6 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Fri, 19 Feb 2021 14:39:40 +0530 Subject: [PATCH] Add request body and some other details in action execution data point (#3096) --- .../server/configurations/SegmentConfig.java | 78 ++++++++++++++----- .../server/services/NewActionServiceImpl.java | 40 ++++++++-- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SegmentConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SegmentConfig.java index dcd5621268..9e4fd7b55d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SegmentConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SegmentConfig.java @@ -2,12 +2,20 @@ package com.appsmith.server.configurations; import com.segment.analytics.Analytics; import com.segment.analytics.Log; +import com.segment.analytics.messages.TrackMessage; +import lombok.Data; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.ObjectUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.Collections; +import java.util.Map; +import java.util.function.Consumer; + @Slf4j @Configuration public class SegmentConfig { @@ -21,30 +29,64 @@ public class SegmentConfig { @Bean @ConditionalOnExpression(value = "!'${segment.writeKey:}'.isEmpty()") public Analytics analyticsRunner() { - final Log logProcessor = new Log() { - @Override - public void print(Level level, String format, Object... args) { - print(level, null, format, args); - } + final LogProcessor logProcessor = new LogProcessor(); + Analytics analyticsOnAnalytics = Analytics.builder(writeKey).log(logProcessor).build(); - @Override - public void print(Level level, Throwable error, String format, Object... args) { - final String message = "SEGMENT: " + format; - if (level == Level.VERBOSE) { - log.trace(message, error, args); - } else if (level == Level.DEBUG) { - log.debug(message, error, args); - } else if (level == Level.ERROR) { - log.error(message, error, args); - } - } - }; + // We use a different analytics instance for sending events about the analytics system itself so we don't end up + // in a recursive state. + final LogProcessor logProcessorWithErrorHandler = new LogProcessor(); + final Analytics analytics = Analytics.builder(writeKey).log(logProcessorWithErrorHandler).build(); + logProcessorWithErrorHandler.onError(logData -> { + analyticsOnAnalytics.enqueue(TrackMessage.builder("segment_error") + .properties(Map.of( + "message", logData.getMessage(), + "error", logData.getError().getMessage(), + "args", ObjectUtils.defaultIfNull(logData.getArgs(), Collections.emptyList()) + )) + ); + }); - return Analytics.builder(writeKey).log(logProcessor).build(); + return analytics; } public String getCeKey() { return ceKey; } + private static class LogProcessor implements Log { + private Consumer errorHandler = null; + + @Override + public void print(Level level, String format, Object... args) { + print(level, null, format, args); + } + + @Override + public void print(Level level, Throwable error, String format, Object... args) { + final String message = "SEGMENT: " + format; + if (level == Level.VERBOSE) { + log.trace(message, error, args); + } else if (level == Level.DEBUG) { + log.debug(message, error, args); + } else if (level == Level.ERROR) { + if (errorHandler != null) { + errorHandler.accept(new LogData(error, format, args)); + } + log.error(message, error, args); + } + } + + public void onError(Consumer handler) { + errorHandler = handler; + } + } + + @Data + @RequiredArgsConstructor + private static class LogData { + final Throwable error; + final String message; + final Object[] args; + } + } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java index 52c6206e5c..a7e86dbd25 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java @@ -36,6 +36,8 @@ import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.MustacheHelper; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.NewActionRepository; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ObjectUtils; @@ -667,20 +669,42 @@ public class NewActionServiceImpl extends BaseService requestData = new HashMap<>(); if (pluginType == PluginType.API) { + + String headersJson; + try { + headersJson = objectMapper.writeValueAsString(actionConfiguration + .getHeaders() + .stream() + .filter(p -> !StringUtils.isEmpty(p.getKey())) + .collect(Collectors.toMap(Property::getKey, Property::getValue, (a, b) -> b))); + } catch (JsonProcessingException e) { + log.warn("Couldn't serialize headers to JSON", e); + headersJson = ""; + } + + String paramsJson; + try { + paramsJson = objectMapper.writeValueAsString(actionConfiguration + .getQueryParameters() + .stream() + .filter(p -> !StringUtils.isEmpty(p.getKey())) + .collect(Collectors.toMap(Property::getKey, Property::getValue, (a, b) -> b))); + } catch (JsonProcessingException e) { + log.warn("Couldn't serialize params to JSON", e); + paramsJson = ""; + } + requestData.putAll(Map.of( "url", actionDTO.getDatasource().getDatasourceConfiguration().getUrl() + actionConfiguration.getPath(), - "headers", actionConfiguration - .getHeaders() - .stream() - .collect(Collectors.toMap(Property::getKey, Property::getValue)), - "parameters", actionConfiguration - .getQueryParameters() - .stream() - .collect(Collectors.toMap(Property::getKey, Property::getValue)) + "headers", headersJson, + "parameters", paramsJson, + "body", ObjectUtils.defaultIfNull(actionConfiguration.getBody(), "") )); + } else if (pluginType == PluginType.DB) { requestData.putAll(Map.of( "query", ObjectUtils.defaultIfNull(actionConfiguration.getBody(), ""),