夜莺监控之自定义插件(v3)

默认情况下,监控采集指标支持进程、端口、日志,如果要采集其他的指标,需要通过编写自定义插件来实现(以v3版本为例子)

注意:windows采集器collector不支持在web页面配置采集插件,只能通过自定义数据上报的形式来实现指标采集

插件名字编写规则:

  • 以数字开头,其中数字表示插件的采集周期,例如:10_disk.sh,表示每个隔10s采集一次磁盘信息(不一定非要数字,但是标准规范以数字开头)

1、编写10_disk.sh脚本,添加内容如下:

说明:

  • disk_root:表示获取到根目录下当前可使用空间大小
  • disk_apps:表示获取到的apps当前可使用空间大小
  • localip:表示当前IP
  • endpoint:获取的本机IP(字符串,加双引号或单引号)
  • timestamp:表示时间戳(数值,不用加双引号或单引号)
  • metric:表示监控指标,可以自定义(字符串,加双引号或单引号)
  • value:表示上面获取的命令值,注意一定要是数字,不是数字插件无法成功

注意:timestamp和value都是数值(必须为整型,不可以为浮点类型),不用加双引号,加了双引号会出现类型错误,导致无法在web端看到监控指标

注意:上述脚本是将获取的结果通过echo输出,输出的格式为json格式,类型为字符串,如果还有多个指标,用逗号分隔开即可,最后一个大括号后面没有逗号,后续编写插件直接按照此模板来编写即可。

2、在/home/n9e目录下创建插件目录plugin(目录名和位置都可以自定义的,为了好找所以放在了/home/n9e目录下),将脚本放入到此目录中,多个插件都放在这个目录下,如图:

注意:脚本一定要具有可执行权限

3、在夜莺平台,点击采集配置—插件—选择节点机器—创建—输入采集名称和插件路径,如图:

注意:如果有多个插件,就要创建多个

4、重启agent后,在即时看图位置,选择节点机器,点击磁盘,可以看到自定义的插件指标已经出现,如图:

上述命令获取的/apps目录可使用空间为18G,因此点击指标后,可以看到图示如下:

5、接下来配置告警策略即可

备注:上述脚本的命令为获取根目录和/apps目录的剩余使用空间,可以在告警策略里设置剩余空间值,当达到某个值后触发告警

上面的例子是通过shell方式来编写的插件,下面的例子是用python来编写插件

例如:编写一个插件,用来对比两个文件夹的修改时间和系统时间,如果文件夹少于40分钟没有数据更新则告警,下面的例子用python2编写,如下:

#!/usr/bin/python           #python2编写
# -*- coding:utf-8 -*-
import commands             #导入此模块可以执行shell命令,python3为subprocess模块
import time
import json
import datetime
import math
def check_time():
    '''
    @本脚本主要功能为获取两个文件夹时间与系统时间做对比
     如果时间差大于40分钟,则告警,此脚本适用于判断文件夹中数据是否实时更新
     如果没有更新那么触发告警
     假定本地已经存在两个文件夹 one 和two
    @author: gongguan
    @time:  20210529
    '''
    result = []
    value = 0       #声明变量并设置初始值
    value1 = 0
    ip = commands.getoutput("ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1 ")
    timestamp = int(commands.getoutput("date +%s"))   #获取时间戳
    date_time = datetime.datetime.now() #获取当前系统时间,注意此时的类型为data_time日期类型
    sys_time = str(date_time).split(' ')[1] #如果要用split切割,需要先转换为字符串才能切割
    sys_hour = sys_time.split(':')[0]  #获取系统的小时
    sys_min = sys_time.split(':')[1]   #获取系统的分钟数
    #one_modify_time=commands.getoutput('''stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' ''')
    one_hour=commands.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $1}' ''')
    one_min=commands.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $2}' ''')
    two_hour=commands.getoutput(''' stat two | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $1}' ''')
    two_min=commands.getoutput(''' stat two | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $2}' ''')
    '''
        @接下来将获取到的文件夹时间与系统的时间做对比并执行逻辑
        @第一个if语句的含义是如果系统时间中的小时减去one文件夹的小时等于23(跨天了,00点减去23点),并且系统的分钟数和one文件夹的
        修改时间的分钟数之间相差小于等于40分钟,那么设置value等于1
        @第二个if语句的含义是如果系统时间中的小时减去one文件夹的修改时间的小时数等于0(说明小时数相同),
        并且系统的分钟数减去one文件夹的修改时间的分钟数小于等于40 ,那么value=1
        @第三个if语句的含义是如果系统时间中的小时减去one文件夹修改时间的小时数大于1(可能一个小时以上没数据更新),
        并且当前系统分钟数和One文件夹的分钟数差值小于等于40分,那么value=1
        @else  其他情况value=0
    '''
    if (abs(int(sys_hour) - int(one_hour)) == 23 ) and ((60 - int(sys_min) + int(one_min)) <= 40):
        value = 1
    elif (abs(int(sys_hour) - int(one_hour)) == 0 ) and ( abs(int(sys_min) - int(one_min)) <= 40 ):
        value = 1
    elif (abs(int(sys_hour) - int(one_hour)) == 1 ) and ( int(sys_min) + 60 - int(one_min) <= 40 ):
        value = 1
    else:
        value = 0
    #定义夜莺上报数据格式
    one_time = {
        "endpoint": ip,
        "tags": "",
        "timestamp": timestamp,
        "metric": "one.time",
        "value": value
    }
    result.append(one_time)   #向列表中添加数据
    #接下来获取prog文件夹时间,与系统时间做对比并执行逻辑
    if (abs(int(sys_hour) - int(two_hour)) == 23 ) and ((60 - int(sys_min) + int(two_min)) <= 40):
        value1 = 1
    elif (abs(int(sys_hour) - int(two_hour)) == 0 ) and ( abs(int(sys_min) - int(two_min)) <= 40 ):
        value1 = 1
    elif (abs(int(sys_hour) - int(two_hour)) == 1 ) and ( int(sys_min) + 60 - int(two_min) <= 40 ):
        value1 = 1
    else:
        value1 = 0
    two_time = {
        "endpoint": ip,
        "tags": "",
        "timestamp": timestamp,
        "metric": "two.time",
        "value": value1
    }
    result.append(two_time)
    stdout_result = json.dumps(result) #将result列表转换为字符串,否则夜鹰不识别
    print stdout_result
