夜莺监控之自定义插件(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))
}
}


