Translate

2015년 3월 6일 금요일

[JSP] JSP 페이지로 Web 에서 Linux 의 tail -f 명령어와 같이 Log 를 보여주는 방법



Web 에서 Linux/Unix 의 'tail -f logfile' 과 같이 보려고 인터넷을 찾아보다 마음에 드는게 없어서 직접 만들게 되었다.

Server 에서 tail 명령어를 사용할 때 grep 을 많이 사용해서 해당 기능도 추가했다.


모습은 다음과 같다.



사용방법
1. tail -f {} 부분에 실시간으로 확인 할 로그파일을 선택
2. grep {} 부분에 문자열 입력 시 해당 문자열이 매칭되는 경우에만 출력
3. grep -v {} 부분에 문자열 입력 시 해당 문자열이 매칭되는 경우에는 출력 제외
(grep 에 넣을 문자열은 정규표현식 지원된다.)




Source
https://github.com/1004lucifer/JSP_tail


<%--
  Created by IntelliJ IDEA.
  User: 1004lucifer
  Date: 2015. 3. 5.
  Time: 오후 9:51
--%>
<%@ page import="java.io.RandomAccessFile" %>
<%@ page import="java.io.FileNotFoundException" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%
    String logPath = "/Users/1004lucifer/test/jsp/tail/";

    String fileName = request.getParameter("log_filename") == null ? "" : request.getParameter("log_filename");

    if ("".equals(fileName.trim()) == false) {

        fileName = logPath + fileName.trim().replaceAll("\\.\\.", "");

        long preEndPoint = request.getParameter("preEndPoint") == null ? 0 : Long.parseLong(request.getParameter("preEndPoint") + "");

        StringBuilder log = new StringBuilder();
        long startPoint = 0;
        long endPoint = 0;

        RandomAccessFile file = null;

        try {
            file = new RandomAccessFile(fileName, "r");
            endPoint = file.length();

            startPoint = preEndPoint > 0 ?
                    preEndPoint : endPoint < 2000 ?
                    0 : endPoint - 2000;

            file.seek(startPoint);

            String str;
            while ((str = file.readLine()) != null) {
                log.append(str);
                log.append("\n");
                endPoint = file.getFilePointer();
                file.seek(endPoint);
            }

        } catch (FileNotFoundException fnfe) {
            log.append("File does not exist.");
            fnfe.printStackTrace();
        } catch (Exception e) {
            log.append("Sorry. An error has occurred.");
            e.printStackTrace();
        } finally {
            try {file.close();} catch (Exception e) {}
        }

        out.print("{\"endPoint\":\"" + endPoint + "\", \"log\":\"" + URLEncoder.encode(new String(log.toString().getBytes("ISO-8859-1"),"UTF-8"), "UTF-8").replaceAll("\\+", "%20") + "\"}");

    } else {


        List<String> fileList = new ArrayList<String>();
        String line = null;
        BufferedReader br = null;
        Process ps = null;
        try {
            Runtime rt = Runtime.getRuntime();
            ps = rt.exec(new String[]{"/bin/sh", "-c", "find "+ logPath + " -maxdepth 1 -type f -exec basename {} \\; | sort"});
            br = new BufferedReader(new InputStreamReader(ps.getInputStream()));

            while( (line = br.readLine()) != null ) {
                fileList.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try { br.close(); } catch(Exception e) {}
        }
%>
<html>
<head>
    <title></title>
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }
        #header {
            position: fixed;
            top: 0;
            left: 50px;
            width: 100%;
            height: 10%;
        }
        #console {
            position: fixed;
            bottom: 0;
            width: 100%;
            height: 90%;
            background-color: black;
            color:white;
            font-size: 15px;
        }
        #runningFlag {
            color: red;
        }
    </style>
    <script type="text/javascript">
        var endPoint = 0;
        var tailFlag = false;
        var fileName;
        var consoleLog;
        var grep;
        var grepV;
        var pattern;
        var patternV;
        var runningFlag;
        var match;
        $(document).ready(function() {
            consoleLog = $('#console');
            runningFlag = $('#runningFlag');

            function startTail() {
                runningFlag.html('Running');
                fileName = $('#fileName').val();
                grep = $.trim($('#grep').val());
                grepV = $.trim($('#grepV').val());
                pattern = new RegExp('.*'+grep+'.*\\n', 'g');
                patternV = new RegExp('.*'+grepV+'.*\\n', 'g');
                function requestLog() {
                    if (tailFlag) {
                        $.ajax({
                            type : 'POST',
                            url : 'tail.jsp',   // #### Caution: The name of the source file
                            dataType : 'json',
                            data : {
                                'log_filename' : fileName,
                                'preEndPoint' : endPoint

                            },
                            success : function(data) {
                                endPoint = data.endPoint == false ? 0 : data.endPoint;
                                logdata = decodeURIComponent(data.log);
                                if (grep != false) {
                                    match = logdata.match(pattern);
                                    logdata = match ? match.join('') : '';
                                }
                                if (grepV != false) {
                                    logdata = logdata.replace(patternV, '');
                                }
                                consoleLog.val(consoleLog.val() + logdata);
                                consoleLog.scrollTop(consoleLog.prop('scrollHeight'));

                                setTimeout(requestLog, 1000);
                            }
                        });
                    }
                }
                requestLog();
            }
            $('#startTail').on('click', function() {tailFlag = true; startTail();});
            $('#stopTail').on('click', function() {
                tailFlag = false;
                runningFlag.html('Stop');
            });
            $('#fileName').change(function() {
                tailFlag = false;
                endPoint = 0;
                runningFlag.html('Stop');
            });
        });
    </script>
