golang+influxdb+grafana 实现 nginx 日志流量、响应时间等监控系统

本贴最后更新于 2333 天前,其中的信息可能已经时过境迁

先看下具体效果图
imagepng

主要是对自己博客服务器的流量的监控
imagepng

主要思路

读取日志-> 正则解析-> 写入 influxdb->grafana 获取数据渲染

首先我们需要先将 influxdb 和 grafana 安装部署好 以便于后面使用

centos7 安装 influxdb

wget https://dl.influxdata.com/influxdb/releases/influxdb-0.13.0.x86_64.rpm sudo yum localinstall influxdb-0.13.0.x86_64.rpm service influxdb restart 如果遇到influxdb 8083端口访问不到web管理页面则需要到/etc/influxdb/influxdb.conf 修改参数 vim 命令模式下:/admin搜索到对应部分去掉注释#[admin] #enabled=true [admin] # Determines whether the admin service is enabled. enabled =true # The default bind address used by the admin service. bind-address = ":8083" # Whether the admin service should use HTTPS. # https-enabled = false # The SSL certificate used when HTTPS is enabled. # https-certificate = "/etc/ssl/influxdb.pem" 然后具体操作添加用户,建库什么均在百度,和mysql大同小异

centos7 下安装 grafana
官方文档中有很多办法
我是通过配置 yum 然后 install 的

Add the following to a new file at /etc/yum.repos.d/grafana.repo [grafana] name=grafana baseurl=https://packagecloud.io/grafana/stable/el/7/$basearch repo_gpgcheck=1 enabled=1 gpgcheck=1 gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt sudo yum install grafana service grafana-server restart 默认跑在3000端口 账号密码为 admin admin

代码实现

type Reader interface { Read(rc chan []byte) } type Writer interface { Write(rc chan *Message) } //存入db的基本数据 type Message struct { Host string TimeLocal time.Time Method, Resource, Protocol string Status string BytesSent int Scheme string Url string UpstreamTime, RequestTime float64 } //扩展性实现接口的struct type ReadFromFile struct { path string } type WriteToInfluxDB struct { influxDBDsn string } //日志解析中转 type LogProcess struct { rc chan []byte wc chan *Message read Reader write Writer }

从文件中读取数据(注释中的数据为我 nginx 日志中的一条数据)

