一、前言
Graylog 是一个强大的平台,基于 Scala 语言开发。使用它能很容易对结构化和非结构化日志进行管理以及调试应用程序。它依赖 Elasticsearch 和 MongoDB。Graylog 的主服务从客户端节点获取数据,同时还提供 Web 接口,方便用户可视化聚合来的日志。
二、部署图
最小化安装:
生产环境安装:
主要逻辑: Graylog Collector -> Graylog Server(封装Elasticsearch) -> Graylog Web
- Graylog Collector:用于收集日志,整合 Collector 把日志发给 Server;
- Graylog Server:封装 Elasticsearch 用于存储收集到的日志信息;
- Graylog Web:通过 Web 端来查看日志。
三、选型优势
- Graylog 一开始定位为强大的日志解决方案,而 ELK 则是大数据解决方案;
- Graylog 可以通过网络协议直接从应用程序接收结构化日志和标准 syslog。相反,ELK 是使用 Logstash 分析已收集的纯文本日志的解决方案,然后解析并将它们传递给 ElasticSearch;
- 相比 ELK 更优秀的报警功能;
- 更好的交互,通过跟踪 Graylog 收到的错误堆栈,支持多行日志,工程师可以快速源代码中的上下文;
- 强大的搜索功能,搜索语法比 ELK 简单,搜索结果支持高亮显示,支持 TB 级别的查询;
- 有归档功能,超过 30 天的所有内容都可以存储在廉价存储中,在出现查询需求时,可以重新导入到 Graylog;
- Python 库支持。
四、使用 Docker Compose 搭建
1、需要下载的 Docker 镜像
docker pull docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10
docker pull mongo:3
docker pull graylog/graylog:3.3
docker pull docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10
docker pull mongo:3
docker pull graylog/graylog:3.3
2、搭建前准备
Docker Compose是一个用于定义和运行多个docker容器应用的工具。使用Compose你可以用YAML文件来配置你的应用服务,然后使用一个命令,你就可以部署你配置的所有服务了。
2.1、下载 Docker Compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
2.2、修改该文件的权限为可执行
$chmod +x /usr/local/bin/docker-compose
$chmod +x /usr/local/bin/docker-compose
2.3、查看是否已经安装成功
$docker-compose --version
$docker-compose --version
3、开始搭建
3.1、使用 docker-compose.yml 脚本启动服务
docker-compose.yml
内容:
version: '2'
services:
# MongoDB: https://hub.docker.com/_/mongo/
mongodb:
image: mongo:3
# Elasticsearch: https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docker.html
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10
environment:
- http.host=0.0.0.0
- transport.host=localhost
- network.host=0.0.0.0
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
mem_limit: 1g
# Graylog: https://hub.docker.com/r/graylog/graylog/
graylog:
image: graylog/graylog:3.3
environment:
# 加密盐值,不设置,graylog会启动失败
# 该字段最少需要16个字符
- GRAYLOG_PASSWORD_SECRET=somepasswordpepper
# 设置用户名
- GRAYLOG_ROOT_USERNAME=admin
# 设置密码,此为密码进过SHA256加密后的字符串
# 加密方式,执行 echo -n "Enter Password: " && head -1 - GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918- GRAYLOG_HTTP_EXTERNAL_URI=http://127.16.106.233:9000/# 设置时区- GRAYLOG_ROOT_TIMEZONE=Asia/Shanghailinks:- mongodb:mongo- elasticsearchdepends_on:- mongodb- elasticsearchports:# Graylog web interface and REST API- 9000:9000# Syslog TCP- 1514:1514# Syslog UDP- 1514:1514/udp# GELF TCP- 12201:12201# GELF UDP- 12201:12201/udp
version: '2'
services:
# MongoDB: https://hub.docker.com/_/mongo/
mongodb:
image: mongo:3
# Elasticsearch: https://www.elastic.co/guide/en/elasticsearch/reference/6.6/docker.html
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10
environment:
- http.host=0.0.0.0
- transport.host=localhost
- network.host=0.0.0.0
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
mem_limit: 1g
# Graylog: https://hub.docker.com/r/graylog/graylog/
graylog:
image: graylog/graylog:3.3
environment:
# 加密盐值,不设置,graylog会启动失败
# 该字段最少需要16个字符
- GRAYLOG_PASSWORD_SECRET=somepasswordpepper
# 设置用户名
- GRAYLOG_ROOT_USERNAME=admin
# 设置密码,此为密码进过SHA256加密后的字符串
# 加密方式,执行 echo -n "Enter Password: " && head -1 - GRAYLOG_ROOT_PASSWORD_SHA2=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918- GRAYLOG_HTTP_EXTERNAL_URI=http://127.16.106.233:9000/# 设置时区- GRAYLOG_ROOT_TIMEZONE=Asia/Shanghailinks:- mongodb:mongo- elasticsearchdepends_on:- mongodb- elasticsearchports:# Graylog web interface and REST API- 9000:9000# Syslog TCP- 1514:1514# Syslog UDP- 1514:1514/udp# GELF TCP- 12201:12201# GELF UDP- 12201:12201/udp
上传到 linux 服务器并使用 docker-compose 命令运行:
docker-compose up -d
docker-compose up -d
注意:Elasticsearch 启动可能需要好几分钟,要耐心等待。
[root@graylog]# docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
graylog_elasticsearch_1 /usr/local/bin/docker-entr ... Up 9200/tcp, 9300/tcp
graylog_graylog_1 tini -- /docker-entrypoint ... Up (healthy) 0.0.0.0:12201->12201/tcp, 0.0.0.0:12201->12201/udp, 0.0.0.0:1514->1514/tcp, 0.0.0.0:1514->1514/udp, 0.0.0.0:9000->9000/tcp
graylog_mongodb_1 docker-entrypoint.sh mongod Up 27017/tcp
[root@centos-7-template graylog]#
[root@graylog]# docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
graylog_elasticsearch_1 /usr/local/bin/docker-entr ... Up 9200/tcp, 9300/tcp
graylog_graylog_1 tini -- /docker-entrypoint ... Up (healthy) 0.0.0.0:12201->12201/tcp, 0.0.0.0:12201->12201/udp, 0.0.0.0:1514->1514/tcp, 0.0.0.0:1514->1514/udp, 0.0.0.0:9000->9000/tcp
graylog_mongodb_1 docker-entrypoint.sh mongod Up 27017/tcp
[root@centos-7-template graylog]#
3.2、关闭防火墙
systemctl stop firewalld
systemctl stop firewalld
四、配置 graylog 控制台,接收日志来源
登录 graylog,打开浏览器访问:http://172.16.106.233:9000/
输入 docker-compose.yml
里配置的 用户名/密码
信息
设置来源信息:
第一步
第二步
第三步
第四步
五、框架集成
1、添加 logback-gelf 依赖
pom.xml
中添加:
<dependency>
<groupId>de.siegmargroupId>
<artifactId>logback-gelfartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>de.siegmargroupId>
<artifactId>logback-gelfartifactId>
<version>2.0.0version>
dependency>
2、添加配置文件 logback-spring.xml 让 logback 的日志输出到 graylog
注意 appender 节点下的 graylogHost 需要改成你自己的 服务地址,比如我的是:
172.16.106.233
。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<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{50}){cyan} %clr(:){faint} %file:%line - %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="GRAY_LOG_FULL_PATTERN"value="%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %file:%line%n%-5level: %msg%n"/>
<property name="GRAY_LOG_SHORT_PATTERN"value="%m%nopex"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>172.16.106.233graylogHost>
<graylogPort>12201graylogPort>
<maxChunkSize>508maxChunkSize>
<useCompression>trueuseCompression>
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
<includeRawMessage>trueincludeRawMessage>
<includeMarker>trueincludeMarker>
<includeMdcData>trueincludeMdcData>
<includeCallerData>falseincludeCallerData>
<includeRootCauseData>falseincludeRootCauseData>
<includeLevelName>trueincludeLevelName>
<shortPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${GRAY_LOG_SHORT_PATTERN}pattern>
shortPatternLayout>
<fullPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${GRAY_LOG_FULL_PATTERN}pattern>
fullPatternLayout>
<staticField>app_name:${APP_NAME}staticField>
<staticField>os_arch:${os.arch}staticField>
<staticField>os_name:${os.name}staticField>
<staticField>os_version:${os.version}staticField>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="GELF" />
root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<Logger name="com.zuozewei" level="DEBUG"/>
configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<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{50}){cyan} %clr(:){faint} %file:%line - %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="GRAY_LOG_FULL_PATTERN"value="%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %file:%line%n%-5level: %msg%n"/>
<property name="GRAY_LOG_SHORT_PATTERN"value="%m%nopex"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
<graylogHost>172.16.106.233graylogHost>
<graylogPort>12201graylogPort>
<maxChunkSize>508maxChunkSize>
<useCompression>trueuseCompression>
<encoder class="de.siegmar.logbackgelf.GelfEncoder">
<includeRawMessage>trueincludeRawMessage>
<includeMarker>trueincludeMarker>
<includeMdcData>trueincludeMdcData>
<includeCallerData>falseincludeCallerData>
<includeRootCauseData>falseincludeRootCauseData>
<includeLevelName>trueincludeLevelName>
<shortPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${GRAY_LOG_SHORT_PATTERN}pattern>
shortPatternLayout>
<fullPatternLayout class="ch.qos.logback.classic.PatternLayout">
<pattern>${GRAY_LOG_FULL_PATTERN}pattern>
fullPatternLayout>
<staticField>app_name:${APP_NAME}staticField>
<staticField>os_arch:${os.arch}staticField>
<staticField>os_name:${os.name}staticField>
<staticField>os_version:${os.version}staticField>
encoder>
appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="GELF" />
root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<Logger name="com.zuozewei" level="DEBUG"/>
configuration>
3、配置 application.yml
spring:
application:
name: graylog
server:
port: 8888
spring:
application:
name: graylog
server:
port: 8888
4、进行测试
添加一个测试类:
SpringBootTest
@Slf4j
public class TestCase extends AbstractTestNGSpringContextTests {
@Test
public void testDemo() {
log.info("输出info");
log.debug("输出debug");
log.error("输出error");
}
}
}
SpringBootTest
@Slf4j
public class TestCase extends AbstractTestNGSpringContextTests {
@Test
public void testDemo() {
log.info("输出info");
log.debug("输出debug");
log.error("输出error");
}
}
}
运行测试:
运行测试
六、Graylog 页面查看日志信息
日志总览
日志详情
七、总结
ELK 和 Graylog 两种解决方案在功能上非常相似,但仍有一些差异需要考虑。在 ELK 中,Kibana 扮演仪表盘的角色并显示从Logstash 收到的数据。Graylog 在这点上更方便,因为它提供了单一应用程序解决方案(不包括 ElasticSearch 作为灵活的数据存储),具有几乎相同的功能。因此,部署所需的时间更短。
此外,与 ELK 相比,Graylog 开箱即用,且具有出色的权限系统,而 Kibana 则不具备此功能。作为 Elasticsearch 的粉丝,我更喜欢 Graylog 而不是ELK,因为它完全符合我在测试框架日志管理方面的需求。
示例代码:
- https://github.com/zuozewei/blog-example/tree/master/Java-api-test/03-log-framework/springboot-logback-graylog-demo
参考资料:
[1]:https://docs.graylog.org/en/3.3/pages/architecture.html