日志在我们日常开发定位错误,链路错误排查时必不可少,如果我们只有一个服务,我们可以只简单的通过打印的日志文件进行排查定位就可以,但是在分布式服务环境下,多个环境的日志统一收集、展示则成为一个问题。目前主流的日志收集服务ELK,即便没用过肯定大家也肯定听说过,就是Elasticsearch + Logstash + Kibana:
ELK有点重,服务占用资源高,并且部署和维护有些复杂,所以后面又衍生出:EFK
Elasticsearch + Filebeat + Kibana,用Filebeat替代Logstash做日志的收集,它是由Golang开发,够轻量,占用资源少,如果没有过滤日志内容进行格式化的需求,用这个替代Logstash确实是很不错的选择。
但今天给大家分享一个更加轻量级别的ELK日志框架,就是GrayLog
Graylog整合方案是使用 Elasticsearch 来存储,使用 MongoDB 来缓存,并且还有带流量控制的(throttling),同时其界面查询简单易用且易于扩展。
Graylog 日志监控系统Graylog 是一个开源的日志聚合、分析、审计、展现和预警工具。在功能上来说,和 ELK 类似,但又比 ELK 要简单很多。
依靠着更加简洁,高效,部署使用简单的优势很快受到许多人的青睐。当然,在扩展性上面确实没有比 ELK 好,但是其有商业版本可以选择。
部署 Graylog 最简单的架构就是单机部署,复杂的也是部署集群模式,架构图示如下所示。
我们可以看到其中包含了三个组件,分别是 Elasticsearch、MongoDB 和 Graylog。
其中
配置 Graylog 服务的核心就是理解对应组件的功能以及其运作方式!
简单来讲,Input 表示日志数据的来源,对不同来源的日志可以通过 Extractors 来进行日志的字段转换,比如将 Nginx 的状态码变成对应的英文表述等。
然后,通过不同的标签类型分组成不用的 Stream,并将这些日志数据存储到指定的Index 库中进行持久化保存。
Graylog 中的核心服务组件如下图所示:
具体流程如下:
1.Graylog 通过 Input 搜集日志,每个 Input 单独配置 Extractors 用来做字段转换。
2.Graylog 中日志搜索的基本单位是 Stream,每个 Stream 可以有自己单独的 Elastic Index Set,也可以共享一个 Index Set。
3.Extractor 在 System/Input 中配置。Graylog 中很方便的一点就是可以加载一条日志,然后基于这个实际的例子进行配置并能直接看到结果。
4.内置的 Extractor 基本可以完成各种字段提取和转换的任务,但是也有些限制,在应用里写日志的时候就需要考虑到这些限制。Input 可以配置多个 Extractors,按照顺序依次执行。
5.系统会有一个默认的 Stream,所有日志默认都会保存到这个 Stream 中,除非匹配了某个 Stream,并且这个 Stream 里配置了不保存日志到默认 Stream。
6.可以通过菜单 Streams 创建更多的 Stream,新创建的 Stream 是暂停状态,需要在配置完成后手动启动。
7.Stream 通过配置条件匹配日志,满足条件的日志添加 stream ID 标识字段并保存到对应的 Elastic Index Set 中。
8.Index Set 通过菜单 System/Indices 创建。日志存储的性能,可靠性和过期策略都通过 Index Set 来配置。
9.性能和可靠性就是配置 Elastic Index 的一些参数,主要参数包括,Shards 和 Replicas。
除了上面提到的日志处理流程,Graylog 还提供了 Pipeline 脚本实现更灵活的日志处理方案。
这里不详细阐述,只介绍如果使用 Pipelines 来过滤不需要的日志。下面是丢弃 level > 6 的所有日志的 Pipeline Rule 的例子。
rule "discard debug messages"
when
to_long($message.level) > 6
then
drop_message();
end
从数据采集(input),字段解析(extractor),分流到 stream,再到 Pipeline 的清洗,一气呵成,无需在通过其他方式进行二次加工。
Sidecar 是一个轻量级的日志采集器,通过访问 Graylog 进行集中式管理,支持 Linux 和 windows 系统。
Sidecar 守护进程会定期访问 Graylog 的 REST API 接口获取 Sidecar 配置文件中定义的标签(tag),Sidecar 在首次运行时会从 Graylog 服务器拉取配置文件中指定标签(tag)的配置信息同步到本地。
目前 Sidecar 支持 NXLog,Filebeat 和 Winlogbeat。他们都通过 Graylog 中的 web 界面进行统一配置,支持 Beats、CEF、Gelf、Json API、NetFlow 等输出类型。
Graylog 最厉害的在于可以在配置文件中指定 Sidecar 把日志发送到哪个 Graylog 群集,并对 Graylog 群集中的多个 input 进行负载均衡,这样在遇到日志量非常庞大的时候,Graylog 也能应付自如。
日志集中保存到 Graylog 后就可以方便的使用搜索了。不过有时候还是需要对数据进行近一步的处理。
主要有两个途径:
更多grayLog的介绍可异步至:https://docs.graylog.org/docs
新建一个docker-compose-graylog.yml 的内容:(PS:上面的官方使用文档中就有,也可以跟着我的思路直接复制粘贴也可以)
version: '3'
services:
mongo:
image: mongo:4.2
networks:
- graylog
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
environment:
- http.host=0.0.0.0
- transport.host=localhost
- network.host=0.0.0.0
- "ES_JAVA_OPTS=-Dlog4j2.formatMsgNoLookups=true -Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
deploy:
resources:
limits:
memory: 1g
networks:
- graylog
graylog:
image: graylog/graylog:4.2
environment:
- GRAYLOG_PASSWORD_SECRET=somepasswordpepper
- GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
- GRAYLOG_HTTP_EXTERNAL_URI=http://127.0.0.1:9000/ # 这里注意要改ip
- GRAYLOG_TIMEZONE=Asia/Shanghai
- GRAYLOG_ROOT_TIMEZONE=Asia/Shanghai
entrypoint: /usr/bin/tini -- wait-for-it elasticsearch:9200 -- /docker-entrypoint.sh
networks:
- graylog
restart: always
depends_on:
- mongo
- elasticsearch
ports:
- 9000:9000
- 1514:1514
- 1514:1514/udp
- 12201:12201
- 12201:12201/udp
networks:
graylog:
driver: bridg
这个文件里唯一需要改动的就是 ip
嗯,写完 docker-compose-graylog.yml 文件,直接运行:
docker-compose -f docker-compose-graylog.yml up -d
即可
如果没有安装docker-compose的可以移步至此:Docker-Compose安装教程
如果出现以下错误:
failed to create network docker-compose_graylog: Error response from daemon: plugin "bridg" not found
可以先通过指令:
docker network create docker-compose_graylog
注意:docker-compose_graylog需要和你报错的network名称一致
然后再输入启动指令
docker-compose -f docker-compose-graylog.yml up -d
启动以后,我们就可以通过 ip:port(127.0.0.1:9000) 访问对应的Graylog后台地址了,默认的账号和密码是 admin/admin启动以后,我们就可以通过 ip:port 访问对应的Graylog后台地址了
随后,我们配置下 inputs 的配置,找到 GELF UDP ,然后点击 Launch new input ,只需要填写 Title 字段,保存就可以了(其他不用动)。
至此,gray-log配置完成
<dependency>
<groupId>de.siegmar</groupId>
<artifactId>logback-gelf</artifactId>
<version>3.0.0</version>
</dependency>
接着在项目的resources目录下,新建一个logback.xml文件,编辑文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- <property name="LOG_PATTERN"-->
<!-- value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) - %cyan(%msg%n)"/>-->
<!-- 定义日志存储的路径,不要配置相对路径 -->
<!-- <property name="FILE_PATH" value="C:/Users/NineSun/Desktop/log/identity-log.%d{yyyy-MM-dd}.%i.log"/>-->
<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr"
converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>127.0.0.1</graylogHost>
<graylogPort>12201</graylogPort>
<!-- 最大GELF数据块大小(单位:字节),508为建议最小值,最大值为65467 -->
<maxChunkSize>508</maxChunkSize>
<!-- 是否使用压缩 -->
<useCompression>true</useCompression>
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
<!-- 是否发送原生的日志信息 -->
<includeRawMessage>true</includeRawMessage>
<includeMarker>true</includeMarker>
<includeMdcData>true</includeMdcData>
<includeCallerData>true</includeCallerData>
<includeRootCauseData>true</includeRootCauseData>
<!-- 是否发送日志级别的名称,否则默认以数字代表日志级别 -->
<includeLevelName>true</includeLevelName>
<shortPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>%m%nopex</pattern>
</shortPatternLayout>
<fullPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d - [%thread] %-5level %logger{35} - %msg%n</pattern>
</fullPatternLayout>
<!-- 配置应用名称(服务名称),通过staticField标签可以自定义一些固定的日志字段 -->
<staticField>app_name:test</staticField>
</encoder>
</appender>
<!-- 控制台输出日志 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志级别过滤INFO以下 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<!-- 按照上面配置的LOG_PATTERN来打印日志 -->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="debug">
<appender-ref ref="GELF"/>
<appender-ref ref="console"/>
</root>
</configuration>
在appilcation.properties配置文件中设置日志配置文件
logging.config=classpath:logback.xml
在这个配置信息里,唯一要改的也只是 ip 的地址,重新启动项目,到这里接入就完毕了,我们再打开控制台,就能看到日志的信息啦。
由于报警与通知种类较多,后面会专出一篇文章来介绍,此处先跳过
这个过程也相对复杂一点,不在本文中赘述,防止影响篇幅,后面也会专门介绍,此处只简单介绍一下其功能
默认情况下是需要配置一个input 就可以自动与all-message streams绑定 做到开箱即用
但是我们有可能想对不同的项目,使用不同的streams 使用不同的索引管理日志,例如 nginx的日志保存3天 业务日志保存一个月
input streams index 的关系如下