Translate

[JSP] ThreadDump 생성/모니터링 페이지 만들기



웹 어플리케이션에 hang이 걸리거나 제대로 뜨지 않을 때 원인분석 할 수 있는 좋은 방법은 Thread Dump 를 생성하여 해당 덤프를 분석하는 방법이다.

하지만 문제가 되는 시점에 Thread Dump 를 생성해야 하며 외부에서 맘대로 서버에 접속 할 수 없는 상황이라면 Thread Dump 를 생성하는 jsp 페이지를 하나 만들어 호출하는 방법이 있다.



방법은 다음과 같다.


아래와 같이 현재 수행중인 WAS(tomcat, jeus) 의 PID 를 알아내는 스크립트를 서버의 특정 위치에 저장한다.
(SERVICE_NAME 부분은 각각의 서버 상태에 따라 문자열을 변경해 주어야 한다. 하단에 설명추가)

GIT Source: https://github.com/1004lucifer/JSP_Create_ThreadDump



pid.sh


#!/bin/bash
ps -ef | grep java | grep SERVICE_NAME | awk '{printf $2}'



dump.jsp


<%@ page import="java.io.*" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<html>
<%!
    private String executeCommand(String command, String seperator) {
        StringBuffer result = new StringBuffer();
        String line = null;
        BufferedReader br = null;
        Process ps = null;
        try {
            Runtime rt = Runtime.getRuntime();
            ps = rt.exec(command);
            br = new BufferedReader(new InputStreamReader(new SequenceInputStream(ps.getInputStream(), ps.getErrorStream())));

            while( (line = br.readLine()) != null ) {
                result.append(line + seperator);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try { br.close(); } catch(Exception e) {}
        }

        return result.toString();
    }

    private void saveLog(String file, String contents) {
        FileOutputStream fos = null;
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String dt = sdf.format(now);
        try {
            fos = new FileOutputStream(file+"_"+dt.substring(0, 8)+".log", true);   // true:이어쓰기, false:덮어쓰기
            for(int i=0; i<contents.length(); i++){
                fos.write(contents.charAt(i));
            }
            fos.write('\r');
            fos.write('\n');
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            if(fos != null){
                try{fos.close();
                }catch(IOException e){}
            }
        }
    }
%>
<body>
<%
    // pid.sh 가 위치한 경로
    String commandGetPid = "/opt/tomcat/project_name/threadDump/pid.sh";
    // ThreadDump 가 기록될 경로
    String logPath = "/opt/tomcat/project_name/threadDump/threadDump_tservice_api";

    String commandThreadDump = "jstack ";   // ThreadDump 를 생성하는 명령어


    String pid = null;
    String threadDump = null;

    try{
        pid = executeCommand(commandGetPid, "");
        threadDump = pid == null ? null : executeCommand(commandThreadDump + pid, "\n");
        
        if (threadDump != null) {
            saveLog(logPath, threadDump);
            out.println(threadDump.replaceAll("\n", "<br/>"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }
%>
</body>
</html>









PS.

1. pid.sh 추가설명

아래와 같이 해당 WAS 의 pid를 가져오기 위해 awk 명령어를 이용했으며 서버 하나에서 구동하는 WAS가 두개 이상이라면 grep 명령어로 하나만 나올 수 있도록 pid.sh 를 변경해 주면 된다.



[1004lucifer@1004lucifer ~]$ ps -ef | grep java
500       6419  6391  0 12:06 pts/1    00:00:00 grep java
root     19629     1  0  2014 ?        02:45:42 /usr/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.endorsed.dirs=/opt/tomcat/endorsed -classpath /opt/tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/tomcat -Dcatalina.home=/opt/tomcat -Djava.io.tmpdir=/opt/tomcat/temp org.apache.catalina.startup.Bootstrap start
[1004lucifer@1004lucifer ~]$
[1004lucifer@1004lucifer ~]$ ps -ef | grep java | grep Bootstrap
root     19629     1  0  2014 ?        02:45:42 /usr/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.endorsed.dirs=/opt/tomcat/endorsed -classpath /opt/tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/tomcat -Dcatalina.home=/opt/tomcat -Djava.io.tmpdir=/opt/tomcat/temp org.apache.catalina.startup.Bootstrap start
[1004lucifer@1004lucifer ~]$
[1004lucifer@1004lucifer ~]$ ps -ef | grep java | grep Bootstrap | awk '{printf $2}'
19629[1004lucifer@1004lucifer ~]$ 
[1004lucifer@1004lucifer ~]$






2. dump.jsp 위치 추가설명 (중요)

dump.jsp 를 만들어서 문제가 되는 시점에 ThreadDump 를 생성하여 현재의 Thread 상태와 그 상태를 Dump파일로 남기려는게 목적이지만 현재 운영중인 WAS 에 해당 페이지를 넣게된다면 해당 WAS 가 문제가 되었을 경우 경우에 따라 dump.jsp 조차도 페이지가 실행이 되지 않을 수 있다.

처음에는 Context 를 하나 더 만들어 추가하면 되지 않을까 싶었는데 복수개의 Context 가 WAS의 Connection 개수를 같이 공유한다면 특정 Context 가 문제가 있을 때 dump.jsp 를 넣은 다른 Context 에 영향을 미치지 않을까 싶기도 하다.
(WAS에 대해서 잘 아는 분이 있다면 댓글로 설명을 달아줬으면 좋겠다.)

운좋게 현재 상황은 Tomcat Instance 가 하나 더 기동이 되어있어서 다른 Instance(프로세스) 의 Tomcat 에 dump.jsp 를 같이 올렸다.

만일 모니터링 하려는 Tomcat이 hang 발생을 하더라도 안심하고 사용할 수 있게 되었다.



댓글