应用系统日志采集解决方案

概述


基于Flume + MongoDB,对现有的多个应用系统进行日志采集。

特点

  1. 采集范围
    每一次用户请求的请求信息。
  2. 数据量大
  3. 尽量减少现有系统的改动

数据流图


说明:

首先考虑的结构体系,是直接在应用系统中,将日志数据写到Flume;但是现有的应用系统都是非Maven的,需要在每一个应用系统中添加20+个jar包。为避免这种情况,抽出了一层日志服务,开放webservice服务给应用系统调用,最终形成上述的体系。

日志存储


1.需要解决的问题

1.1 借助Flume,写日志到MongoDB

参考:Flume学习应用:Java写日志数据到MongoDB

- 外网参考:Flume学习应用:Java写日志数据到MongoDB

1.2 发布webservice服务

参考:在web项目中发布jaxws

- 外网参考:在web项目中发布jaxws

2.日志服务实现

一个简单的web项目,对外发布一个webservice服务,实现写日志到Flume。

2.1 文件结构

src/main/java
    |---- cn.sinobest.asj.log
              |---- ISALog.java # 日志服务接口
              |---- SALogImpl.java # 日志服务实现类
    |---- cn.sinobest.asj.log.exception
              |---- InvalidGradeException.java # 表示无效的日志等级
              |---- InvalidFormatExceptioin.java # 表示无效的消息格式(要求是JSON格式字符串)
    |---- cn.sinobest.asj.log.util
              |---- ValidGrade.java # 枚举,所有有效的日志等级(DEBUG, INFO, WARN, ERROR)
              |---- MessageTemplate.java # 消息模板
src/main/resources
    |---- log4j.properties
src/main/webapp
    |---- WEB-INF
              |---- sun-jaxws.xml
              |---- web.xml
    |----  index.jsp # 这个可以忽略
pom.xml

2.2  文件内容

