ELK的灵活性、使用场景,在很多文章中都有介绍,包括官方文档。当你看到这篇文章时,我假设你已经知道你要用ELK解决什么问题。
其实,正是ELK提供的灵活性,特别是早期提供的傻瓜式用法(schemaless),让很多人认为使用ELK没有任何约束。有些弊端在数据量小的时候没有感知,有些弊端在非精细化使用时也不会注意到。
这篇文档主要想讨论:在大规模使用下,需要注意的一些基本约定,这些约定对管理和使用体验上的价值。
很多同学其实没有想过这个问题,或者没有站着数据分析的维度去想过这个问题。数据化运营已经是如今互联网公司最核心的能力之一。传统的公司,很多数据分析的数据都是来自数据库。而互联网公司,有大量的数据来自日志,量级可能是数据库的几十到上百倍。所以,从大的方面,日志至少分为两类:
1、业务日志
来自业务需求所做的埋点日志,这种日志类似数据库表,只是通过日志形式收集,提升效率。
2、研发运维日志
(1)研发同学分析系统性能指标等埋点日志
(2)研发同学诊断系统问题的Trace日志、各种开源软件等产生的系统日志
当把日志进行了分类之后,其实日志规范就很容易制定了,而且这样分也方便后续架构改进、容灾降级、故障评估等各种需求,因为毕竟业务日志是最重要的。
日志收集的架构大同小异,每个组件也会随着需求不断迭代和优化,ELK基本pipeline如下:

用户把日志打到磁盘上,我们通过logstash从磁盘把日志实时的收集放入kafka。在kafka中的日志,平台会根据用户需求选择性的同步到hive和elasticsearch。同步到Elasticsearch的日志,用户可以实时检索和分析。
存放规范
根据上面的日志分类,对于两类日志,我们按照不同的路径存放,具体存放路径如下:
我们会使用{topic_name} 作为kafka上的topic_name和hive上的table_name;
对于第三方系统日志,如mysql,mongodb,redis等,如果能做变更最好也按照研发运维日志规范存放,如果已经投产无法变更,目前只能做hardcode适配;
建议日志命名(topic_name)规则为:
{business_name}_{service_name}_{optional}
示例:
业务日志:bike_order_api
研发运维日志:bike_order_error
因为业务日志会动态增长,按照size切割并设置maxBackup可能因为日志量太大存在覆盖写问题。建议使用DailyRollingFileAppender,不限定单个文件大小。
预定义schema,统一使用json格式输出
分三种情况:
(1)应用程序主动打印的debug日志
必须遵循标准的规范日志格式,这样输出的日志易于通过字段过滤、聚合并追踪问题,需要stackTrace的只要把stack做format输出即可。格式如下:
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n其中msg部分必须json格式化输出
a. 看起来规范的个性化日志
2017-12-07 13:19:09,155 INFO org.springframework.web.servlet.handler.SimpleUrlHandlerMapping orderId: 1, userId:30; paid:1, exception: failed to ....
2017-12-07 13:19:09,155 INFO org.springframework.web.servlet.handler.SimpleUrlHandlerMapping orderId=1, userId=30, paid=1, exception: failed to ....b. 标准的规范日志
2017-12-07 13:19:09,155 INFO org.springframework.web.servlet.handler.SimpleUrlHandlerMapping {"orderId": 1, "userId":30, "payed":1, "exception":"failed to ....."}a和b的差别在于:
a 对人友好,但对机器不友好,机器没法自动化识别每个字段的提取规则。为了适配规则,需要花费额外的代价维护规则引擎,并且因为规则引擎没有明确约束,很容易因为用户改变的内容导致引擎失效。例子中,有些日志key,value用“=”号分割,有些用“:”分割,如果整条日志不做切割,对于userId=30这样的查询没法有效执行。
b 可以自解析,不管张三还是李四打的,都可以用一套解析逻辑解析出每个需要的字段,并写入Elasticsearch方便查询。比如:查询userId=30的订单链路;
(2)框架输出日志
这类日志可控性较差,可用的形式需要探索,但也要遵循标准的日志format格式,如有需要,收集的时候可以在头部加上机器hostname等信息
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n(3)第三方系统日志
目前只能hardcode适配解析,因为格式较为稳定,也不会变更schema。
安装ELK外部已经有各种资料,可以根据自身运维规范选择rpm还是tar包安装,重点讲下对应上面规范进行的配置和使用效果。
logstash配置
input {
file {
path => "/opt/logs/_data_/*/*.event"
type => "data"
}
file {
path => "/opt/logs/_logs_/*/*.log"
type => "logs"
}
file {
path => "/opt/logs/*-advertisement-*.log"
type => "advertisement"
codec => multiline {
pattern => "^20"
negate => true
what => previous
}
}
}
filter {
if [type] == "data" {
grok {
match => { "path" => "/opt/logs/_data_/(?<category>[a-zA-Z0-9._-]+)/*" }
}
}
if [type] == "logs" {
grok {
match => { "path" => "/opt/logs/_logs_/(?<category>[a-zA-Z0-9._-]+)/*" }
}
}
}
output {
if [type] == "data" {
kafka {
codec => plain {
format => "%{message}"
}
topic_id => "%{category}"
batch_size => 2000
buffer_memory => 335544320
bootstrap_servers => "xxxx:9092"
retries => 3
}
}
else if [type] == "logs" {
kafka {
codec => plain {
format => "%{message} ${HOSTNAME}"
}
topic_id => "log.%{category}"
batch_size => 2000
buffer_memory => 335544320
bootstrap_servers => "xxxxx:9092"
retries => 3
}
}
else {
kafka {
codec => plain {
format => "%{message}"
}
topic_id => "%{type}"
batch_size => 2000
buffer_memory => 335544320
bootstrap_servers => "xxxx:9092"
retries => 3
}
}
}配置中的path => "/opt/logs/*-advertisement-*.log",就是为了解决第三方系统的日志,大家也知道,如果所有的日志都没有规范路径,每增加一个日志将要变更一次配置,运维成本非常高。
kibana对比效果
(1)日志内容格式不规范,没有切割字段效果

(2)日志内容格式规范,切割字段效果

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31509949/viewspace-3165551/ ,如需转载,请注明出处,否则将追究法律责任。