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

1 资源

资源信息版本号备注
springboot2.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 日志采集框架(二):日志模块开发

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

第④篇:ELK 日志采集框架(四):Kafka安装与配置

第⑤篇:ELK 日志采集框架(五):Logstash安装与配置

第⑥篇:ELK 日志采集框架(六):ElasticSearch安装与配置

第⑦篇:ELK 日志采集框架(七):Kibana安装与配置

第⑧篇:ELK 日志采集框架(八):功能测试与告警集成

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小P聊技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值