使线程转储智能化
很久以前,我了解了一個(gè)稱為L(zhǎng)og MDC的東西,我對(duì)此非常感興趣。 我突然能夠理解日志文件中發(fā)生的所有事情,并指出特定的日志條目,并找出對(duì)錯(cuò),特別是在調(diào)試生產(chǎn)中的錯(cuò)誤時(shí)。
在2013年,我受委托從事一個(gè)項(xiàng)目,該項(xiàng)目正在一些麻煩的水域(幾件事情的結(jié)合)中運(yùn)行,幾乎每個(gè)星期,我不得不經(jīng)歷幾次Java Thread Dump,以弄清應(yīng)用程序中發(fā)生的事情以使其停止。 另外,有時(shí)我不得不將諸如AppDynamic,jProfiler,jConsole之類的探查器全部連接到應(yīng)用程序,以試圖找出問(wèn)題所在,更重要的是是什么引發(fā)了問(wèn)題。 jStack是我使用過(guò)的最有用的工具之一,但是碰到的線程轉(zhuǎn)儲(chǔ)沒(méi)有我可以使用的上下文信息。 我被困在看到10(s)個(gè)轉(zhuǎn)儲(chǔ)的堆棧跟蹤中,哪些類導(dǎo)致了該塊,但是沒(méi)有有關(guān)什么是什么以及什么輸入導(dǎo)致了該問(wèn)題的信息,并且它很快就令人沮喪。 最終,我們找到了問(wèn)題,但是它們主要是在經(jīng)過(guò)數(shù)輪深度調(diào)試后使用各種數(shù)據(jù)集對(duì)代碼進(jìn)行的。
一旦完成該項(xiàng)目,我發(fā)誓我再也不會(huì)陷入那種境地了。 我探索了可以使用類似于Log4j的NDC但在線程中使用它的方式,以便我的轉(zhuǎn)儲(chǔ)有意義。 而且我發(fā)現(xiàn)我可以更改ThreadName。 我的下一個(gè)項(xiàng)目確實(shí)非常有效地使用了它。 我最近遇到了一篇文章,很好地解釋了這個(gè)概念。 我不會(huì)重寫(xiě)他們所說(shuō)的所有內(nèi)容,因此這里是他們博客文章的鏈接 。
所以上周我開(kāi)始一個(gè)新項(xiàng)目,當(dāng)我開(kāi)始編碼框架時(shí)(使用Spring 4.1和Spring Boot),這是我為應(yīng)用程序編寫(xiě)的第一個(gè)類,并確保過(guò)濾器盡快進(jìn)入代碼。幫助我們進(jìn)行后期制作,但也使我的開(kāi)發(fā)日志有意義。
下面是Log4j NDC和設(shè)置ThreadName的代碼副本。
import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date;import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.filter.OncePerRequestFilter;/*** This is a very Spring opinionated HTTPFilter used for intercepting all requests and decorate the thread name with additional contextual* information. We have extenced the filter from {@link OncePerRequestFilter} class provided by Spring Framework to ensure that the filter is absolutely * executd only once per request. * * The following information will be added:* <ul>* <li>Old Thread name: to ensure that we are not losing any original context with thread names;</li>* <li>Time when the request was intercepted;</li>* <li>The RequestURI that proviced information on what RestFUL endpoint was accessed as part of this request;</li>* <li>A Token that was received in the header. This token is encrypted and does not exposes any confidential information. Also, this token provides* context which helps during debugging;</li>* <li>The Payload from the token. This information will be very helpful when we have to debug for issues that may be happening with a call request* as this holds all the information sent from the called.</li>* </ul>* * This filter will also reset the ThreadName back to it's original name once the processing is complete.* * @author Kapil Viren Ahuja**/ public class DecorateThreadNameFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {final Logger LOGGER = LoggerFactory.getLogger(DecorateThreadNameFilter.class);final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");Thread thread = Thread.currentThread();String threadOriginalName = thread.getName();String uri = request.getRequestURI();String time = dateFormat.format(new Date());String token = request.getHeader("authorization");try {thread.setName(String.format("%s StartTime \"%s\" RequestURI \"%s\" Token \"%s\"", threadOriginalName, time, uri, token));} catch (Exception ex) {LOGGER.error("Failed to set the thread name.", ex);// this is an internal filter and an error here should not impact// the request processing, hence eat the exception}try {filterChain.doFilter(request, response);} finally {try {thread.setName(threadOriginalName);} catch (Exception ex) {LOGGER.error("Failed to reset the thread name.", ex);// this is an internal filter and an error here should not// impact the request processing, hence eat the exception}}} }/*** Generic filter for intercepting all requests and perform the following generic tasks:* * <ul>* <li>Intercepts the request and then pushed the user domain into the session if one exists.</li>* <li> Pushes a uniquely generated request identifier to the LOG4J NDC context. This identifier will then be prepended* to all log messages generated using LOG4J. This allows tracing all log messages generated as part of the same* request; </li>* <li> Pushes the HTTP session identifier to the LOG4J NDC context. This identifier will then be prepended to all log* messages generated using LOG4J. This allows tracing all log messages generated as part of the same HTTP session;* </li>* <li> Pushes the IP address of the client to the LOG4J NDC context. The IP address will then be prepended to all log* messages generated using LOG4J. This allows tying back multiple user sessions initiated with the same logon name to* be correctly tied back to their actual origins. </li>* </ul>*/ public class RequestInterceptorFilter implements Filter {/*** <p>* <ul>* <li>Initializes the LOG4J NDC context before executing an HTTP requests.</li>* <li>Pushes the domain into the session</li>* </ul>* </p>*/public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{HttpServletRequest httpRequest = (HttpServletRequest) request;if (httpRequest.isRequestedSessionIdFromCookie() && !httpRequest.isRequestedSessionIdValid()){// TODO: Need to define an session expiration page and redirect the application to that page// As of now this is a non-issue as we are handling session expirations on Flex (Front-end) and hence// no request will come to server in case the session timeout occurs// HttpServletResponse httpServletResponse = (HttpServletResponse) response;// httpServletResponse.sendRedirect(httpRequest.getContextPath() + "?expired");}else{// Create an NDC context string that will be prepended to all log messages written to files.org.apache.log4j.NDC.push(getContextualInformation(httpRequest));// Process the chain of filterschain.doFilter(request, response);// Clear the NDC context string so that if the thread is reused for another request, a new context string is// used.org.apache.log4j.NDC.remove();}}public void init(FilterConfig arg0) throws ServletException{}public void destroy(){}/*** <p>* Generates the Contextual information to be put in the log4j's context. This information helps in tracing requests* </p>* * @param httpRequest* @return*/private String getContextualInformation(HttpServletRequest httpRequest){String httpRequestIdentifier = UUID.randomUUID().toString();String httpSessionIdentifier = httpRequest.getSession().getId();String clientAddress = httpRequest.getRemoteAddr();StringBuffer logNDC = new StringBuffer(httpRequestIdentifier + " | " + httpSessionIdentifier + " | " + clientAddress);String userName = (String)httpRequest.getSession().getAttribute(WebConstants.USERNAME);if (userName != null){logNDC.append(" | " + userName);}String domain = (String)httpRequest.getSession().getAttribute(WebConstants.DOMAIN);if (domain != null){logNDC.append(" | " + domain);}// Create an NDC context string that will be prepended to all log messages written to files.return logNDC.toString();} }翻譯自: https://www.javacodegeeks.com/2015/08/making-thread-dumps-intelligent.html
總結(jié)
- 上一篇: __macosx_在5分钟内在MacOS
- 下一篇: (usb驱动 linux)