logagent finished
parent
eba6d6569d
commit
bbb93ac5cb
|
@ -5,4 +5,4 @@ chan_max_size=100000
|
||||||
[etcd]
|
[etcd]
|
||||||
address=43.143.245.135:2379
|
address=43.143.245.135:2379
|
||||||
timeout=5
|
timeout=5
|
||||||
collect_log_key=/logagent/collect_log_config
|
collect_log_key=/logagent/%s/collect_log_config
|
13
etcd/etcd.go
13
etcd/etcd.go
|
@ -57,12 +57,23 @@ func GetConf(key string) (logEntryConf []*LogEntry, err error) {
|
||||||
|
|
||||||
//etcd的哨兵
|
//etcd的哨兵
|
||||||
|
|
||||||
func Watcher(key string) {
|
func Watcher(key string, newConfCh chan<- []*LogEntry) {
|
||||||
ch := cli.Watch(context.Background(), key)
|
ch := cli.Watch(context.Background(), key)
|
||||||
for w := range ch {
|
for w := range ch {
|
||||||
for _, evt := range w.Events {
|
for _, evt := range w.Events {
|
||||||
fmt.Printf("index:%v value:%v\n", evt.Type, string(evt.Kv.Value))
|
fmt.Printf("index:%v value:%v\n", evt.Type, string(evt.Kv.Value))
|
||||||
//通知taillog.tailTskMgr
|
//通知taillog.tailTskMgr
|
||||||
|
//1.先判断操作的类型
|
||||||
|
var newConf []*LogEntry
|
||||||
|
if evt.Type != clientv3.EventTypeDelete {
|
||||||
|
err := json.Unmarshal(evt.Kv.Value, &newConf)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("parse etcd config failed,error:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("get etcd new config success", newConf)
|
||||||
|
newConfCh <- newConf
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
21
main.go
21
main.go
|
@ -7,6 +7,8 @@ import (
|
||||||
"logagent/etcd"
|
"logagent/etcd"
|
||||||
"logagent/kafka"
|
"logagent/kafka"
|
||||||
"logagent/taillog"
|
"logagent/taillog"
|
||||||
|
"logagent/utils"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,20 +50,33 @@ func main() {
|
||||||
fmt.Printf("init etcd failed,error:%v\n", err)
|
fmt.Printf("init etcd failed,error:%v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//为了实现每个logagent都拉取自己独有的配置。所以要以自己的ip地址作为区分
|
||||||
|
ipStr, err := utils.GetBoundIP()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
etcdConfKey := fmt.Sprintf(cfg.EtcdConf.Key, ipStr)
|
||||||
//1.从etcd中获取日志收集项的配置信息
|
//1.从etcd中获取日志收集项的配置信息
|
||||||
logEntryConf, err := etcd.GetConf(cfg.EtcdConf.Key)
|
logEntryConf, err := etcd.GetConf(etcdConfKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("get etcd failed,error:%v\n", err)
|
fmt.Printf("get etcd failed,error:%v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("get conf from etcd success,logEntryConf:%#v\n", logEntryConf)
|
fmt.Printf("get conf from etcd success,logEntryConf:%#v\n", logEntryConf)
|
||||||
|
|
||||||
//派一个哨兵去监视etcd配置的变化
|
//派一个哨兵去监视etcd配置的变化
|
||||||
|
|
||||||
for k, v := range logEntryConf {
|
for k, v := range logEntryConf {
|
||||||
fmt.Printf("index:%v,value:%v\n", k, v)
|
fmt.Printf("index:%v,value:%v\n", k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
//收集日志发送到kafka
|
//收集日志发送到kafka
|
||||||
taillog.Init(logEntryConf)
|
taillog.Init(logEntryConf) //初始化通道在这个函数中
|
||||||
go etcd.Watcher(cfg.EtcdConf.Key)
|
newConfChan := taillog.NewConfChan() //从taillog包中获取对外暴露的通道
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go etcd.Watcher(etcdConfKey, newConfChan) //哨兵接受到最新的配置信息通知上面的通道
|
||||||
|
wg.Wait()
|
||||||
//2.打开日志文件准备收集日志
|
//2.打开日志文件准备收集日志
|
||||||
//err = taillog.Init(cfg.TailLog.FileName)
|
//err = taillog.Init(cfg.TailLog.FileName)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"go.etcd.io/etcd/client/v3"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cli, err := clientv3.New(clientv3.Config{
|
||||||
|
Endpoints: []string{"43.143.245.135:2379"},
|
||||||
|
DialTimeout: 5 * time.Second,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// handle error!
|
||||||
|
fmt.Println("connect etcd server failed,error:", err)
|
||||||
|
}
|
||||||
|
defer cli.Close()
|
||||||
|
// put
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
value := `[{"path":"c:/tmp/nginx.log","topic":"web_log"},{"path":"d:/xxx/redis.log","topic":"redis_log"}]`
|
||||||
|
_, err = cli.Put(ctx, "/logagent/collect_log_config", value)
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("put to etcd failed,error:", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package taillog
|
package taillog
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hpcloud/tail"
|
"github.com/hpcloud/tail"
|
||||||
"logagent/kafka"
|
"logagent/kafka"
|
||||||
|
@ -14,12 +15,18 @@ type TailTask struct {
|
||||||
path string
|
path string
|
||||||
topic string
|
topic string
|
||||||
instance *tail.Tail
|
instance *tail.Tail
|
||||||
|
//为了实现退出t.run()
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTailTask(path string, topic string) (tailChan *TailTask) {
|
func NewTailTask(path string, topic string) (tailChan *TailTask) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
tailChan = &TailTask{
|
tailChan = &TailTask{
|
||||||
path: path,
|
path: path,
|
||||||
topic: topic,
|
topic: topic,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
tailChan.init() //根据路径去打开日志
|
tailChan.init() //根据路径去打开日志
|
||||||
return
|
return
|
||||||
|
@ -39,6 +46,7 @@ func (t *TailTask) init() {
|
||||||
fmt.Printf("tail file failed,err:%v\n", err)
|
fmt.Printf("tail file failed,err:%v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
//当goroutine执行的函数退出的时候,goroutine就结束了
|
||||||
go t.run() //直接采集日志发送到kafka
|
go t.run() //直接采集日志发送到kafka
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +57,9 @@ func (t *TailTask) ReadChan() <-chan *tail.Line {
|
||||||
func (t *TailTask) run() {
|
func (t *TailTask) run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-t.ctx.Done():
|
||||||
|
fmt.Printf("tail task:%v exit\n", t.path+t.topic)
|
||||||
|
return
|
||||||
case line := <-t.ReadChan():
|
case line := <-t.ReadChan():
|
||||||
fmt.Printf("日志已发送kafka,text:,%v\n", line.Text)
|
fmt.Printf("日志已发送kafka,text:,%v\n", line.Text)
|
||||||
//kafka.SendToKafka(t.topic, line.Text) //函数调用函数
|
//kafka.SendToKafka(t.topic, line.Text) //函数调用函数
|
||||||
|
|
|
@ -25,13 +25,16 @@ func Init(logEntryConf []*etcd.LogEntry) {
|
||||||
for _, logEntry := range logEntryConf {
|
for _, logEntry := range logEntryConf {
|
||||||
//logEntry: *etcd.LogEntry
|
//logEntry: *etcd.LogEntry
|
||||||
//要收集的日志文件的路径
|
//要收集的日志文件的路径
|
||||||
NewTailTask(logEntry.Path, logEntry.Topic)
|
//初始化的时候启动了多少tailTask,需要记下来
|
||||||
|
tailTask := NewTailTask(logEntry.Path, logEntry.Topic)
|
||||||
|
mKey := fmt.Sprintf("%v_%v", logEntry.Topic, logEntry.Path)
|
||||||
|
tskMgr.tskMap[mKey] = tailTask
|
||||||
|
|
||||||
}
|
}
|
||||||
go tskMgr.run()
|
go tskMgr.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听自己的newConfChan 有了新的配置过来就做对应的除了
|
// 监听自己的newConfChan 有了新的配置过来就做对应的处理
|
||||||
|
|
||||||
func (t *tailLogMgr) run() {
|
func (t *tailLogMgr) run() {
|
||||||
for {
|
for {
|
||||||
|
@ -40,9 +43,43 @@ func (t *tailLogMgr) run() {
|
||||||
// 1.配置新增
|
// 1.配置新增
|
||||||
// 2.配置删除
|
// 2.配置删除
|
||||||
// 3.配置变更
|
// 3.配置变更
|
||||||
|
|
||||||
|
for _, conf := range newConf {
|
||||||
|
mKey := fmt.Sprintf("%v_%v", conf.Topic, conf.Path)
|
||||||
|
_, ok := t.tskMap[mKey]
|
||||||
|
if !ok {
|
||||||
|
//新增的
|
||||||
|
tailTask := NewTailTask(conf.Path, conf.Topic)
|
||||||
|
t.tskMap[mKey] = tailTask
|
||||||
|
} else {
|
||||||
|
//原来就有,不需要操作
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//找出t.tskMap有,但是newConf里没有的,删除
|
||||||
|
for _, c1 := range t.logEntry { //从原来的配置中依次拿出配置项
|
||||||
|
isDelete := true
|
||||||
|
for _, c2 := range newConf { //去新的配置项中逐一进行比较
|
||||||
|
if c1.Path == c2.Path && c1.Topic == c2.Topic {
|
||||||
|
isDelete = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isDelete {
|
||||||
|
//把c1对应的tailTask停掉
|
||||||
|
mKey := fmt.Sprintf("%v_%v", c1.Topic, c1.Path)
|
||||||
|
t.tskMap[mKey].cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
fmt.Printf("新的配置来了%v\n", newConf)
|
fmt.Printf("新的配置来了%v\n", newConf)
|
||||||
default:
|
default:
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//向外暴露tskMgr的newConfChan
|
||||||
|
|
||||||
|
func NewConfChan() chan<- []*etcd.LogEntry {
|
||||||
|
return tskMgr.newConfChan
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//获取本机ip
|
||||||
|
|
||||||
|
func GetBoundIP() (ip string, err error) {
|
||||||
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
ip = strings.Split(localAddr.String(), ":")[0]
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue