文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

GoLang日志监控系统实现

2022-12-15 12:02

关注

日志监控系统

Nginx(日志文件) -> log_process (实时读取解析写入) -> influxdb(存储) ->grafana(前端日志展示器)

influxdb 属于GO语言编写的开源的时序型数据,着力于高性能 查询与存储时序型数据,influxdb 广泛的应用于存储系统的监控数据,IOT行业的实时数据。

目前市面上流行 TSDB(时序型处理数据库):influxDB, TimescaleDB, QuestDBinfluxDB 类似于NOSQL体验,自动适合标记集模型的技术的数据集;TimescaleDB 与 postgreSQL 兼容, 更加适合物联网数据,与PostgreSQL更好的兼容QuestDB: 支持InfluxDB内联协议和PostgreSQL, 但是生态问题比较大

项目简答介绍

本日志系统 DEMO,但是可以直接使用到生产环境上面,使用LOG_Process 读取Nginx ./Access.log, 使用influxDB 进行存取

log_process -path ./access.log influxdsn http://127.0.0.1:8086@imooc@imoocpass@immoc@s

常见并发模型

在go 语言中 并发执行 ,使用三个不同 gorountine, 一个负责装填,一个负责运输,一个负责处理 ,让程序并发的运行起来,让任务更加的职责单一化 这种思想 也可以将 日志解析读取,写入模块进行单独小模块,每个模块让使用gorountine ,通过channel 数据交互,至于这么多gorountine 是在一个CPU调度执行还是分配到多个CPU上进行执行 ,取决于系统.

go 语言有自己的调度器, go fun() 属于一个独立的工作单元,go的调度器,根据每个可用的物理处理器分配一个逻辑处理器,通过这个逻辑处理器对 独立单元进行处理,
通过设置: runtime.GOMAXPROCS(1)//给调度器分配多小个具体的逻辑处理器
一台服务器的 物理处理器越多 ,go 获取到逻辑处理器也越多,导致器允许速度越快。 参考:传送门

系统架构

日志解析的基本流程化的伪函数,如下的函数有两个缺陷,解析介入和解析后输出只能写死,所以需要进行扩展,接口方式进行扩展

package main
import (
	"fmt"
	"strings"
	"time"
)

type LogProcess struct {
	path        string      // 读取文件路径
	influxDBDsn string      // influx data source
	rc          chan string // read module to process
	wc          chan string // process to influx
}
// 返回函数使用 指针, 结构体很大 不需要进行拷贝 性能优化
func (l *LogProcess) ReadFromFile() {
	// 文件读取模块
	line := "message"
	l.rc <- line
}
func (l *LogProcess) Process() {
	// 文件解析模块
	data := <-l.rc
	l.wc <- strings.ToUpper(data)
}
func (l *LogProcess) writeToInfluxDB() {
	fmt.Println(<-l.wc)
}
func main() {
	// lp 引用类型
	lp := &LogProcess{
		path:        "./tmp/access.log",
		influxDBDsn: "username&password...",
		rc:          make(chan string),
		wc:          make(chan string),
	}
	// tree goroutine run
	go lp.ReadFromFile()
	go lp.Process()
	// 需要定义 chan 将 Process 数据 传递给 influxDB
	go lp.writeToInfluxDB()
	time.Sleep(2 * time.Second)
}

接口方式约束 输入和输出 进行优化

package main
import (
	"fmt"
	"strings"
	"time"
)

type LogProcess struct {
	rc    chan string // read module to process
	wc    chan string // process to influx
	read  Read
	write Writer
}
func (l *LogProcess) Process() {
	// 文件解析模块
	data := <-l.rc
	l.wc <- strings.ToUpper(data)
}
type Writer interface {
	writer(wc chan string)
}
type WriteToInfluxDB struct {
	influxDBDsn string // influx data source
}
func (w *WriteToInfluxDB) writer(wc chan string) {
	fmt.Println(<-wc)
}
type Read interface {
	read(rc chan string)
}
type ReadFromFile struct {
	path string // 读取文件
}
func (r *ReadFromFile) read(rc chan string) {
	// 读取模块
	line := "message"
	rc <- line
}
func main() {
	// lp 引用类型
	r := &ReadFromFile{
		path: "./tmp/access.log",
	}
	w := &WriteToInfluxDB{
		influxDBDsn: "username&password"}
	lp := &LogProcess{
		rc:    make(chan string),
		wc:    make(chan string),
		read:  r,
		write: w,
	}
	// 通过接口方式 约束其功能
	go lp.read.read(lp.rc)
	go lp.Process()
	go lp.write.writer(lp.wc)
	// 通过参数注入方式
	time.Sleep(2 * time.Second)
}

读取模块具体实现

从上次读取光标后开始逐行进行读取,无需每次都全部文件读取

package main
import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"time"
)

type LogProcess struct {
	rc    chan []byte // read module to process
	wc    chan string // process to influx
	read  Read
	write Writer
}
func (l *LogProcess) Process() {
	// 文件解析模块
	for v := range l.rc {
		l.wc <- strings.ToUpper(string(v))
	}
}
type Writer interface {
	writer(wc chan string)
}
type WriteToInfluxDB struct {
	influxDBDsn string // influx data source
}
func (w *WriteToInfluxDB) writer(wc chan string) {
	// wc 通道另外一种读取方式
	for x := range wc {
		fmt.Println(x)
	}
}
type Read interface {
	read(rc chan []byte)
}
type ReadFromFile struct {
	path string // 读取文件
}
func (r *ReadFromFile) read(rc chan []byte) {
	// 实时系统: 从文件末尾逐行进行读取
	f, err := os.Open(r.path)
	if err != nil {
		panic(fmt.Sprintln("open file error:%s", err.Error()))
	}
	// 文件末尾最开始进行读取
	f.Seek(0, 2)
	rd := bufio.NewReader(f)
	for {
		line, err := rd.ReadBytes('\n')
		if err == io.EOF {
			// d读取到文件末尾, 日志还没有写入
			time.Sleep(500 * time.Millisecond)
			continue
		} else if err != nil {
			panic(fmt.Sprintln("ReadBytes error:%s", err.Error()))
		}
		rc <- line[:len(line)-1]
	}
}
func main() {
	// lp 引用类型
	r := &ReadFromFile{
		path: "H:\\code\\goprogarm\\src\\access.log",
	}
	w := &WriteToInfluxDB{
		influxDBDsn: "username&password"}
	lp := &LogProcess{
		rc:    make(chan []byte),
		wc:    make(chan string),
		read:  r,
		write: w,
	}
	// 通过接口方式 约束其功能
	go lp.read.read(lp.rc)
	go lp.Process()
	go lp.write.writer(lp.wc)
	// 通过参数注入方式
	time.Sleep(100 * time.Second)
}

日志解析模块

package main
import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"strconv"
	"time"
)

type LogProcess struct {
	rc    chan []byte // read module to process
	wc    chan *Message // process to influx
	read  Read
	write Writer
}
//日志写入结构体
type Message struct {
	TimeLocal time.Time
	BytesSent int
	Path, Method, Scheme, Status string
	UpstreamTime, RequestTime float64
}
func (l *LogProcess) Process() {
	// 通过正则表达式进行解析数据
	r := regexp.MustCompile(`(\s*)`)
	loc, _ := time.LoadLocation("Asia/shanghai")
	// 文件解析模块
	for v := range l.rc {
		ret := r.FindStringSubmatch(string(v))
		if len(ret) != 13 {
			log.Println("FindStringSub match fail:", string(v))
			continue
		}
		message := &Message{
		}
		location, err := time.ParseInLocation("02/Jan/2006:15:04:05 +0000", ret[4], loc)
		if err != nil {
			log.Println("ParseInLocation fail:", err.Error(), ret[4])
		}
		message.TimeLocal = location
		// 字符串类型转换成int
		atoi, err := strconv.Atoi(ret[8])
		if err != nil {
			log.Println("strconv.Atoi fail:", err.Error(), ret[4])
		}
		message.BytesSent = atoi
		l.wc <- message
	}
}
type Writer interface {
	writer(wc chan *Message)
}
type WriteToInfluxDB struct {
	influxDBDsn string // influx data source
}
func (w *WriteToInfluxDB) writer(wc chan *Message) {
	// wc 通道另外一种读取方式
	for x := range wc {
		fmt.Println(x)
	}
}
type Read interface {
	read(rc chan []byte)
}
type ReadFromFile struct {
	path string // 读取文件
}
func (r *ReadFromFile) read(rc chan []byte) {
	// 实时系统: 从文件末尾逐行进行读取
	f, err := os.Open(r.path)
	if err != nil {
		panic(fmt.Sprintf("open file error:%s\n", err.Error()))
	}
	// 文件末尾最开始进行读取
	f.Seek(0, 2)
	rd := bufio.NewReader(f)
	for {
		line, err := rd.ReadBytes('\n')
		if err == io.EOF {
			// d读取到文件末尾, 日志还没有写入
			time.Sleep(500 * time.Millisecond)
			continue
		} else if err != nil {
			panic(fmt.Sprintf("ReadBytes error:%s\n", err.Error()))
		}
		rc <- line[:len(line)-1]
	}
}
func main() {
	// lp 引用类型
	r := &ReadFromFile{
		path: "H:\\code\\goprogarm\\src\\access.log",
	}
	w := &WriteToInfluxDB{
		influxDBDsn: "username&password"}
	lp := &LogProcess{
		rc:    make(chan []byte),
		wc:    make(chan *Message),
		read:  r,
		write: w,
	}
	// 通过接口方式 约束其功能
	go lp.read.read(lp.rc)
	go lp.Process()
	go lp.write.writer(lp.wc)
	// 通过参数注入方式
	time.Sleep(100 * time.Second)
}

到此这篇关于GoLang日志监控系统实现的文章就介绍到这了,更多相关GoLang日志监控内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