你可以直接从log-service拿到源代码,并跳过这一节的内容。

  1. pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>cn.sinobest.asj</groupId>
        <artifactId>log-service</artifactId>
        <packaging>war</packaging>
        <version>0.0.1-SNAPSHOT</version>
        <name>log-service Maven Webapp</name>
        <url>http://maven.apache.org</url>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.16</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1.1</version>
            </dependency>
            <!-- for log to Flume -->
            <dependency>
                <groupId>org.apache.flume.flume-ng-clients</groupId>
                <artifactId>flume-ng-log4jappender</artifactId>
                <version>1.6.0</version>
            </dependency>
            <!-- for jax-ws -->
            <dependency>
                <groupId>com.sun.xml.ws</groupId>
                <artifactId>jaxws-rt</artifactId>
                <version>2.2.10</version>
            </dependency>
            <!-- for test the log content is a json-format or not -->
            <dependency>
                <groupId>org.mongodb</groupId>
                <artifactId>mongo-java-driver</artifactId>
                <version>2.13.0</version>
            </dependency>
        </dependencies>
        <build>
            <finalName>log-service</finalName>
        </build>
    </project>
  2. web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
             id="WebApp_ID" version="3.0" metadata-complete="false">
        <display-name>Archetype Created Web Application</display-name>
    </web-app>

    注意:如果是servlet3.0以下的版本,需要额外的配置。

  3. log4j.properties

    # 配置Log4jAppender,能写日志到Flume
    log4j.appender.flumeAvro=org.apache.flume.clients.log4jappender.Log4jAppender
    log4j.appender.flumeAvro.Hostname=localhost
    log4j.appender.flumeAvro.Port=44444
    log4j.appender.flumeAvro.UnsafeMode=true
    log4j.appender.flumeAvro.layout=org.apache.log4j.PatternLayout
    log4j.appender.flumeAvro.layout.ConversionPattern=%m
    # set root logger
    log4j.rootLogger=INFO, flumeAvro
  4. ISALog.java

    package cn.sinobest.asj.log;
    import javax.jws.WebParam;
    import javax.jws.WebService;
    import cn.sinobest.asj.log.exception.InvalidFormatExceptioin;
    import cn.sinobest.asj.log.exception.InvalidGradeException;
    /**
     * SINOBEST ASJ Log - 为实现日志的统一采集和管理.
     *
     * @author lijinlong
     *
     */
    @WebService
    public interface ISALog {
        /**
         * 日志记录.
         *
         * @param grade
         *            日志等级描述 - 忽略大小写.
         * @param content
         *            日志内容 - 需要为JSON格式的字符串.
         */
        public void log(@WebParam(name = "grade") String grade,
                @WebParam(name = "content") String content)
                throws InvalidGradeException, InvalidFormatExceptioin;
    }
  5. SALogImpl.java

    package cn.sinobest.asj.log;
    import javax.jws.WebService;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import cn.sinobest.asj.log.exception.InvalidFormatExceptioin;
    import cn.sinobest.asj.log.exception.InvalidGradeException;
    import cn.sinobest.asj.log.util.MessageTemplate;
    import cn.sinobest.asj.log.util.ValidGrade;
    import com.mongodb.util.JSON;
    @WebService(endpointInterface = "cn.sinobest.asj.log.ISALog")
    public class SALogImpl implements ISALog {
        static final Log log = LogFactory.getLog(SALogImpl.class);
        public void log(String grade, String content) throws InvalidGradeException,
                InvalidFormatExceptioin {
            checkGrade(grade);
            checkContent(content);
            ValidGrade vg = ValidGrade.valueOf(grade.toUpperCase());
            log(vg, content);
        }
        /**
         * 根据日志等级,调用{@link log}的不同方法记录日志.
         *
         * @param vg
         *            日志等级
         * @param content
         *            日志内容
         */
        private void log(ValidGrade vg, String content) {
            switch (vg) {
            case DEBUG:
                log.debug(content);
                break;
            case INFO:
                log.info(content);
                break;
            case WARN:
                log.warn(content);
                break;
            case ERROR:
                log.error(content);
                break;
            default:
                break;
            }
        }
        /**
         * 检查日志等级的有效性.
         *
         * @param grade
         *            日志等级描述.
         * @throws InvalidGradeException
         *             当日志等级无效时,抛出此异常.
         */
        private void checkGrade(String grade) throws InvalidGradeException {
            boolean valid = ValidGrade.isValid(grade);
            if (!valid) {
                String message = String.format(MessageTemplate.INVALID_GRADE,
                        grade, ValidGrade.getEnumContent());
                throw new InvalidGradeException(message);
            }
        }
        /**
         * 检查日志内容格式的有效性.<br>
         * 要求为JSON格式的字符串.
         *
         * @param content
         *            日志内容.
         * @throws InvalidFormatExceptioin
         *             当日志内容格式无效时,抛出此异常.
         */
        private void checkContent(String content) throws InvalidFormatExceptioin {
            boolean valid = true;
            if (content == null || content.isEmpty()) {
                valid = false;
            } else {
                try {
                    JSON.parse(content);
                    valid = true;
                } catch (com.mongodb.util.JSONParseException e) {
                    valid = false;
                }
            }
            if (!valid) {
                String message = String.format(MessageTemplate.INVALID_FORMAT,
                        content);
                throw new InvalidFormatExceptioin(message);
            }
        }
        /**
         * just for test.
         *
         * @param args
         */
        public static void main(String[] args) {
            String[][] data = { { "info", "{‘name‘:‘ljl‘,‘age‘:26}" },
                    { "INFO", "trouble is a friend." },
                    { "JOKE", "{‘message‘:‘I am feeling down.‘}" } };
            ISALog ilog = new SALogImpl();
            for (String[] dat : data) {
                String grade = dat[0];
                String content = dat[1];
                try {
                    ilog.log(grade, content);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
  6. InvalidGradeException.java

    package cn.sinobest.asj.log.exception;
    /**
     * 表示无效的日志等级.
     * @author lijinlong
     *
     */
    public class InvalidGradeException extends Exception {
        private static final long serialVersionUID = 1341726127995938030L;
        public InvalidGradeException(String message) {
            super(message);
        }
    }
  7. InvalidFormatExceptioin.java

    package cn.sinobest.asj.log.exception;
    /**
     * 表示无效的日志等级.
     * @author lijinlong
     *
     */
    public class InvalidGradeException extends Exception {
        private static final long serialVersionUID = 1341726127995938030L;
        public InvalidGradeException(String message) {
            super(message);
        }
    }
  8. ValidGrade.java

    package cn.sinobest.asj.log.util;
    /**
     * 有效的日志等级.
     *
     * @author lijinlong
     *
     */
    public enum ValidGrade {
        DEBUG, INFO, WARN, ERROR;
        /** 有效日志等级的枚举内容 */
        private static String enumContent;
        /**
         * 获取所有有效的日志等级.
         *
         * @return
         */
        public static String getEnumContent() {
            if (enumContent != null && !enumContent.isEmpty())
                return enumContent;
            ValidGrade[] vgs = ValidGrade.values();
            StringBuilder builder = new StringBuilder(30);
            for (ValidGrade vg : vgs) {
                builder.append(vg).append(",");
            }
            builder.delete(builder.length() - 1, builder.length());
            enumContent = builder.toString();
            return enumContent;
        }
    
        /**
         * 判断日志等级是否有效.
         * @param grade 日志等级 - 忽略大小写.
         * @return
         */
        public static boolean isValid(String grade) {
            if (grade == null || grade.isEmpty())
                return false;
    
            boolean result = false;
    
            final String GRADE = grade.toUpperCase();
            ValidGrade[] vgs = ValidGrade.values();
            for (ValidGrade vg : vgs) {
                if (vg.toString().equals(GRADE)) {
                    result = true;
                    break;
                }
            }
    
            return result;
        }
    
        /**
         * just for test.
         * @param args
         */
        public static void main(String[] args) {
            String content = getEnumContent();
            System.out.println(content);
    
            String[] testGrade = {"DEBUG", "INFO", "WARN", "ERROR", "TEST"};
            for (String tg : testGrade) {
                if (!ValidGrade.isValid(tg)) {
                    String message = String.format("%s is invalid.", tg);
                    System.out.println(message);
                }
            }
        }
    }
  9. MessageTemplate.java

    package cn.sinobest.asj.log.util;
    /**
     * 消息模板.
     * @author lijinlong
     *
     */
    public class MessageTemplate {
        /** 无效的消息等级 */
        public static final String INVALID_GRADE = "无效的日志等级[%s]。服务支持的日志等级有:%s。";
    
        /** 无效的消息内容格式 */
        public static final String INVALID_FORMAT = "无效的日志内容格式:\n%s\n,请检查是否为JSON格式的字符串。";
    }
  10. sun-jaxws.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
        version="2.0">
        <endpoint name="defaultLog" implementation="cn.sinobest.asj.log.SALogImpl"
            url-pattern="/log.action" />
    </endpoints>

应用系统群


1.需要考虑的问题

1.1 拦截

使用Filter可以实现拦截。

1.2 组织日志内容

视需求而定,当前仅对request中的部分信息进行了采集。

1.3 格式化

日志信息需要格式化为JSON字符串,才能正确的写到MongoDB。

1.4 请求webservice服务

参考:基于wsimport生成代码的客户端

- 外网参考:基于wsimport生成代码的客户端

2. demo

2.1 文件结构图

src
 |---- cn.sinobest.asj.log
           |----  LogFilter.java
 |---- cn.sinobest.asj.log.wsimport # 存放wsimport生成的代码
           # 省略
basic
 |---- WEB-INF
           |---- web.xml

2.2 文件内容

  1. LogFilter.java

    package cn.sinobest.asj.log;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.json.JSONObject;
    import cn.sinobest.asj.log.wsimport.ISALog;
    import cn.sinobest.asj.log.wsimport.InvalidFormatExceptioin_Exception;
    import cn.sinobest.asj.log.wsimport.InvalidGradeException_Exception;
    import cn.sinobest.asj.log.wsimport.SALogImplService;
    public class LogFilter implements Filter {
        static final Log log = LogFactory.getLog(LogFilter.class);
        static final String WSDL_LOCATION = "http://localhost:8080/logserv/log.action?wsdl";
        @Override
        public void destroy() {
        }
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            try {
                log(request);
            } catch (InvalidFormatExceptioin_Exception e) {
                e.printStackTrace();
            } catch (InvalidGradeException_Exception e) {
                e.printStackTrace();
            } finally {
                chain.doFilter(request, response);
            }
        }
        private void log(ServletRequest request) throws MalformedURLException,
                InvalidFormatExceptioin_Exception, InvalidGradeException_Exception {
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("appid", "zfba");
            data.put("time", new Date());
            data.put("localAddr", request.getLocalAddr());
            data.put("localName", request.getLocalName());
            data.put("localPort", request.getLocalPort());
            data.put("remoteAddr", request.getRemoteAddr());
            data.put("remoteHost", request.getRemoteHost());
            data.put("remotePort", request.getRemotePort());
            // data.put("serverName", request.getServerName());
            // data.put("serverPort", request.getServerPort());
            HttpServletRequest hrequest = (HttpServletRequest) request;
            data.put("pathInfo", hrequest.getPathInfo());
            data.put("pathTranslated", hrequest.getPathTranslated());
            data.put("remoteUser", hrequest.getRemoteUser());
            data.put("requestURI", hrequest.getRequestURI());
            data.put("requestURL", hrequest.getRequestURL());
            data.put("servletPath", hrequest.getServletPath());
            JSONObject cont = new JSONObject(data);
            URL url = new URL(WSDL_LOCATION);
            SALogImplService ss = new SALogImplService(url);
            ISALog service = ss.getSALogImplPort();
            service.log("info", cont.toString());
        }
        @Override
        public void init(FilterConfig arg0) throws ServletException {
        }
    }
  2. web.xml
    这里仅贴出新增的内容:

        <!-- 测试日志 -->
        <filter>
            <filter-name>log-filter</filter-name>
            <filter-class>cn.sinobest.asj.log.LogFilter</filter-class>
        </filter>
        <!-- 测试日志 -->
        <filter-mapping>
            <filter-name>log-filter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

测试


  1. 启动MongoDB
    参考《Flume学习应用:Java写日志数据到MongoDB》
  2. 配置并启动Flume
    参考《Flume学习应用:Java写日志数据到MongoDB》
  3. 启动日志服务
    参考《在web项目中发布jaxws》
  4. 启动应用系统,并进行访问
  5. 查看MongoDB数据库
    参考《Flume学习应用:Java写日志数据到MongoDB》

附录


相关文章

  1. Flume学习应用:Java写日志数据到MongoDB
    博客园:Flume学习应用:Java写日志数据到MongoDB
  2. 在web项目中发布jaxws
    博客园:在web项目中发布jaxws
  3. 基于wsimport生成代码的客户端博客园:基于wsimport生成代码的客户端
时间: 2024-10-30 15:37:37

应用系统日志采集解决方案的相关文章

网络分流器系列之网络分流器解决方案

网络分流器系列之网络分流器解决方案声明:此方案为湖南戎腾网络公司独有方案,仅供参考, 有需要的朋友可以联系湖南戎腾公司销售总监:18774055368 [微信同号] 湖南戎腾公司为专业的方案解决商,分流器供应该研发商!固网前端解决方案1.引言随着互联网络在文化领域的运用,互联网上的文化扩张和文化霸权将会成为一个新的问题.如何在网络时代保持传统文化,维护本国文化的个性,将会具有越来越重要的意义.在国内,伴随着网络信息总量的不断攀升,网上出现大量以声.图.文形式传播的暴力.反动.迷信.×××.×××

Linux(RadHat)基础学习—系统日志管理

系统日志管理 1.系统日志 系统日志是记录系统中硬件.软件和系统问题的信息,同时还可以监视系统中发生的事件.用户可以通过它来检查错误发生的原因,或者寻找受到×××时×××者留下的痕迹.系统日志包括系统日志.应用程序日志和安全日志. 2.系统日志默认分类 /var/log/messages 系统服务的日志,包括服务的信息,报错等等 /var/log/secure 系统登陆认证信息日志 /var/log/maillog 系统邮件服务信息日志 /var/log/cron 系统定时任务信息日志 /var

.NET开源分布式日志框架ExceptionLess实战演练(公开版)

一.课程介绍 在以前,我们做日志收集大多使用 Log4net,Nlog 等框架,在应用程序变得复杂并且集群的时候,可能传统的方式已经不是很好的适用了,因为收集各个日志并且分析他们将变得麻烦而且浪费时间.相信大家的项目中日志功能已经做为基础设施里必不可少的一部分了,日志记录不仅可以更好的记录用户行为,还可以记录系统运行日志,从而看到判断系统运行的健壮性.了解决实时日志监控问题,ELK提供的一套的解决方案就应运而生了 ,作为NET技术的我们,开源的轻量级分布式ExceptionLess 日志框架或许

Net分布式系统之七:日志采集系统(1)(转)

http://www.cnblogs.com/Andon_liu/p/7508107.html 日志对大型应用系统或者平台尤其重要,系统日志采集.分析是系统运维.维护及用户分析的基础. 一.系统日志分类 一般系统日志可分为三大类: 1.用户行为日志:通过采集系统用户使用系统过程中,一系列的操作日志. 2.业务变更日志:特定业务场景需要,采集某用户在某时使用某功能,对某业务(对象.数据)进行某操作,由A变成B. 3.系统运行日志:系统运行服务器资源.网络及基础中间件的情况进行定时采集日志分析. 二

Net分布式系统之七:日志采集系统(1)

日志对大型应用系统或者平台尤其重要,系统日志采集.分析是系统运维.维护及用户分析的基础. 一.系统日志分类 一般系统日志可分为三大类: 1.用户行为日志:通过采集系统用户使用系统过程中,一系列的操作日志. 2.业务变更日志:特定业务场景需要,采集某用户在某时使用某功能,对某业务(对象.数据)进行某操作,由A变成B. 3.系统运行日志:系统运行服务器资源.网络及基础中间件的情况进行定时采集日志分析. 二.常见日志分析应用场景 日志采集分析是由需求驱动,根据某种场景的需要日志采集,采集的日志进行针对

大数据 --&gt; 大数据关键技术

大数据关键技术 大数据环境下数据来源非常丰富且数据类型多样,存储和分析挖掘的数据量庞大,对数据展现的要求较高,并且很看重数据处理的高效性和可用性. 传统数据处理方法的不足 传统的数据采集来源单一,且存储.管理和分析数据量也相对较小,大多采用关系型数据库和并行数据仓库即可处理.对依靠并行计算提升数据处理速度方面而言,传统的并行数据库技术追求高度一致性和容错性,根据CAP理论,难以保证其可用性和扩展性. 传统的数据处理方法是以处理器为中心,而大数据环境下,需要采取以数据为中心的模式,减少数据移动带来

LTE230方案示意图

普天LTE230产品及解决方案 责任编辑:耿鹏飞 2016.12.26 16:42 来源:通信世界网 LTE230 普天 一.LTE230系统特点 (一)系统背景 随着社会的快速发展,能源危机.环境恶化等问题日益突出.为保证社会的可持续发展.确保能源安全,国家积极推动新能源开发.清洁能源替代.能源互联网等战略.在能源领域开展信息化和精细化管理,确保能源供应安全及能源领域信息安全. 近年来通信技术的快速发展为"能源互联网+"和行业通信提供了技术上可行性,由于经济效益等原因近二十年来无线通

网络分流器|智能网卡作用于服务器降低负载

网络分流器|智能网卡|服务器降负载戎腾网络分流器|智能网卡主要用于降低分析服务器的负载,降低服务器的负载有以下三种技术途径: (1) 提高报文到达服务器多核中每个核的性能及流量分配的均衡能力: (2) 通过多种过滤机制在网卡上衰减无关流量: (3) 在执行应用层分析时,通过将会话层还原(俗称拼包)过程下移(Offload)到智能网卡上,通过智能网卡与CPU的并行计算提高整体应用层分析性能.戎腾的智能网卡包括基于定制硬件的PCAP和基于多核平台的SMA两类.前者用于报文过滤与捕获,同时解决10G

网络分流器|智能网卡|服务器降负载

网络分流器|智能网卡|服务器降负载戎腾网络分流器|智能网卡主要用于降低分析服务器的负载,降低服务器的负载有以下三种技术途径:(1) 提高报文到达服务器多核中每个核的性能及流量分配的均衡能力:(2) 通过多种过滤机制在网卡上衰减无关流量:(3) 在执行应用层分析时,通过将会话层还原(俗称拼包)过程下移(Offload)到智能网卡上,通过智能网卡与CPU的并行计算提高整体应用层分析性能.戎腾的智能网卡包括基于定制硬件的PCAP和基于多核平台的SMA两类.前者用于报文过滤与捕获,同时解决10G POS