if __name__ == '__main__':
    check_time()    #调用主函数

下面的例子用python3编写,如图:

#!/usr/bin/python3      #用python3编写
# -*- coding:utf-8 -*-
import time
import datetime
import math
import json
import subprocess #python执行shell用此模块,python2用commands模块
def check_time():
    '''
    @本脚本主要功能为获取两个文件夹时间与系统时间做对比
     如果时间差大于40分钟,则告警
    @author: gongguan
    @time:  20210529
    '''
    result = []
    value = 0
    value1 = 0
    ip = subprocess.getoutput('''ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1''')
    timestamp = int(time.time())
    date_time = datetime.datetime.now() #获取当前系统时间,注意此时的类型为data_time日期类型
    sys_time = str(date_time).split(' ')[1] #如果要用split切割,需要先转换为字符串才能切割
    sys_hour = sys_time.split(':')[0]  #获取系统的小时
    sys_min = sys_time.split(':')[1]   #获取系统的分钟数
    one_modify_time = subprocess.getoutput('''stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' ''')
    one_hour = subprocess.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $1}' ''')
    one_min=subprocess.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $2}' ''')
    two_hour = subprocess.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $1}' ''')
    two_min = subprocess.getoutput(''' stat one | grep Modify | awk '{print $3}' |awk -F "." '{print $1}' |awk -F ':' '{print $2}' ''')
    #接下来将获取到的文件夹时间与系统的时间做对比并执行逻辑
    if (abs(int(sys_hour) - int(one_hour)) == 23 ) and ((60 - int(sys_min) + int(one_min)) <= 40):
        value = 1
    elif (abs(int(sys_hour) - int(one_hour)) == 0 ) and ( abs(int(sys_min) - int(one_min)) <= 40 ):
        value = 1
    else:
        value = 0
    one_time = {
        'endpoint': ip,
        'tags': '',
        'timestamp': timestamp,
        'metric': 'one.time',
        'value': value
    }
    result.append(one_time)
    #接下来获取prog文件夹时间,与系统时间做对比并执行逻辑
    if (abs(int(sys_hour) - int(two_hour)) == 23 ) and ((60 - int(sys_min) + int(two_min)) <= 40):
        value1 = 1
    elif (abs(int(sys_hour) - int(two_hour)) == 0 ) and ( abs(int(sys_min) - int(two_min)) <= 40 ):
        value1 = 1
    else:
        value1 = 0
    two_time = {
        'endpoint': ip,
        'tags': '',
        'timestamp': timestamp,
        'metric': 'two.time',
        'value': value1
    }
    result.append(two_time)      #向列表中添加元素
    check_result = json.dumps(result)   #注意:一定要将列表转换为字符串类型的json格式,否则夜莺不识别
    print(check_result)
if __name__ == '__main__':
    check_time()

注意:一定要将列表转换为json类型的字符串才能被夜莺识别

备注:

如果在windows下编写的python插件,放在linux上采集时可能会提示no such file or directory,此时可以将脚本内容重新拷贝一份,然后在Linux上重新编辑新的脚本文件,粘贴进去即可

下面例子使用golang编写的判断mysql从库状态,如下:

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"time"
)

// 此处的结构体名可自定义
type slavestatus struct {
	SlaveIORunning      string `gorm:"column:Slave_IO_Running"`
	SlaveSQLRunning     string `gorm:"column:Slave_SQL_Running"`
	SecondsBehindMaster int    `gorm:"column:Seconds_Behind_Master"`
}

var db *gorm.DB

func init() {
	user := "root"
	password := "123456"
	dbName := "mysql"
	host := "172.23.1.8
	port := "3307"
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", user, password, host, port, dbName)
	var err error
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	sqlDB, err := db.DB()
	//SetMaxIdleConns 设置空闲连接池中连接的最大数量
	sqlDB.SetMaxIdleConns(10)
	//SetMaxOpenConns 设置打开数据库连接的最大数量
	sqlDB.SetMaxOpenConns(100)
	//SetConnMaxLifetime 设置了连接可复用的最大时间
	sqlDB.SetConnMaxLifetime(time.Hour)
}
func main() {
	out := `
          [ 
            {
               "endpoint": "172.23.1.8",
               "tags": "",
               "timestamp": 0,
               "metric": "erp_slave_status",
               "value": %d
            }
          ]
        `
	var ss slavestatus
	db.Raw("show slave status").Scan(&ss)
	//fmt.Println("从库IO线程:", ss.SlaveIORunning)
	//fmt.Println("从库SQL线程:", ss.SlaveSQLRunning)
	//fmt.Println("从库落后主库时间:", ss.SecondsBehindMaster)
        if ss.SlaveIORunning != "Yes" || ss.SlaveSQLRunning != "Yes"{
             fmt.Println(fmt.Sprintf(out,0))
        }else {
            fmt.Println(fmt.Sprintf(out,1))
        }
}

标签