springmvc+logback项目的日志搭建

一、重写HTMLLayout

两个自定义类:LzhHTMLLayoutBaseLzhHTMLLayout

LzhHTMLLayoutBase代码如下:

package top.liaozhenghan.logback.study.layout;

import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.liaozhenghan.util.dateutil.DateStyle;
import com.liaozhenghan.util.dateutil.DateUtil;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;
import ch.qos.logback.core.html.CssBuilder;
import ch.qos.logback.core.pattern.Converter;
import ch.qos.logback.core.pattern.ConverterUtil;
import ch.qos.logback.core.pattern.parser.Node;
import ch.qos.logback.core.pattern.parser.Parser;
import ch.qos.logback.core.spi.ScanException;

public abstract class LzhHTMLLayoutBase<E> extends LayoutBase<E>{
    
    protected String pattern;

    protected Converter<E> head;

    protected String title = "Logback Log Messages";

    // It is the responsibility of derived classes to set
    // this variable in their constructor to a default value.
    protected CssBuilder cssBuilder;

    // counter keeping track of the rows output
    protected long counter = 0;

    /**
     * Set the <b>ConversionPattern </b> option. This is the string which controls
     * formatting and consists of a mix of literal content and conversion
     * specifiers.
     */
    public void setPattern(String conversionPattern) {
        pattern = conversionPattern;
    }

    /**
     * Returns the value of the <b>ConversionPattern </b> option.
     */
    public String getPattern() {
        return pattern;
    }

    public CssBuilder getCssBuilder() {
        return cssBuilder;
    }

    public void setCssBuilder(CssBuilder cssBuilder) {
        this.cssBuilder = cssBuilder;
    }

    /**
     * Parses the pattern and creates the Converter linked list.
     */
    @Override
    public void start() {
        int errorCount = 0;

        try {
            Parser<E> p = new Parser<E>(pattern);
            p.setContext(getContext());
            Node t = p.parse();
            this.head = p.compile(t, getEffectiveConverterMap());
            ConverterUtil.startConverters(this.head);
        } catch (ScanException ex) {
            addError("Incorrect pattern found", ex);
            errorCount++;
        }

        if (errorCount == 0) {
            super.started = true;
        }
    }

    protected abstract Map<String, String> getDefaultConverterMap();

    /**
     * Returns a map where the default converter map is merged with the map
     * contained in the context.
     */
    public Map<String, String> getEffectiveConverterMap() {
        Map<String, String> effectiveMap = new HashMap<String, String>();

        // add the least specific map fist
        Map<String, String> defaultMap = getDefaultConverterMap();
        if (defaultMap != null) {
            effectiveMap.putAll(defaultMap);
        }

        // contextMap is more specific than the default map
        Context context = getContext();
        if (context != null) {
            @SuppressWarnings("unchecked")
            Map<String, String> contextMap = (Map<String, String>) context.getObject(CoreConstants.PATTERN_RULE_REGISTRY);
            if (contextMap != null) {
                effectiveMap.putAll(contextMap);
            }
        }
        return effectiveMap;
    }

    /**
     * The <b>Title </b> option takes a String value. This option sets the
     * document title of the generated HTML document.
     * 
     * <p>
     * Defaults to 'Logback Log Messages'.
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Returns the current value of the <b>Title </b> option.
     */
    public String getTitle() {
        return title;
    }

    /**
     * Returns the content type output by this layout, i.e "text/html".
     */
    @Override
    public String getContentType() {
        return "text/html";
    }

    /**
     * Returns appropriate HTML headers.
     */
    @Override
    public String getFileHeader() {
        StringBuilder sbuf = new StringBuilder();
        sbuf.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"");
        sbuf.append(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("<html>");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("  <head>");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("    <title>");
        sbuf.append(title);
        sbuf.append("</title>");
        sbuf.append(LINE_SEPARATOR);

        cssBuilder.addCss(sbuf);

        sbuf.append(LINE_SEPARATOR);
        sbuf.append("  </head>");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("<body>");
        sbuf.append(LINE_SEPARATOR);

        return sbuf.toString();
    }

