Golang监听日志文件并发送到kafka中
作者:zhijie 发布时间:2024-04-25 13:20:44
前言
日志收集项目的准备中,本文主要讲的是利用golang的tail
库,监听日志文件的变动,将日志信息发送到kafka中。
涉及的golang库和可视化工具:
go-ini
,sarama
,tail
其中:
go-ini
:用于读取配置文件,统一管理配置项,有利于后其的维护sarama
:是一个go操作kafka的客户端。目前我用于向kefka发送消息tail
:类似于linux的tail命令了,读取文件的后几行。如果文件有追加数据,会检测到。就是通过它来监听日志文件
可视化工具:
offsetexplorer
:是kafka的可视化工具,这里用来查看消息是否投递成功
工作的流程
加载配置,初始化
sarama
和kafka
。起一个的协程,利用
tail
不断去监听日志文件的变化。主协程中一直阻塞等待
tail
发送消息,两者通过一个管道通讯。一旦主协程接收到新日志,组装格式,然后发送到kafka中
环境准备
环境的话,确保zookeeper
和kafka
正常运行。因为还没有使用sarama
读取数据,使用offsetexplorer
来查看任务是否真的投递成功了。
代码分层
serve来存放写tail
服务类和sarama
服务类,conf存放ini配置文件
main函数为程序入口
关键的代码
main.go
main函数做的有:构建配置结构体,映射配置文件。调用和初始化tail
,srama
服务。
package main
import (
"fmt"
"sarama/serve"
"github.com/go-ini/ini"
)
type KafkaConfig struct {
Address string `ini:"address"`
ChannelSize int `ini:"chan_size"`
}
type TailConfig struct {
Path string `ini:"path"`
Filename string `ini:"fileName"`
// 如果是结构体,则指明分区名
Children `ini:"tailfile.children"`
}
type Config struct {
KafkaConfig `ini:"kafka"`
TailConfig `ini:"tailfile"`
}
type Children struct {
Name string `ini:"name"`
}
func main() {
// 加载配置
var cfg = new(Config)
err := ini.MapTo(cfg, "./conf/go-conf.ini")
if err != nil {
fmt.Print(err)
}
// 初始化kafka
ks := &serve.KafukaServe{}
// 启动kafka消息监听。异步
ks.InitKafka([]string{cfg.KafkaConfig.Address}, int64(cfg.KafkaConfig.ChannelSize))
// 关闭主协程时,关闭channel
defer ks.Destruct()
// 初始化tail
ts := &serve.TailServe{}
ts.TailInit(cfg.TailConfig.Path + "/" + cfg.TailConfig.Filename)
// 阻塞
ts.Listener(ks.MsgChan)
}
kafka.go
有3个方法 :
InitKafka
,组装配置项以及初始化接收消息的管道,Listener
,监听管道消息,收到消息后,将消息组装,发送到kafkaDestruct
, 关闭管道
package serve
import (
"fmt"
"github.com/Shopify/sarama"
)
type KafukaServe struct {
MsgChan chan string
//err error
}
func (ks *KafukaServe) InitKafka(addr []string, chanSize int64) {
// 读取配置
config := sarama.NewConfig()
// 1. 初始化生产者配置
config.Producer.RequiredAcks = sarama.WaitForAll
// 选择分区
config.Producer.Partitioner = sarama.NewRandomPartitioner
// 成功交付的信息
config.Producer.Return.Successes = true
ks.MsgChan = make(chan string, chanSize)
go ks.Listener(addr, chanSize, config)
}
func (ks *KafukaServe) Listener(addr []string, chanSize int64, config *sarama.Config) {
// 连接kafka
var kafkaClient, _ = sarama.NewSyncProducer(addr, config)
defer kafkaClient.Close()
for {
select {
case content := <-ks.MsgChan:
//
msg := &sarama.ProducerMessage{
Topic: "weblog",
Value: sarama.StringEncoder(content),
}
partition, offset, err := kafkaClient.SendMessage(msg)
if err != nil {
fmt.Println(err)
}
fmt.Println("分区,偏移量:")
fmt.Println(partition, offset)
fmt.Println("___")
}
}
}
func (ks *KafukaServe) Destruct() {
close(ks.MsgChan)
}
tail.go
主要包括了两个方法:
TailInit
初始化,组装tail
配置。Listener
Listener
,保存kafka
服务类初始化之后的管道。监听日志文件,如果有新日志,就往管道里发送
package serve
import (
"fmt"
"github.com/hpcloud/tail"
)
type TailServe struct {
tails *tail.Tail
}
func (ts *TailServe) TailInit(filenName string) {
config := tail.Config{
ReOpen: true,
Follow: true,
Location: &tail.SeekInfo{Offset: 0, Whence: 2},
MustExist: false,
Poll: true,
}
// 打开文件开始读取数据
ts.tails, _ = tail.TailFile(filenName, config)
// if err != nil {
// fmt.Println("tails %s failed,err:%v\n", filenName, err)
// return nil, err
// }
fmt.Println("启动," + filenName + "监听")
}
func (ts *TailServe) Listener(MsgChan chan string) {
for {
msg, ok := <-ts.tails.Lines
if !ok {
// todo
fmt.Println("数据接收失败")
return
}
fmt.Println(msg.Text)
MsgChan <- msg.Text
}
}
// 测试案例
func Demo() {
filename := `E:\xx.log`
config := tail.Config{
ReOpen: true,
Follow: true,
Location: &tail.SeekInfo{Offset: 0, Whence: 2},
MustExist: false,
Poll: true,
}
// 打开文件开始读取数据
tails, err := tail.TailFile(filename, config)
if err != nil {
fmt.Println("tails %s failed,err:%v\n", filename, err)
return
}
var (
msg *tail.Line
ok bool
)
fmt.Println("启动")
for {
msg, ok = <-tails.Lines
if !ok {
fmt.Println("tails file close reopen,filename:$s\n", tails.Filename)
}
fmt.Println("msg:", msg.Text)
}
}
来源:https://juejin.cn/post/7086105702057377828
猜你喜欢
- 本文实例讲述了基于进程内通讯的python聊天室实现方法。分享给大家供大家参考。具体如下:#!/usr/bin/env python# Ad
- 具体代码如下所示:package mainimport ( "bufio" "io&q
- 正则表达式(Regular Expression)为字符串模式匹配提供了一种高效、方便的方法。几乎所有高级语言都提供了对正则表达式的支持,或
- 下面列出Python正则表达式的几种匹配用法,具体内容如下所示:此外,关于正则的一切http://deerchao.net/tutorial
- 最简单、直观的认识,将 yield 看做 return 对待,只是 return 返回一个值,而 yield 返回一个生成器。要理解 yie
- 二元运算符特殊方法+__add__,__radd__-__sub__,__rsub__*__mul__,__rmul__/__div__,_
- 目录一、Python 异常处理:错误与异常1、什么是错误?2、什么是异常?二、如何使用 Try-Except 处理异常?1、什么是 Try-
- 如下所示:#-*- encoding:utf-8 -*-from tkinter import *root = Tk()def create
- 本文实例为大家分享了python实现猜拳游戏的具体代码,供大家参考,具体内容如下项目功能:1.系统生成随机的石头剪刀布,玩家输入石头剪刀布2
- 一、创建配置文件在D盘建立一个配置文件,名字为:test.ini内容如下:[baseconf]host=127.0.0.1port=3306
- Blender 并不是唯一一款允许你为场景编程和自动化任务的3D软件; 随着每一个新版本的推出,Blender 正逐渐成为一个可靠的 CG
- 本文主要介绍了pytorch cnn 识别手写的字实现自建图片数据,分享给大家,具体如下:# library# standard libra
- 使用xhtml+css布局经常性地会使用到float,很多邪门的事儿都有可能是浮动在作怪,那么清除浮动就是必须要做的,而且随时性地对父级元素
- SQL(结构化查询语言)就是负责与ANSI维护的数据库交互的标准。最新的版本是SQL-99,还有一个新标准SQL-200n尚处于制定过程中。
- 前言:在motplotlib的学习过程中,我们使用最多的就是numpy模块。numpy 模块被称为 matplotlib 模块绘制图表伴侣。
- Python Logging原来真的远比我想象的要复杂很多很多,学习路线堪比git。但是又绕不过去,alternatives又少,所以必须要
- 一、xpath:基本属性定位上一篇文章讲了通过元素的id、name、class这些属性定位的用户,使用xpath方法结合元素属性也可以很准确
- 本文实例讲述了Python使用matplotlib 画矩形的三种方式。分享给大家供大家参考,具体如下:假设矩形两点坐标如下,分别为:x1,
- 这是一个网页设计中经常会用到的图片特效,实现多个图片之间的轮换,并分别带有连接。以前的代码只能适用于IE,在FF下始终没有得到很好的解决今天
- 在实现TextStraem的时候,找到判断文件编码的代码是VBS的,但是在JScript中是没有ASC等函数的,也不能对二进制数据进行处理,