Skip to content

Commit

Permalink
增加邮件、Slack、WebHook通知
Browse files Browse the repository at this point in the history
  • Loading branch information
ouqiang committed Jun 4, 2017
1 parent 5d1c717 commit 91f4565
Show file tree
Hide file tree
Showing 45 changed files with 6,848 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
.idear
80 changes: 79 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,80 @@
# supervisor-listener
# supervisor-event-listener
Supervisor事件通知, 支持邮件, Slack, WebHook

## 简介
Supervisor是*nix环境下的进程管理工具, 可以把前台进程转换为守护进程, 当进程异常退出时自动重启.
supervisor-event-listener监听进程异常退出事件, 并发送通知.

## 下载
* [Linux-64位](http://opns468ov.bkt.clouddn.com/supervisor/supervisor-event-listener-linux-amd64.tar.gz)
* [Mac OS-64位](http://opns468ov.bkt.clouddn.com/supervisor/supervisor-event-listener-darwin-amd64.tar.gz)

### 源码安装
* `go get -u github.com/ouqiang/supervisor-event-listener`

## Supervisor配置
```ini
[eventlistener:supervisor-event-listener]
; 默认读取配置文件/etc/supervisor-event-listener.ini
command=/path/to/supervisor-event-listener
; 指定配置文件路径
;command=/path/to/supervisor-event-listener -c /path/to/supervisor-event-listener.ini
events=PROCESS_STATE_EXITED
......
```

## 配置文件, 默认读取`/etc/supervisor-event-listener.ini`

```ini
[default]
# 通知类型 mail,slack,webhook 只能选择一种
notify_type = mail

# 邮件服务器配置
mail.server.user = [email protected]
mail.server.password = 123456
mail.server.host = smtp.163.com
mail.server.port = 25

# 邮件收件人配置, 多个收件人, 逗号分隔
mail.user = [email protected]

# Slack配置
slack.webhook_url = https://hooks.slack.com/services/xxxx/xxx/xxxx
slack.channel = exception

# WebHook通知URL配置
webhook_url = http://my.webhook.com

```

## 通知内容
邮件、Slack
```shell
Host: ip(hostname)
Process: process-name
PID: 6152
EXITED FROM state: RUNNING
```
WebHook, Post请求, 字段含义查看Supervisor文档
```json
{
"Header": {
"Ver": "3.0",
"Server": "supervisor",
"Serial": 11,
"Pool": "supervisor-listener",
"PoolSerial": 11,
"EventName": "PROCESS_STATE_EXITED",
"Len": 84
},
"Payload": {
"Ip": "ip(hostname)",
"ProcessName": "process-name",
"GroupName": "group-name",
"FromState": "RUNNING",
"Expected": 0,
"Pid": 6371
}
}
```
96 changes: 96 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

# set -x -u
# 构建应用, supervisor-event-listener.tar.gz
# ./build.sh -p linux -a amd64
# 参数含义
# -p 指定平台(linux|darwin)
# -a 指定体系架构(amd64|386), 默认amd64


TEMP_DIR=`date +%s`-temp-`echo $RANDOM`

# 目标平台 linux,darwin
OS=''
# 目标平台架构
ARCH=''
# 应用名称
APP_NAME='supervisor-event-listener'
# 可执行文件名
EXEC_NAME=''
# 压缩包名称
COMPRESS_FILE=''


# -p 平台 -a 架构
while getopts "p:a:" OPT;
do
case $OPT in
p) OS=$OPTARG
;;
a) ARCH=$OPTARG
;;
esac
done

if [[ -z $OS ]];then
echo "平台不能为空"
exit 1
fi

if [[ $OS && $OS != 'linux' && $OS != 'darwin' ]];then
echo '平台错误,支持的平台 linux darmin(osx)'
exit 1
fi

if [[ -z $ARCH ]];then
ARCH='amd64'
fi

if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
echo 'arch错误,仅支持 386 amd64'
exit 1
fi

echo '开始编译'

GOOS=$OS GOARCH=$ARCH go build -ldflags '-w'

if [[ $? != 0 ]];then
exit 1
fi
echo '编译完成'


