美文网首页
ELK 日志采集框架(二):日志模块开发

ELK 日志采集框架(二):日志模块开发

作者: 小P聊技术 | 来源:发表于2021-05-02 13:00 被阅读0次

    1 资源

    资源信息 版本号 备注
    springboot 2.1.5.RELEASE

    springboot-elk-demo 源码 下载:https://download.csdn.net/download/qq_15769939/16756675

    2 配置信息

    2.1 pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <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/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.5.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.auskat.demo</groupId>
        <artifactId>springboot-elk-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <!--    排除spring-boot-starter-logging -->
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <!-- log4j2 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
            </dependency>
            <dependency>
                <groupId>com.lmax</groupId>
                <artifactId>disruptor</artifactId>
                <version>3.3.4</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.58</version>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>collector</finalName>
            <!-- 打包时包含properties、xml -->
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <!-- 是否替换资源中的属性-->
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>com.auskat.demo.elk.Application</mainClass>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

    2.2 配置信息

    2.2.1 application.yml

    # 设置访问路径
    server.servlet.context-path=/
    # 设置端口号
    server.port=8001
    # 设置应用名称
    spring.application.name=springboot-elk-demo
    # 设置http编码
    spring.http.encoding.charset=UTF-8
    # 设置时间格式标准
    spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
    # 设置时区
    spring.jackson.time-zone=GMT+8
    # 设置JSON默认格式化字段规则
    spring.jackson.default-property-inclusion=NON_NULL
    

    3.2.2 log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="INFO" schema="Log4J-V2.0.xsd" monitorInterval="600" >
        <Properties>
            <!-- 输出 日志文件路径 -->
            <Property name="LOG_HOME">logs</Property>
            <!-- 输出 日志文件名称 -->
            <property name="FILE_NAME">elk-demo</property>
            <!-- 输出 日志格式 -->
            <property name="patternLayout">[%d{yyyy-MM-dd'T'HH:mm:ss.SSSZZ}] [%level{length=5}] [%thread-%tid] [%logger] [%X{hostName}] [%X{ip}] [%X{applicationName}] [%F,%L,%C,%M] [%m] ## '%ex'%n</property>
        </Properties>
        <Appenders>
            <!-- 输出的组件 -->
            <Console name="CONSOLE" target="SYSTEM_OUT">
                <PatternLayout pattern="${patternLayout}"/>
            </Console>
            <!-- 输出 常规日志 -->
            <RollingRandomAccessFile name="appAppender" fileName="${LOG_HOME}/app-${FILE_NAME}.log" filePattern="${LOG_HOME}/app-${FILE_NAME}-%d{yyyy-MM-dd}-%i.log" >
                <PatternLayout pattern="${patternLayout}" />
                <Policies>
                    <TimeBasedTriggeringPolicy interval="1"/>
                    <SizeBasedTriggeringPolicy size="500MB"/>
                </Policies>
                <DefaultRolloverStrategy max="20"/>
            </RollingRandomAccessFile>
            <!-- 输出 错误日志 -->
            <RollingRandomAccessFile name="errorAppender" fileName="${LOG_HOME}/error-${FILE_NAME}.log" filePattern="${LOG_HOME}/error-${FILE_NAME}-%d{yyyy-MM-dd}-%i.log" >
                <PatternLayout pattern="${patternLayout}" />
                <!-- 允许 waring级别以上的日志收集  -->
                <Filters>
                    <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
                <Policies>
                    <TimeBasedTriggeringPolicy interval="1"/>
                    <SizeBasedTriggeringPolicy size="500MB"/>
                </Policies>
                <DefaultRolloverStrategy max="20"/>
            </RollingRandomAccessFile>
        </Appenders>
        <Loggers>
            <!-- 业务相关 异步logger -->
            <AsyncLogger name="com.auskat.*" level="info" includeLocation="true">
                <AppenderRef ref="appAppender"/>
            </AsyncLogger>
            <AsyncLogger name="com.auskat.*" level="info" includeLocation="true">
                <AppenderRef ref="errorAppender"/>
            </AsyncLogger>
            <Root level="info">
                <Appender-Ref ref="CONSOLE"/>
                <Appender-Ref ref="appAppender"/>
                <AppenderRef ref="errorAppender"/>
            </Root>
        </Loggers>
    </Configuration>
    

    3 功能实现

    3.1 messge对象

    package com.auskat.demo.elk.entity;
    
    import lombok.Data;
    
    /**
     * 类文件: AccurateWatcherMessage
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/28 0028
     * <p>
     * 时     间: 14:07
     * <p>
     */
    @Data
    public class AccurateWatcherMessage {
    
        /**
         * 标题
         */
        private String title;
    
        /**
         * 执行时间
         */
        private String executionTime;
    
        /**
         * 业务名称
         */
        private String applicationName;
    
        /**
         * 等级
         */
        private String level;
    
        /**
         * 内容
         */
        private String body;
    }
    

    3.2 工具类

    3.2.1 NetUtil

    package com.auskat.demo.elk.utils;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.RuntimeMXBean;
    import java.net.InetAddress;
    import java.net.NetworkInterface;
    import java.net.SocketAddress;
    import java.net.UnknownHostException;
    import java.nio.channels.SocketChannel;
    import java.util.Enumeration;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * 类文件: NetUtil
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/26 0026
     * <p>
     * 时     间: 14:14
     * <p>
     */
    public class NetUtil {
    
        public static String normalizeAddress(String address){
            String[] blocks = address.split("[:]");
            if(blocks.length > 2){
                throw new IllegalArgumentException(address + " is invalid");
            }
            String host = blocks[0];
            int port = 80;
            if(blocks.length > 1){
                port = Integer.valueOf(blocks[1]);
            } else {
                address += ":"+port; //use default 80
            }
            String serverAddr = String.format("%s:%d", host, port);
            return serverAddr;
        }
    
        public static String getLocalAddress(String address){
            String[] blocks = address.split("[:]");
            if(blocks.length != 2){
                throw new IllegalArgumentException(address + " is invalid address");
            }
            String host = blocks[0];
            int port = Integer.valueOf(blocks[1]);
    
            if("0.0.0.0".equals(host)){
                return String.format("%s:%d",NetUtil.getLocalIp(), port);
            }
            return address;
        }
    
        private static int matchedIndex(String ip, String[] prefix){
            for(int i=0; i<prefix.length; i++){
                String p = prefix[i];
                if("*".equals(p)){ //*, assumed to be IP
                    if(ip.startsWith("127.") ||
                            ip.startsWith("10.") ||
                            ip.startsWith("172.") ||
                            ip.startsWith("192.")){
                        continue;
                    }
                    return i;
                } else {
                    if(ip.startsWith(p)){
                        return i;
                    }
                }
            }
    
            return -1;
        }
    
        public static String getLocalIp(String ipPreference) {
            if(ipPreference == null){
                ipPreference = "*>10>172>192>127";
            }
            String[] prefix = ipPreference.split("[> ]+");
            try {
                Pattern pattern = Pattern.compile("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
                Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
                String matchedIp = null;
                int matchedIdx = -1;
                while (interfaces.hasMoreElements()) {
                    NetworkInterface ni = interfaces.nextElement();
                    Enumeration<InetAddress> en = ni.getInetAddresses();
                    while (en.hasMoreElements()) {
                        InetAddress addr = en.nextElement();
                        String ip = addr.getHostAddress();
                        Matcher matcher = pattern.matcher(ip);
                        if (matcher.matches()) {
                            int idx = matchedIndex(ip, prefix);
                            if(idx == -1) continue;
                            if(matchedIdx == -1){
                                matchedIdx = idx;
                                matchedIp = ip;
                            } else {
                                if(matchedIdx>idx){
                                    matchedIdx = idx;
                                    matchedIp = ip;
                                }
                            }
                        }
                    }
                }
                if(matchedIp != null) return matchedIp;
                return "127.0.0.1";
            } catch (Exception e) {
                return "127.0.0.1";
            }
        }
    
        public static String getLocalIp() {
            return getLocalIp("*>10>172>192>127");
        }
    
        public static String remoteAddress(SocketChannel channel){
            SocketAddress addr = channel.socket().getRemoteSocketAddress();
            String res = String.format("%s", addr);
            return res;
        }
    
        public static String localAddress(SocketChannel channel){
            SocketAddress addr = channel.socket().getLocalSocketAddress();
            String res = String.format("%s", addr);
            return addr==null? res: res.substring(1);
        }
    
        public static String getPid(){
            RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
            String name = runtime.getName();
            int index = name.indexOf("@");
            if (index != -1) {
                return name.substring(0, index);
            }
            return null;
        }
    
        public static String getLocalHostName() {
            try {
                return (InetAddress.getLocalHost()).getHostName();
            } catch (UnknownHostException uhe) {
                String host = uhe.getMessage();
                if (host != null) {
                    int colon = host.indexOf(':');
                    if (colon > 0) {
                        return host.substring(0, colon);
                    }
                }
                return "UnknownHost";
            }
        }
    }
    

    3.2.2 InputMDC

    package com.auskat.demo.elk.utils;
    
    import org.jboss.logging.MDC;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    
    /**
     * 类文件: InputMDC
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/26 0026
     * <p>
     * 时     间: 14:13
     * <p>
     */
    @Component
    public class InputMDC implements EnvironmentAware {
    
        private static Environment environment;
    
        @Override
        public void setEnvironment(Environment environment) {
            InputMDC.environment = environment;
        }
    
        public static void putMDC() {
            MDC.put("hostName", NetUtil.getLocalHostName());
            MDC.put("ip", NetUtil.getLocalIp());
            MDC.put("applicationName", environment.getProperty("spring.application.name"));
        }
    }
    

    3.2.3 FastJsonConverUtil

    package com.auskat.demo.elk.utils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 类文件: FastJsonConvertUtil
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/26 0026
     * <p>
     * 时     间: 14:13
     * <p>
     */
    @Slf4j
    public class FastJsonConvertUtil {
    
        private static final SerializerFeature[] featuresWithNullValue = {
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullBooleanAsFalse,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteNullStringAsEmpty
        };
    
        /**
         * <B>方法名称:</B>将JSON字符串转换为实体对象<BR>
         * <B>概要说明:</B>将JSON字符串转换为实体对象<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:53:49
         * @param data JSON字符串
         * @param clzss 转换对象
         * @return T
         */
        public static <T> T convertJSONToObject(String data, Class<T> clzss) {
            try {
                T t = JSON.parseObject(data, clzss);
                return t;
            } catch (Exception e) {
                log.error("convertJSONToObject Exception", e);
                return null;
            }
        }
    
        /**
         * <B>方法名称:</B>将JSONObject对象转换为实体对象<BR>
         * <B>概要说明:</B>将JSONObject对象转换为实体对象<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:54:32
         * @param data JSONObject对象
         * @param clzss 转换对象
         * @return T
         */
        public static <T> T convertJSONToObject(JSONObject data, Class<T> clzss) {
            try {
                T t = JSONObject.toJavaObject(data, clzss);
                return t;
            } catch (Exception e) {
                log.error("convertJSONToObject Exception", e);
                return null;
            }
        }
    
        /**
         * <B>方法名称:</B>将JSON字符串数组转为List集合对象<BR>
         * <B>概要说明:</B>将JSON字符串数组转为List集合对象<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:54:50
         * @param data JSON字符串数组
         * @param clzss 转换对象
         * @return List<T>集合对象
         */
        public static <T> List<T> convertJSONToArray(String data, Class<T> clzss) {
            try {
                List<T> t = JSON.parseArray(data, clzss);
                return t;
            } catch (Exception e) {
                log.error("convertJSONToArray Exception", e);
                return null;
            }
        }
    
        /**
         * <B>方法名称:</B>将List<JSONObject>转为List集合对象<BR>
         * <B>概要说明:</B>将List<JSONObject>转为List集合对象<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:55:11
         * @param data List<JSONObject>
         * @param clzss 转换对象
         * @return List<T>集合对象
         */
        public static <T> List<T> convertJSONToArray(List<JSONObject> data, Class<T> clzss) {
            try {
                List<T> t = new ArrayList<T>();
                for (JSONObject jsonObject : data) {
                    t.add(convertJSONToObject(jsonObject, clzss));
                }
                return t;
            } catch (Exception e) {
                log.error("convertJSONToArray Exception", e);
                return null;
            }
        }
    
        /**
         * <B>方法名称:</B>将对象转为JSON字符串<BR>
         * <B>概要说明:</B>将对象转为JSON字符串<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:55:41
         * @param obj 任意对象
         * @return JSON字符串
         */
        public static String convertObjectToJSON(Object obj) {
            try {
                String text = JSON.toJSONString(obj);
                return text;
            } catch (Exception e) {
                log.error("convertObjectToJSON Exception", e);
                return null;
            }
        }
    
        /**
         * <B>方法名称:</B>将对象转为JSONObject对象<BR>
         * <B>概要说明:</B>将对象转为JSONObject对象<BR>
         * @author hezhuo.bai
         * @since 2019年1月15日 下午4:55:55
         * @param obj 任意对象
         * @return JSONObject对象
         */
        public static JSONObject convertObjectToJSONObject(Object obj){
            try {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(obj);
                return jsonObject;
            } catch (Exception e) {
                log.error("convertObjectToJSONObject Exception", e);
                return null;
            }
        }
    
        public static String convertObjectToJSONWithNullValue(Object obj) {
            try {
                String text = JSON.toJSONString(obj, featuresWithNullValue);
                return text;
            } catch (Exception e) {
                log.error("convertObjectToJSONWithNullValue Exception", e);
                return null;
            }
        }
    }
    

    3.3 接口

    3.3.1 日志接口

    package com.auskat.demo.elk.controller;
    
    import com.auskat.demo.elk.utils.InputMDC;
    import org.jboss.logging.MDC;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 类文件: IndexController
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/26 0026
     * <p>
     * 时     间: 14:15
     * <p>
     */
    @Slf4j
    @RestController
    public class IndexController {
    
        /**
         * [%d{yyyy-MM-dd'T'HH:mm:ss.SSSZZ}]
         * [%level{length=5}]
         * [%thread-%tid]
         * [%logger]
         * [%X{hostName}]
         * [%X{ip}]
         * [%X{applicationName}]
         * [%F,%L,%C,%M]  F 当前执行类, L 行数, C class ,M method
         * [%m] ## '%ex'%n
         * -----------------------------------------------
         * [2019-09-18T14:42:51.451+08:00]
         * [INFO]
         * [main-1]
         * [org.springframework.boot.web.embedded.tomcat.TomcatWebServer]
         * []
         * []
         * []
         * [TomcatWebServer.java,90,org.springframework.boot.web.embedded.tomcat.TomcatWebServer,initialize]
         * [Tomcat initialized with port(s): 8001 (http)] ## ''
         *
         * ["message",
         * "\[%{NOTSPACE:currentDateTime}\]
         *  \[%{NOTSPACE:level}\]
         *  \[%{NOTSPACE:thread-id}\]
         *  \[%{NOTSPACE:class}\]
         *  \[%{DATA:hostName}\]
         *  \[%{DATA:ip}\]
         *  \[%{DATA:applicationName}\]
         *  \[%{DATA:location}\]
         *  \[%{DATA:messageInfo}\]
         *  ## (\'\'|%{QUOTEDSTRING:throwable})"]
         * @return
         */
        @RequestMapping(value = "/index")
        public String index() {
    
            // MDC 当前线程绑定的局部变量
            InputMDC.putMDC();
    
            log.info("我是一条info日志");
    
            log.warn("我是一条warn日志");
    
            log.error("我是一条error日志");
    
            return "idx";
        }
    
    
        @RequestMapping(value = "/err")
        public String err() {
            InputMDC.putMDC();
            try {
                int a = 1/0;
            } catch (Exception e) {
                log.error("算术异常", e);
            }
            return "err";
        }
    
    }
    

    3.3.2 告警接口

    package com.auskat.demo.elk.controller;
    
    import com.alibaba.fastjson.JSON;
    import com.auskat.demo.elk.entity.AccurateWatcherMessage;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 类文件: WatcherController
     * <p>
     * <p>
     * 类描述:
     * <p>
     * 作     者: AusKa_T
     * <p>
     * 日     期: 2021/3/28 0028
     * <p>
     * 时     间: 14:06
     * <p>
     */
    @RestController
    public class WatcherController {
    
        @RequestMapping(value ="/accurateWatch")
        public String watch(@RequestBody AccurateWatcherMessage accurateWatcherMessage) {
            String ret = JSON.toJSONString(accurateWatcherMessage);
            System.err.println("----告警内容----:" + ret);
            return "is watched" + ret;
        }
    }
    

    4 相关信息

    上一篇:ELK 日志采集框架(一):架构设计

    下一篇:ELK 日志采集框架(三):Filebeat安装与配置

    博文不易,辛苦各位猿友点个关注和赞,感谢

    相关文章

      网友评论

          本文标题:ELK 日志采集框架(二):日志模块开发

          本文链接:https://www.haomeiwen.com/subject/ffutdltx.html