use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.
the class ExceptionHandlingFilter method tryRecordExceptionAndRequestNotification.
private Optional<ExceptionRecordKey> tryRecordExceptionAndRequestNotification(HttpServletRequest request, Throwable exception) {
try {
String callOrigin;
Optional<Class<? extends BaseHandler>> handlerClass = RequestAttributeTool.get(request, BaseHandler.HANDLER_CLASS);
Optional<Method> handlerMethod = RequestAttributeTool.get(request, BaseHandler.HANDLER_METHOD);
if (handlerClass.isPresent() && handlerMethod.isPresent()) {
callOrigin = handlerClass.get().getName() + "." + handlerMethod.get().getName();
} else {
callOrigin = null;
}
String methodName = null;
String name = null;
String type = null;
Pair<String, Integer> pair = searchJspName(exception);
String location = pair.getLeft();
Integer lineNumber = pair.getRight();
if (location == null) {
ExceptionRecorderDetails details = exceptionDetailsDetector.detect(exception, callOrigin, webSettings.stackTraceHighlights.get());
location = details.className;
methodName = details.methodName;
name = details.parsedName;
type = details.type;
lineNumber = details.lineNumber;
}
ExceptionRecordDto exceptionRecord = exceptionRecorder.recordExceptionAndHttpRequest(exception, location, methodName, name, type, lineNumber, request, callOrigin);
return Optional.of(new ExceptionRecordKey(exceptionRecord.id));
} catch (Exception e) {
logger.error("Exception while logging", e);
return Optional.empty();
}
}
use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.
the class TraceFilter method doFilter.
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
try {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// get or create TraceContext
Long traceCreated = Trace2Dto.getCurrentTimeInNs();
String traceparentStr = request.getHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACEPARENT);
String tracestateStr = request.getHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACESTATE);
W3TraceContext traceContext = new W3TraceContext(traceparentStr, tracestateStr, traceCreated);
String initialParentId = traceContext.getTraceparent().parentId;
traceContext.updateParentIdAndAddTracestateMember();
RequestAttributeTool.set(request, BaseHandler.TRACE_URL_REQUEST_ATTRIBUTE, urlBuilder.buildTraceForCurrentServer(traceContext.getTraceId(), traceContext.getParentId()));
RequestAttributeTool.set(request, BaseHandler.TRACE_CONTEXT, traceContext.copy());
// traceflag might be incorrect because it might have been modified during request processing
if (traceSettings.addTraceparentHeader.get()) {
response.setHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACEPARENT, traceContext.getTraceparent().toString());
}
// bind these to all threads, even if tracing is disabled
Tracer tracer = new DatarouterTracer(serverName.get(), null, traceContext);
tracer.setSaveThreadCpuTime(traceSettings.saveThreadCpuTime.get());
tracer.setSaveThreadMemoryAllocated(traceSettings.saveThreadMemoryAllocated.get());
tracer.setSaveSpanCpuTime(traceSettings.saveSpanCpuTime.get());
tracer.setSaveSpanMemoryAllocated(traceSettings.saveSpanMemoryAllocated.get());
TracerThreadLocal.bindToThread(tracer);
String requestThreadName = (request.getContextPath() + " request").trim();
tracer.createAndStartThread(requestThreadName, Trace2Dto.getCurrentTimeInNs());
Long threadId = Thread.currentThread().getId();
boolean saveCpuTime = traceSettings.saveTraceCpuTime.get();
Long cpuTimeBegin = saveCpuTime ? MxBeans.THREAD.getCurrentThreadCpuTime() : null;
boolean saveAllocatedBytes = traceSettings.saveTraceAllocatedBytes.get();
Long threadAllocatedBytesBegin = saveAllocatedBytes ? MxBeans.THREAD.getThreadAllocatedBytes(threadId) : null;
boolean errored = false;
try {
fc.doFilter(req, res);
} catch (Exception e) {
errored = true;
throw e;
} finally {
long ended = Trace2Dto.getCurrentTimeInNs();
Long cpuTimeEnded = saveCpuTime ? MxBeans.THREAD.getCurrentThreadCpuTime() : null;
Long threadAllocatedBytesEnded = saveAllocatedBytes ? MxBeans.THREAD.getThreadAllocatedBytes(threadId) : null;
Traceparent traceparent = tracer.getTraceContext().get().getTraceparent();
Trace2ThreadDto rootThread = null;
if (tracer.getCurrentThreadId() != null) {
rootThread = ((DatarouterTracer) tracer).getCurrentThread();
rootThread.setCpuTimeEndedNs(cpuTimeEnded);
rootThread.setMemoryAllocatedBytesEnded(threadAllocatedBytesEnded);
rootThread.setEnded(ended);
((DatarouterTracer) tracer).setCurrentThread(null);
}
Trace2Dto trace2 = new Trace2Dto(traceparent, initialParentId, request.getContextPath(), request.getRequestURI().toString(), request.getQueryString(), traceCreated, ended, serviceName.get(), tracer.getDiscardedThreadCount(), tracer.getThreadQueue().size(), cpuTimeBegin, cpuTimeEnded, threadAllocatedBytesBegin, threadAllocatedBytesEnded);
Long traceDurationMs = trace2.getDurationInMs();
Long cpuTime = saveCpuTime ? TimeUnit.NANOSECONDS.toMillis(cpuTimeEnded - cpuTimeBegin) : null;
Long threadAllocatedKB = saveAllocatedBytes ? (threadAllocatedBytesEnded - threadAllocatedBytesBegin) / 1024 : null;
String saveReason = null;
if (traceSettings.saveTraces.get()) {
if (traceDurationMs > traceSettings.saveTracesOverMs.get()) {
saveReason = "duration";
}
if (RequestTool.getBoolean(request, "trace", false)) {
saveReason = "queryParam";
}
if (tracer.shouldSample()) {
saveReason = "traceContext";
}
if (errored) {
saveReason = "error";
}
}
if (saveReason != null) {
Counters.inc("traceSaved " + saveReason);
List<Trace2ThreadDto> threads = new ArrayList<>(tracer.getThreadQueue());
List<Trace2SpanDto> spans = new ArrayList<>(tracer.getSpanQueue());
if (rootThread != null) {
rootThread.setTotalSpanCount(spans.size());
// force to save the rootThread even though the queue could be full
threads.add(rootThread);
}
String userAgent = RequestTool.getUserAgent(request);
String userToken = currentSessionInfo.getSession(request).map(Session::getUserToken).orElse("unknown");
HttpRequestRecordDto httpRequestRecord = buildHttpRequestRecord(errored, request, traceCreated, userToken, traceparent);
String destination = offerTrace2(new Trace2BundleDto(trace2, threads, spans), httpRequestRecord);
logger.warn("Trace saved to={} traceparent={} initialParentId={} durationMs={}" + " cpuTimeMs={} threadAllocatedKB={} path={} query={} userAgent=\"{}\" userToken={}", destination, traceparent, initialParentId, traceDurationMs, cpuTime, threadAllocatedKB, trace2.type, trace2.params, userAgent, userToken);
} else if (traceDurationMs > traceSettings.logTracesOverMs.get() || TracerTool.shouldLog()) {
// only log once
logger.warn("Trace logged durationMs={} cpuTimeMs={} threadAllocatedKB={} path={}" + " query={}, traceparent={}", traceDurationMs, cpuTime, threadAllocatedKB, trace2.type, trace2.params, traceparent);
}
Optional<Class<? extends BaseHandler>> handlerClassOpt = RequestAttributeTool.get(request, BaseHandler.HANDLER_CLASS);
Optional<Method> handlerMethodOpt = RequestAttributeTool.get(request, BaseHandler.HANDLER_METHOD);
if (handlerClassOpt.isPresent() && handlerMethodOpt.isPresent()) {
Class<? extends BaseHandler> handlerClass = handlerClassOpt.get();
if (traceSettings.latencyRecordedHandlers.get().contains(handlerClass.getName())) {
handlerMetrics.saveMethodLatency(handlerClass, handlerMethodOpt.get(), traceDurationMs);
}
}
}
} finally {
TracerThreadLocal.clearFromThread();
}
}
use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.
the class Dispatcher method handleRequestIfUrlMatch.
public RoutingResult handleRequestIfUrlMatch(HttpServletRequest request, HttpServletResponse response, BaseRouteSet routeSet) throws ServletException, IOException {
String uri = request.getRequestURI();
if (!uri.startsWith(servletContext.get().getContextPath() + routeSet.getUrlPrefix())) {
return RoutingResult.NOT_FOUND;
}
BaseHandler handler = null;
String afterContextPath = uri.substring(servletContext.get().getContextPath().length());
if (afterContextPath.contains(JSESSIONID_PATH_PARAM)) {
afterContextPath = afterContextPath.substring(0, afterContextPath.indexOf(JSESSIONID_PATH_PARAM));
}
for (DispatchRule rule : routeSet.getDispatchRules()) {
if (rule.getPattern().matcher(afterContextPath).matches()) {
SecurityValidationResult securityCheckResult = rule.applySecurityValidation(request);
request = securityCheckResult.getWrappedRequest();
if (!securityCheckResult.isSuccess()) {
injector.getInstance(rule.getDefaultHandlerEncoder()).sendForbiddenResponse(request, response, securityCheckResult);
return RoutingResult.FORBIDDEN;
}
if (authenticationConfig.useDatarouterAuthentication() && !rule.checkRoles(request)) {
handleMissingRoles(request, response, rule.getAllowedRoles());
return RoutingResult.ROUTED;
}
handler = injector.getInstance(rule.getHandlerClass());
handler.setDefaultHandlerEncoder(rule.getDefaultHandlerEncoder());
handler.setDefaultHandlerDecoder(rule.getDefaultHandlerDecoder());
RequestAttributeTool.set(request, BaseHandler.HANDLER_ENCODER_ATTRIBUTE, injector.getInstance(rule.getDefaultHandlerEncoder()));
RequestAttributeTool.set(request, TRANSMITS_PII, rule.doesTransmitPii());
if (rule.hasApiKey()) {
// TODO avoid re evaluating the rule
Pair<Boolean, String> apiKeyPredicateExistsWithName = rule.getApiKeyPredicate().check(rule, request);
if (apiKeyPredicateExistsWithName.getLeft()) {
handler.setAccountName(apiKeyPredicateExistsWithName.getRight());
}
}
break;
}
}
Class<? extends BaseHandler> defaultHandlerClass = routeSet.getDefaultHandlerClass();
if (handler == null) {
if (defaultHandlerClass == null) {
return RoutingResult.NOT_FOUND;
}
handler = injector.getInstance(defaultHandlerClass);
}
handler.setRequest(request);
handler.setResponse(response);
handler.setServletContext(servletContext.get());
Params params = parseParams(request, handler.getDefaultMultipartCharset());
handler.setParams(params);
handler.handleWrapper();
return RoutingResult.ROUTED;
}
use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.
the class ApiDocService method buildEndpointDocumentation.
private List<DocumentedEndpointJspDto> buildEndpointDocumentation(DispatchRule rule) {
List<DocumentedEndpointJspDto> endpoints = new ArrayList<>();
Class<? extends BaseHandler> handler = rule.getHandlerClass();
while (handler != null && !handler.getName().equals(BaseHandler.class.getName())) {
for (Method method : handler.getDeclaredMethods()) {
if (!method.isAnnotationPresent(Handler.class)) {
continue;
}
Handler handlerAnnotation = method.getAnnotation(Handler.class);
String url = rule.getPattern().pattern();
if (!url.contains(BaseRouteSet.REGEX_ONE_DIRECTORY)) {
// not a handleDir
if (!url.endsWith(method.getName()) && !handlerAnnotation.defaultHandler()) {
continue;
}
}
if (url.contains(BaseRouteSet.REGEX_ONE_DIRECTORY)) {
String urlSuffix = handlerAnnotation.defaultHandler() ? "" : "/" + method.getName();
url = url.replace(BaseRouteSet.REGEX_ONE_DIRECTORY, "") + urlSuffix;
}
String implementation = handler.getSimpleName();
List<DocumentedParameterJspDto> parameters = new ArrayList<>();
String description = handlerAnnotation.description();
if (description.isEmpty()) {
if (EndpointTool.paramIsEndpointObject(method)) {
@SuppressWarnings("unchecked") Class<BaseEndpoint<?, ?>> baseEndpoint = (Class<BaseEndpoint<?, ?>>) method.getParameters()[0].getType();
Endpoint annotation = baseEndpoint.getAnnotation(Endpoint.class);
if (annotation != null) {
description = annotation.description();
}
}
}
DocumentedSecurityDetails securityDetails = createApplicableSecurityParameters(rule);
parameters.addAll(securityDetails.parameters);
parameters.addAll(createMethodParameters(method));
Type responseType = method.getGenericReturnType();
String responseExample;
Set<DocumentedExampleEnumDto> responseExampleEnumDtos = new HashSet<>();
if (responseType == Void.TYPE) {
responseExample = null;
} else {
try {
DocumentedExampleDto responseObject = createBestExample(responseType, new HashSet<>());
responseExampleEnumDtos = responseObject.exampleEnumDtos;
responseExample = GSON.toJson(responseObject.exampleObject);
} catch (Exception e) {
responseExample = "Impossible to render";
logger.warn("Could not create response example for {}", responseType, e);
}
}
String responseTypeString = buildTypeString(responseType);
var response = new DocumentedResponseJspDto(responseTypeString, responseExample, buildEnumValuesString(responseExampleEnumDtos));
boolean isDeprecated = method.isAnnotationPresent(Deprecated.class) || handler.isAnnotationPresent(Deprecated.class);
List<DocumentedErrorJspDto> errors = buildError(method);
Set<DocumentedExampleEnumDto> requestParamExampleEnumDtos = Scanner.of(parameters).concatIter(parameter -> parameter.exampleEnumDtos).collect(Collectors.toSet());
var endpoint = new DocumentedEndpointJspDto(url, implementation, parameters, securityDetails.apiKeyFieldName, description, response, isDeprecated, errors, buildEnumValuesString(requestParamExampleEnumDtos));
endpoints.add(endpoint);
}
handler = handler.getSuperclass().asSubclass(BaseHandler.class);
}
return endpoints;
}
Aggregations