├── logs └── README.md ├── .gitignore ├── run.sh ├── stop.sh ├── README.md ├── src ├── tools │ ├── logger │ │ ├── dup.go │ │ └── logger.go │ ├── mail │ │ └── mail.go │ ├── export.go │ ├── cfg │ │ └── cfg.go │ ├── logfile │ │ └── logfile.go │ ├── timer │ │ └── timer.go │ └── process │ │ └── process.go ├── global │ └── os.go └── DaemonServer │ └── main.go ├── Makefile ├── scripts └── restart.sh └── data └── config.json /logs/README.md: -------------------------------------------------------------------------------- 1 | log存放目录 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .idea/ 3 | bin/ 4 | pkg 5 | *.log 6 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sh stop.sh 4 | 5 | $GOSERVERDAEMON_PATH/bin/DaemonServer 6 | -------------------------------------------------------------------------------- /stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | function func(){ 3 | killall -9 $1 4 | 5 | killall -0 $1 6 | while [ $? -ne 1 ]; do 7 | sleep 1 8 | killall -0 $1 9 | done 10 | } 11 | 12 | if [ $# -eq 0 ] 13 | then 14 | func DaemonServer 15 | else 16 | func $1 17 | fi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoServerDaemon 2 | 3 | ###介绍 4 | 5 | 使用go编写的一个游戏服务器监控程序,测试中。。。 6 | 7 | 包含功能: 8 | 9 | 1:检测进程是否存在,若不存在则邮件报警,执行配置的脚本 10 | 11 | 2:检测CPU占用,超过90%则邮件报警 12 | 13 | 3:检测内存占用,超过90%则邮件报警 14 | 15 | 4:检测游戏log,检测指定关键字,若存在,则邮件报警,执行配置的脚本 16 | 17 | 18 | === 19 | -------------------------------------------------------------------------------- /src/tools/logger/dup.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import "io" 4 | 5 | type Repeater struct { 6 | out1, out2 io.Writer // 1-input -> 2-output 7 | } 8 | 9 | //----------------------------------------------- output replicator 10 | func (r *Repeater) Write(p []byte) (int, error) { 11 | var n int 12 | var e error 13 | 14 | if r.out1 != nil { 15 | n, e = r.out1.Write(p) 16 | } 17 | 18 | if r.out2 != nil { 19 | n, e = r.out2.Write(p) 20 | } 21 | 22 | return n, e 23 | } 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: .FORCE 2 | GO=go 3 | DOT=dot 4 | GOYACC=$(GO) tool yacc 5 | 6 | SRC_DIR = ./src 7 | PROTO_INSTALL_FILE_DIR = ./src/code.google.com/p/goprotobuf/ 8 | 9 | all: 10 | $(GO) install DaemonServer 11 | 12 | clean: 13 | rm -rf bin pkg 14 | 15 | fmt: 16 | $(GO) fmt $(SRC_DIR)/... 17 | 18 | #交叉编译: 19 | #首先进入go源码目录 20 | #cd /usr/local/go/src/ 21 | #执行sudo GOOS=linux GOARCH=amd64 ./make.bash 生成linux下的编译文件pkg 22 | #以上步骤在go1.5中应该已不再需要,但还未测试 23 | publish: 24 | GOOS=linux GOARCH=amd64 $(GO) build -o DaemonServer DaemonServer 25 | -------------------------------------------------------------------------------- /src/tools/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "strings" 7 | "global" 8 | ) 9 | 10 | //系统日志 11 | func StartLogger(path string) { 12 | if !strings.HasPrefix(path, "/") { 13 | path = global.GetCurrPath() + "../logs/" + path 14 | } 15 | 16 | // 打开日志文件 17 | file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) 18 | if err != nil { 19 | log.Println("cannot open logfile %v\n", err) 20 | } 21 | 22 | // 创建MUX 23 | var r Repeater 24 | r.out1 = os.Stdout 25 | r.out2 = file 26 | log.SetOutput(&r) 27 | } -------------------------------------------------------------------------------- /src/global/os.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path/filepath" 7 | "strings" 8 | "runtime" 9 | ) 10 | 11 | 12 | //获取当前执行路径 13 | func GetCurrPath() string { 14 | file, _ := exec.LookPath(os.Args[0]) 15 | path, _ := filepath.Abs(file) 16 | splitstring := strings.Split(path, "/") 17 | size := len(splitstring) 18 | splitstring = strings.Split(path, splitstring[size-1]) 19 | return splitstring[0] 20 | } 21 | 22 | //当前是否是Linux系统 23 | func IsLinux() bool { 24 | return runtime.GOOS == "linux" 25 | } 26 | 27 | //当前是否是Mac系统 28 | func IsMac() bool { 29 | return runtime.GOOS == "darwin" 30 | } 31 | -------------------------------------------------------------------------------- /src/tools/mail/mail.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "strings" 5 | "net/smtp" 6 | ) 7 | 8 | func SendToMail(user string, password string, host string, to string, subject string, body string, mailtype string) error { 9 | hp := strings.Split(host, ":") 10 | auth := smtp.PlainAuth("", user, password, hp[0]) 11 | var content_type string 12 | if mailtype == "html" { 13 | content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8" 14 | } else { 15 | content_type = "Content-Type: text/plain" + "; charset=UTF-8" 16 | } 17 | 18 | msg := []byte("To: " + to + "\r\nFrom: " + user + ">\r\nSubject: " + subject + "\r\n" + content_type + "\r\n\r\n" + body) 19 | send_to := strings.Split(to, ";") 20 | err := smtp.SendMail(host, auth, user, send_to, msg) 21 | return err 22 | } 23 | -------------------------------------------------------------------------------- /scripts/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | function func(){ 4 | killall -9 $1 5 | 6 | killall -0 $1 7 | while [ $? -ne 1 ]; do 8 | sleep 1 9 | killall -0 $1 10 | done 11 | } 12 | 13 | if [ $# -eq 0 ] 14 | then 15 | func GateServer 16 | func LoginServer 17 | func GameServer 18 | func WorldServer 19 | func DBServer 20 | func LogServer 21 | else 22 | func $1 23 | fi 24 | 25 | $GOGAMESERVER_PATH/bin/LogServer & 26 | sleep 1 27 | $GOGAMESERVER_PATH/bin/DBServer & 28 | sleep 1 29 | $GOGAMESERVER_PATH/bin/GateServer & 30 | sleep 1 31 | $GOGAMESERVER_PATH/bin/LoginServer & 32 | sleep 1 33 | $GOGAMESERVER_PATH/bin/WorldServer & 34 | sleep 1 35 | $GOGAMESERVER_PATH/bin/GameServer -s=1 & 36 | sleep 1 37 | $GOGAMESERVER_PATH/bin/GameServer -s=2 & 38 | sleep 1 39 | $GOGAMESERVER_PATH/bin/GameServer -s=3 & 40 | -------------------------------------------------------------------------------- /src/tools/export.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "strings" 7 | ) 8 | 9 | import ( 10 | "tools/cfg" 11 | ) 12 | 13 | var ( 14 | debug_open bool 15 | ) 16 | 17 | func init() { 18 | debug_open = cfg.Get().Debug 19 | } 20 | 21 | //------------------------------------------------ 严重程度由高到低 22 | func ERR(v ...interface{}) { 23 | log.Printf("\033[1;4;31m[ERROR] %v \033[0m\n", strings.TrimRight(fmt.Sprintln(v...), "\n")) 24 | } 25 | 26 | func WARN(v ...interface{}) { 27 | log.Printf("\033[1;33m[WARN] %v \033[0m\n", strings.TrimRight(fmt.Sprintln(v...), "\n")) 28 | } 29 | 30 | func INFO(v ...interface{}) { 31 | log.Printf("\033[32m[INFO] %v \033[0m\n", strings.TrimRight(fmt.Sprintln(v...), "\n")) 32 | } 33 | 34 | func NOTICE(v ...interface{}) { 35 | log.Printf("[NOTICE] %v\n", strings.TrimRight(fmt.Sprintln(v...), "\n")) 36 | } 37 | 38 | func DEBUG(v ...interface{}) { 39 | if debug_open { 40 | log.Printf("\033[1;35m[DEBUG] %v \033[0m\n", strings.TrimRight(fmt.Sprintln(v...), "\n")) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/tools/cfg/cfg.go: -------------------------------------------------------------------------------- 1 | package cfg 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "io/ioutil" 7 | "encoding/json" 8 | "global" 9 | ) 10 | 11 | type ConfigInfo struct { 12 | Debug bool `json:"debug"` 13 | Daemon_log string `json:"daemon_log"` 14 | LoopTime_Process int64 `json:"loopTime_process"` 15 | LoopTime_Cpu int64 `json:"loopTime_cpu"` 16 | LoopTime_Mem int64 `json:"loopTime_mem"` 17 | LoopTime_Log int64 `json:"loopTime_log"` 18 | Servers []ServerInfo `json:"servers"` 19 | SendMailUser string `json:"send_mail_user"` 20 | SendMailPwd string `json:"send_mail_pwd"` 21 | SendMailHost string `json:"send_mail_host"` 22 | ReceiveMailList string `json:"receive_mail_list"` 23 | ServerName string `json:"server_name"` 24 | LogPath string `json:"logs_path"` 25 | LogKeywords []KeywordInfo `json:"logs_keywords"` 26 | } 27 | 28 | type ServerInfo struct { 29 | Flag string `json:"flag"` 30 | User string `json:"user"` 31 | RestartScript string `json:"restart_script"` 32 | } 33 | 34 | type KeywordInfo struct { 35 | Key string `json:"key"` 36 | RestartScript string `json:"restart_script"` 37 | } 38 | 39 | var ( 40 | info ConfigInfo 41 | ) 42 | 43 | 44 | func init() { 45 | var configPath string = global.GetCurrPath() + "../data/config.json" 46 | _load_config(configPath) 47 | 48 | } 49 | 50 | func Get() ConfigInfo { 51 | return info 52 | } 53 | 54 | func _load_config(path string) { 55 | f, err := os.Open(path) 56 | if err != nil { 57 | log.Println(path, err) 58 | return 59 | } 60 | 61 | bytes, err := ioutil.ReadAll(f) 62 | if err != nil { 63 | log.Println(path, err) 64 | return 65 | } 66 | 67 | err = json.Unmarshal(bytes, &info) 68 | if err != nil { 69 | log.Println(path, err) 70 | return 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug" : true, 3 | "daemon_log" : "daemon.log", 4 | "loopTime_process" : 120, 5 | "loopTime_cpu" : 120, 6 | "loopTime_mem" : 120, 7 | "loopTime_log" : 300, 8 | "send_mail_user" : "xxxx@163.com", 9 | "send_mail_pwd" : "xxxx", 10 | "send_mail_host" : "smtp.163.com:25", 11 | "receive_mail_list" : "4607384@qq.com;yangsong@egret.com", 12 | "server_name" : "太极1服", 13 | "servers" : [ 14 | { 15 | "flag" : "LogServer", 16 | "user" : "egret", 17 | "restart_script": "restart.sh" 18 | }, 19 | { 20 | "flag" : "DBServer", 21 | "user" : "egret", 22 | "restart_script": "restart.sh" 23 | }, 24 | { 25 | "flag" : "GateServer", 26 | "user" : "egret", 27 | "restart_script": "restart.sh" 28 | }, 29 | { 30 | "flag" : "LoginServer", 31 | "user" : "egret", 32 | "restart_script": "restart.sh" 33 | }, 34 | { 35 | "flag" : "WorldServer", 36 | "user" : "egret", 37 | "restart_script": "restart.sh" 38 | }, 39 | { 40 | "flag" : "GameServer -s=1", 41 | "user" : "egret", 42 | "restart_script": "restart.sh" 43 | }, 44 | { 45 | "flag" : "GameServer -s=2", 46 | "user" : "egret", 47 | "restart_script": "restart.sh" 48 | }, 49 | { 50 | "flag" : "GameServer -s=3", 51 | "user" : "egret", 52 | "restart_script": "restart.sh" 53 | }, 54 | { 55 | "flag" : "redis-server", 56 | "user" : "egret", 57 | "restart_script": "" 58 | }, 59 | { 60 | "flag" : "mysqld", 61 | "user" : "_mysql", 62 | "restart_script": "" 63 | } 64 | ], 65 | "logs_path" : "/Users/egret/Documents/taiji/log/", 66 | "logs_keywords" : [ 67 | { 68 | "key" : "queue is full! ", 69 | "restart_script": "restart.sh" 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /src/tools/logfile/logfile.go: -------------------------------------------------------------------------------- 1 | package logfile 2 | 3 | import ( 4 | "os" 5 | . "tools" 6 | "path/filepath" 7 | "bufio" 8 | ) 9 | 10 | var ( 11 | LogFiles map[string]uint64 = make(map[string]uint64) 12 | ) 13 | 14 | func InitLogFile(dirPath string) { 15 | err := filepath.Walk(dirPath, func(filePath string, f os.FileInfo, err error) error { 16 | if f == nil { 17 | return err 18 | } 19 | if f.IsDir() { 20 | return nil 21 | } 22 | 23 | LogFiles[filePath] = GetFileLine(filePath) 24 | return nil 25 | }) 26 | 27 | if err != nil{ 28 | ERR("遍历log文件夹错误:", err) 29 | } 30 | } 31 | 32 | func GetFileLine(filePath string) uint64 { 33 | f, err := os.Open(filePath) 34 | if err != nil { 35 | ERR(filePath, err) 36 | return 0 37 | } 38 | 39 | defer f.Close() 40 | 41 | var sumLine uint64 = 0 42 | buf := bufio.NewReader(f) 43 | for { 44 | _, err := buf.ReadString('\n') 45 | if err != nil { 46 | break 47 | } 48 | sumLine += 1 49 | } 50 | 51 | return sumLine 52 | } 53 | 54 | func CheckLogFileUpdate(dirPath string) string { 55 | var updateTexts string = "" 56 | err := filepath.Walk(dirPath, func(filePath string, fInfo os.FileInfo, err error) error { 57 | if fInfo == nil { 58 | return err 59 | } 60 | if fInfo.IsDir() { 61 | return nil 62 | } 63 | 64 | f, err := os.Open(filePath) 65 | if err != nil { 66 | ERR(filePath, err) 67 | return err 68 | } 69 | 70 | defer f.Close() 71 | 72 | oldLine, exists := LogFiles[filePath] 73 | if exists == false { 74 | oldLine = 0 75 | } 76 | 77 | var sumLine uint64 = 0 78 | buf := bufio.NewReader(f) 79 | for { 80 | line, err := buf.ReadString('\n') 81 | if err != nil { 82 | break 83 | } 84 | sumLine += 1 85 | if sumLine > oldLine { 86 | oldLine += 1 87 | updateTexts += line 88 | } 89 | } 90 | 91 | LogFiles[filePath] = sumLine 92 | 93 | return nil 94 | }) 95 | 96 | if err != nil{ 97 | ERR("遍历log文件夹错误:", err) 98 | } 99 | 100 | return updateTexts 101 | } -------------------------------------------------------------------------------- /src/tools/timer/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "container/list" 5 | "sync/atomic" 6 | "time" 7 | . "tools" 8 | ) 9 | 10 | type timerEvent struct { 11 | ID uint64 // TimerID 12 | Func func() // 回调函数 13 | Delay int64 // 执行间隔 14 | RepeatCount int // 执行次数 15 | } 16 | 17 | var ( 18 | eventQueues map[int64]*list.List 19 | events map[uint64]*timerEvent 20 | maxSessionId uint64 21 | ) 22 | 23 | func init() { 24 | eventQueues = make(map[int64]*list.List) 25 | events = make(map[uint64]*timerEvent) 26 | maxSessionId = 0 27 | go timer() 28 | } 29 | 30 | func timer() { 31 | defer func() { 32 | if x := recover(); x != nil { 33 | ERR("TIMER CRASHED", x) 34 | } 35 | }() 36 | 37 | nowTime := time.Now().Unix() 38 | sleep_timer := time.NewTimer(time.Second) 39 | for { 40 | select { 41 | case <-sleep_timer.C: 42 | sleep_timer.Reset(time.Second) 43 | nowTime += 1 44 | queues, exists := eventQueues[nowTime] 45 | if exists { 46 | for evt := queues.Front(); evt != nil; evt = evt.Next() { 47 | eventID := evt.Value.(uint64) 48 | if event, existsEvent := events[eventID]; existsEvent { 49 | event.Func() 50 | if event.RepeatCount > 0 { 51 | event.RepeatCount -= 1 52 | if event.RepeatCount == 0 { 53 | delete(events, eventID) 54 | } else { 55 | addToQueue(nowTime, event) 56 | } 57 | } else { 58 | addToQueue(nowTime, event) 59 | } 60 | } 61 | } 62 | delete(eventQueues, nowTime) 63 | } 64 | } 65 | } 66 | } 67 | 68 | //无限次数执行 69 | func DoTimer(delay int64, callback func()) uint64 { 70 | return Do(delay, 0, callback) 71 | } 72 | 73 | //延时处理 74 | func SetTimeOut(delay int64, callback func()) uint64 { 75 | return Do(delay, 1, callback) 76 | } 77 | 78 | //移除一个定时器 79 | func Remove(timerID uint64) { 80 | if _, exists := events[timerID]; exists { 81 | delete(events, timerID) 82 | } 83 | } 84 | 85 | //时间间隔,执行次数,回调函数 86 | func Do(delay int64, repeatCount int, callback func()) uint64 { 87 | //最小单位1秒 88 | if delay < 1 { 89 | callback() 90 | return 0 91 | } 92 | 93 | if repeatCount < 0 { 94 | repeatCount = 0 95 | } 96 | 97 | event := &timerEvent{ 98 | ID: atomic.AddUint64(&maxSessionId, 1), 99 | Func: callback, 100 | Delay: delay, 101 | RepeatCount: repeatCount, 102 | } 103 | events[event.ID] = event 104 | 105 | addToQueue(time.Now().Unix(), event) 106 | 107 | return event.ID 108 | } 109 | 110 | //添加到执行队列 111 | func addToQueue(nowTime int64, event *timerEvent) { 112 | dotime := nowTime + event.Delay 113 | events, exists := eventQueues[dotime] 114 | if !exists { 115 | events = list.New() 116 | } 117 | events.PushBack(event.ID) 118 | eventQueues[dotime] = events 119 | } 120 | -------------------------------------------------------------------------------- /src/tools/process/process.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "os/exec" 5 | "bytes" 6 | . "tools" 7 | "strings" 8 | "strconv" 9 | "global" 10 | ) 11 | 12 | type Process struct { 13 | User string 14 | Pid int 15 | Cpu float64 16 | Mem float64 17 | Command string 18 | } 19 | 20 | func GetAllProcess() []*Process { 21 | processes := make([]*Process, 0) 22 | 23 | cmd := exec.Command("ps", "aux") 24 | var out bytes.Buffer 25 | cmd.Stdout = &out 26 | err := cmd.Run() 27 | if err != nil { 28 | ERR(err) 29 | return processes 30 | } 31 | 32 | for { 33 | line, err := out.ReadString('\n') 34 | if err!=nil { 35 | break; 36 | } 37 | 38 | tokens := strings.Split(line, " ") 39 | ft := make([]string, 0) 40 | for _, t := range(tokens) { 41 | if t != "" && t != "\t" { 42 | ft = append(ft, t) 43 | } 44 | } 45 | 46 | user := ft[0] 47 | 48 | pid, err := strconv.Atoi(ft[1]) 49 | if err != nil { 50 | continue 51 | } 52 | cpu, err := strconv.ParseFloat(ft[2], 64) 53 | if err != nil { 54 | continue 55 | } 56 | mem, err := strconv.ParseFloat(ft[3], 64) 57 | if err != nil { 58 | continue 59 | } 60 | command := "" 61 | for i := 10; i