    public String getPresentationHeader() {
        StringBuilder sbuf = new StringBuilder();
        sbuf.append("<hr/>");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("<p><span style='color:red;'>日志启动时间:</span> ");
        sbuf.append(DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD_HH_MM_SS_CN));
        sbuf.append("</p><p></p>");
        sbuf.append(LINE_SEPARATOR);
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("<table cellspacing=\"0\">");
        sbuf.append(LINE_SEPARATOR);

        buildHeaderRowForTable(sbuf);

        return sbuf.toString();
    }

    private void buildHeaderRowForTable(StringBuilder sbuf) {
        Converter c = head;
        String name;
        sbuf.append("<tr class=\"header\">");
        sbuf.append(LINE_SEPARATOR);
        while (c != null) {
            name = computeConverterName(c);
            if (name == null) {
                c = c.getNext();
                continue;
            }
            sbuf.append("<td class=\"");
            sbuf.append(name);
            sbuf.append("\">");
            sbuf.append(filterTableTitleName(name));
            sbuf.append("</td>");
            sbuf.append(LINE_SEPARATOR);
            c = c.getNext();
        }
        sbuf.append("</tr>");
        sbuf.append(LINE_SEPARATOR);
    }
    
    private String filterTableTitleName(String name) {
        
        String str = name.toLowerCase();
        if ("level".equals(str)) {
            name = "级别";
        } else if ("date".equals(str)) {
            name = "执行时间";
        } else if ("callerdata".equals(str)) {
            name = "所在行";
        } else if ("message".equals(str)) {
            name = "信息";
        }
        
        return name;
    }

    public String getPresentationFooter() {
        StringBuilder sbuf = new StringBuilder();
        sbuf.append("</table>");
        return sbuf.toString();
    }

    /**
     * Returns the appropriate HTML footers.
     */
    @Override
    public String getFileFooter() {
        StringBuilder sbuf = new StringBuilder();
        sbuf.append(LINE_SEPARATOR);
        sbuf.append("</body></html>");
        return sbuf.toString();
    }

    protected void startNewTableIfLimitReached(StringBuilder sbuf) {
        if (this.counter >= CoreConstants.TABLE_ROW_LIMIT) {
            counter = 0;
            sbuf.append("</table>");
            sbuf.append(LINE_SEPARATOR);
            sbuf.append("<p></p>");
            sbuf.append("<table cellspacing=\"0\">");
            sbuf.append(LINE_SEPARATOR);
            buildHeaderRowForTable(sbuf);
        }
    }

    protected String computeConverterName(Converter c) {
        
        String name = null;
        
        String className = c.getClass().getSimpleName();
        int index = className.indexOf("Converter");
        if (index == -1) {
            name = className;
        } else {
            name = className.substring(0, index);
        }
        
        return name;
    }

}
View Code

LzhHTMLLayout代码如下:

package top.liaozhenghan.logback.study.layout;

import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR;

import java.util.Map;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.html.DefaultCssBuilder;
import ch.qos.logback.classic.html.DefaultThrowableRenderer;
import ch.qos.logback.classic.pattern.MDCConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.helpers.Transform;
import ch.qos.logback.core.html.IThrowableRenderer;
import ch.qos.logback.core.pattern.Converter;

public class LzhHTMLLayout extends LzhHTMLLayoutBase<ILoggingEvent>{

    /**
     * Default pattern string for log output.
     */
    static final String DEFAULT_CONVERSION_PATTERN = "%date{HH:mm:ss}%level%caller{1}%msg";

    IThrowableRenderer<ILoggingEvent> throwableRenderer;

    /**
     * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
     * 
     * The default pattern just produces the application supplied message.
     */
    public LzhHTMLLayout() {
        pattern = DEFAULT_CONVERSION_PATTERN;
        throwableRenderer = new DefaultThrowableRenderer();
        cssBuilder = new DefaultCssBuilder();
    }

    @Override
    public void start() {
        int errorCount = 0;
        if (throwableRenderer == null) {
            addError("ThrowableRender cannot be null.");
            errorCount++;
        }
        if (errorCount == 0) {
            super.start();
        }
    }

    protected Map<String, String> getDefaultConverterMap() {
        return PatternLayout.defaultConverterMap;
    }

