commit
0fd6176ac6
|
@ -0,0 +1 @@
|
|||
web
|
|
@ -0,0 +1,28 @@
|
|||
FROM golang AS builder
|
||||
# RUN apk add --no-cache git gcc
|
||||
WORKDIR /app
|
||||
|
||||
# comment this if using vendor
|
||||
# ENV GOPROXY=https://mod.gokit.info
|
||||
# COPY go.mod go.sum ./
|
||||
# RUN go mod download
|
||||
|
||||
COPY . .
|
||||
ENV GOPROXY=https://mod.gokit.info
|
||||
RUN ./control build docker
|
||||
|
||||
FROM buildpack-deps:buster-curl
|
||||
LABEL maintainer="llitfkitfk@gmail.com"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /app/docker/scripts /app/scripts
|
||||
COPY --from=builder /app/etc /app/etc
|
||||
# Change default address (hard code)
|
||||
RUN ./scripts/sed.sh
|
||||
|
||||
COPY --from=builder /app/bin /usr/local/bin
|
||||
|
||||
|
||||
# ENTRYPOINT []
|
||||
# CMD []
|
12
README.md
12
README.md
|
@ -15,7 +15,17 @@ Nightingale user manual: [https://n9e.didiyun.com/](https://n9e.didiyun.com/)
|
|||
mkdir -p $GOPATH/src/github.com/didi
|
||||
cd $GOPATH/src/github.com/didi
|
||||
git clone https://github.com/didi/nightingale.git
|
||||
cd nightingale && ./control build
|
||||
cd nightingale
|
||||
# export env[GOPROXY] if your network is not good
|
||||
# export GOPROXY=https://mirrors.aliyun.com/goproxy/
|
||||
./control build
|
||||
```
|
||||
|
||||
## Quick Start (need install docker for [mac](https://docs.docker.com/docker-for-mac/install/)/[win](https://docs.docker.com/docker-for-windows/install/))
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
# open http://localhost in web browser
|
||||
```
|
||||
|
||||
## Team
|
||||
|
|
|
@ -15,7 +15,10 @@ Nightingale是一套衍生自Open-Falcon的互联网监控解决方案,融入
|
|||
mkdir -p $GOPATH/src/github.com/didi
|
||||
cd $GOPATH/src/github.com/didi
|
||||
git clone https://github.com/didi/nightingale.git
|
||||
cd nightingale && ./control build
|
||||
cd nightingale
|
||||
# 如果网络环境不好可以尝试aliyun的mirror
|
||||
# export GOPROXY=https://mirrors.aliyun.com/goproxy/
|
||||
./control build
|
||||
```
|
||||
|
||||
## 团队
|
||||
|
|
18
control
18
control
|
@ -139,8 +139,16 @@ build_one()
|
|||
go build -o n9e-${mod} --tags "md5" src/modules/${mod}/${mod}.go
|
||||
}
|
||||
|
||||
build_docker()
|
||||
{
|
||||
mod=$1
|
||||
go build -o bin/n9e-${mod} --tags "md5" src/modules/${mod}/${mod}.go
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
export GO111MODULE=on
|
||||
|
||||
mod=$1
|
||||
if [ "x${mod}" = "x" ]; then
|
||||
build_one monapi
|
||||
|
@ -151,6 +159,16 @@ build()
|
|||
build_one tsdb
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "x${mod}" = "xdocker" ]; then
|
||||
build_docker monapi
|
||||
build_docker transfer
|
||||
build_docker index
|
||||
build_docker judge
|
||||
build_docker collector
|
||||
build_docker tsdb
|
||||
return
|
||||
fi
|
||||
|
||||
build_one $mod
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
version: "3"
|
||||
volumes:
|
||||
mysql-data:
|
||||
services:
|
||||
nginx:
|
||||
image: nginx:stable-alpine
|
||||
ports:
|
||||
- 80:80
|
||||
volumes:
|
||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./docker/nginx/conf.d:/etc/nginx/conf.d
|
||||
- ./pub:/home/n9e/pub
|
||||
api:
|
||||
build: .
|
||||
image: api
|
||||
|
||||
monapi:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-monapi
|
||||
ports:
|
||||
- 5800:5800
|
||||
|
||||
transfer:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-transfer
|
||||
ports:
|
||||
- 5810:5810
|
||||
- 5811:5811
|
||||
|
||||
tsdb:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-tsdb
|
||||
ports:
|
||||
- 5820:5820
|
||||
- 5821:5821
|
||||
|
||||
index:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-index
|
||||
ports:
|
||||
- 5830:5830
|
||||
- 5831:5831
|
||||
|
||||
judge:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-judge
|
||||
ports:
|
||||
- 5840:5840
|
||||
- 5841:5841
|
||||
|
||||
collector:
|
||||
image: api
|
||||
restart: always
|
||||
command: n9e-collector
|
||||
ports:
|
||||
- 2058:2058
|
||||
|
||||
# web:
|
||||
# build:
|
||||
# context: web
|
||||
# restart: always
|
||||
# command: npm run dev
|
||||
# ports:
|
||||
# - 8010:8010
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
restart: always
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=1234
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- ./sql:/docker-entrypoint-initdb.d
|
||||
- mysql-data:/var/lib/mysql
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
user root;
|
||||
|
||||
worker_processes auto;
|
||||
worker_cpu_affinity auto;
|
||||
|
||||
error_log /var/log/nginx/error.log;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
include /usr/share/nginx/modules/*.conf;
|
||||
|
||||
events {
|
||||
use epoll;
|
||||
worker_connections 204800;
|
||||
}
|
||||
|
||||
http {
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
|
||||
proxy_connect_timeout 500ms;
|
||||
proxy_send_timeout 1000ms;
|
||||
proxy_read_timeout 3000ms;
|
||||
proxy_buffers 64 8k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
proxy_redirect off;
|
||||
proxy_next_upstream error invalid_header timeout http_502 http_504;
|
||||
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Real-Port $remote_port;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
upstream n9e.monapi {
|
||||
server monapi:5800;
|
||||
keepalive 10;
|
||||
}
|
||||
|
||||
upstream n9e.index {
|
||||
server index:5830;
|
||||
keepalive 10;
|
||||
}
|
||||
|
||||
upstream n9e.transfer {
|
||||
server transfer:5810;
|
||||
keepalive 10;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
# Load configuration files for the default server block.
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
|
||||
location / {
|
||||
root /home/n9e/pub;
|
||||
}
|
||||
|
||||
location /api/portal {
|
||||
proxy_pass http://n9e.monapi;
|
||||
}
|
||||
|
||||
location /api/index {
|
||||
proxy_pass http://n9e.index;
|
||||
}
|
||||
|
||||
location /api/transfer {
|
||||
proxy_pass http://n9e.transfer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
|
||||
sed -i 's/127.0.0.1:6379/redis:6379/g' /app/etc/judge.yml
|
||||
sed -i 's/127.0.0.1:6379/redis:6379/g' /app/etc/monapi.yml
|
||||
sed -i 's/127.0.0.1:3306/mysql:3306/g' /app/etc/mysql.yml
|
||||
sed -i 's/127.0.0.1:5821/tsdb:5821/g' /app/etc/transfer.yml
|
|
@ -1,11 +1,10 @@
|
|||
# use shell if specify is blank
|
||||
logger:
|
||||
dir: logs/collector
|
||||
level: WARNING
|
||||
keepHours: 2
|
||||
identity:
|
||||
specify: ""
|
||||
shell: /usr/sbin/ifconfig `/usr/sbin/route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1
|
||||
shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|awk -F ':' '{print $NF}'|head -n 1
|
||||
sys:
|
||||
# timeout in ms
|
||||
# interval in second
|
||||
|
@ -28,4 +27,4 @@ sys:
|
|||
- cpu.core.irq
|
||||
- cpu.core.softirq
|
||||
- cpu.core.iowait
|
||||
- cpu.core.steal
|
||||
- cpu.core.steal
|
||||
|
|
|
@ -4,4 +4,4 @@ logger:
|
|||
keepHours: 2
|
||||
identity:
|
||||
specify: ""
|
||||
shell: /usr/sbin/ifconfig `/usr/sbin/route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1
|
||||
shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|awk -F ':' '{print $NF}'|head -n 1
|
||||
|
|
|
@ -6,6 +6,7 @@ query:
|
|||
redis:
|
||||
addrs:
|
||||
- 127.0.0.1:6379
|
||||
db: 0
|
||||
pass: ""
|
||||
# timeout:
|
||||
# conn: 500
|
||||
|
@ -14,9 +15,9 @@ redis:
|
|||
|
||||
identity:
|
||||
specify: ""
|
||||
shell: /usr/sbin/ifconfig `/usr/sbin/route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|head -n 1
|
||||
shell: ifconfig `route|grep '^default'|awk '{print $NF}'`|grep inet|awk '{print $2}'|awk -F ':' '{print $NF}'|head -n 1
|
||||
|
||||
logger:
|
||||
dir: logs/judge
|
||||
level: WARNING
|
||||
keepHours: 2
|
||||
keepHours: 2
|
||||
|
|
|
@ -14,11 +14,19 @@ ldap:
|
|||
host: "ldap.example.org"
|
||||
port: 389
|
||||
baseDn: "dc=example,dc=org"
|
||||
# AD: manange@example.org
|
||||
bindUser: "cn=manager,dc=example,dc=org"
|
||||
bindPass: "*******"
|
||||
# openldap: (&(uid=%s))
|
||||
# AD: (&(sAMAccountName=%s))
|
||||
authFilter: "(&(uid=%s))"
|
||||
attributes:
|
||||
dispname: "cn"
|
||||
email: "mail"
|
||||
phone: "mobile"
|
||||
im: ""
|
||||
coverAttributes: false
|
||||
autoRegist: false
|
||||
tls: false
|
||||
startTLS: false
|
||||
|
||||
|
@ -44,6 +52,7 @@ link:
|
|||
# for alarm event and message queue
|
||||
redis:
|
||||
addr: "127.0.0.1:6379"
|
||||
db: 0
|
||||
pass: ""
|
||||
# in ms
|
||||
# timeout:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
user nginx;
|
||||
user root;
|
||||
|
||||
worker_processes auto;
|
||||
worker_cpu_affinity auto;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
n9e-collector
|
|
@ -1 +0,0 @@
|
|||
sshd
|
|
@ -0,0 +1 @@
|
|||
n9e-monapi
|
|
@ -0,0 +1 @@
|
|||
n9e-transfer
|
|
@ -0,0 +1 @@
|
|||
n9e-tsdb
|
|
@ -0,0 +1 @@
|
|||
n9e-index
|
|
@ -0,0 +1 @@
|
|||
n9e-judge
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index-3eb7c9ca4d88e3ad5c58.js","sources":["webpack:///index-3eb7c9ca4d88e3ad5c58.js"],"mappings":"AAAA;;;;;;;AAmsYA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index-5c8cbf958683e20086f5.js","sources":["webpack:///index-5c8cbf958683e20086f5.js"],"mappings":"AAAA;;;;;;;AAmsYA","sourceRoot":""}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html><head><meta charset="UTF-8"><title>Nightingale</title><link rel="shortcut icon" href="/favicon.ico"><link href="/index-3eb7c9ca4d88e3ad5c58.css" rel="stylesheet"></head><body><div id="react-content"></div><script src="/lib-033bee8514de110e36ef.dll.js"></script><script src="/index-3eb7c9ca4d88e3ad5c58.js"></script></body></html>
|
||||
<!doctype html><html><head><meta charset="UTF-8"><title>Nightingale</title><link rel="shortcut icon" href="/favicon.ico"><link href="/index-5c8cbf958683e20086f5.css" rel="stylesheet"></head><body><div id="react-content"></div><script src="/lib-033bee8514de110e36ef.dll.js"></script><script src="/index-5c8cbf958683e20086f5.js"></script></body></html>
|
|
@ -91,8 +91,8 @@ create table `tmp_chart` (
|
|||
create table `event_cur` (
|
||||
`id` bigint(20) unsigned not null AUTO_INCREMENT comment 'id',
|
||||
`sid` bigint(20) unsigned not null default 0 comment 'sid',
|
||||
`sname` varchar(256) not null default '' comment 'name, 报警通知名称',
|
||||
`node_path` varchar(256) not null default '' comment 'node path',
|
||||
`sname` varchar(255) not null default '' comment 'name, 报警通知名称',
|
||||
`node_path` varchar(255) not null default '' comment 'node path',
|
||||
`nid` int unsigned not null default '0' comment 'node id',
|
||||
`endpoint` varchar(255) not null default '' comment 'endpoint',
|
||||
`endpoint_alias` varchar(255) not null default '' comment 'endpoint alias',
|
||||
|
@ -103,7 +103,7 @@ create table `event_cur` (
|
|||
`detail` text comment 'counter points pred_points 详情',
|
||||
`hashid` varchar(128) not null default '' comment 'sid+counter hash',
|
||||
`etime` bigint(20) not null default 0 comment 'event ts',
|
||||
`value` varchar(256) not null default '' comment '当前值',
|
||||
`value` varchar(255) not null default '' comment '当前值',
|
||||
`users` varchar(512) not null default '[]' comment 'notify users',
|
||||
`groups` varchar(512) not null default '[]' comment 'notify groups',
|
||||
`info` varchar(512) not null default '' comment 'strategy info',
|
||||
|
@ -122,8 +122,8 @@ create table `event_cur` (
|
|||
create table `event` (
|
||||
`id` bigint(20) unsigned not null AUTO_INCREMENT comment 'id',
|
||||
`sid` bigint(20) unsigned not null default 0 comment 'sid',
|
||||
`sname` varchar(256) not null default '' comment 'name, 报警通知名称',
|
||||
`node_path` varchar(256) not null default '' comment 'node path',
|
||||
`sname` varchar(255) not null default '' comment 'name, 报警通知名称',
|
||||
`node_path` varchar(255) not null default '' comment 'node path',
|
||||
`nid` int unsigned not null default '0' comment 'node id',
|
||||
`endpoint` varchar(255) not null default '' comment 'endpoint',
|
||||
`endpoint_alias` varchar(255) not null default '' comment 'endpoint alias',
|
||||
|
@ -134,7 +134,7 @@ create table `event` (
|
|||
`detail` text comment 'counter points pred_points 详情',
|
||||
`hashid` varchar(128) not null default '' comment 'sid+counter hash',
|
||||
`etime` bigint(20) not null default 0 comment 'event ts',
|
||||
`value` varchar(256) not null default '' comment '当前值',
|
||||
`value` varchar(255) not null default '' comment '当前值',
|
||||
`users` varchar(512) not null default '[]' comment 'notify users',
|
||||
`groups` varchar(512) not null default '[]' comment 'notify groups',
|
||||
`info` varchar(512) not null default '' comment 'strategy info',
|
||||
|
@ -167,8 +167,8 @@ CREATE TABLE `stra` (
|
|||
`converge` varchar(45) NOT NULL DEFAULT '' COMMENT 'n秒最多报m次警',
|
||||
`recovery_notify` int(1) NOT NULL DEFAULT 1 COMMENT '1 发送恢复通知 0不发送恢复通知',
|
||||
`priority` int(1) NOT NULL DEFAULT 3 COMMENT '告警等级',
|
||||
`notify_group` varchar(256) NOT NULL DEFAULT '' COMMENT '告警通知组',
|
||||
`notify_user` varchar(256) NOT NULL DEFAULT '' COMMENT '告警通知人',
|
||||
`notify_group` varchar(255) NOT NULL DEFAULT '' COMMENT '告警通知组',
|
||||
`notify_user` varchar(255) NOT NULL DEFAULT '' COMMENT '告警通知人',
|
||||
`callback` varchar(1024) NOT NULL DEFAULT '' COMMENT 'callback url',
|
||||
`creator` varchar(64) NOT NULL COMMENT '创建者',
|
||||
`created` timestamp NOT NULL DEFAULT '1971-01-01 00:00:00' COMMENT 'created',
|
||||
|
|
|
@ -63,17 +63,14 @@ func (m *MetricValue) CheckValidity() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
//将保留字替换
|
||||
var illegal bool
|
||||
m.Metric, illegal = ReplaceReservedWords(m.Metric)
|
||||
if illegal {
|
||||
err = fmt.Errorf("Metric contains reserved word")
|
||||
//检测保留字
|
||||
if HasReservedWords(m.Metric) {
|
||||
err = fmt.Errorf("metric:%s contains reserved words:[\\t] [\\r] [\\n] [,] [ ] [=]", m.Metric)
|
||||
return
|
||||
}
|
||||
|
||||
m.Endpoint, illegal = ReplaceReservedWords(m.Endpoint)
|
||||
if illegal {
|
||||
err = fmt.Errorf("Endpoint contains reserved word:%s", m.Endpoint)
|
||||
if HasReservedWords(m.Endpoint) {
|
||||
err = fmt.Errorf("endpoint:%s contains reserved words:[\\t] [\\r] [\\n] [,] [ ] [=]", m.Endpoint)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -151,7 +148,7 @@ func (m *MetricValue) CheckValidity() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func ReplaceReservedWords(str string) (string, bool) {
|
||||
func HasReservedWords(str string) bool {
|
||||
if -1 == strings.IndexFunc(str,
|
||||
func(r rune) bool {
|
||||
return r == '\t' ||
|
||||
|
@ -159,27 +156,13 @@ func ReplaceReservedWords(str string) (string, bool) {
|
|||
r == '\n' ||
|
||||
r == ',' ||
|
||||
r == ' ' ||
|
||||
r == ':' ||
|
||||
r == '='
|
||||
}) {
|
||||
|
||||
return str, false
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Map(func(r rune) rune {
|
||||
if r == '\t' ||
|
||||
r == '\r' ||
|
||||
r == '\n' ||
|
||||
r == ',' ||
|
||||
r == ' ' ||
|
||||
r == ':' ||
|
||||
r == '=' {
|
||||
return '_'
|
||||
}
|
||||
return r
|
||||
}, str), true
|
||||
|
||||
return str, false
|
||||
return true
|
||||
}
|
||||
|
||||
func SortedTags(tags map[string]string) string {
|
||||
|
|
|
@ -24,6 +24,17 @@ type QueryDataForUI struct {
|
|||
Comparisons []int64 `json:"comparisons"` //环比多少时间
|
||||
}
|
||||
|
||||
type QueryDataForUIResp struct {
|
||||
Start int64 `json:"start"`
|
||||
End int64 `json:"end"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Counter string `json:"counter"`
|
||||
DsType string `json:"dstype"`
|
||||
Step int `json:"step"`
|
||||
Values []*RRDData `json:"values"`
|
||||
Comparison int64 `json:"comparison"`
|
||||
}
|
||||
|
||||
type QueryDataResp struct {
|
||||
Data []*TsdbQueryResponse
|
||||
Msg string
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/didi/nightingale/src/modules/monapi/config"
|
||||
"gopkg.in/ldap.v3"
|
||||
)
|
||||
|
||||
func genLdapAttributeSearchList() []string {
|
||||
ldapAttributes := []string{}
|
||||
attrs := config.Get().LDAP.Attributes
|
||||
if attrs.Dispname != "" {
|
||||
ldapAttributes = append(ldapAttributes, attrs.Dispname)
|
||||
}
|
||||
if attrs.Email != "" {
|
||||
ldapAttributes = append(ldapAttributes, attrs.Email)
|
||||
}
|
||||
if attrs.Phone != "" {
|
||||
ldapAttributes = append(ldapAttributes, attrs.Phone)
|
||||
}
|
||||
if attrs.Im != "" {
|
||||
ldapAttributes = append(ldapAttributes, attrs.Im)
|
||||
}
|
||||
return ldapAttributes
|
||||
}
|
||||
|
||||
func ldapReq(user, pass string) (*ldap.SearchResult, error) {
|
||||
var conn *ldap.Conn
|
||||
var err error
|
||||
lc := config.Get().LDAP
|
||||
addr := fmt.Sprintf("%s:%d", lc.Host, lc.Port)
|
||||
|
||||
if lc.TLS {
|
||||
conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: true})
|
||||
} else {
|
||||
conn, err = ldap.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot dial ldap: %v", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
if !lc.TLS && lc.StartTLS {
|
||||
if err := conn.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
|
||||
return nil, fmt.Errorf("ldap.conn startTLS fail: %v", err)
|
||||
}
|
||||
}
|
||||
//if bindUser is empty, anonymousSearch mode
|
||||
if lc.BindUser != "" {
|
||||
//BindSearch mode
|
||||
if err := conn.Bind(lc.BindUser, lc.BindPass); err != nil {
|
||||
return nil, fmt.Errorf("bind ldap fail: %v, use %s", err, lc.BindUser)
|
||||
}
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
lc.BaseDn, // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf(lc.AuthFilter, user), // The filter to apply
|
||||
genLdapAttributeSearchList(), // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
sr, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ldap search fail: %v", err)
|
||||
}
|
||||
|
||||
if len(sr.Entries) == 0 {
|
||||
return nil, fmt.Errorf("cannot find such user: %v", user)
|
||||
}
|
||||
|
||||
if len(sr.Entries) > 1 {
|
||||
return nil, fmt.Errorf("multi users is search, query user: %v", user)
|
||||
}
|
||||
|
||||
if err := conn.Bind(sr.Entries[0].DN, pass); err != nil {
|
||||
return nil, fmt.Errorf("password error")
|
||||
}
|
||||
return sr, nil
|
||||
}
|
|
@ -79,7 +79,7 @@ func NodeQueryPath(query string, limit int) (nodes []Node, err error) {
|
|||
|
||||
func TreeSearchByPath(query string) (nodes []Node, err error) {
|
||||
session := DB["mon"].NewSession()
|
||||
defer session.Clone()
|
||||
defer session.Close()
|
||||
|
||||
if strings.Contains(query, " ") {
|
||||
arr := strings.Fields(query)
|
||||
|
|
|
@ -20,7 +20,7 @@ type Team struct {
|
|||
|
||||
func (t *Team) Del() error {
|
||||
session := DB["uic"].NewSession()
|
||||
defer session.Clone()
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Begin(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
@ -118,6 +117,23 @@ func (u *User) CanModifyTeam(t *Team) (bool, error) {
|
|||
return cnt > 0, err
|
||||
}
|
||||
|
||||
func (u *User) CopyLdapAttr(sr *ldap.SearchResult) {
|
||||
attrs := config.Get().LDAP.Attributes
|
||||
if attrs.Dispname != "" {
|
||||
u.Dispname = sr.Entries[0].GetAttributeValue(attrs.Dispname)
|
||||
}
|
||||
if attrs.Email != "" {
|
||||
u.Email = sr.Entries[0].GetAttributeValue(attrs.Email)
|
||||
}
|
||||
if attrs.Phone != "" {
|
||||
u.Phone = sr.Entries[0].GetAttributeValue(attrs.Phone)
|
||||
}
|
||||
if attrs.Im != "" {
|
||||
u.Im = sr.Entries[0].GetAttributeValue(attrs.Im)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func InitRoot() {
|
||||
var u User
|
||||
has, err := DB["uic"].Where("username=?", "root").Get(&u)
|
||||
|
@ -147,78 +163,31 @@ func InitRoot() {
|
|||
}
|
||||
|
||||
func LdapLogin(user, pass string) error {
|
||||
var conn *ldap.Conn
|
||||
var err error
|
||||
|
||||
lc := config.Get().LDAP
|
||||
addr := fmt.Sprintf("%s:%d", lc.Host, lc.Port)
|
||||
|
||||
if lc.TLS {
|
||||
conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: true})
|
||||
} else {
|
||||
conn, err = ldap.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot dial ldap: %v", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
if !lc.TLS && lc.StartTLS {
|
||||
err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap.conn startTLS fail: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = conn.Bind(lc.BindUser, lc.BindPass)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bind ldap fail: %v, use %s", err, lc.BindUser)
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
lc.BaseDn, // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf(lc.AuthFilter, user), // The filter to apply
|
||||
[]string{}, // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
|
||||
sr, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ldap search fail: %v", err)
|
||||
}
|
||||
|
||||
if len(sr.Entries) == 0 {
|
||||
return fmt.Errorf("cannot find such user: %v", user)
|
||||
}
|
||||
|
||||
if len(sr.Entries) > 1 {
|
||||
return fmt.Errorf("multi users is search, query user: %v", user)
|
||||
}
|
||||
|
||||
err = conn.Bind(sr.Entries[0].DN, pass)
|
||||
if err != nil {
|
||||
return fmt.Errorf("password error")
|
||||
}
|
||||
|
||||
cnt, err := DB["uic"].Where("username=?", user).Count(new(User))
|
||||
sr, err := ldapReq(user, pass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cnt > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
u := &User{
|
||||
Username: user,
|
||||
Password: "******",
|
||||
Dispname: "",
|
||||
Email: "",
|
||||
var u User
|
||||
has, err := DB["uic"].Where("username=?", user).Get(&u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.CopyLdapAttr(sr)
|
||||
if has {
|
||||
if config.Get().LDAP.CoverAttributes {
|
||||
_, err := DB["uic"].Where("id=?", u.Id).Update(u)
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if !config.Get().LDAP.AutoRegist {
|
||||
return fmt.Errorf("user has not be created, may be you should enable auto regist: %v", user)
|
||||
}
|
||||
|
||||
u.Username = user
|
||||
u.Password = "******"
|
||||
_, err = DB["uic"].Insert(u)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ func main() {
|
|||
identity.Init(cfg.Identity)
|
||||
if identity.Identity == "127.0.0.1" {
|
||||
log.Fatalln("endpoint: 127.0.0.1, cannot work")
|
||||
} else {
|
||||
log.Println("endpoint:", identity.Identity)
|
||||
}
|
||||
|
||||
sys.Init(cfg.Sys)
|
||||
|
@ -70,7 +72,6 @@ func main() {
|
|||
|
||||
funcs.BuildMappers()
|
||||
funcs.Collect()
|
||||
stra.GetCollects()
|
||||
|
||||
//插件采集
|
||||
plugins.Detect()
|
||||
|
@ -137,6 +138,5 @@ func ending() {
|
|||
func start() {
|
||||
runner.Init()
|
||||
fmt.Println("collector start, use configuration file:", *conf)
|
||||
fmt.Println("runner.Cwd:", runner.Cwd)
|
||||
fmt.Println("runner.Endpoint:", runner.Hostname)
|
||||
fmt.Println("runner.cwd:", runner.Cwd)
|
||||
}
|
||||
|
|
|
@ -62,16 +62,16 @@ func Parse(conf string) error {
|
|||
"enable": true,
|
||||
"timeout": 1000,
|
||||
"interval": 10, //采集策略更新时间
|
||||
"portPath": "/home/n9e/etc/port",
|
||||
"procPath": "/home/n9e/etc/proc",
|
||||
"logPath": "/home/n9e/etc/log",
|
||||
"portPath": "./etc/port",
|
||||
"procPath": "./etc/proc",
|
||||
"logPath": "./etc/log",
|
||||
"api": "/api/portal/collects/",
|
||||
})
|
||||
|
||||
viper.SetDefault("sys", map[string]interface{}{
|
||||
"timeout": 1000, //请求超时时间
|
||||
"interval": 10, //基础指标上报周期
|
||||
"plugin": "/home/n9e/plugin",
|
||||
"plugin": "./plugin",
|
||||
})
|
||||
|
||||
err = viper.Unmarshal(&Config)
|
||||
|
|
|
@ -18,9 +18,9 @@ func init() {
|
|||
}
|
||||
|
||||
func Update() error {
|
||||
strategys := stra.GetLogCollects()
|
||||
strategies := stra.GetLogCollects()
|
||||
|
||||
err := UpdateGlobalStrategy(strategys)
|
||||
err := UpdateGlobalStrategy(strategies)
|
||||
if err != nil {
|
||||
logger.Errorf("Update Strategy cache error ! [msg:%v]", err)
|
||||
return err
|
||||
|
|
|
@ -74,7 +74,7 @@ func getCollects() (CollectResp, error) {
|
|||
url := fmt.Sprintf("http://%s%s%s", addr, StraConfig.Api, identity.Identity)
|
||||
err = httplib.Get(url).SetTimeout(time.Duration(StraConfig.Timeout) * time.Millisecond).ToJSON(&res)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("get collects from remote failed, error:%v", err)
|
||||
err = fmt.Errorf("get collects from remote:%s failed, error:%v", url, err)
|
||||
}
|
||||
|
||||
return res, err
|
||||
|
|
|
@ -146,8 +146,8 @@ func DeepCopyStringMap(p map[string]string) map[string]string {
|
|||
|
||||
const PATTERN_EXCLUDE_PARTITION = "```EXCLUDE```"
|
||||
|
||||
func parsePattern(strategys []*Strategy) {
|
||||
for _, st := range strategys {
|
||||
func parsePattern(strategies []*Strategy) {
|
||||
for _, st := range strategies {
|
||||
patList := strings.Split(st.Pattern, PATTERN_EXCLUDE_PARTITION)
|
||||
|
||||
if len(patList) == 1 {
|
||||
|
@ -163,8 +163,8 @@ func parsePattern(strategys []*Strategy) {
|
|||
}
|
||||
}
|
||||
|
||||
func updateRegs(strategys []*Strategy) {
|
||||
for _, st := range strategys {
|
||||
func updateRegs(strategies []*Strategy) {
|
||||
for _, st := range strategies {
|
||||
st.TagRegs = make(map[string]*regexp.Regexp, 0)
|
||||
st.ParseSucc = false
|
||||
|
||||
|
|
|
@ -48,7 +48,9 @@ func Push(items []*dataobj.MetricValue) {
|
|||
logger.Error(err)
|
||||
continue
|
||||
} else {
|
||||
logger.Info("push succ, reply: ", reply)
|
||||
if reply.Msg != "ok" {
|
||||
logger.Error("some item push err", reply)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ func ListPlugins(dir string) map[string]*Plugin {
|
|||
filename := f.Name()
|
||||
arr := strings.Split(filename, "_")
|
||||
if len(arr) < 2 {
|
||||
logger.Warningf("plugin:%s name illegal, should be: $cycle_$xx", filename)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -39,10 +40,16 @@ func ListPlugins(dir string) map[string]*Plugin {
|
|||
var cycle int
|
||||
cycle, err = strconv.Atoi(arr[0])
|
||||
if err != nil {
|
||||
logger.Warningf("plugin:%s name illegal, should be: $cycle_$xx %v", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fpath, err := filepath.Abs(filepath.Join(dir, filename))
|
||||
if err != nil {
|
||||
logger.Warningf("plugin:%s absolute path get err:%v", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fpath := filepath.Join(dir, filename)
|
||||
plugin := &Plugin{FilePath: fpath, MTime: f.ModTime().Unix(), Cycle: cycle}
|
||||
ret[fpath] = plugin
|
||||
}
|
||||
|
|
|
@ -50,8 +50,7 @@ func PluginRun(plugin *Plugin) {
|
|||
|
||||
timeout := plugin.Cycle*1000 - 500 //比运行周期少500毫秒
|
||||
|
||||
fpath := filepath.Join(plugin.FilePath)
|
||||
|
||||
fpath := plugin.FilePath
|
||||
if !file.IsExist(fpath) {
|
||||
logger.Error("no such plugin:", fpath)
|
||||
return
|
||||
|
|
|
@ -20,6 +20,6 @@ func loopDetect() {
|
|||
|
||||
func detect() {
|
||||
ps := stra.GetProcCollects()
|
||||
DelNoPorcCollect(ps)
|
||||
AddNewPorcCollect(ps)
|
||||
DelNoProcCollect(ps)
|
||||
AddNewProcCollect(ps)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ var (
|
|||
ProcsWithScheduler = make(map[string]*ProcScheduler)
|
||||
)
|
||||
|
||||
func DelNoPorcCollect(newCollect map[string]*model.ProcCollect) {
|
||||
func DelNoProcCollect(newCollect map[string]*model.ProcCollect) {
|
||||
for currKey, currProc := range Procs {
|
||||
newProc, ok := newCollect[currKey]
|
||||
if !ok || currProc.LastUpdated != newProc.LastUpdated {
|
||||
|
@ -18,7 +18,7 @@ func DelNoPorcCollect(newCollect map[string]*model.ProcCollect) {
|
|||
}
|
||||
}
|
||||
|
||||
func AddNewPorcCollect(newCollect map[string]*model.ProcCollect) {
|
||||
func AddNewProcCollect(newCollect map[string]*model.ProcCollect) {
|
||||
for target, newProc := range newCollect {
|
||||
if _, ok := Procs[target]; ok && newProc.LastUpdated == Procs[target].LastUpdated {
|
||||
continue
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/didi/nightingale/src/toolkits/address"
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
|
||||
"github.com/toolkits/pkg/concurrent/semaphore"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
|
@ -49,11 +50,12 @@ func reportEndpoint(endpoints []interface{}) {
|
|||
err := httplib.Post(url).JSONBodyQuiet(m).SetTimeout(3*time.Second).Header("x-srv-token", "monapi-builtin-token").ToJSON(&body)
|
||||
if err != nil {
|
||||
logger.Warningf("curl %s fail: %v. retry", url, err)
|
||||
stats.Counter.Set("report.endpoint.err", 1)
|
||||
continue
|
||||
}
|
||||
|
||||
if body.Err != "" {
|
||||
if body.Err != "" { //数据库连接出错会出现此情况
|
||||
logger.Warningf("curl %s fail: %s. retry", url, body.Err)
|
||||
stats.Counter.Set("report.endpoint.err", 1)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/didi/nightingale/src/toolkits/compress"
|
||||
"github.com/didi/nightingale/src/toolkits/identity"
|
||||
"github.com/didi/nightingale/src/toolkits/report"
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
)
|
||||
|
||||
type CacheSection struct {
|
||||
|
@ -72,8 +73,8 @@ func StartPersist(interval int) {
|
|||
err := Persist("normal")
|
||||
if err != nil {
|
||||
logger.Error("Persist err:", err)
|
||||
stats.Counter.Set("persist.err", 1)
|
||||
}
|
||||
//logger.Infof("clean %+v, took %.2f ms\n", cleanRet, float64(time.Since(start).Nanoseconds())*1e-6)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@ func (t *TagkvIndex) GetTagkv() []*TagPair {
|
|||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
tagkvs := []*TagPair{}
|
||||
var vs []string
|
||||
|
||||
for k, vm := range t.Tagkv {
|
||||
var vs []string
|
||||
for v, _ := range vm {
|
||||
vs = append(vs, v)
|
||||
}
|
||||
|
@ -50,8 +51,8 @@ func (t *TagkvIndex) GetTagkvMap() map[string][]string {
|
|||
defer t.RUnlock()
|
||||
tagkvs := make(map[string][]string)
|
||||
|
||||
var vs []string
|
||||
for k, vm := range t.Tagkv {
|
||||
var vs []string
|
||||
for v, _ := range vm {
|
||||
vs = append(vs, v)
|
||||
}
|
||||
|
|
|
@ -93,9 +93,8 @@ func GetTagPairs(c *gin.Context) {
|
|||
|
||||
resp := []*IndexTagkvResp{}
|
||||
|
||||
tagkvFilter := make(map[string]map[string]struct{})
|
||||
|
||||
for _, metric := range recv.Metrics {
|
||||
tagkvFilter := make(map[string]map[string]struct{})
|
||||
tagkvs := []*cache.TagPair{}
|
||||
|
||||
for _, endpoint := range recv.Endpoints {
|
||||
|
@ -107,6 +106,7 @@ func GetTagPairs(c *gin.Context) {
|
|||
}
|
||||
|
||||
tagkvMap := metricIndex.TagkvMap.GetTagkvMap()
|
||||
|
||||
for tagk, tagvs := range tagkvMap {
|
||||
tagvFilter, exists := tagkvFilter[tagk]
|
||||
if !exists {
|
||||
|
|
|
@ -42,6 +42,5 @@ func Push(event *dataobj.Event) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
stats.Counter.Set("redis.failed", 1)
|
||||
return fmt.Errorf("redis publish failed finally:%v", err)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
@ -14,6 +15,7 @@ var Config RedisSection
|
|||
type RedisSection struct {
|
||||
Addrs []string `yaml:"addrs"`
|
||||
Pass string `yaml:"pass"`
|
||||
DB int `yaml:"db"`
|
||||
Idle int `yaml:"idle"`
|
||||
Timeout TimeoutSection `yaml:"timeout"`
|
||||
Prefix string `yaml:"prefix"`
|
||||
|
@ -30,6 +32,7 @@ func Init(cfg RedisSection) {
|
|||
|
||||
addrs := cfg.Addrs
|
||||
pass := cfg.Pass
|
||||
db := cfg.DB
|
||||
maxIdle := cfg.Idle
|
||||
idleTimeout := 240 * time.Second
|
||||
|
||||
|
@ -44,6 +47,7 @@ func Init(cfg RedisSection) {
|
|||
c, err := redis.Dial("tcp", addr, redis.DialConnectTimeout(connTimeout), redis.DialReadTimeout(readTimeout), redis.DialWriteTimeout(writeTimeout))
|
||||
if err != nil {
|
||||
logger.Errorf("conn redis err:%v", err)
|
||||
stats.Counter.Set("redis.conn.failed", 1)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -51,6 +55,17 @@ func Init(cfg RedisSection) {
|
|||
if _, err := c.Do("AUTH", pass); err != nil {
|
||||
c.Close()
|
||||
logger.Errorf("ERR: redis auth fail:%v", err)
|
||||
stats.Counter.Set("redis.conn.failed", 1)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if db != 0 {
|
||||
if _, err := c.Do("SELECT", db); err != nil {
|
||||
c.Close()
|
||||
logger.Error("redis select db fail, db: ", db)
|
||||
stats.Counter.Set("redis.conn.failed", 1)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ func Judge(stra *model.Stra, exps []model.Exp, historyData []*dataobj.RRDData, f
|
|||
stats.Counter.Set("running", 1)
|
||||
|
||||
if len(exps) < 1 {
|
||||
stats.Counter.Set("stra.err", 1)
|
||||
stats.Counter.Set("stra.illegal", 1)
|
||||
logger.Warningf("stra:%v exp is null", stra)
|
||||
return
|
||||
}
|
||||
|
@ -105,21 +105,21 @@ func Judge(stra *model.Stra, exps []model.Exp, historyData []*dataobj.RRDData, f
|
|||
Hashid: getHashId(stra.Id, firstItem),
|
||||
}
|
||||
|
||||
sendEventIfNeed(historyData, isTriggered, now, event)
|
||||
sendEventIfNeed(historyData, isTriggered, event)
|
||||
}
|
||||
}()
|
||||
|
||||
leftValue, isTriggered = judgeItemWithStrategy(stra, historyData, exps[0], firstItem, now)
|
||||
if !isTriggered {
|
||||
return
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
value = fmt.Sprintf("%s: %v", exp.Metric, leftValue)
|
||||
} else {
|
||||
value += fmt.Sprintf("; %s: %v", exp.Metric, leftValue)
|
||||
}
|
||||
|
||||
if !isTriggered {
|
||||
return
|
||||
}
|
||||
|
||||
//与条件情况下执行
|
||||
if len(exps) > 1 {
|
||||
if exps[1].Func == "nodata" { //nodata重新查询索引来进行告警判断
|
||||
|
@ -387,7 +387,7 @@ func GetReqs(stra *model.Stra, metric string, endpoints []string, now int64) ([]
|
|||
return reqs, nil
|
||||
}
|
||||
|
||||
func sendEventIfNeed(historyData []*dataobj.RRDData, isTriggered bool, now int64, event *dataobj.Event) {
|
||||
func sendEventIfNeed(historyData []*dataobj.RRDData, isTriggered bool, event *dataobj.Event) {
|
||||
lastEvent, exists := cache.LastEvents.Get(event.ID)
|
||||
if isTriggered {
|
||||
event.EventType = EVENT_ALERT
|
||||
|
@ -421,6 +421,7 @@ func sendEvent(event *dataobj.Event) {
|
|||
|
||||
err := redi.Push(event)
|
||||
if err != nil {
|
||||
stats.Counter.Set("redis.push.failed", 1)
|
||||
logger.Errorf("push event:%v err:%v", event, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,11 +56,13 @@ func getStrategy(opts StrategySection) {
|
|||
|
||||
if err != nil {
|
||||
logger.Warningf("get strategy from remote failed, error:%v", err)
|
||||
stats.Counter.Set("stra.get.err", 1)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.Err != "" {
|
||||
logger.Warningf("get strategy from remote failed, error:%v", resp.Err)
|
||||
stats.Counter.Set("stra.get.err", 1)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ type cleanerSection struct {
|
|||
type redisSection struct {
|
||||
Addr string `yaml:"addr"`
|
||||
Pass string `yaml:"pass"`
|
||||
DB int `yaml:"db"`
|
||||
Idle int `yaml:"idle"`
|
||||
Timeout timeoutSection `yaml:"timeout"`
|
||||
}
|
||||
|
@ -64,14 +65,24 @@ type httpSection struct {
|
|||
}
|
||||
|
||||
type ldapSection struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
BaseDn string `yaml:"baseDn"`
|
||||
BindUser string `yaml:"bindUser"`
|
||||
BindPass string `yaml:"bindPass"`
|
||||
AuthFilter string `yaml:"authFilter"`
|
||||
TLS bool `yaml:"tls"`
|
||||
StartTLS bool `yaml:"startTLS"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
BaseDn string `yaml:"baseDn"`
|
||||
BindUser string `yaml:"bindUser"`
|
||||
BindPass string `yaml:"bindPass"`
|
||||
AuthFilter string `yaml:"authFilter"`
|
||||
Attributes ldapAttributes `yaml:"attributes"`
|
||||
CoverAttributes bool `yaml:"coverAttributes"`
|
||||
AutoRegist bool `yaml:"autoRegist"`
|
||||
TLS bool `yaml:"tls"`
|
||||
StartTLS bool `yaml:"startTLS"`
|
||||
}
|
||||
|
||||
type ldapAttributes struct {
|
||||
Dispname string `yaml:"dispname"`
|
||||
Phone string `yaml:"phone"`
|
||||
Email string `yaml:"email"`
|
||||
Im string `yaml:"im"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/didi/nightingale/src/model"
|
||||
"github.com/didi/nightingale/src/modules/monapi/config"
|
||||
"github.com/didi/nightingale/src/modules/monapi/scache"
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
)
|
||||
|
||||
func CheckJudgeLoop() {
|
||||
|
@ -19,6 +20,7 @@ func CheckJudgeLoop() {
|
|||
time.Sleep(duration)
|
||||
err := CheckJudge()
|
||||
if err != nil {
|
||||
stats.Counter.Set("get.judge.err", 1)
|
||||
logger.Error("check judge fail: ", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func consume(event *model.Event) {
|
|||
SetEventStatus(event, model.STATUS_UPGRADE)
|
||||
|
||||
if needNotify {
|
||||
if event.EventType == config.ALERT && NeedCallback(event.Sid) {
|
||||
if NeedCallback(event.Sid) {
|
||||
if err := PushCallbackEvent(event); err != nil {
|
||||
logger.Errorf("push event to callback queue failed, callbackEvent: %+v", event)
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func consume(event *model.Event) {
|
|||
return
|
||||
}
|
||||
|
||||
if event.EventType == config.ALERT && NeedCallback(event.Sid) {
|
||||
if NeedCallback(event.Sid) {
|
||||
if err := PushCallbackEvent(event); err != nil {
|
||||
logger.Errorf("push event to callback queue failed, callbackEvent: %+v", event)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/toolkits/pkg/logger"
|
||||
|
||||
"github.com/didi/nightingale/src/modules/monapi/config"
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
)
|
||||
|
||||
var RedisConnPool *redis.Pool
|
||||
|
@ -16,6 +17,7 @@ func InitRedis() {
|
|||
|
||||
addr := cfg.Redis.Addr
|
||||
pass := cfg.Redis.Pass
|
||||
db := cfg.Redis.DB
|
||||
maxIdle := cfg.Redis.Idle
|
||||
idleTimeout := 240 * time.Second
|
||||
|
||||
|
@ -29,6 +31,8 @@ func InitRedis() {
|
|||
Dial: func() (redis.Conn, error) {
|
||||
c, err := redis.Dial("tcp", addr, redis.DialConnectTimeout(connTimeout), redis.DialReadTimeout(readTimeout), redis.DialWriteTimeout(writeTimeout))
|
||||
if err != nil {
|
||||
logger.Errorf("conn redis err:%v", err)
|
||||
stats.Counter.Set("redis.conn.failed", 1)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -36,6 +40,16 @@ func InitRedis() {
|
|||
if _, err := c.Do("AUTH", pass); err != nil {
|
||||
c.Close()
|
||||
logger.Error("redis auth fail, pass: ", pass)
|
||||
stats.Counter.Set("redis.conn.failed", 1)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if db != 0 {
|
||||
if _, err := c.Do("SELECT", db); err != nil {
|
||||
c.Close()
|
||||
logger.Error("redis select db fail, db: ", db)
|
||||
stats.Counter.Set("redis.select.failed", 1)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/didi/nightingale/src/dataobj"
|
||||
"github.com/didi/nightingale/src/modules/transfer/calc"
|
||||
"github.com/didi/nightingale/src/toolkits/address"
|
||||
"github.com/didi/nightingale/src/toolkits/stats"
|
||||
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/net/httplib"
|
||||
|
@ -95,15 +96,15 @@ func FetchDataForUI(input dataobj.QueryDataForUI) []*dataobj.TsdbQueryResponse {
|
|||
//进行数据计算
|
||||
aggrDatas := []*dataobj.TsdbQueryResponse{}
|
||||
if input.AggrFunc != "" && len(resp) > 1 {
|
||||
aggrData := &dataobj.TsdbQueryResponse{
|
||||
Start: input.Start,
|
||||
End: input.End,
|
||||
}
|
||||
|
||||
aggrCounter := make(map[string][]*dataobj.TsdbQueryResponse)
|
||||
if len(input.GroupKey) == 0 || getTags(resp[0].Counter) == "" {
|
||||
aggrData := &dataobj.TsdbQueryResponse{
|
||||
Start: input.Start,
|
||||
End: input.End,
|
||||
Values: calc.Compute(input.AggrFunc, resp),
|
||||
}
|
||||
//没有聚合 tag, 或者曲线没有其他 tags, 直接所有曲线进行计算
|
||||
aggrData.Values = calc.Compute(input.AggrFunc, resp)
|
||||
aggrDatas = append(aggrDatas, aggrData)
|
||||
} else {
|
||||
for _, data := range resp {
|
||||
|
@ -132,9 +133,12 @@ func FetchDataForUI(input dataobj.QueryDataForUI) []*dataobj.TsdbQueryResponse {
|
|||
}
|
||||
|
||||
for counter, datas := range aggrCounter {
|
||||
aggrData.Counter = counter
|
||||
aggrData.Values = calc.Compute(input.AggrFunc, datas)
|
||||
|
||||
aggrData := &dataobj.TsdbQueryResponse{
|
||||
Start: input.Start,
|
||||
End: input.End,
|
||||
Counter: counter,
|
||||
Values: calc.Compute(input.AggrFunc, datas),
|
||||
}
|
||||
aggrDatas = append(aggrDatas, aggrData)
|
||||
}
|
||||
}
|
||||
|
@ -161,10 +165,12 @@ func fetchDataSync(start, end int64, consolFun, endpoint, counter string, step i
|
|||
defer func() {
|
||||
<-worker
|
||||
}()
|
||||
stats.Counter.Set("query.tsdb", 1)
|
||||
|
||||
data, err := fetchData(start, end, consolFun, endpoint, counter, step)
|
||||
if err != nil {
|
||||
logger.Warning(err)
|
||||
stats.Counter.Set("query.data.err", 1)
|
||||
}
|
||||
dataChan <- data
|
||||
return
|
||||
|
|
|
@ -107,6 +107,7 @@ func Send2TsdbTask(Q *list.SafeListLimited, node string, addr string, concurrent
|
|||
|
||||
// 将数据 打入 某个Tsdb的发送缓存队列, 具体是哪一个Tsdb 由一致性哈希 决定
|
||||
func Push2TsdbSendQueue(items []*dataobj.MetricValue) {
|
||||
errCnt := 0
|
||||
for _, item := range items {
|
||||
tsdbItem := convert2TsdbItem(item)
|
||||
stats.Counter.Set("tsdb.queue.push", 1)
|
||||
|
@ -118,19 +119,18 @@ func Push2TsdbSendQueue(items []*dataobj.MetricValue) {
|
|||
}
|
||||
|
||||
cnode := Config.ClusterList[node]
|
||||
errCnt := 0
|
||||
for _, addr := range cnode.Addrs {
|
||||
Q := TsdbQueues[node+addr]
|
||||
if !Q.PushFront(tsdbItem) {
|
||||
errCnt += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// statistics
|
||||
if errCnt > 0 {
|
||||
stats.Counter.Set("tsdb.queue.err", errCnt)
|
||||
logger.Error("Push2TsdbSendQueue err num: ", errCnt)
|
||||
}
|
||||
// statistics
|
||||
if errCnt > 0 {
|
||||
stats.Counter.Set("tsdb.queue.err", errCnt)
|
||||
logger.Error("Push2TsdbSendQueue err num: ", errCnt)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ func Send2JudgeTask(Q *list.SafeListLimited, addr string, concurrent int) {
|
|||
|
||||
if !sendOk {
|
||||
stats.Counter.Set("points.out.judge.err", 1)
|
||||
logger.Errorf("send judge %s fail: %v", addr, err)
|
||||
logger.Errorf("send %v to judge %s fail: %v", judgeItems, addr, err)
|
||||
}
|
||||
|
||||
}(addr, judgeItems, count)
|
||||
|
@ -180,6 +180,7 @@ func Send2JudgeTask(Q *list.SafeListLimited, addr string, concurrent int) {
|
|||
}
|
||||
|
||||
func Push2JudgeSendQueue(items []*dataobj.MetricValue) {
|
||||
errCnt := 0
|
||||
for _, item := range items {
|
||||
key := str.PK(item.Metric, item.Endpoint)
|
||||
stras := cache.StraMap.GetByKey(key)
|
||||
|
@ -203,11 +204,13 @@ func Push2JudgeSendQueue(items []*dataobj.MetricValue) {
|
|||
|
||||
q, exists := JudgeQueues.Get(stra.JudgeInstance)
|
||||
if exists {
|
||||
q.PushFront(judgeItem)
|
||||
if !q.PushFront(judgeItem) {
|
||||
errCnt += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
stats.Counter.Set("judge.queue.err", errCnt)
|
||||
}
|
||||
|
||||
// 打到Tsdb的数据,要根据rrdtool的特定 来限制 step、counterType、timestamp
|
||||
|
|
|
@ -46,18 +46,50 @@ func QueryData(c *gin.Context) {
|
|||
func QueryDataForUI(c *gin.Context) {
|
||||
stats.Counter.Set("data.ui.qp10s", 1)
|
||||
var input dataobj.QueryDataForUI
|
||||
|
||||
var respData []*dataobj.QueryDataForUIResp
|
||||
errors.Dangerous(c.ShouldBindJSON(&input))
|
||||
start := input.Start
|
||||
end := input.End
|
||||
|
||||
resp := backend.FetchDataForUI(input)
|
||||
for _, d := range resp {
|
||||
data := &dataobj.QueryDataForUIResp{
|
||||
Start: d.Start,
|
||||
End: d.End,
|
||||
Endpoint: d.Endpoint,
|
||||
Counter: d.Counter,
|
||||
DsType: d.DsType,
|
||||
Step: d.Step,
|
||||
Values: d.Values,
|
||||
}
|
||||
respData = append(respData, data)
|
||||
}
|
||||
|
||||
if len(input.Comparisons) > 1 {
|
||||
for i := 1; i < len(input.Comparisons); i++ {
|
||||
input.Start = input.Start - input.Comparisons[i]
|
||||
input.End = input.End - input.Comparisons[i]
|
||||
comparison := input.Comparisons[i]
|
||||
input.Start = start - comparison
|
||||
input.End = end - comparison
|
||||
res := backend.FetchDataForUI(input)
|
||||
resp = append(resp, res...)
|
||||
for _, d := range res {
|
||||
for j := range d.Values {
|
||||
d.Values[j].Timestamp += comparison
|
||||
}
|
||||
|
||||
data := &dataobj.QueryDataForUIResp{
|
||||
Start: d.Start,
|
||||
End: d.End,
|
||||
Endpoint: d.Endpoint,
|
||||
Counter: d.Counter,
|
||||
DsType: d.DsType,
|
||||
Step: d.Step,
|
||||
Values: d.Values,
|
||||
Comparison: comparison,
|
||||
}
|
||||
respData = append(respData, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render.Data(c, resp, nil)
|
||||
render.Data(c, respData, nil)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func Config(r *gin.Engine) {
|
|||
sys.GET("/alive-judges", judges)
|
||||
|
||||
sys.POST("/push", PushData)
|
||||
sys.POST("/data", QueryDataForJudge)
|
||||
sys.POST("/data", QueryData)
|
||||
sys.POST("/data/ui", QueryDataForUI)
|
||||
}
|
||||
|
||||
|
|
|
@ -27,9 +27,10 @@ func (t *Transfer) Push(args []*dataobj.MetricValue, reply *dataobj.TransferResp
|
|||
err := v.CheckValidity()
|
||||
if err != nil {
|
||||
stats.Counter.Set("points.in.err", 1)
|
||||
logger.Warningf("item is illegal item:%s err:%v", v, err)
|
||||
msg := fmt.Sprintf("item is illegal item:%s err:%v", v, err)
|
||||
logger.Warningf(msg)
|
||||
reply.Invalid += 1
|
||||
reply.Msg += fmt.Sprintf("%v\n", err)
|
||||
reply.Msg += msg
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ func GetIndexLoop() {
|
|||
func GetIndex() {
|
||||
instances, err := report.GetAlive("index", Config.HbsMod)
|
||||
if err != nil {
|
||||
stats.Counter.Set("index.get.err", 1)
|
||||
stats.Counter.Set("get.index.err", 1)
|
||||
logger.Warningf("get index list err:%v", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ func handleItems(items []*dataobj.TsdbItem) {
|
|||
//todo hash冲突问题需要解决
|
||||
if err := cache.Caches.Push(item.Key, item.Timestamp, item.Value); err != nil {
|
||||
stats.Counter.Set("points.in.err", 1)
|
||||
|
||||
logger.Warningf("push obj error, obj: %v, error: %v\n", items[i], err)
|
||||
fail++
|
||||
}
|
||||
|
|
|
@ -231,6 +231,7 @@ func FlushRRD(flushChunks map[interface{}][]*cache.Chunk) {
|
|||
|
||||
err := FlushFile(seriesID, items)
|
||||
if err != nil {
|
||||
stats.Counter.Set("flush.rrd.err", 1)
|
||||
logger.Errorf("flush %v data to rrd err:%v", seriesID, err)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -18,11 +18,12 @@ type IdentitySection struct {
|
|||
func Init(identity IdentitySection) {
|
||||
if identity.Specify != "" {
|
||||
Identity = identity.Specify
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
Identity, err = sys.CmdOutTrim("bash", "-c", identity.Shell)
|
||||
if err != nil {
|
||||
log.Fatalln("[F] cannot get hostname")
|
||||
log.Fatalln("[F] cannot get identity")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,17 @@ package stats
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/didi/nightingale/src/dataobj"
|
||||
"github.com/didi/nightingale/src/toolkits/identity"
|
||||
"github.com/didi/nightingale/src/toolkits/address"
|
||||
|
||||
"github.com/toolkits/pkg/file"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
"github.com/toolkits/pkg/runner"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -17,8 +21,14 @@ var (
|
|||
)
|
||||
|
||||
func Init(prefix string, addr ...string) {
|
||||
if len(addr) > 0 {
|
||||
if len(addr) > 0 && addr[0] != "" {
|
||||
//如果配置了 addr,使用 addr 参数
|
||||
PushUrl = addr[0]
|
||||
|
||||
} else if file.IsExist(path.Join(runner.Cwd, "etc", "address.yml")) {
|
||||
//address.yml 存在,则使用配置文件的地址
|
||||
port := address.GetHTTPPort("collector")
|
||||
PushUrl = fmt.Sprintf("http://127.0.0.1:%d/api/collector/push", port)
|
||||
}
|
||||
|
||||
Counter = NewCounter(prefix)
|
||||
|
@ -42,7 +52,6 @@ func Push() {
|
|||
func NewMetricValue(metric string, value int64) *dataobj.MetricValue {
|
||||
item := &dataobj.MetricValue{
|
||||
Metric: metric,
|
||||
Endpoint: identity.Identity,
|
||||
Timestamp: time.Now().Unix(),
|
||||
ValueUntyped: value,
|
||||
CounterType: "GAUGE",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -0,0 +1,7 @@
|
|||
FROM node:lts-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install
|
|
@ -76,7 +76,10 @@ class index extends Component<Props, State> {
|
|||
processData = async (endpoints: Endpoint[]) => {
|
||||
if (this.state.displayBindNode && endpoints) {
|
||||
const idents = _.map(endpoints, item => item.ident);
|
||||
const endpointNodes = await request(`${api.endpoint}s/bindings?idents=${_.join(idents, ',')}`);
|
||||
let endpointNodes: any[] = [];
|
||||
if (idents.length) {
|
||||
endpointNodes = await request(`${api.endpoint}s/bindings?idents=${_.join(idents, ',')}`);
|
||||
}
|
||||
const newEndpoints = _.map(endpoints, (item) => {
|
||||
const current = _.find(endpointNodes, { ident: item.ident });
|
||||
const nodes = _.get(current, 'nodes', []);
|
||||
|
|
|
@ -184,7 +184,7 @@ class index extends Component<null, State> {
|
|||
<div className="mt10 mb10" style={{ wordBreak: 'break-word' }}>
|
||||
{_.get(selectedNode, 'path')}
|
||||
</div>
|
||||
<Popconfirm title="确定要删除这个节点吗?" onConfirm={this.handleDelNode}>
|
||||
<Popconfirm disabled={isPdlNode} title="确定要删除这个节点吗?" onConfirm={this.handleDelNode}>
|
||||
<Button disabled={isPdlNode}>删除</Button>
|
||||
</Popconfirm>
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue