开发者

SpringBoot整合Graylog做日志收集实现过程

目录
  • 日志收集折腾过程
    • ELK
    • EFK
    • ELFK
    • 自己撸一个
  • Graylog
    • 环境搭建
    • 创建输入
    • Spring Boot整合Graylog
  • 总结

    日志收集折腾过程

    ELK

    之前整合过ELK做日志采集,就是Elasticsearch + Logstash + Kibana:

    • Elasticsearch:存储引擎,存放日志内容,利于全文检索
    • Logstash:数据传输管道,将日志内容传输到Elasticsearch,并且支持过滤内容,将内容格式化后再传输,可以满足绝大部分的应用场景
    • Kibana:开源的分析和可视化平台,在这里查看Elasticsearch中的数据

    对我来说ELK有点重,服务占用资源高,并且部署和维护有些复杂,我的个人服务器玩这个有点力不从心,所以一直有在寻找替代方案。

    EFK

    Elasticsearch + Filebeat + Kibana,用Filebeat替代Logstash做日志的收集,它是由golang开发,够轻量,占用资源少,如果没有过滤日志内容进行格式化的需求,用这个替代Logstash是很不错的选择。

    ELFK

    四个框架全用,网上看到有大佬这样用,应该是企业级别的部署,看着我就敬而远之,不敢玩。

    自己撸一个

    我对EFK的服务占用感到不满,于是自己用Golang写了一个轻量级工具,没有做采集、过滤,仅仅是从日志文件夹中grep出想要的内容,其实和手动grep没区别,不过可以用接口的方式查出想要的内容,而且极其轻量,这个工具我还用过好一段时间。

    我把EFK的搭建过程和手撸工具的过程写在了这里,感兴趣可以去看看。

    Spring Boot日志收集以及链路追踪

    Graylog

    最近我在折腾另一个日志收集方案,并且感觉不错,就是Graylog,它需要整合Mongo + Elasticsearch,它比较简单易用,提供网页端可视化页面,相当于Kibana,还支持日志报警。

    值得说明的是,它支持处理多行php日志,而在ELK中,多行日志需要用Logstash做一些格式化配置,这一点来说Graylog就做的很棒。

    至于为什么需要整合Mongo,是因为需要借助Mongo来保存一些Graylog的配置信息。

    环境搭建

    我喜欢用docker来搭建环境,所以如果你通过其他方式,可以到官网寻求答案

    首先拉取一下镜像:

    docker pull elasticsearch:7.12.0
    docker pull graylog/graylog:4.3.6
    docker pull mongo:4.2
    

    docker-compose.yml:

    version: '3'
    services:
      mongo:
        image: mongo:4.2
        container_name: mongo # graylog内默认连接名为mongo,所以这个不建议改
        restart: always
        volumes:
          - /home/mycontainers/mongo/data:/data/db # 路径映射
        ports:
          - 27017:27017
        network_mode: mynetwork # 设置网段
      elasticsearch:
        image: elasticsearch:7.12.0
        container_name: elasticsearch # graylog内默认连接名为elasticsearch,所以不建议改
        environment:
          - "TAKE_FILE_OWNERSHIP=true" # 挂载目录需要这个,不然没有权限
          - "discovery.type=single-node"    # 设置为单节点,集群就等进阶再说了
          - "ES_Java_OPTS=-Xms512m -Xmx512m"    # 分配堆大小
        volumes:
          - /home/mycontainers/es/data:/usr/share/elasticsearch/data
          - /home/mycontainers/es/logs:/usr/share/elasticsearch/logs
        ulimits: # 调整 ulimits 以及 nprocedit
          memlock:
            soft: -1
            hard: -1
        deploy:
          resources:
            limits:
              memory: 1g # 限制使用内存
        ports:
          - 9200:9200
          - 9300:9300
        network_mode: mynetwork
      graylog:
        image: gray开发者_开发教程log/graylog:4.3.6
        container_name: graylog
        environment:
          # echo -n "Enter Password: " && head -1 < /dev/stdin | tr -d '\n' | sha256sum | cut -d " " -f1
          - GRAYLOG_PASSWORD_SECRET=somepasswordpepper  # 用于密码加密加盐
          - GRAYLOG_ROOT_PASSWORD_SHA2=8c编程客栈6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 # 密码,默认是admin,可以用上面的echo命令生成自己的密码
          - GRAYLOG_HTTP_EXTERNAL_URI=http://127.0.0.1:9009/    # 对外开放的链接,注意端口,我改成了9009
        # volumes:
          # - /home/mycontainers/graylog/config/graylog.conf:/usr/share/graylog/data/config/graylog.conf
        network_mode: mynetwork
        restart: always
        depends_on:
          - mongo # 依赖于mongo和es两个环境
          - elasticsearch
        ports:
          - 9009:9000 # 端口映射
          # Syslog TCP
          - 1514:1514
          # Syslog UDP
          - 1514:1514/udp
          # GELF TCP
          - 12201:12201
          # GELF UDP
          - 12201:12201/udp
    

    执行运行命令:

    docker-compose -f docker-compose.yml up
    

    但有时候,我们在之前就已经部署好了Mongo和ES环境,所以不会在一个docker-compose文件中配置三个环境,我们把内容拆开如下:

    es.yml:

    version: '3'
    services:
      elasticsearch:
        image: elasticsearch:7.12.0
        container_name: elasticsearch # graylog内默认连接名为elasticsearch,所以不建议改
        environment:
          - "TAKE_FILE_OWNERSHIP=true"
          - "discovery.type=single-node"
          - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
        volumes:
          - /etc/localtime:/etc/localtime
          - /home/mycontainers/es/data:/usr/share/elasticsearch/data
          - /home/mycontainers/es/logs:/usr/share/elasticsearch/logs
        ujavascriptlimits: # 调整 ulimits 以及 nprocedit
          memlock:
            soft: -1
            hard: -1
        deploy:
          resources:
            limits:
              memory: 1g # 限制使用内存
        ports:
          - 9200:9200
          - 9300:9300
        network_mode: mynetwork
    

    mongo.yml:

    version: '3'
    services:
      mongo:
        image: mongo:4.2
        container_name: mongo # graylog内默认连接名为mongo,所以这个不建议改
        restart: always
        volumes:
          - /etc/localtime:/etc/localtime
          - /home/mycontainers/mongo/data:/data/db
        ports:
          - 27017:27017
        network_mode: mynetwork
    

    graylog.yml:

    version: '3'
    services:
      graylog:
        image: graylog/graylog:4.3.6
        container_name: graylog
        environment:
          # echo -n "Enter Password: " && head -1 < /dev/stdin | tr -d '\n' | sha256sum | cut -d " " -f1
          - GRAYLOG_PASSWORD_SECRET=somepasswordpepper
          - GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
          - GRAYLOG_HTTP_EXTERNAL_URI=http://127.0.0.1:9009/
          - GARYLOG_ELASTICSEARCH_HOSTS=http://elasticsearch:9200 # 链接es,这里是容器间通讯,所以写容器名
          - GRAYLOG_MongoDB_URI=mongodb://mongo:27017/graylog # 同上
        volumes:
          - /home/mycontainers/graylog/config/graylog.conf:/usr/share/graylog/data/config/graylog.conf # 指定配置文件,用于修改时区
        network_mode: mynetwork
        restart: always
        ports:
          - 9009:9000
          # Syslog TCP
          - 1514:1514
          # Syslog UDP
          - 1514:1514/udp
          # GELF TCP
          - 12201:12201
          # GELF UDP
          - 12201:12201/udp
    

    不同点在于手动配置mongo和es,还有多了一个配置文件映射,因为graylog默认UTC时区,我们的日志文件会相差8小时,所以在第一次启动成功graylog后,我们把它的配置文件拷贝出来,修改里面的root_timezone参数,再映射回去即可。

    创建输入

    现在我们可以访问Graylog了:http://xxx:9009

    来创建一个输入:

    SpringBoot整合Graylog做日志收集实现过程

    SpringBoot整合Graylog做日志收集实现过程

    SpringBoot整合Graylog做日志收集实现过程

    >

    勾选Global,Title随便写一个,其他不用改,保存即可,就能得到:

    SpringBoot整合Graylog做日志收集实现过程

    回到Search标签页,等日志文件输入即可。

    Spring Boot整合Graylog

    Maven依赖:

    <!--graylog日志依赖-->
    <dependency>
        <groupId>de.siegmar</groupId>
        <artifactId>logback-gelf</artifactId>
        <version>3.0.0</version>
    </dependency>
    

    然后是logback的配置,这个根据需要使用就好,在resource中:

    logback-spring.XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!--解决在项目目录中生成LOG_PATH_IS_UNDEFINED文件-->
        <property name="LOG_PATH" value="${LOG_PATH:-${java.io.tmpdir:-/logs}}"/>
        <!-- 引入SpringBoot的默认配置文件defaults.xml -->
        <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
        <!-- 引入SpringBoot中内置的控制台输出配置文件console-appender.xml -->
        <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
        <!-- 引入自定义的文件输出配置文件logback-spring-file-level.xml -->
        <include resource="logback-spring-file-level.xml"/>
        <!-- 设置root logger的级别为INFO,并将控制台输出和文件输出中的appender都添加到root logger下 -->
        <root level="INFO">
            <!--没有这行,控制台将不会有输出,完全由日志进行输出-->
            <appender-ref ref="Cwww.devze.comONSOLE"/>
            <appender-ref ref="INFO_FILE"/>
            <appender-ref ref="WARN_FILE"/>
            <appender-ref ref="ERROR_FILE"/>
            <appender-ref ref="GELF"/>
        </root>
        <!-- jmx可以动态管理logback配置-->
        <jmxConfigurator/>
    </configuration>
    

    logback-spring-file-level.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <included>
        <!-- 从配置文件中读取-->
        <springProperty scope="context" name="APP_NAME" source="graylog.appName"/>
        <springProperty scope="context" name="GRAYLOG_HOST" source="graylog.host"/>
        <springProperty scope="context" name="GRAYLOG_PORT" source="graylog.port"/>
        <!--INFO Level的日志-->
        <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!-- %i用来标记分割日志的序号 -->
                <fileNamePattern>${LOG_PATH}.INFO.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <!-- 单个日志文件最大maxFileSizeMB, 保存maxHistory天的历史日志, 所有日志文件最大totalSizeCapMB -->
                <!-- 经过试验,maxHistory是指指定天数内,而不是多少天-->
                <maxFileSize>50MB</maxFileSize>
                <maxHistory>15</maxHistory>
                <totalSizeCap>50MB</totalSizeCap>
            </rollingPolicy>
            <!-- 配置日志的级别过滤器,只保留INFO Level的日志-->
            <filter class="ch.qos.logback.classic.filter.LevelFiltphper">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 格式化输出-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{"yyyy-MM-dd HH:mm:ss.SSS"} %-5level -[%X{traceId}] - %msg%n</pattern>
            </encoder>
        </appender>
        <!--WARN Level的日志-->
        <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!-- %i用来标记分割日志的序号 -->
                <fileNamePattern>${LOG_PATH}.WARN.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <!-- 单个日志文件最大maxFileSizeMB, 保存maxHistory天的历史日志, 所有日志文件最大totalSizeCapMB -->
                <maxFileSize>50MB</maxFileSize>
                <maxHistory>15</maxHistory>
                <totalSizeCap>50MB</totalSizeCap>
            </rollingPolicy>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!--过滤级别-->
                <level>WARN</level>
                <!--onMatch:符合过滤级别的日志。ACCEPT:立即处理-->
                <onMatch>ACCEPT</onMatch>
                <!--onMismatch:不符合过滤级别的日志。DENY:立即抛弃-->
                <onMismatch>DENY</onMismatch>
            </filter>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{"yyyy-MM-dd HH:mm:ss.SSS"} %-5level -[%X{traceId}] - %msg%n</pattern>
            </encoder>
        </appender>
        <!--ERROR Level的日志-->
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!-- %i用来标记分割日志的序号 -->
                <fileNamePattern>${LOG_PATH}.ERROR.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <!-- 单个日志文件最大maxFileSizeMB, 保存maxHistory天的历史日志, 所有日志文件最大totalSizeCapMB -->
                <maxFileSize>50MB</maxFileSize>
                <maxHistory>15</maxHistory>
                <totalSizeCap>50MB</totalSizeCap>
                <!--<cleanHistoryOnStart>true</cleanHistoryOnStart>-->
            </rollingPolicy>
            <!--对指定级别的日志进行过滤-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!--过滤级别-->
                <level>ERROR</level>
                <!--onMatch:符合过滤级别的日志。ACCEPT:立即处理-->
                <onMatch>ACCEPT</onMatch>
                <!--onMismatch:不符合过滤级别的日志。DENY:立即抛弃-->
                <onMismatch>DENY</onMismatch>
            </filter>
            <!--日志输出格式-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{"yyyy-MM-dd HH:mm:ss.SSS"} %-5level - [%X{traceId}] - %msg%n</pattern>
            </encoder>
        </appender>
        <!--自定义日志-->
        <appender name="CUSTOM_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                <!-- %i用来标记分割日志的序号 -->
                <fileNamePattern>${LOG_PATH}.MYLOGGER.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <!-- 单个日志文件最大maxFileSizeMB, 保存maxHistory天的历史日志, 所有日志文件最大totalSizeCapMB -->
                <!-- 经过试验,maxHistory是指指定天数内,而不是多少天-->
                <maxFileSize>300MB</maxFileSize>
                <maxHistory>15</maxHistory>
                <totalSizeCap>300MB</totalSizeCap>
            </rollingPolicy>
            <!-- 配置日志的级别过滤器,只保留INFO Level的日志-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
            <!-- 格式化输出-->
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>%d{"yyyy-MM-dd HH:mm:ss.SSS"}\t%X{traceId}\t%msg%n</pattern>
            </encoder>
        </appender>
        <!--自定义日志日志不用绑定在root下,只记录指定输出-->
        <logger name="my_logger" additivity="false">
            <appender-ref ref= "CUSTOM_FILE"/>
        </logger>
        <appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
            <!--graylog服务地址-->
            <graylogHost>${GRAYLOG_HOST}</graylogHost>
            <!--连接端口-->
            <graylogPort>${GRAYLOG_PORT}</graylogPort>
            <encoder class="de.siegmar.logbackgelf.GelfEncoder">
                <originHost>${APP_NAME:-demo}</originHost>
                <!--发送日志级别名称,默认以数字代表日志级别-->
                <includeLevelName>true</includeLevelName>
            </encoder>
        </appender>
    </included>
    

    我们在logback中引用了配置文件的系统变量,所以在application.yml中要添加这一段,当然硬写进xml也可以:

    application.yml

    logging:
      file:
        path: _mylogs/${server.port}.logs # 日志保存路径
    graylog:
      host: mylocalhost # graylog服务host
      port: 12201   # graylog服务端口
      appName: graylogDemo  # 应用名,可填
    

    启动Spring应用,打印几条日志:

    @RestController
    public class TestController {
        private static final Logger log = LoggerFactory.getLogger(TestController.class);
        @GetMapping("/i")
        public void info() {
            log.info("info...................");
        }
        @GetMapping("/w")
        public void warn() {
            log.warn("warn...................");
        }
        @GetMapping("/e")
        public void error() {
    //        log.error("error...................");
            int i = 1/0;
    //        LogUtil.error("自定义异常");
        }
    }
    

    稍等片刻,顺利的话我们就能在Graylog中查看到刚刚输出的日志了,至此大功告成。

    SpringBoot整合Graylog做日志收集实现过程

    总结

    本篇文章只是说明了Graylog的一个入门使用,进阶的玩法可能要后面才有时间整理了。

    不管用什么方案做日志收集,我认为只要简单易用,稳定靠谱就好,ELK作为当前主流的日志收集框架,除了部署麻烦些,耗费资源高些之外并没有明显短板,所以ELK也好,Graylog也罢,只要适合自己就可以。

    以上就是SpringBoot整合Graylog做日志收集实现过程的详细内容,更多关于SpringBoot Graylog日志收集的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