diff --git a/js/context.js b/js/context.js
index 7e278e0..0a31511 100644
--- a/js/context.js
+++ b/js/context.js
@@ -8,6 +8,12 @@ var context = {
elapsedTimes: [{
[actionName]: 123, // 操作名称:耗时
}],
+
+ // exception info
+ exceptionMessage: "",
+ exceptionStacks: "",
+ exceptionData: "", // such as script source code that cause exception
+
// 客户输入和接口的返回结果
input: {
diff --git a/src/main/java/we/exception/ExecuteScriptException.java b/src/main/java/we/exception/ExecuteScriptException.java
new file mode 100644
index 0000000..98bffd9
--- /dev/null
+++ b/src/main/java/we/exception/ExecuteScriptException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package we.exception;
+
+import we.fizz.StepContext;
+
+/**
+ * @author Francis
+ */
+public class ExecuteScriptException extends RuntimeException {
+
+ private StepContext stepContext;
+
+ private Object data;
+
+ public ExecuteScriptException(String message, StepContext stepContext, Object data) {
+ super(message);
+ this.data = data;
+ this.stepContext = stepContext;
+ this.stepContext.setExceptionInfo(this, data);
+ }
+
+ public ExecuteScriptException(Throwable cause, StepContext stepContext, Object data) {
+ super("execute script failed: " + cause.getMessage(), cause);
+ this.data = data;
+ this.stepContext = stepContext;
+ this.setStackTrace(cause.getStackTrace());
+ this.stepContext.setExceptionInfo(this, data);
+ }
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public StepContext getStepContext() {
+ return stepContext;
+ }
+
+ public void setStepContext(StepContext stepContext) {
+ this.stepContext = stepContext;
+ }
+
+}
diff --git a/src/main/java/we/filter/FilterExceptionHandlerConfig.java b/src/main/java/we/filter/FilterExceptionHandlerConfig.java
index 30b77aa..ac8eea1 100644
--- a/src/main/java/we/filter/FilterExceptionHandlerConfig.java
+++ b/src/main/java/we/filter/FilterExceptionHandlerConfig.java
@@ -29,8 +29,11 @@ import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
+import we.exception.ExecuteScriptException;
import we.exception.RedirectException;
import we.exception.StopAndResponseException;
+import we.legacy.RespEntity;
+import we.util.JacksonUtils;
import we.util.WebUtils;
/**
@@ -60,6 +63,21 @@ public class FilterExceptionHandlerConfig {
resp.getHeaders().setLocation(URI.create(ex.getRedirectUrl()));
return Mono.empty();
}
+ }
+ if (t instanceof ExecuteScriptException) {
+ ExecuteScriptException ex = (ExecuteScriptException) t;
+ ServerHttpResponse resp = exchange.getResponse();
+ resp.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+
+ RespEntity rs = null;
+ String reqId = exchange.getRequest().getId();
+ if (ex.getStepContext() != null && ex.getStepContext().returnContext()) {
+ rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId, ex.getStepContext());
+ return resp.writeWith(Mono.just(resp.bufferFactory().wrap(JacksonUtils.writeValueAsString(rs).getBytes())));
+ }else {
+ rs = new RespEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), reqId);
+ return resp.writeWith(Mono.just(resp.bufferFactory().wrap(rs.toString().getBytes())));
+ }
}
Mono vm = WebUtils.responseError(exchange, filterExceptionHandler, HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), t);
return vm;
diff --git a/src/main/java/we/filter/FizzGatewayFilter.java b/src/main/java/we/filter/FizzGatewayFilter.java
index 7c4860f..d6c4fc4 100644
--- a/src/main/java/we/filter/FizzGatewayFilter.java
+++ b/src/main/java/we/filter/FizzGatewayFilter.java
@@ -74,7 +74,7 @@ public class FizzGatewayFilter implements WebFilter {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse serverHttpResponse = exchange.getResponse();
- String path = WebUtils.PATH_PREFIX + WebUtils.getServiceId(exchange) + WebUtils.getReqPath(exchange);
+ String path = WebUtils.getPathPrefix(exchange) + WebUtils.getServiceId(exchange) + WebUtils.getReqPath(exchange);
String method = request.getMethodValue();
AggregateResource aggregateResource = configLoader.matchAggregateResource(method, path);
if (aggregateResource == null) {
diff --git a/src/main/java/we/fizz/AggregateResult.java b/src/main/java/we/fizz/AggregateResult.java
index 3f8abaa..72e4f50 100644
--- a/src/main/java/we/fizz/AggregateResult.java
+++ b/src/main/java/we/fizz/AggregateResult.java
@@ -29,9 +29,11 @@ import org.springframework.util.MultiValueMap;
public class AggregateResult {
private MultiValueMap headers;
-
+
private Map body;
+ private StepContext stepContext;
+
public MultiValueMap getHeaders() {
return headers;
}
@@ -48,4 +50,12 @@ public class AggregateResult {
this.body = body;
}
+ public StepContext getStepContext() {
+ return stepContext;
+ }
+
+ public void setStepContext(StepContext stepContext) {
+ this.stepContext = stepContext;
+ }
+
}
diff --git a/src/main/java/we/fizz/Pipeline.java b/src/main/java/we/fizz/Pipeline.java
index 87baf7c..3fa6564 100644
--- a/src/main/java/we/fizz/Pipeline.java
+++ b/src/main/java/we/fizz/Pipeline.java
@@ -36,12 +36,14 @@ import com.alibaba.fastjson.JSON;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import we.exception.ExecuteScriptException;
import we.fizz.input.ClientInputConfig;
import we.fizz.input.Input;
import we.fizz.input.InputConfig;
import we.fizz.input.PathMapping;
import we.fizz.input.ScriptHelper;
import we.flume.clients.log4j2appender.LogService;
+import we.util.JacksonUtils;
import we.util.JsonSchemaUtils;
import we.util.MapUtil;
@@ -55,7 +57,7 @@ import we.util.MapUtil;
public class Pipeline {
private static final Logger LOGGER = LoggerFactory.getLogger(Pipeline.class);
private LinkedList steps = new LinkedList();
- private StepContext stepContext= new StepContext<>();
+ private StepContext stepContext = new StepContext<>();
public void addStep(Step step) {
steps.add(step);
}
@@ -184,8 +186,8 @@ public class Pipeline {
stepResponse.setResult(stepBody);
}
} catch (ScriptException e) {
- LOGGER.warn("execute script failed, {}", e);
- throw new RuntimeException("execute script failed");
+ LOGGER.warn("execute script failed, {}", JacksonUtils.writeValueAsString(scriptCfg), e);
+ throw new ExecuteScriptException(e, stepContext, scriptCfg);
}
}
}
@@ -238,8 +240,8 @@ public class Pipeline {
body.putAll((Map) respBody);
}
} catch (ScriptException e) {
- LOGGER.warn("execute script failed, {}", e);
- throw new RuntimeException("execute script failed");
+ LOGGER.warn("execute script failed, {}", JacksonUtils.writeValueAsString(scriptCfg), e);
+ throw new ExecuteScriptException(e, stepContext, scriptCfg);
}
}
response.put("body", body);
@@ -249,7 +251,7 @@ public class Pipeline {
Map respBody = (Map) response.get("body");
// 测试模式返回StepContext
if(stepContext.returnContext()) {
- respBody.put("_context", stepContext);
+ respBody.put(stepContext.CONTEXT_FIELD, stepContext);
}
aggResult.setBody((Map) response.get("body"));
@@ -302,8 +304,8 @@ public class Pipeline {
return errorList;
}
} catch (ScriptException e) {
- LOGGER.warn("execute script failed", e);
- throw new RuntimeException("execute script failed");
+ LOGGER.warn("execute script failed, {}", JacksonUtils.writeValueAsString(scriptValidate), e);
+ throw new ExecuteScriptException(e, stepContext, scriptValidate);
}
}
}
diff --git a/src/main/java/we/fizz/StepContext.java b/src/main/java/we/fizz/StepContext.java
index 342ad0c..dd8d296 100644
--- a/src/main/java/we/fizz/StepContext.java
+++ b/src/main/java/we/fizz/StepContext.java
@@ -39,6 +39,13 @@ public class StepContext extends ConcurrentHashMap {
public static final String ELAPSED_TIMES = "elapsedTimes";
public static final String DEBUG = "debug";
public static final String RETURN_CONTEXT = "returnContext";
+ // context field in response body
+ public static final String CONTEXT_FIELD = "_context";
+
+ // exception info
+ public static final String EXCEPTION_MESSAGE = "exceptionMessage";
+ public static final String EXCEPTION_STACKS = "exceptionStacks";
+ public static final String EXCEPTION_DATA = "exceptionData";
public void setDebug(Boolean debug) {
this.put((K)DEBUG, (V)debug);
@@ -68,6 +75,29 @@ public class StepContext extends ConcurrentHashMap {
return Boolean.valueOf((String)getInputReqHeader(RETURN_CONTEXT));
}
+ /**
+ * set exception information
+ * @param cause exception
+ * @param exceptionData data that cause the exception, such as script source code, etc.
+ */
+ public void setExceptionInfo(Throwable cause, Object exceptionData) {
+ this.put((K) EXCEPTION_MESSAGE, (V) cause.getMessage());
+ this.put((K) EXCEPTION_DATA, (V) exceptionData);
+
+ StackTraceElement[] stacks = cause.getStackTrace();
+ if (stacks != null && stacks.length > 0) {
+ String[] arr = new String[stacks.length];
+ for (int i = 0; i < stacks.length; i++) {
+ StackTraceElement ste = stacks[i];
+ StringBuffer sb = new StringBuffer();
+ sb.append(ste.getClassName()).append(".").append(ste.getMethodName()).append("(")
+ .append(ste.getFileName()).append(":").append(ste.getLineNumber()).append(")");
+ arr[i] = sb.toString();
+ }
+ this.put((K) EXCEPTION_STACKS, (V) arr);
+ }
+ }
+
public synchronized void addElapsedTime(String actionName, Long milliSeconds) {
List