EXEC_NAME=${APP_NAME}
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz

mkdir -p $TEMP_DIR/$APP_NAME
if [[ $? != 0 ]]; then
exit 1
fi

# 需要打包的文件
PACKAGE_FILENAME=(supervisor-event-listener.ini ${EXEC_NAME})

echo '复制文件到临时目录'
# 复制文件到临时目录
for i in ${PACKAGE_FILENAME[*]}
do
cp -r $i $TEMP_DIR/$APP_NAME
done


echo '压缩文件'
# 压缩文件
cd $TEMP_DIR

tar czf $COMPRESS_FILE *
mv $COMPRESS_FILE ../
cd ../

rm $EXEC_NAME
rm -rf $TEMP_DIR

echo '打包完成'
echo '生成压缩文件--' $COMPRESS_FILE
142 changes: 142 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package config

import (
"flag"
"strings"
"fmt"
"os"
"gopkg.in/ini.v1"
"github.com/ouqiang/supervisor-event-listener/utils"
)

type Config struct {
NotifyType string
WebHook WebHook
MailServer MailServer
MailUser MailUser
Slack Slack
}

type WebHook struct {
Url string
}

type Slack struct {
WebHookUrl string
Channel string
}

// 邮件服务器
type MailServer struct {
User string
Password string
Host string
Port int
}

// 接收邮件的用户
type MailUser struct {
Email []string
}

func ParseConfig() *Config {
var configFile string
flag.StringVar(&configFile, "c", "/etc/supervisor-event-listener.ini", "config file")
flag.Parse()
configFile = strings.TrimSpace(configFile)
if configFile == "" {
Exit("请指定配置文件路径")
}
file, err := ini.Load(configFile)
if err != nil {
Exit("读取配置文件失败#" + err.Error())
}
section := file.Section("default")
notifyType := section.Key("notify_type").String()
notifyType = strings.TrimSpace(notifyType)
if !utils.InStringSlice([]string{"mail", "slack", "webhook"}, notifyType) {
Exit("不支持的通知类型-" + notifyType)
}

config := &Config{}
config.NotifyType = notifyType
switch notifyType {
case "mail":
config.MailServer = parseMailServer(section)
config.MailUser = parseMailUser(section)
case "slack":
config.Slack = parseSlack(section)
case "webhook":
config.WebHook = parseWebHook(section)
}

return config
}

func parseMailServer(section *ini.Section) MailServer {
user := section.Key("mail.server.user").String()
user = strings.TrimSpace(user)
password := section.Key("mail.server.password").String()
password = strings.TrimSpace(password)
host := section.Key("mail.server.host").String()
host = strings.TrimSpace(host)
port, portErr := section.Key("mail.server.port").Int()
if user == "" || password == "" || host == "" || portErr != nil {
Exit("邮件服务器配置错误")
}

mailServer := MailServer{}
mailServer.User = user
mailServer.Password = password
mailServer.Host = host
mailServer.Port = port

return mailServer
}

func parseMailUser(section *ini.Section) MailUser {
user := section.Key("mail.user").String()
user = strings.TrimSpace(user)
if user == "" {
Exit("邮件收件人配置错误")
}
mailUser := MailUser{}
mailUser.Email = strings.Split(user, ",")

return mailUser
}


func parseSlack(section *ini.Section) Slack {
webHookUrl := section.Key("slack.webhook_url").String()
webHookUrl = strings.TrimSpace(webHookUrl)
channel := section.Key("slack.channel").String()
channel = strings.TrimSpace(channel)
if webHookUrl == "" || channel == "" {
Exit("Slack配置错误")
}

slack := Slack{}
slack.WebHookUrl = webHookUrl
slack.Channel = channel

return slack
}

func parseWebHook(section *ini.Section) WebHook {
url := section.Key("webhook_url").String()
url = strings.TrimSpace(url)
if url == "" {
Exit("WebHookUrl配置错误")
}
webHook := WebHook{}
webHook.Url = url


return webHook
}

func Exit(msg string) {
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}
Loading

0 comments on commit 91f4565

Please sign in to comment.