#15 return exception information while failed to execute script

This commit is contained in:
Francis Dong
2020-11-19 16:52:34 +08:00
committed by dxfeng10
parent beef51be1a
commit 70c132059b
11 changed files with 170 additions and 19 deletions

View File

@@ -9,6 +9,12 @@ var context = {
[actionName]: 123, // 操作名称:耗时
}],
// exception info
exceptionMessage: "",
exceptionStacks: "",
exceptionData: "", // such as script source code that cause exception
// 客户输入和接口的返回结果
input: {
request:{

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
package we.exception;
import we.fizz.StepContext;
/**
* @author Francis
*/
public class ExecuteScriptException extends RuntimeException {
private StepContext<String, Object> stepContext;
private Object data;
public ExecuteScriptException(String message, StepContext<String, Object> stepContext, Object data) {
super(message);
this.data = data;
this.stepContext = stepContext;
this.stepContext.setExceptionInfo(this, data);
}
public ExecuteScriptException(Throwable cause, StepContext<String, Object> 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<String, Object> getStepContext() {
return stepContext;
}
public void setStepContext(StepContext<String, Object> stepContext) {
this.stepContext = stepContext;
}
}

View File

@@ -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<Void> vm = WebUtils.responseError(exchange, filterExceptionHandler, HttpStatus.INTERNAL_SERVER_ERROR.value(), t.getMessage(), t);
return vm;

View File

@@ -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) {

View File

@@ -32,6 +32,8 @@ public class AggregateResult {
private Map<String, Object> body;
private StepContext<String, Object> stepContext;
public MultiValueMap<String, String> getHeaders() {
return headers;
}
@@ -48,4 +50,12 @@ public class AggregateResult {
this.body = body;
}
public StepContext<String, Object> getStepContext() {
return stepContext;
}
public void setStepContext(StepContext<String, Object> stepContext) {
this.stepContext = stepContext;
}
}

View File

@@ -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;
@@ -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<String, Object>) 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<String, Object> respBody = (Map<String, Object>) response.get("body");
// 测试模式返回StepContext
if(stepContext.returnContext()) {
respBody.put("_context", stepContext);
respBody.put(stepContext.CONTEXT_FIELD, stepContext);
}
aggResult.setBody((Map<String, Object>) 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);
}
}
}

View File

@@ -39,6 +39,13 @@ public class StepContext<K, V> extends ConcurrentHashMap<K, V> {
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<K, V> extends ConcurrentHashMap<K, V> {
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<Map<String, Long>> elapsedTimes = (List<Map<String, Long>>) this.get(ELAPSED_TIMES);
if (elapsedTimes == null) {

View File

@@ -38,10 +38,12 @@ import com.alibaba.fastjson.JSON;
import reactor.core.publisher.Mono;
import we.FizzAppContext;
import we.constants.CommonConstants;
import we.exception.ExecuteScriptException;
import we.fizz.StepContext;
import we.fizz.StepResponse;
import we.flume.clients.log4j2appender.LogService;
import we.proxy.FizzWebClient;
import we.util.JacksonUtils;
import we.util.MapUtil;
/**
@@ -131,8 +133,8 @@ public class RequestInput extends Input {
}
} catch (ScriptException e) {
LogService.setBizId(inputContext.getStepContext().getTraceId());
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);
}
}
request.put("body", body);
@@ -187,8 +189,8 @@ public class RequestInput extends Input {
}
} catch (ScriptException e) {
LogService.setBizId(inputContext.getStepContext().getTraceId());
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);
@@ -247,8 +249,8 @@ public class RequestInput extends Input {
return needRun != null ? needRun : Boolean.TRUE;
} catch (ScriptException e) {
LogService.setBizId(inputContext.getStepContext().getTraceId());
LOGGER.warn("execute script failed", e);
throw new RuntimeException("execute script failed");
LOGGER.warn("execute script failed, {}", JacksonUtils.writeValueAsString(condition), e);
throw new ExecuteScriptException(e, stepContext, condition);
}
}

View File

@@ -32,9 +32,11 @@ import com.alibaba.fastjson.JSON;
import org.springframework.util.StringUtils;
import we.constants.CommonConstants;
import we.exception.ExecuteScriptException;
import we.exception.RedirectException;
import we.exception.StopAndResponseException;
import we.fizz.StepContext;
import we.util.JacksonUtils;
import we.util.Script;
import we.util.ScriptUtils;
@@ -118,8 +120,8 @@ public class ScriptHelper {
PathMapping.setByPath(target, entry.getKey(), execute(scriptCfg, ctxNode, stepContext, clazz));
}
} 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);
}
}
if(starEntryKey != null) {
@@ -145,7 +147,7 @@ public class ScriptHelper {
// 测试模式返回StepContext
if (stepContext.returnContext()) {
rs.put("_context", stepContext);
rs.put(stepContext.CONTEXT_FIELD, stepContext);
}
// exception

View File

@@ -38,12 +38,21 @@ public class RespEntity {
public String reqId;
public Object _context;
public RespEntity(int code, String msg, @Nullable String reqId) {
msgCode = code;
message = msg;
this.reqId = reqId;
}
public RespEntity(int code, String msg, @Nullable String reqId, Object stepContext) {
msgCode = code;
message = msg;
this.reqId = reqId;
this._context = stepContext;
}
private static final String resb = "$resb";
static {

View File

@@ -121,6 +121,11 @@ public abstract class WebUtils {
return svc;
}
public static String getPathPrefix(ServerWebExchange exchange) {
String p = exchange.getRequest().getPath().value();
return p.substring(0, p.indexOf(getServiceId(exchange)));
}
public static Mono<Void> getDirectResponse(ServerWebExchange exchange) {
return (Mono<Void>) exchange.getAttributes().get(WebUtils.directResponse);
}