• 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=" 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=" 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=" 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

     注:logback.xml在tomcat环境下的日志目录可改为:

    <property name="LOG_PATH" value="${catalina.base}/logs" />

    在java代码中获取文件目录改为:

    String catalina_base = System.getProperty("catalina.base");
    String filepath = catalina_base + "/logs/study/";
  • 相关阅读:
    git 命令
    Spring Boot Web应用开发 CORS 跨域请求支持
    layui demo
    jQuery jqGrid 4.7
    jQurey zTree Demo 3.5
    jQurey zTree API 3.5
    jQurey Select2 4.0
    jQurey Validation 1.16
    Laytpl 1.2
    My97DatePicker 4.8
  • 原文地址:https://www.cnblogs.com/liaozhenghan/p/9642207.html
Copyright © 2020-2023  润新知