/*14.215.176.15 - - [23/APR/2018:21:43:39 +0800] "GET /CONSOLE/DIST/LAYOUTS/DEFAULT.F5E6C608DE637CAC3F50.JS HTTP/1.1" 200 5665 "HTTPS://WWW.XHXBLOG.CN/?B3ID=H9OXZSYM" "MOZILLA/5.0 (WINDOWS NT 6.1; WOW64; RV:43.0) GECKO/20100101 FIREFOX/43.0" "-"*/ func (r *ReadFromFile) Read(rc chan []byte) { //读取数据 f, err := os.Open(r.path) if err != nil { panic(fmt.Sprintf("open file fail:%s", err.Error())) } //跳到文件末尾 f.Seek(0, 2) rd := bufio.NewReader(f) //循环读取数据将每行数据送入rc channel for { line, err := rd.ReadBytes('\n') if err == io.EOF { time.Sleep(500 * time.Millisecond) continue } else if err != nil { panic(fmt.Sprintf("ReadBytes error: %s", err.Error())) } TypeMonitorChan <- TypeHandleLine rc <- line[:len(line)-1] } }

正则解析数据(参考正则表达解析,使用该工具调试)

正则测试模块没有单独写出来了,放在如下函数的注释中测试

其中 method,resource,protocol 等是通过正则解析后的字符串再次通过空格 split 分割获取的

func (l *LogProcess) Process() { //解析数据 r := regexp.MustCompile(`([\d\.]+)\s+([^\[]+)\s+([^\[]+)\s+\[([^\]]+)\]\s+\"([^"]+)\"\s+(\d{3})\s+(\d+)\s+\"([^"]+)\"\s+\"([^"]+)\"\s+`) /** 测试flag */ //flag := 0 loc, _ := time.LoadLocation("Asia/Shanghai") for v := range l.rc { ret := r.FindStringSubmatch(string(v)) /*测试**/ /* if flag != 2 { sp := strings.Split(ret[5], " ") fmt.Println(ret) fmt.Println("Host:", ret[1]) fmt.Println("LocalTime:", ret[4]) fmt.Println("Method:", sp[0]) uu, _ := url.Parse(sp[1]) fmt.Println(uu.Path) fmt.Println("Path:", sp[1]) fmt.Println("Protocol:", sp[2]) fmt.Println("Status:", ret[6]) fmt.Println("BytesSent:", ret[7]) fmt.Println("Scheme:", ret[9]) fmt.Println(ret[8]) flag++ } */ if len(ret) != 10 { TypeMonitorChan <- TypeErrNum log.Println("FindStringSubmatch fail:", string(v)) continue } t, err := time.ParseInLocation("02/Jan/2006:15:04:05 +0800", ret[4], loc) if err != nil { log.Println("ParseInLocation fail:", err.Error(), ret[4]) } message := &Message{} //14.215.176.15 message.Host = ret[1] //23/APR/2018:21:43:39 +0800 message.TimeLocal = t //MOZILLA/5.0 (WINDOWS NT 6.1; WOW64; RV:43.0) GECKO/20100101 FIREFOX/43.0 message.Scheme = ret[9] //GET /CONSOLE/DIST/LAYOUTS/DEFAULT.F5E6C608DE637CAC3F50.JS HTTP/1.1 sp := strings.Split(ret[5], " ") if len(sp) != 3 { TypeMonitorChan <- TypeErrNum log.Println("strings.Split fail:", ret[5]) continue } //请求方法 message.Method = sp[0] //请求路径 u, err := url.Parse(sp[1]) if err != nil { TypeMonitorChan <- TypeErrNum log.Println("url parse fail:", err) continue } message.Resource = u.Path //请求协议 message.Protocol = sp[2] //200 message.Status = ret[7] //HTTPS://WWW.XHXBLOG.CN/?B3ID=H9OXZSYM message.Url = ret[8] //5665 message.BytesSent, _ = strconv.Atoi(ret[7]) l.wc <- message } }

写入 influxdb 客户端是用 golang 写的 influxdb gay 地址为:InfluxDB Client

func (w *WriteToInfluxDB) Write(wc chan *Message) { sp := strings.Split(w.influxDBDsn, "@") // Create a new HTTPClient c, err := client.NewHTTPClient(client.HTTPConfig{ Addr: sp[0], Username: sp[1], Password: sp[2], }) if err != nil { log.Fatal(err) } defer c.Close() for v := range wc { // Create a new point batch bp, err := client.NewBatchPoints(client.BatchPointsConfig{ Database: sp[3], Precision: sp[4], }) if err != nil { log.Fatal(err) } // Create a point and add to batch tags := map[string]string{"Path": v.Resource, "Method": v.Method, "Scheme": v.Scheme, "Status": v.Status, "Protocol": v.Protocol} fields := map[string]interface{}{ "RequestTime": 2.0, "BytesSent": v.BytesSent, } pt, err := client.NewPoint("nginx_log", tags, fields, v.TimeLocal) if err != nil { log.Fatal(err) } bp.AddPoint(pt) // Write the batch if err := c.Write(bp); err != nil { log.Fatal(err) } // Close client resources if err := c.Close(); err != nil { log.Fatal(err) } log.Println("write success") } }
type SystemInfo struct { HandleLine int `json:"handleLine"` Tps float64 `json:"tps"` ReadChanLen int `json:"readChanLen"` WriteChanLen int `json:"writeChanLen"` RunTime string `json:"runTime"` ErrNum int `json:"errNum"` } type Monitor struct { startTime time.Time data SystemInfo tpsSli []int }

此处主要监控运行时间,channel 阻塞数量,处理条数监听一个端口在 8999 上
其次该监听将阻塞在 main 函数上

//专门接收内容的channel,错误数量和处理条数 var TypeMonitorChan = make(chan int, 200) func (m *Monitor) start(lp *LogProcess) { go func() { for n := range TypeMonitorChan { switch n { case TypeErrNum: m.data.ErrNum += 1 case TypeHandleLine: m.data.HandleLine += 1 } } }() ticker := time.NewTicker(time.Second * 5) go func() { <-ticker.C m.tpsSli = append(m.tpsSli, m.data.HandleLine) //目的是为了通过两次读取的行数除以单位时间就能得到大概的吞吐量 if len(m.tpsSli) > 2 { m.tpsSli = m.tpsSli[1:] } }() http.HandleFunc("/monitor", func(writer http.ResponseWriter, request *http.Request) { m.data.RunTime = time.Now().Sub(m.startTime).String() m.data.ReadChanLen = len(lp.rc) m.data.WriteChanLen = len(lp.wc) if len(m.tpsSli) >= 2 { m.data.Tps = float64(m.tpsSli[1]-m.tpsSli[0]) / 5 } ret, _ := json.MarshalIndent(m.data, "", "\t") io.WriteString(writer, string(ret)) }) http.ListenAndServe(":8999", nil) }
func main() { //通过命令行模式输入参数简单化,不输则为默认值 var path, influxDsn string flag.StringVar(&path, "path", "/var/log/nginx/access.log", "read file path") flag.StringVar(&influxDsn, "influxDsn", "http://127.0.0.1:8086@haoxiong@8080@nginx_log@s", "influx data source") flag.Parse() r := &ReadFromFile{ path: path, } w := &WriteToInfluxDB{ influxDBDsn: influxDsn, } lp := &LogProcess{ rc: make(chan []byte, 200), wc: make(chan *Message), read: r, write: w, } go lp.read.Read(lp.rc) for i := 0; i < 2; i++ { go lp.Process() } for i := 0; i < 4; i++ { go lp.write.Write(lp.wc) } m := &Monitor{ startTime: time.Now(), data: SystemInfo{}, } //监听一个端口阻塞main m.start(lp) }
具体代码
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    498 引用 • 1395 回帖 • 257 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
xhaoxiong
站在巨人的肩膀上学习与创新

推荐标签 标签

  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    586 引用 • 3538 回帖
  • AWS
    11 引用 • 28 回帖 • 9 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 662 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 1 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 7 关注
  • OpenCV
    15 引用 • 36 回帖
  • 音乐

    你听到信仰的声音了么?

    61 引用 • 512 回帖
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    57 引用 • 25 回帖 • 7 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 99 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    245 引用 • 1338 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    498 引用 • 1395 回帖 • 257 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    31 引用 • 96 回帖
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    7 引用 • 27 回帖
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 719 关注
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    76 引用 • 389 回帖
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖 • 1 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    132 引用 • 873 回帖 • 2 关注
  • Word
    13 引用 • 40 回帖
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 85 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 2 关注
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 2 关注
  • 996
    13 引用 • 200 回帖 • 2 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    28 引用 • 197 回帖 • 25 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    409 引用 • 3586 回帖
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