in apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/ServletApiAdvice.java [171:298]
public static <HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> void onExitServlet(
ServletApiAdapter<HttpServletRequest, HttpServletResponse, ServletContext, ServletContextEvent, FilterConfig, ServletConfig> adapter,
Object servletRequest,
Object servletResponse,
@Nullable Object transactionOrScopeOrSpan,
@Nullable Throwable t,
Object thiz) {
Tracer tracer = GlobalTracer.get();
Transaction<?> transaction = null;
Scope scope = null;
Span<?> span = null;
if (transactionOrScopeOrSpan instanceof Transaction<?>) {
transaction = (Transaction<?>) transactionOrScopeOrSpan;
} else if (transactionOrScopeOrSpan instanceof Scope) {
scope = (Scope) transactionOrScopeOrSpan;
} else if (transactionOrScopeOrSpan instanceof Span<?>) {
span = (Span<?>) transactionOrScopeOrSpan;
}
excluded.remove();
if (scope != null) {
scope.close();
}
HttpServletRequest httpServletRequest = adapter.asHttpServletRequest(servletRequest);
HttpServletResponse httpServletResponse = adapter.asHttpServletResponse(servletResponse);
if (adapter.isInstanceOfHttpServlet(thiz) && httpServletRequest != null) {
Transaction<?> currentTransaction = tracer.currentTransaction();
if (currentTransaction != null) {
TransactionNameUtils.setTransactionNameByServletClass(adapter.getMethod(httpServletRequest), thiz.getClass(), currentTransaction.getAndOverrideName(PRIORITY_LOW_LEVEL_FRAMEWORK));
String userName = ServletTransactionHelper.getUserFromPrincipal(adapter.getUserPrincipal(httpServletRequest));
if (userName != null) {
ServletTransactionHelper.setUsernameIfUnset(userName, currentTransaction.getContext());
}
}
}
if (transaction != null &&
httpServletRequest != null &&
httpServletResponse != null) {
if (adapter.getAttribute(httpServletRequest, ServletTransactionHelper.ASYNC_ATTRIBUTE) != null) {
// HttpServletRequest.startAsync was invoked on this httpServletRequest.
// The transaction should be handled from now on by the other thread committing the response
transaction.deactivate();
} else {
// this is not an async httpServletRequest, so we can end the transaction immediately
if (transaction.isSampled() && tracer.getConfig(CoreConfiguration.class).isCaptureHeaders()) {
final Response resp = transaction.getContext().getResponse();
for (String headerName : adapter.getHeaderNames(httpServletResponse)) {
resp.addHeader(headerName, adapter.getHeaders(httpServletResponse, headerName));
}
}
// httpServletRequest.getParameterMap() may allocate a new map, depending on the servlet container implementation
// so only call this method if necessary
final String contentTypeHeader = adapter.getHeader(httpServletRequest, "Content-Type");
final Map<String, String[]> parameterMap;
if (transaction.isSampled() && servletTransactionHelper.captureParameters(adapter.getMethod(httpServletRequest), contentTypeHeader)) {
parameterMap = adapter.getParameterMap(httpServletRequest);
} else {
parameterMap = null;
}
Throwable t2 = null;
boolean overrideStatusCodeOnThrowable = true;
if (t == null) {
final int size = requestExceptionAttributes.size();
for (int i = 0; i < size; i++) {
String attributeName = requestExceptionAttributes.get(i);
Object throwable = adapter.getAttribute(httpServletRequest, attributeName);
// We don't check tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions() here,
// because in that case the corrupt pointer is already stored in the request attributes
// In that case we already lost, because the GC will complain about the broken pointer
// when the request attributes entry is GCed
if (throwable instanceof Throwable) {
t2 = (Throwable) throwable;
// elastic exception can be removed as it's not needed after transaction end
if (attributeName.equals(ELASTIC_EXCEPTION)) {
adapter.removeAttribute(httpServletRequest, attributeName);
}
if (!attributeName.equals(JAVAX_ERROR_EXCEPTION) && !attributeName.equals(JAKARTA_ERROR_EXCEPTION)) {
overrideStatusCodeOnThrowable = false;
}
break;
}
}
if (t2 == null) {
t2 = transaction.getPendingTransactionException();
if(t2 != null) {
overrideStatusCodeOnThrowable = false;
}
}
}
ServletContext servletContext = adapter.getServletContext(httpServletRequest);
String servletPath = adapter.getServletPath(httpServletRequest);
String pathInfo = adapter.getPathInfo(httpServletRequest);
if ((servletPath == null || servletPath.isEmpty()) && servletContext != null) {
String contextPath = adapter.getContextPath(servletContext);
String requestURI = adapter.getRequestURI(httpServletRequest);
servletPath = servletTransactionHelper.normalizeServletPath(requestURI, contextPath, servletPath, pathInfo);
}
servletTransactionHelper.onAfter(
transaction, t == null ? t2 : t,
adapter.isCommitted(httpServletResponse),
adapter.getStatus(httpServletResponse),
overrideStatusCodeOnThrowable,
adapter.getMethod(httpServletRequest),
parameterMap,
servletPath,
pathInfo,
contentTypeHeader,
true);
}
}
if (span != null) {
servletPathTL.remove();
pathInfoTL.remove();
span.captureException(t)
.withOutcome(t != null ? Outcome.FAILURE : Outcome.SUCCESS)
.deactivate()
.end();
}
}