</head>
<body>
<div id="header">
    <h2>Log Tail</h2>
    tail -f
    <select id="fileName">
        <%  for (String file : fileList) {  %>
        <option value="<%=file%>"><%=file%></option>
        <%  }   %>
    </select>
    | grep <input id="grep" type="text" />
    | grep -v <input id="grepV" type="text" />

    <br/>
    <input id="startTail" type="button" value="startTail" />&nbsp;&nbsp;&nbsp;
    <input id="stopTail" type="button" value="stopTail" />&nbsp;&nbsp;&nbsp;
    <span id="runningFlag">Stop</span><br/>

</div>
<textarea id="console"></textarea>
</body>
</html>
<%
    }
%>




PS.
다음의 기능을 지원한다.
1. UTF-8 인코딩으로 다국어 지원
2. 특정 디렉토리의 파일을 읽어온 후 소팅해서 Select 할 수 있게 보여준다.
3. 상위 디렉토리에 접근 할 수 없도록 ".." 문자열 치환
4. 큰 용량의 파일을 읽을 수 있도록 RandomAccessFile 사용 (2GB 이상의 로그 파일 테스트완료)
5. 페이지를 닫으면 더 이상 서버에 정보를 요청하지 않는다.


======================
2015.03.10 수정
1. 퍼포먼스 최적화
2. 서버요청 최소화


======================
2015.03.20 수정
1. grep 버그 수정


댓글 2개 :

  1. 질문이 있어서 댓글 남깁니다.

    String str;를 try 안에 선언하셔서

    str cannot be resolved 이런식으로

    out.print 할때 str를 찾을 수 없다고 에러가 나는데

    str을 try밖에 빼도 안되고

    out.print를 try안에 넣으면 에러는 안나오는데

    로그가 안뜹니다.

    뭐가 문제인지 알려주실 수 있나요? ㅠㅠ


    답글삭제
    답글
    1. IntelliJ 에서 확인해보니 위 소스에서 따로 에러가 검출되지 않습니다.

      String str;
      의 str이 try 밖에서 사용되지 않기에 문법적으로도 문제는 없습니다.

      아마 IDE의 문제일수도 있지 않을까 의심이 되는데..
      혹시 컴파일이 안되서 jsp페이지가 보여지지 않는 상황인가요??

      삭제