-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzabbix.go
174 lines (130 loc) · 3.97 KB
/
zabbix.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Package zabbix provides client for sending metrics to Zabbix Server 3+
package zabbix
// ////////////////////////////////////////////////////////////////////////////////// //
// //
// Copyright (c) 2024 ESSENTIAL KAOS //
// Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> //
// //
// ////////////////////////////////////////////////////////////////////////////////// //
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"time"
)
// ////////////////////////////////////////////////////////////////////////////////// //
// Client is Zabbix client
type Client struct {
Hostname string
ConnectTimeout time.Duration
WriteTimeout time.Duration
ReadTimeout time.Duration
dialer *net.Dialer
addr *net.TCPAddr
data []*Metric
}
// ////////////////////////////////////////////////////////////////////////////////// //
// NewClient creates new client
func NewClient(address, hostname string) (*Client, error) {
addr, err := net.ResolveTCPAddr("tcp4", address)
if err != nil {
return nil, err
}
dialer := &net.Dialer{Timeout: time.Second * 5}
return &Client{addr: addr, dialer: dialer, Hostname: hostname}, nil
}
// ////////////////////////////////////////////////////////////////////////////////// //
// Add adds new metric to stack
func (c *Client) Add(key string, value any) *Metric {
now := time.Now()
metric := &Metric{
id: len(c.data),
Clock: now.Unix(),
NS: now.Nanosecond(),
Host: c.Hostname,
Key: key,
Value: formatValue(value),
}
c.data = append(c.data, metric)
return metric
}
// Num returns number of metrics in stack
func (c *Client) Num() int {
return len(c.data)
}
// Clear clears all metrics in stack
func (c *Client) Clear() {
c.data = nil
}
// Send sends data to Zabbix server
func (c *Client) Send() (Response, error) {
if len(c.data) == 0 {
return Response{"nothing to send", 0, 0, 0, 0.0}, nil
}
conn, err := connectToServer(c)
if err != nil {
return Response{}, err
}
defer conn.Close() // Zabbix doesn't support persistent connections
err = writeToConnection(conn, encodeMetrics(c.data), c.WriteTimeout)
if err != nil {
return Response{}, err
}
c.data = nil
respMeta := make([]byte, 13)
err = readFromConnection(conn, respMeta, c.ReadTimeout)
if err != nil {
return Response{}, err
}
if !bytes.Equal(respMeta[:5], zabbixHeader) {
return Response{}, fmt.Errorf("Wrong header format")
}
respSize := binary.LittleEndian.Uint64(respMeta[5:])
respBuf := make([]byte, respSize)
err = readFromConnection(conn, respBuf, c.ReadTimeout)
if err != nil {
return Response{}, err
}
return decodeResponse(respBuf)
}
// ////////////////////////////////////////////////////////////////////////////////// //
// connectToServer makes connection to Zabbix server
func connectToServer(c *Client) (*net.TCPConn, error) {
if c.ConnectTimeout > 0 && c.dialer.Timeout != c.ConnectTimeout {
c.dialer.Timeout = c.ConnectTimeout
}
conn, err := c.dialer.Dial(c.addr.Network(), c.addr.String())
if err != nil {
return nil, err
}
return conn.(*net.TCPConn), nil
}
// readFromConnection reads data from connection
func readFromConnection(conn *net.TCPConn, buf []byte, timeout time.Duration) error {
if timeout > 0 {
conn.SetReadDeadline(time.Now().Add(timeout))
}
_, err := io.ReadFull(conn, buf)
return err
}
// writeToConnection writes data into connection
func writeToConnection(conn *net.TCPConn, data []byte, timeout time.Duration) error {
if timeout > 0 {
conn.SetWriteDeadline(time.Now().Add(timeout))
}
_, err := conn.Write(data)
return err
}
// formatValue convert value to string
func formatValue(v any) string {
switch t := v.(type) {
case float32:
return fmt.Sprintf("%.6f", t)
case float64:
return fmt.Sprintf("%.6f", t)
default:
return fmt.Sprint(t)
}
}