    public String doLayout(ILoggingEvent event) {
        StringBuilder buf = new StringBuilder();
        startNewTableIfLimitReached(buf);

        boolean odd = true;
        if (((counter++) & 1) == 0) {
            odd = false;
        }

        String level = event.getLevel().toString().toLowerCase();

        buf.append(LINE_SEPARATOR);
        buf.append("<tr class=\"");
        buf.append(level);
        if (odd) {
            buf.append(" odd\">");
        } else {
            buf.append(" even\">");
        }
        buf.append(LINE_SEPARATOR);

        Converter<ILoggingEvent> c = head;
        while (c != null) {
            appendEventToBuffer(buf, c, event);
            c = c.getNext();
        }
        buf.append("</tr>");
        buf.append(LINE_SEPARATOR);

        if (event.getThrowableProxy() != null) {
            throwableRenderer.render(buf, event);
        }
        return buf.toString();
    }

    private void appendEventToBuffer(StringBuilder buf, Converter<ILoggingEvent> c, ILoggingEvent event) {
        buf.append("<td class=\"");
        buf.append(computeConverterName(c));
        buf.append("\">");
        buf.append(Transform.escapeTags(c.convert(event)));
        buf.append("</td>");
        buf.append(LINE_SEPARATOR);
    }

    public IThrowableRenderer getThrowableRenderer() {
        return throwableRenderer;
    }

    public void setThrowableRenderer(IThrowableRenderer<ILoggingEvent> throwableRenderer) {
        this.throwableRenderer = throwableRenderer;
    }

    @Override
    protected String computeConverterName(Converter c) {
        if (c instanceof MDCConverter) {
            MDCConverter mc = (MDCConverter) c;
            String key = mc.getFirstOption();
            if (key != null) {
                return key;
            } else {
                return "MDC";
            }
        } else {
            return super.computeConverterName(c);
        }
    }
}
View Code

二、logback.xml配置

 
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="3 seconds">
    <!-- 定义变量 -->
    <if condition='p("os.name").contains("Windows")'>
        <then>
            <property name="LOG_PATH" value="c:/logs" />
        </then>
    </if>
    <if condition='p("os.name").contains("Linux")'>
        <then>
            <property name="LOG_PATH" value="logs" />
        </then>
    </if>
    <property name="APP_NAME" value="study" />
    <property name="LOG_ROOT_LEVEL" value="debug" />
    <property name="maxHistory" value="30" />
    
    <contextName>${APP_NAME}</contextName>

    <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                <!-- 设置日志输出格式 -->
                [%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] %logger{36} - %m%n
            </pattern>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE_ERROR"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}/ERROR.log</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="top.liaozhenghan.logback.study.layout.LzhHTMLLayout"/>
        </encoder>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${APP_NAME}/ERROR.%d.log
            </FileNamePattern>
            <MaxHistory>${maxHistory}</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    
    <appender name="FILE_WARN"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}/WARN.log</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="top.liaozhenghan.logback.study.layout.LzhHTMLLayout"/>
        </encoder>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${APP_NAME}/WARN.%d.log
            </FileNamePattern>
            <MaxHistory>${maxHistory}</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    
    <appender name="FILE_INFO"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}/INFO.log</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="top.liaozhenghan.logback.study.layout.LzhHTMLLayout"/>
        </encoder>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${APP_NAME}/INFO.%d.log
            </FileNamePattern>
            <MaxHistory>${maxHistory}</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>
    
    <appender name="FILE_DEBUG"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}/DEBUG.log</file>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="top.liaozhenghan.logback.study.layout.LzhHTMLLayout"/>
        </encoder>
        <rollingPolicy
            class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/${APP_NAME}/DEBUG.%d.log
            </FileNamePattern>
            <MaxHistory>${maxHistory}</MaxHistory>
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <root level="${LOG_ROOT_LEVEL}">
        <!-- 控制台输出 -->
        <appender-ref ref="STDOUT" />
        <!-- 文件输出 -->
        <appender-ref ref="FILE_ERROR" />
        <appender-ref ref="FILE_WARN" />
        <appender-ref ref="FILE_INFO" />
        <appender-ref ref="FILE_DEBUG" />
    </root>
</configuration>
logback.xml
 

三、查看日志的jsp页面

用了三个页面:登录页面loglogin.jsp,日志级别选择页面logselect.jsp,日志显示页面log.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
    <title>日志系统登录</title>
    <meta charset="UTF-8">
    <!-- meta信息,可维护 -->
    <meta name="format-detection" content="telephone=no">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="apple-touch-fullscreen" content="yes">
    
