Golang实战记:服务器进程监控器

引子

大模型的训练和推理是很耗时的,如果没有及时知晓训练/推理完成的时刻,则可能会造成时间上的损失。比如睡前明明可以再跑一个模型,结果认为上一个模型没跑完,就浪费了一晚上的时间。代码开源在https://github.com/coder109/Email-After-Process-Done

所以,我做了这样一个工具,监控进程运行的开始、结束时刻,结束了就把信息发给我的邮箱。大概的设计是这样的:记录时间-运行进程-记录时间-发送邮件。

主函数

主函数也是按照设计来的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func main() {
// Init
var info pkg.Information
pkg.GetTime(&info, "start")
email_info := pkg.InitPrivateEmailInformation()

// Get the Command Name
command := pkg.BuildCommand(os.Args)
pkg.GetCommandName(&info)

// Find the Path
path := pkg.LookPath(command)

// Start the Process
pro, err := os.StartProcess(path, command, &os.ProcAttr{})
if err != nil {
panic(err)
}

// Wait
pro.Wait()

pkg.GetTime(&info, "end")
content := pkg.InformationToString(info)
fmt.Println(content)
pkg.SendEmail(content, email_info.Sender, email_info.Recver, email_info.Port, email_info.Smtp_server, email_info.Password)
}

  1. 维护一个信息,便于最后发送邮件。
  2. 随后获取当前的时间,也就是进程开始运行的时间。
  3. 记录命令,并且将命令也记录到信息中。
  4. 记录可执行程序的地址,便于StartProcess函数使用。
  5. 如果程序完美无缺地执行了,就等待程序运行结束,记录结束时间并发送邮件。
  6. 否则,就报错。

这里存在着可以优化的一点,就是如果出错了也发邮件,但是,这一个更新我想放在后面,将运行结果、日志或者错误作为附件。

command的维护

command的维护主要集中在代码中的executor.go文件中,其主要内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func BuildCommand(args []string) []string {
if len(args) == 1 {
panic("No Command")
}

command := append([]string{}, args[1:]...)
return command
}

func LookPath(command_name []string) string {
path, err := exec.LookPath(command_name[0])
if err != nil {
panic(err)
}
return path
}

这里的内容很简单,没什么可说的。

邮件发送

邮件发送的代码在代码的sender.go中,主要应用了外部的email库。

信息维护

信息维护主要集中在代码的timelogger.go中,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
type Information struct {
command_name []string
time_start [6]string
time_end [6]string
}

func Month2Num(month string) int {
return map[string]int{
"January": 1,
"February": 2,
"March": 3,
"April": 4,
"May": 5,
"June": 6,
"July": 7,
"August": 8,
"September": 9,
"October": 10,
"November": 11,
"December": 12,
}[month]
}

func GetCommandName(information *Information) {
information.command_name = os.Args[1:]
}

func GetTime(information *Information, flag string) {
now := time.Now()
year, raw_month, day := now.Date()
hour, minute, second := now.Clock()

month := Month2Num(raw_month.String())

if flag == "start" {
information.time_start = [6]string{fmt.Sprint(year), fmt.Sprint(month), fmt.Sprint(day), fmt.Sprint(hour), fmt.Sprint(minute), fmt.Sprint(second)}
} else if flag == "end" {
information.time_end = [6]string{fmt.Sprint(year), fmt.Sprint(month), fmt.Sprint(day), fmt.Sprint(hour), fmt.Sprint(minute), fmt.Sprint(second)}
} else {
panic("Invalid Flag")
}
}

func InformationToString(information Information) string {
return fmt.Sprintf(
"The command name is %v\nThe start time is %s-%s-%s %s:%s:%s\nThe end time is %s-%s-%s %s:%s:%s\n",
information.command_name,
information.time_start[0], information.time_start[1], information.time_start[2], information.time_start[3], information.time_start[4], information.time_start[5],
information.time_end[0], information.time_end[1], information.time_end[2], information.time_end[3], information.time_end[4], information.time_end[5],
)
}

主要就是对时间的处理,以及最后的信息输出。

总结

这个代码很简单,但是也能体现出golang相比于C的优势——表达力更强,可应用的库多且易写。

文章作者:
文章链接: https://www.coderlock.site/2025/12/01/Golang实战记:服务器进程监控器/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 寒夜雨