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

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

先看下具体效果图
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 发布的第二款编程语言。

    500 引用 • 1396 回帖 • 253 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 2 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 10 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    187 引用 • 3914 回帖
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    10320 引用 • 46857 回帖 • 61 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    119 引用 • 323 回帖
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    16 引用 • 236 回帖 • 238 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 507 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 368 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 555 关注
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 44 关注
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖
  • 自由行
    1 关注
  • 脑图

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

    32 引用 • 100 回帖
  • sts
    2 引用 • 2 回帖 • 247 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 413 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用 • 2 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    232 引用 • 484 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    101 引用 • 905 回帖
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 384 回帖
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    118 引用 • 54 回帖 • 9 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 2 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 14 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 34 关注