</head>
<body style="width: 100%;">

    <form action="" method="post">
        <p>密码:</p><input name="password"><br>
        <input type="submit" value="登录日志系统">
    </form>
</body>
</html>
loglogin.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
    <title>日志记录</title>
    <meta charset="UTF-8">
    <!-- meta信息,可维护 -->
    <meta name="format-detection" content="telephone=no">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="apple-touch-fullscreen" content="yes">
    <script src="js/jquery.min.js" type="text/javascript"></script>
    
    <style type="text/css">
        .lzh_div td{
        }
    </style>
    
</head>
<body style="width: 100%;">

    <form action="" method="post">
        <input id="date" type="date" name="date">
        <select id="levels" name="level">
            <!-- <option value="TRACE">TRACE</option> -->
            <option value="DEBUG">DEBUG</option>
            <option value="INFO">INFO</option>
            <option value="WARN">WARN</option>
            <option value="ERROR">ERROR</option>
        </select>
        <input type="submit" value="查看日志">
    </form>
    <script type="text/javascript">
        var level = "${level}";
        var date = "${date}";
        $("#levels").val(level);
        $("#date").val(date);
    </script>
    <div class="lzh_div" style="width: 100%;">${html}</div>
</body>
</html>
logselect.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
${html}
log.jsp

四、LogController.java

package top.liaozhenghan.logback.study.controller;

import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.liaozhenghan.util.dateutil.DateStyle;
import com.liaozhenghan.util.dateutil.DateUtil;
import com.liaozhenghan.util.httputil.SrcUtil;

@Controller
public class LogController {
    
    final static Logger LOG = LoggerFactory.getLogger(LogController.class);
    final static String LOG_LOGIN = "LOG_LOGIN";

    @RequestMapping(value="/log")
    public String log(HttpServletRequest request, Model model) {
        
        String date = request.getParameter("date");
        String level = request.getParameter("level");
        String password = request.getParameter("password");
        String html = null;
        
        // 判断是否登录
        Object login = request.getSession().getAttribute(LOG_LOGIN);
        if (login == null) {
            String PASSWORD = "baofa2018";
            
            if (PASSWORD.equals(password)) {
                request.getSession().setAttribute(LOG_LOGIN, LOG_LOGIN);
            } else {
                return "logs/loglogin";
            }
        }
        
        // 1.判断参数是否为空
        if (StringUtils.isEmpty(level) && StringUtils.isEmpty(date)) {
            date = DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD);
            level = "ERROR";
            model.addAttribute("date", date);
            model.addAttribute("level", level);
            return "logs/logselect";
        }
        
        // 2.判断路径
        String filepath = "c:/logs/study/";
        String osName = System.getProperty("os.name");
        if (StringUtils.isEmpty(osName)) {
            html = "系统错误";
            model.addAttribute("date", date);
            model.addAttribute("level", level);
            model.addAttribute("html", html);
            return "logs/logselect";
        } else if (osName.contains("Window")) {
            filepath = "c:/logs/study/";
        } else if (osName.contains("Linux")) {
            filepath = "logs/study/";
        } else {
            html = "未识别的操作系统:" + osName;
            model.addAttribute("date", date);
            model.addAttribute("level", level);
            model.addAttribute("html", html);
            return "logs/logselect";
        }
        
        if (StringUtils.isNotEmpty(date) && !DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD).equals(date)) {
            filepath += level + "." + date + ".log";
        } else {
            filepath += level + ".log";
            date = DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD);
        }
        
        File file = new File(filepath);
        html = "无日志记录";
        if (file.exists()) {
            try {
                html = FileUtils.readFileToString(file, "UTF-8");
            } catch (IOException e) {
                LOG.error(DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD_HH_MM_SS) 
                        + "【文件名】" + filepath, e);
            }
        }
        
        LOG.debug("测试");
        LOG.info("测试");
        LOG.warn("测试");
        LOG.error("测试");
        
        model.addAttribute("html", html);
        model.addAttribute("date", date);
        model.addAttribute("level", level);
        return "logs/log";
    }
}
View Code

猜你喜欢

转载自www.cnblogs.com/liaozhenghan/p/9642207.html
今日推荐