Skip to content

Commit 4b5df3b

Browse files
committed
Update xutil
1 parent 29e6895 commit 4b5df3b

12 files changed

Lines changed: 763 additions & 53 deletions

File tree

‎src/xutil/README.md‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,26 @@ A set of tools that keep Golang sweet.
88
go get github.com/mix-go/xutil
99
```
1010

11-
| Function | Description |
12-
|--------------------------------------------------------------------|--------------------------------------------------------------------------|
13-
| xutil.IsNumeric(s string) bool | Used to check if the variable is a number or a numeric string. |
14-
| xutil.SubString(s string, start int, length int) string | Return part of a string |
15-
| xutil.Capitalize(s string) string | The function converts the first letter of the input string to uppercase. |
16-
| xutil.StructToMap(i interface{}) map[string]interface{} | Convert struct to map. |
17-
| xutil.StringToBytes(s string) []byte | Convert string to bytes (0 copy). |
18-
| xutil.BytesToString(b []byte) string | Convert bytes to bytes (0 copy). |
19-
| xutil.AESEncrypt(plainText, mode, key, iv string) (string, error) | AES encryption |
20-
| xutil.AESDecrypt(cipherText, mode, key, iv string) (string, error) | AES Decryption |
11+
## Functions
12+
13+
| Function | Description |
14+
|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
15+
| xutil.StartAlerter(ntype NotifierType, credential, titlePrefix string, rateLimitInterval time.Duration, mqSize int, logger Logger) error | Launch an Alerter. |
16+
| xutil.PushAlert(ctx context.Context, title, content, uuid string, mentionAll bool) error | Use Alerter to push alerts via a queue, and limit frequency. |
17+
| xutil.SendAlert(ctx context.Context, title, content string, mentionAll bool) error | Use Alerter to send alerts directly. |
18+
| xutil.ErrorAlert(err error) | Use Alerter to push error alerts via a queue, and limit frequency. |
19+
| xutil.StartGrpcMonitoring(interval time.Duration, logger Logger) | Launch a gRPC monitor. |
20+
| xutil.StatsServerOptions(logger Logger) []grpc.ServerOption | Track the daily request count for the FullMethod of the gRPC server. |
21+
| xutil.StartPerformanceMonitoring(interval time.Duration, logger Logger, handler func(*PerformanceStats)) | Launch a Go performance monitor. |
22+
| xutil.SubString(s string, start int, length int) string | Return part of a string. |
23+
| xutil.Capitalize(s string) string | The function converts the first letter of the input string to uppercase. |
24+
| xutil.IsNumeric(s string) bool | Used to check if the variable is a number or a numeric string. |
25+
| xutil.StructToMap(i interface{}) map[string]interface{} | Convert struct to map. |
26+
| xutil.StringToBytes(s string) []byte | Convert string to bytes (0 copy). |
27+
| xutil.BytesToString(b []byte) string | Convert bytes to bytes (0 copy). |
28+
| xutil.AESEncrypt(plainText, mode, key, iv string) (string, error) | AES encryption. |
29+
| xutil.AESDecrypt(cipherText, mode, key, iv string) (string, error) | AES decryption. |
30+
| xutil.MD5Hash(b []byte) string | MD5 encryption. |
2131

2232
## License
2333

‎src/xutil/aes.go‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
)
1010

1111
const (
12-
ModeCBC = "CBC"
13-
ModeCFB = "CFB"
14-
ModeOFB = "OFB"
12+
AESModeCBC = "CBC"
13+
AESModeCFB = "CFB"
14+
AESModeOFB = "OFB"
1515
)
1616

1717
var ErrUnknownMode = errors.New("unknown mode")
@@ -30,13 +30,13 @@ func AESEncrypt(plainText, mode, key, iv string) (string, error) {
3030
cipherText := make([]byte, len(plainTextBytes))
3131

3232
switch mode {
33-
case ModeCBC:
33+
case AESModeCBC:
3434
cbc := cipher.NewCBCEncrypter(block, []byte(iv))
3535
cbc.CryptBlocks(cipherText, plainTextBytes)
36-
case ModeCFB:
36+
case AESModeCFB:
3737
cfb := cipher.NewCFBEncrypter(block, []byte(iv))
3838
cfb.XORKeyStream(cipherText, plainTextBytes)
39-
case ModeOFB:
39+
case AESModeOFB:
4040
ofb := cipher.NewOFB(block, []byte(iv))
4141
ofb.XORKeyStream(cipherText, plainTextBytes)
4242
default:
@@ -60,13 +60,13 @@ func AESDecrypt(cipherText, mode, key, iv string) (string, error) {
6060
decryptedText := make([]byte, len(cipherTextBytes))
6161

6262
switch mode {
63-
case ModeCBC:
63+
case AESModeCBC:
6464
cbc := cipher.NewCBCDecrypter(block, []byte(iv))
6565
cbc.CryptBlocks(decryptedText, cipherTextBytes)
66-
case ModeCFB:
66+
case AESModeCFB:
6767
cfb := cipher.NewCFBDecrypter(block, []byte(iv))
6868
cfb.XORKeyStream(decryptedText, cipherTextBytes)
69-
case ModeOFB:
69+
case AESModeOFB:
7070
ofb := cipher.NewOFB(block, []byte(iv))
7171
ofb.XORKeyStream(decryptedText, cipherTextBytes)
7272
default:

‎src/xutil/aes_test.go‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ func TestAESEncryptCBC(t *testing.T) {
1515
key256 := "abcdefghijklmnopabcdefghijklmnop" // 32 bytes = 256 bits
1616
ivInfo := "abcdefghijklmnop"
1717

18-
cipherText128, _ := AESEncrypt(plain, ModeCBC, key128, ivInfo)
18+
cipherText128, _ := AESEncrypt(plain, AESModeCBC, key128, ivInfo)
1919
fmt.Printf("Cipher text for AES-128-CBC: %s\n", cipherText128)
20-
cipherText192, _ := AESEncrypt(plain, ModeCBC, key192, ivInfo)
20+
cipherText192, _ := AESEncrypt(plain, AESModeCBC, key192, ivInfo)
2121
fmt.Printf("Cipher text for AES-192-CBC: %s\n", cipherText192)
22-
cipherText256, _ := AESEncrypt(plain, ModeCBC, key256, ivInfo)
22+
cipherText256, _ := AESEncrypt(plain, AESModeCBC, key256, ivInfo)
2323
fmt.Printf("Cipher text for AES-256-CBC: %s\n", cipherText256)
2424

25-
decryptedText128, _ := AESDecrypt(cipherText128, ModeCBC, key128, ivInfo)
25+
decryptedText128, _ := AESDecrypt(cipherText128, AESModeCBC, key128, ivInfo)
2626
fmt.Printf("Decrypted text for AES-128-CBC: %s\n", decryptedText128)
27-
decryptedText192, _ := AESDecrypt(cipherText192, ModeCBC, key192, ivInfo)
27+
decryptedText192, _ := AESDecrypt(cipherText192, AESModeCBC, key192, ivInfo)
2828
fmt.Printf("Decrypted text for AES-192-CBC: %s\n", decryptedText192)
29-
decryptedText256, _ := AESDecrypt(cipherText256, ModeCBC, key256, ivInfo)
29+
decryptedText256, _ := AESDecrypt(cipherText256, AESModeCBC, key256, ivInfo)
3030
fmt.Printf("Decrypted text for AES-256-CBC: %s\n", decryptedText256)
3131

3232
a.Equal(plain, decryptedText128)
@@ -43,18 +43,18 @@ func TestAESEncryptCFB(t *testing.T) {
4343
key256 := "abcdefghijklmnopabcdefghijklmnop" // 32 bytes = 256 bits
4444
ivInfo := "abcdefghijklmnop"
4545

46-
cipherText128, _ := AESEncrypt(plain, ModeCFB, key128, ivInfo)
46+
cipherText128, _ := AESEncrypt(plain, AESModeCFB, key128, ivInfo)
4747
fmt.Printf("Cipher text for AES-128-CBC: %s\n", cipherText128)
48-
cipherText192, _ := AESEncrypt(plain, ModeCFB, key192, ivInfo)
48+
cipherText192, _ := AESEncrypt(plain, AESModeCFB, key192, ivInfo)
4949
fmt.Printf("Cipher text for AES-192-CBC: %s\n", cipherText192)
50-
cipherText256, _ := AESEncrypt(plain, ModeCFB, key256, ivInfo)
50+
cipherText256, _ := AESEncrypt(plain, AESModeCFB, key256, ivInfo)
5151
fmt.Printf("Cipher text for AES-256-CBC: %s\n", cipherText256)
5252

53-
decryptedText128, _ := AESDecrypt(cipherText128, ModeCFB, key128, ivInfo)
53+
decryptedText128, _ := AESDecrypt(cipherText128, AESModeCFB, key128, ivInfo)
5454
fmt.Printf("Decrypted text for AES-128-CBC: %s\n", decryptedText128)
55-
decryptedText192, _ := AESDecrypt(cipherText192, ModeCFB, key192, ivInfo)
55+
decryptedText192, _ := AESDecrypt(cipherText192, AESModeCFB, key192, ivInfo)
5656
fmt.Printf("Decrypted text for AES-192-CBC: %s\n", decryptedText192)
57-
decryptedText256, _ := AESDecrypt(cipherText256, ModeCFB, key256, ivInfo)
57+
decryptedText256, _ := AESDecrypt(cipherText256, AESModeCFB, key256, ivInfo)
5858
fmt.Printf("Decrypted text for AES-256-CBC: %s\n", decryptedText256)
5959

6060
a.Equal(plain, decryptedText128)
@@ -71,18 +71,18 @@ func TestAESEncryptOFB(t *testing.T) {
7171
key256 := "abcdefghijklmnopabcdefghijklmnop" // 32 bytes = 256 bits
7272
ivInfo := "abcdefghijklmnop"
7373

74-
cipherText128, _ := AESEncrypt(plain, ModeOFB, key128, ivInfo)
74+
cipherText128, _ := AESEncrypt(plain, AESModeOFB, key128, ivInfo)
7575
fmt.Printf("Cipher text for AES-128-CBC: %s\n", cipherText128)
76-
cipherText192, _ := AESEncrypt(plain, ModeOFB, key192, ivInfo)
76+
cipherText192, _ := AESEncrypt(plain, AESModeOFB, key192, ivInfo)
7777
fmt.Printf("Cipher text for AES-192-CBC: %s\n", cipherText192)
78-
cipherText256, _ := AESEncrypt(plain, ModeOFB, key256, ivInfo)
78+
cipherText256, _ := AESEncrypt(plain, AESModeOFB, key256, ivInfo)
7979
fmt.Printf("Cipher text for AES-256-CBC: %s\n", cipherText256)
8080

81-
decryptedText128, _ := AESDecrypt(cipherText128, ModeOFB, key128, ivInfo)
81+
decryptedText128, _ := AESDecrypt(cipherText128, AESModeOFB, key128, ivInfo)
8282
fmt.Printf("Decrypted text for AES-128-CBC: %s\n", decryptedText128)
83-
decryptedText192, _ := AESDecrypt(cipherText192, ModeOFB, key192, ivInfo)
83+
decryptedText192, _ := AESDecrypt(cipherText192, AESModeOFB, key192, ivInfo)
8484
fmt.Printf("Decrypted text for AES-192-CBC: %s\n", decryptedText192)
85-
decryptedText256, _ := AESDecrypt(cipherText256, ModeOFB, key256, ivInfo)
85+
decryptedText256, _ := AESDecrypt(cipherText256, AESModeOFB, key256, ivInfo)
8686
fmt.Printf("Decrypted text for AES-256-CBC: %s\n", decryptedText256)
8787

8888
a.Equal(plain, decryptedText128)

‎src/xutil/alert.go‎

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package xutil
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"github.com/patrickmn/go-cache"
8+
"runtime"
9+
"runtime/debug"
10+
"time"
11+
)
12+
13+
var alerterSingleton *Alerter
14+
15+
type Alerter struct {
16+
RateLimitInterval time.Duration
17+
Notifier *Notifier
18+
Logger Logger
19+
cache *cache.Cache
20+
ch chan *AlertMsg
21+
}
22+
23+
type AlertMsg struct {
24+
Context context.Context
25+
UUID string
26+
Title string
27+
Content string
28+
MentionAll bool
29+
}
30+
31+
func NewAlert(ntype NotifierType, credential, titlePrefix string, rateLimitInterval time.Duration, mqSize int, logger Logger) (*Alerter, error) {
32+
n, err := NewNotifier(ntype, credential, titlePrefix)
33+
if err != nil {
34+
return nil, err
35+
}
36+
a := &Alerter{
37+
RateLimitInterval: rateLimitInterval,
38+
Notifier: n,
39+
Logger: logger,
40+
cache: cache.New(rateLimitInterval, rateLimitInterval*2),
41+
ch: make(chan *AlertMsg, mqSize),
42+
}
43+
44+
go func() {
45+
for {
46+
msg := <-a.ch
47+
48+
// 降频
49+
if msg.UUID != "" {
50+
_, ok := a.cache.Get(msg.UUID)
51+
if ok {
52+
continue
53+
}
54+
}
55+
56+
if err := a.Notifier.SendAlert(msg.Context, msg.Title, msg.Content, msg.MentionAll); err != nil {
57+
a.Logger.Errorw("alert: send alert msg fail", "uuid", msg.UUID)
58+
}
59+
60+
if msg.UUID != "" {
61+
a.cache.SetDefault(msg.UUID, true)
62+
}
63+
}
64+
}()
65+
66+
return a, nil
67+
}
68+
69+
// StartAlerter 启动报警器
70+
func StartAlerter(ntype NotifierType, credential, titlePrefix string, rateLimitInterval time.Duration, mqSize int, logger Logger) error {
71+
alert, err := NewAlert(ntype, credential, titlePrefix, rateLimitInterval, mqSize, logger)
72+
if err != nil {
73+
return err
74+
}
75+
alerterSingleton = alert
76+
return nil
77+
}
78+
79+
// PushAlert 队列限速发送
80+
func PushAlert(ctx context.Context, title, content, uuid string, mentionAll bool) error {
81+
if alerterSingleton == nil {
82+
return errors.New("alert: instance not initialized")
83+
}
84+
85+
select {
86+
case alerterSingleton.ch <- &AlertMsg{
87+
Context: ctx,
88+
UUID: uuid,
89+
Title: title,
90+
Content: content,
91+
MentionAll: mentionAll,
92+
}:
93+
default:
94+
// 队列满就丢弃
95+
}
96+
return nil
97+
}
98+
99+
// SendAlert 直接发送
100+
func SendAlert(ctx context.Context, title, content string, mentionAll bool) error {
101+
if alerterSingleton == nil {
102+
return errors.New("alert: instance not initialized")
103+
}
104+
105+
if err := alerterSingleton.Notifier.SendAlert(ctx, title, content, mentionAll); err != nil {
106+
return fmt.Errorf("alert: send alert msg fail: %s", err)
107+
}
108+
109+
return nil
110+
}
111+
112+
// ErrorAlert 错误警报
113+
func ErrorAlert(err error) {
114+
if err == nil {
115+
return
116+
}
117+
118+
_, file, line, _ := runtime.Caller(1)
119+
uuid := fmt.Sprintf("%s:%d", file, line)
120+
121+
title := fmt.Sprintf("ERROR: %s", err)
122+
content := fmt.Sprintf("%s:%d\n%s", file, line, string(debug.Stack()))
123+
124+
_ = PushAlert(context.Background(), title, content, uuid, true)
125+
}

‎src/xutil/go.mod‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
module github.com/mix-go/xutil
22

3-
go 1.20
3+
go 1.24.0
44

5-
require github.com/stretchr/testify v1.8.2
5+
require (
6+
github.com/go-lark/lark/v2 v2.0.0-beta.5
7+
github.com/patrickmn/go-cache v2.1.0+incompatible
8+
github.com/shirou/gopsutil/v3 v3.24.5
9+
github.com/slack-go/slack v0.17.3
10+
github.com/stretchr/testify v1.10.0
11+
google.golang.org/grpc v1.77.0
12+
)
613

714
require (
815
github.com/davecgh/go-spew v1.1.1 // indirect
16+
github.com/go-lark/card-builder v1.0.0-beta.2 // indirect
17+
github.com/go-ole/go-ole v1.2.6 // indirect
18+
github.com/gorilla/websocket v1.5.3 // indirect
19+
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
920
github.com/pmezard/go-difflib v1.0.0 // indirect
21+
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
22+
github.com/shoenig/go-m1cpu v0.1.6 // indirect
23+
github.com/tklauser/go-sysconf v0.3.12 // indirect
24+
github.com/tklauser/numcpus v0.6.1 // indirect
25+
github.com/yusufpapurcu/wmi v1.2.4 // indirect
26+
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
27+
golang.org/x/sys v0.37.0 // indirect
28+
golang.org/x/text v0.30.0 // indirect
29+
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
30+
google.golang.org/protobuf v1.36.10 // indirect
1031
gopkg.in/yaml.v3 v3.0.1 // indirect
1132
)

0 commit comments

Comments
 (0)