对java、scala等运行于jvm的程序进行实时日志采集、索引和可视化,对系统进行进程级别的监控,对系统内部的操作进行策略性的报警、对分布式的rpc调用进行trace跟踪以便于进行性能分析
- APP: 接入skyeye-client的系统会通过kafkaAppender向kafka写入日志
- es-indexer-group: kafka的es消费组,读取kafka的数据并批量bulk到es
- monitor-group: kafka的监控消费组,app在日志中进行各种event埋点(如:第三方异常报警、请求耗时异常报警等)
- business-group: kafka的业务消费组
- trace-group: 通过日志进行rpc调用trace跟踪(dapper论文)
- es: 日志存储db,并建立相关索引
- zookeeper: app注册中心
- monitor: 监控中心,监听zookeeper注册中心中相应的节点变化进行监控报警
- rabbitmq: 监控报警缓冲队列
- alert: 具体报警手段,包括邮件和微信
日志生产系统,包括应用系统的日志入队列、埋点、应用系统注册等
- 自定义一些log框架的appender,包含logback和log4j
- 向注册中心注册应用
- 应用埋点进行监控报警
- rpc trace数据产生器
每个项目都需要修改gradle文件中的私服地址(这样才能打包deploy到自己的本地私服)
本项目没有具体的业务逻辑,主要是各个模块通用的类定义,如:常量、dto、dapper相关、公用util,所以该项目无需部署,只需要打包。
cd skyeye-base
gradle clean install uploadArchives
本项目主要是提供给对接的项目使用,包含了log4j和logback的自定义appender和项目注册相关,所以该项目无需部署,只需要打包提供给对接方对接。
cd skyeye-client
gradle clean install uploadArchives
本项目主要是用来提供和数据操作相关的中间件,具体分为以下5个子modoule。本项目无需部署,只需要打包。
cd skyeye-data
gradle clean install uploadArchives
该项目主要是自定义的spring-boot的dubbox starter,为spring-boot相关的项目使用dubbox提供简易的方式并集成spring-boot的auto configuration,见我的另一个开源项目:spring-boot-starter-dubbox
该项目主要是自定义的spring-boot的hbase starter,为hbase的query和更新等操作提供简易的api并集成spring-boot的auto configuration,见我的另一个开源项目:spring-boot-starter-hbase
该项目主要使用连接池简单封装了http的请求,如果项目中使用的spring版本较高可以使用RestTemplate代替。
该项目主要是jpa相关的定义,包含domain、repository、dto相关的定义,主要用来操作mysql的查询。
该项目主要封装了报警模块中存取rabbitmq中消息的相关代码。
该项目封装了所有rpc trace相关的代码,包含rpc数据采集器、分布式唯一ID生成、分布式递增ID生成、注册中心、采样器、跟踪器等功能,该项目无需部署,只需要打包。
由于使用dubbox,为了能够采集到dubbox里面的rpc数据,需要修改dubbox的源码,见我修改的dubbox项目:dubbox,该项目主要实现了rpc跟踪的具体实现,需要单独打包。
git clone https://github.com/JThink/dubbox.git
cd dubbox
git checkout skyeye-trace
修改相关pom中的私服地址
mvn clean install deploy -Dmaven.test.skip=true
如果软件版本和以下所列不一致,需要修改gradle中的依赖版本,并且需自行测试可用性(hadoop、hbase、spark等相应的版本可以自己来指定,代码层面无需修改,需要修改依赖)。
软件名 | 版本 | 备注 |
---|---|---|
mysql | 5.5+ | |
elasticsearch | 2.3.3 | 未测试5.x版本(开发的时候最新版本只有2.3.x),需要假设sql引擎,见: elasticsearch-sql,需要安装IK分词并启动,见: es ik分词 |
kafka | 0.10.0.1 | 如果spark的版本较低,那么需要将kafka的日志的格式降低,具体在kafka的配置项加入:log.message.format.version=0.8.2,该项按需配置 |
jdk | 1.7+ | |
zookeeper | 3.4.6 | |
rabbitmq | 3.5.7 | |
hbase | 1.0.0-cdh5.4.0 | 不支持1.x以下的版本,比如0.9x.x |
gradle | 3.0 | |
hadoop | 2.6.0-cdh5.4.0 | |
spark | 1.3.0-cdh5.4.0 | |
redis | 3.x | 单机版即可 |
mysql -uroot -p
source skyeye-data/skyeye-data-jpa/src/main/resources/sql/init.sql
创建三张表,用来保存rpc的数据(一张数据表,两张二级索引表)
hbase shell
执行skyeye-collector/src/main/resources/shell/hbase/hbase这个文件里面的内容
首先安装相应的es python的module,然后再创建索引,根据需要修改es的的ip、端口
cd skyeye-collector/src/main/resources/shell/es/
./install.sh
cd app-log
bash start.sh app-log http://192.168.xx.xx:9200,http://192.168.xx.xx:9200,......
cd event-log
bash start.sh event-log http://192.168.xx.xx:9200,http://192.168.xx.xx:9200,......
注意点:如果es版本为5.x,那么需要修改skyeye-collector/src/main/resources/shell/es/app-log/create-index.py的49和50行为下面内容:
'messageSmart': { 'type': 'text', 'analyzer': 'ik_smart', 'search_analyzer': 'ik_smart', 'include_in_all': 'true', 'boost': 8},
'messageMax': { 'type': 'text', 'analyzer': 'ik_max_word', 'search_analyzer': 'ik_max_word', 'include_in_all': 'true', 'boost': 8}
创建相应的topic,根据需要修改—partitions和zk的ip、端口的值,如果日志量特别大可以适当提高这个值
kafka-topics.sh --create --zookeeper 192.168.xx.xx:2181,192.168.xx.xx:2181,192.168.xx.xx:2181/kafka/0.10.0.1 --replication-factor 3 --partitions 9 --topic app-log
初始化注册中心的节点信息
./zkCli.sh
执行skyeye-monitor/src/main/resources/shell/zk这个文件里面的内容
相关项目启动的时候会自动创建相关的队列
配置文件外部化,需要在机器上创建配置文件
ssh 到部署节点
mkdir -p /opt/jthink/jthink-config/skyeye/alarm
vim alarm.properties
# log_mailer request queue
rabbit.request.addresses=localhost:5672
rabbit.request.username=jthink
rabbit.request.password=jthink
rabbit.request.vhost=/dev
rabbit.request.channelCacheSize=50
rabbit.request.queue=log_mailer
rabbit.request.exchange=direct.log
rabbit.request.routingKey=log.key
# mail
mail.jthink.smtphost=smtp.xxx.com
mail.jthink.port=25
mail.jthink.from=xxx@xxx.com
mail.jthink.cc=xxx@xxx.com
mail.jthink.password=jthink_0926
需要修改rabbitmq和邮件相关的配置
cd skyeye-alarm
gradle clean distZip -x test
cd target/distributions
unzip skyeye-alarm-x.x.x.zip(替换相应的x为自己的版本)
cd skyeye-alarm-x.x.x
nohup bin/skyeye-alarm &
配置文件外部化,需要在机器上创建配置文件,根据对接系统的个数和产生日志的量进行部署,最好部署3个节点(每个节点消费3个partition的数据)
ssh 到部署节点
mkdir -p /opt/jthink/jthink-config/skyeye/collector
vim collector.properties
# kafka config
kafka.brokers=riot01:9092,riot02:9092,riot03:9092
kafka.topic=app-log
kafka.group.indexer=es-indexer-consume-group
kafka.poll.timeout=100
kafka.group.collect=info-collect-consume-group
kafka.group.backup=log-backup-consume-group
kafka.group.rpc.trace=rpc-trace-consume-group
kafka.hdfs.file.root=/tmp/monitor-center/
kafka.hdfs.file.server.id=0 # 如果部署多个节点,第一个节点值为0,第二个节点就是1,第三个节点是2,以此类推
# es config
es.ips=riot01,riot02,riot03
es.cluster=mondeo # 需要修改成搭建es的时候那个值
es.port=9300
es.sniff=true
es.index=app-log
es.doc=log
es.index.event=event-log
es.doc.event=log
# redis config
redis.host=localhost
redis.port=6379
redis.password=
# mysql config
database.address=localhost:3306
database.name=monitor-center
database.username=root
database.password=root
# log_mailer request queue
rabbit.request.addresses=localhost:5672
rabbit.request.username=jthink
rabbit.request.password=jthink
rabbit.request.vhost=/dev
rabbit.request.channelCacheSize=50
rabbit.request.queue=log_mailer
rabbit.request.exchange=direct.log
rabbit.request.routingKey=log.key
# zk
zookeeper.zkServers=riot01:2181,riot02:2181,riot03:2181
zookeeper.sessionTimeout=60000
zookeeper.connectionTimeout=5000
# hdfs
hadoop.hdfs.namenode.port=8020
hadoop.hdfs.namenode.host=192.168.88.131
hadoop.hdfs.user=qianjicheng
hadoop.hdfs.baseDir=/user/qianjicheng/JThink/
upload.log.cron=0 30 0 * * ? # 按需修改,每天零点30分上传前一天的日志到hdfs,建议不改
# hbase config
hbase.quorum=panda-01
需要修改相关的配置,注释过的是要注意的,别的ip和端口根据需要进行修改(rabbitmq的配置需和alarm一致)
多个节点部署需要部署多次
cd skyeye-collector
gradle clean distZip -x test
cd target/distributions
unzip skyeye-collector-x.x.x.zip(替换相应的x为自己的版本)
cd skyeye-collector-x.x.x
nohup bin/skyeye-collector &
配置文件外部化,需要在机器上创建配置文件
ssh 到部署节点
mkdir -p /opt/jthink/jthink-config/skyeye/monitor
vim monitor.properties
# zk
zookeeper.zkServers=riot01:2181,riot02:2181,riot03:2181
zookeeper.sessionTimeout=60000
zookeeper.connectionTimeout=5000
zookeeper.baseSleepTimeMs=1000
zookeeper.maxRetries=3
# log_mailer request queue
rabbit.request.addresses=localhost:5672
rabbit.request.username=jthink
rabbit.request.password=jthink
rabbit.request.vhost=/dev
rabbit.request.channelCacheSize=50
rabbit.request.queue=log_mailer
rabbit.request.exchange=direct.log
rabbit.request.routingKey=log.key
# mysql config
database.address=localhost:3306
database.name=monitor-center
database.username=root
database.password=root
需要修改相关的配置(rabbitmq的配置需和alarm一致,zk也需要前后一致)
cd skyeye-monitor
gradle clean distZip -x test
cd target/distributions
unzip skyeye-monitor-x.x.x.zip(替换相应的x为自己的版本)
cd skyeye-monitor-x.x.x
nohup bin/skyeye-monitor &
配置文件外部化,需要在机器上创建配置文件
ssh 到部署节点
mkdir -p /opt/jthink/jthink-config/skyeye/web
vim web.properties
# server
serverAddress=0.0.0.0
serverPort=8090
# mysql config
database.address=localhost:3306
database.name=monitor-center
database.username=root
database.password=root
# es sql url
es.sql.url=http://riot01:9200/_sql?sql=
es.sql.sql=select * from app-log/log
es.query.delay=10
es.sql.index.event=event-log/log
# log_mailer request queue
rabbit.request.addresses=localhost:5672
rabbit.request.username=jthink
rabbit.request.password=jthink
rabbit.request.vhost=/dev
rabbit.request.channelCacheSize=50
rabbit.request.queue=log_mailer
rabbit.request.exchange=direct.log
rabbit.request.routingKey=log.key
# monitor
monitor.es.window=*/10 * * * * ? # 监控代码执行的周期,建议不修改
monitor.es.mail=leviqian@sina.com
monitor.es.interval=10 # 采集多久之前的数据进行分析(单位:分钟),建议按需修改
monitor.es.middlewareResponseTime=1000 # 中间件操作耗时大于多少(毫秒),建议根据实际报警情况来定,防止出现报警风暴(比如大于5秒,这个值就要设置5000)
monitor.es.middlewareThreshold=0.1 # 中间件的报警阈值(耗时大于 monitor.es.middlewareResponseTime 该值的比例大于该值),该值需要按照实际运行过程的情况不断得调节,防止出现报警风暴
monitor.es.apiResponseTime=1000
monitor.es.apiThreshold=0.1
monitor.es.thirdResponseTime=1000
monitor.es.thirdThreshold=0.1
需要修改相关的配置(rabbitmq的配置需和alarm一致,es也需要前后一致),注释过的是要注意的
cd skyeye-web
gradle clean distZip -x test
cd target/distributions
unzip skyeye-web-x.x.x.zip(替换相应的x为自己的版本)
cd skyeye-web-x.x.x
nohup bin/skyeye-web &
需要进行日志采集的项目需要按照如下操作
gradle或者pom中加入skyeye-client的依赖
compile ("skyeye:skyeye-client:0.0.1") {
exclude group: 'log4j', module: 'log4j'
}
在logback.xml中加入一个kafkaAppender,并在properties中配置好相关的值,如下:
<property name="APP_NAME" value="your-app-name" />
<!-- kafka appender -->
<appender name="kafkaAppender" class="com.jthink.skyeye.client.kafka.logback.KafkaAppender">
<encoder class="com.jthink.skyeye.client.kafka.logback.encoder.KafkaLayoutEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS};${CONTEXT_NAME};${HOSTNAME};%thread;%-5level;%logger{96};%line;%msg%n</pattern>
</layout>
</encoder>
<topic>${kafka.topic}</topic>
<zkServers>${zookeeper.servers}</zkServers>
<mail>${mail}</mail>
<keyBuilder class="com.jthink.skyeye.client.kafka.partitioner.AppHostKeyBuilder" />
<config>bootstrap.servers=${kafka.bootstrap.servers}</config>
<config>acks=0</config>
<config>linger.ms=100</config>
<config>max.block.ms=5000</config>
<config>client.id=${CONTEXT_NAME}-${HOSTNAME}-logback</config>
</appender>
gradle或者pom中加入skyeye-client的依赖
compile ("skyeye:skyeye-client:0.0.1") {
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
在log4j.xml中加入一个kafkaAppender
<appender name="kafkaAppender" class="com.jthink.skyeye.client.kafka.log4j.KafkaAppender">
<param name="topic" value="${kafka.topic}"/>
<param name="zkServers" value="${zookeeper.servers}"/>
<param name="app" value="${app.name}"/>
<param name="mail" value="${mail}"/>
<param name="bootstrapServers" value="${kafka.bootstrap.servers}"/>
<param name="acks" value="0"/>
<param name="maxBlockMs" value="5000"/>
<param name="lingerMs" value="100"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS};APP_NAME;HOSTNAME;%t;%p;%c;%L;%m%n"/>
</layout>
</appender>
- 目前公司很多项目采用的是spring-boot,版本为1.3.6.RELEASE,该版本自带logback版本为1.1.7,该版本结合kafka有bug,需要降低一个版本
- logback bug: logback bug, 1.1.8版本会fix
- 示例:
compile ("org.springframework.boot:spring-boot-starter") {
exclude group: 'ch.qos.logback', module: 'logback-classic'
exclude group: 'ch.qos.logback', module: 'logback-core'
}
compile "ch.qos.logback:logback-classic:1.1.6"
compile "ch.qos.logback:logback-core:1.1.6"
由于log4j本身的appender比较复杂难写,所以在稳定性和性能上没有logback支持得好,应用能使用logback请尽量使用logback
如果项目中有使用到zkClient、,统一使用自己打包的版本,以防日志收集出错或者异常(PS:zk必须为3.4.6版本,尽量使用gradle进行打包部署)
使用自己打包的dubbox(dubbox),在soa中间件dubbox中封装了rpc的跟踪
compile "com.101tec:zkclient:0.9.1-up"
compile ("com.alibaba:dubbo:2.8.4-skyeye-trace") {
exclude group: 'org.springframework', module: 'spring'
}
如果项目使用的是spring-boot+logback,那么需要将spring-boot对logback的初始化去掉,防止初始化的时候在zk注册两次而报错,具体见我的几篇博客就可以解决:
http://blog.csdn.net/jthink_/article/details/52513963
http://blog.csdn.net/jthink_/article/details/52613953
http://blog.csdn.net/jthink_/article/details/73106745
日志类型 | 说明 |
---|---|
normal | 正常入库日志 |
invoke_interface | api调用日志 |
middleware_opt | 中间件操作日志(目前仅支持hbase和mongo) |
job_execute | job执行日志 |
rpc_trace | rpc trace跟踪日志 |
custom_log | 自定义埋点日志 |
thirdparty_call | 第三方系统调用日志 |
LOGGER.info("我是测试日志打印")
// 参数依次为EventType(事件类型)、api、账号、请求耗时、成功还是失败、具体自定义的日志内容
LOGGER.info(ApiLog.buildApiLog(EventType.invoke_interface, "/app/status", "800001", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock api成功日志").toString());
LOGGER.info(ApiLog.buildApiLog(EventType.invoke_interface, "/app/status", "800001", 10, EventLog.MONITOR_STATUS_FAILED, "我是mock api失败日志").toString());
// 参数依次为EventType(事件类型)、MiddleWare(中间件名称)、操作耗时、成功还是失败、具体自定义的日志内容
LOGGER.info(EventLog.buildEventLog(EventType.middleware_opt, MiddleWare.HBASE.symbol(), 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock middle ware成功日志").toString());
LOGGER.info(EventLog.buildEventLog(EventType.middleware_opt, MiddleWare.MONGO.symbol(), 10, EventLog.MONITOR_STATUS_FAILED, "我是mock middle ware失败日志").toString());
// job执行仅仅处理失败的日志(成功的不做处理,所以只需要构造失败的日志), 参数依次为EventType(事件类型)、job 的id号、操作耗时、失败、具体自定义的日志内容
LOGGER.info(EventLog.buildEventLog(EventType.job_execute, "application_1477705439920_0544", 10, EventLog.MONITOR_STATUS_FAILED, "我是mock job exec失败日志").toString());
// 参数依次为EventType(事件类型)、第三方名称、操作耗时、成功还是失败、具体自定义的日志内容
LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "xx1", 100, EventLog.MONITOR_STATUS_FAILED, "我是mock third 失败日志").toString());
LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "xx1", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock third 成功日志").toString());
LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "xx2", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock third 成功日志").toString());
LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "xx2", 100, EventLog.MONITOR_STATUS_FAILED, "我是mock third 失败日志").toString());
- QQ群: 624054633
- Email: leviqian@sina.com
- blog: http://blog.csdn.net/jthink_