@@ -16,12 +16,25 @@ package helper
16
16
17
17
import (
18
18
"fmt"
19
+ "math"
19
20
"sort"
20
21
"strconv"
21
22
"strings"
22
23
"time"
23
24
25
+ "github.com/alibaba/ilogtail/pkg/config"
24
26
"github.com/alibaba/ilogtail/pkg/protocol"
27
+ "github.com/alibaba/ilogtail/pkg/util"
28
+ )
29
+
30
+ const (
31
+ // StaleNaN is a signaling NaN, due to the MSB of the mantissa being 0.
32
+ // This value is chosen with many leading 0s, so we have scope to store more
33
+ // complicated values in the future. It is 2 rather than 1 to make
34
+ // it easier to distinguish from the NormalNaN by a human when debugging.
35
+ StaleNaN uint64 = 0x7ff0000000000002
36
+ StaleNan = "__STALE_NAN__"
37
+ SlsMetricstoreInvalidReplaceCharacter = '_'
25
38
)
26
39
27
40
func CreateLog (t time.Time , configTag map [string ]string , logTags map [string ]string , fields map [string ]string ) (* protocol.Log , error ) {
@@ -96,25 +109,112 @@ type MetricLabel struct {
96
109
}
97
110
98
111
// Labels for metric labels
99
- type MetricLabels []MetricLabel
112
+ type MetricLabels struct {
113
+ keyValues []* MetricLabel
114
+ sorted bool
115
+ formatStr string
116
+ }
117
+
118
+ func (kv * MetricLabels ) clearCache () {
119
+ kv .sorted = false
120
+ kv .formatStr = ""
121
+ }
122
+
123
+ func (kv * MetricLabels ) Len () int {
124
+ return len (kv .keyValues )
125
+ }
100
126
101
- func (l MetricLabels ) Len () int {
102
- return len ( l )
127
+ func (kv * MetricLabels ) Swap ( i int , j int ) {
128
+ kv . keyValues [ i ], kv . keyValues [ j ] = kv . keyValues [ j ], kv . keyValues [ i ]
103
129
}
104
130
105
- func (l MetricLabels ) Swap (i int , j int ) {
106
- l [i ], l [ j ] = l [j ], l [ i ]
131
+ func (kv * MetricLabels ) Less (i int , j int ) bool {
132
+ return kv . keyValues [i ]. Name < kv . keyValues [j ]. Name
107
133
}
108
134
109
- func (l MetricLabels ) Less (i int , j int ) bool {
110
- return l [i ].Name < l [j ].Name
135
+ func (kv * MetricLabels ) Replace (key , value string ) {
136
+ findIndex := sort .Search (len (kv .keyValues ), func (index int ) bool {
137
+ return kv .keyValues [index ].Name >= key
138
+ })
139
+ if findIndex < len (kv .keyValues ) && kv .keyValues [findIndex ].Name == key {
140
+ kv .keyValues [findIndex ].Value = value
141
+ } else {
142
+ kv .Append (key , value )
143
+ }
144
+ kv .clearCache ()
145
+ }
146
+
147
+ func (kv * MetricLabels ) Clone () * MetricLabels {
148
+ if kv == nil {
149
+ return & MetricLabels {}
150
+ }
151
+ var newKeyValues MetricLabels
152
+ kv .CloneInto (& newKeyValues )
153
+ return & newKeyValues
111
154
}
112
155
113
- func MinInt (a , b int ) int {
114
- if a < b {
115
- return a
156
+ func (kv * MetricLabels ) CloneInto (dst * MetricLabels ) * MetricLabels {
157
+ if kv == nil {
158
+ return & MetricLabels {}
159
+ }
160
+ if dst == nil {
161
+ return kv .Clone ()
162
+ }
163
+ if len (kv .keyValues ) < cap (dst .keyValues ) {
164
+ dst .keyValues = dst .keyValues [:len (kv .keyValues )]
165
+ } else {
166
+ dst .keyValues = make ([]* MetricLabel , len (kv .keyValues ))
167
+ }
168
+ dst .sorted = kv .sorted
169
+ dst .formatStr = kv .formatStr
170
+ for i , value := range kv .keyValues {
171
+ cp := * value
172
+ dst .keyValues [i ] = & cp
116
173
}
117
- return b
174
+ return dst
175
+ }
176
+
177
+ // AppendMap ...
178
+ func (kv * MetricLabels ) AppendMap (mapVal map [string ]string ) {
179
+ for key , value := range mapVal {
180
+ kv .Append (key , value )
181
+ }
182
+ kv .clearCache ()
183
+ }
184
+
185
+ // Append ...
186
+ func (kv * MetricLabels ) Append (key , value string ) {
187
+ kv .keyValues = append (kv .keyValues , & MetricLabel {
188
+ formatLabelKey (key ),
189
+ formatLabelValue (value ),
190
+ })
191
+ kv .clearCache ()
192
+ }
193
+
194
+ func (kv * MetricLabels ) SubSlice (begin , end int ) {
195
+ kv .keyValues = kv .keyValues [begin :end ]
196
+ kv .clearCache ()
197
+ }
198
+
199
+ func (kv * MetricLabels ) String () string {
200
+ if kv == nil {
201
+ return ""
202
+ }
203
+ if ! kv .sorted || kv .formatStr == "" {
204
+ sort .Sort (kv )
205
+ var builder strings.Builder
206
+ for index , label := range kv .keyValues {
207
+ builder .WriteString (label .Name )
208
+ builder .WriteString ("#$#" )
209
+ builder .WriteString (label .Value )
210
+ if index != len (kv .keyValues )- 1 {
211
+ builder .WriteByte ('|' )
212
+ }
213
+ }
214
+ kv .formatStr = builder .String ()
215
+ kv .sorted = true
216
+ }
217
+ return kv .formatStr
118
218
}
119
219
120
220
// DefBucket ...
@@ -131,42 +231,128 @@ type HistogramData struct {
131
231
}
132
232
133
233
// ToMetricLogs ..
134
- func (hd * HistogramData ) ToMetricLogs (name string , timeMs int64 , labels MetricLabels ) []* protocol.Log {
234
+ func (hd * HistogramData ) ToMetricLogs (name string , timeMs int64 , labels * MetricLabels ) []* protocol.Log {
135
235
logs := make ([]* protocol.Log , 0 , len (hd .Buckets )+ 2 )
136
- sort .Sort (labels )
236
+ logs = append (logs , NewMetricLog (name + "_count" , timeMs , float64 (hd .Count ), labels ))
237
+ logs = append (logs , NewMetricLog (name + "_sum" , timeMs , hd .Sum , labels ))
137
238
for _ , v := range hd .Buckets {
138
- newLabels := make (MetricLabels , len (labels ), len (labels )+ 1 )
139
- copy (newLabels , labels )
140
- newLabels = append (newLabels , MetricLabel {Name : "le" , Value : strconv .FormatFloat (v .Le , 'g' , - 1 , 64 )})
141
- sort .Sort (newLabels )
142
- logs = append (logs , NewMetricLog (name + "_bucket" , timeMs , strconv .FormatInt (v .Count , 10 ), newLabels ))
143
- }
144
- logs = append (logs , NewMetricLog (name + "_count" , timeMs , strconv .FormatInt (hd .Count , 10 ), labels ))
145
- logs = append (logs , NewMetricLog (name + "_sum" , timeMs , strconv .FormatFloat (hd .Sum , 'g' , - 1 , 64 ), labels ))
239
+ labels .Replace ("le" , strconv .FormatFloat (v .Le , 'g' , - 1 , 64 ))
240
+ logs = append (logs , NewMetricLog (name + "_bucket" , timeMs , float64 (v .Count ), labels ))
241
+ }
242
+
146
243
return logs
147
244
}
148
245
149
- // NewMetricLog caller must sort labels
150
- func NewMetricLog (name string , timeMs int64 , value string , labels []MetricLabel ) * protocol.Log {
151
- strTime := strconv .FormatInt (timeMs , 10 )
246
+ // NewMetricLog create a metric log, time support unix milliseconds and unix nanoseconds.
247
+ func NewMetricLog (name string , t int64 , value float64 , labels * MetricLabels ) * protocol.Log {
248
+ var valStr string
249
+ if math .Float64bits (value ) == StaleNaN {
250
+ valStr = StaleNan
251
+ } else {
252
+ valStr = strconv .FormatFloat (value , 'g' , - 1 , 64 )
253
+ }
254
+ return NewMetricLogStringVal (name , t , valStr , labels )
255
+ }
256
+
257
+ // NewMetricLogStringVal create a metric log with val string, time support unix milliseconds and unix nanoseconds.
258
+ func NewMetricLogStringVal (name string , t int64 , value string , labels * MetricLabels ) * protocol.Log {
259
+ strTime := strconv .FormatInt (t , 10 )
152
260
metric := & protocol.Log {}
153
- protocol .SetLogTimeWithNano (metric , uint32 (timeMs / 1000 ), uint32 ((timeMs * 1e6 )% 1e9 ))
154
- metric .Contents = []* protocol.Log_Content {}
155
- metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__name__" , Value : name })
261
+ switch len (strTime ) {
262
+ case 13 :
263
+ protocol .SetLogTimeWithNano (metric , uint32 (t / 1000 ), uint32 ((t * 1e6 )% 1e9 ))
264
+ strTime += "000000"
265
+ case 19 :
266
+ protocol .SetLogTimeWithNano (metric , uint32 (t / 1e9 ), uint32 (t % 1e9 ))
267
+ default :
268
+ t = int64 (float64 (t ) * math .Pow10 (19 - len (strTime )))
269
+ strTime = strconv .FormatInt (t , 10 )
270
+ protocol .SetLogTimeWithNano (metric , uint32 (t / 1e9 ), uint32 (t % 1e9 ))
271
+ }
272
+ metric .Contents = make ([]* protocol.Log_Content , 0 , 4 )
273
+ metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__name__" , Value : formatNewMetricName (name )})
156
274
metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__time_nano__" , Value : strTime })
275
+ metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__labels__" , Value : labels .String ()})
276
+ metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__value__" , Value : value })
277
+ return metric
278
+ }
279
+
280
+ func formatLabelKey (key string ) string {
281
+ if ! config .LogtailGlobalConfig .EnableSlsMetricsFormat {
282
+ return key
283
+ }
284
+ var newKey []byte
285
+ for i := 0 ; i < len (key ); i ++ {
286
+ b := key [i ]
287
+ if (b >= 'a' && b <= 'z' ) ||
288
+ (b >= 'A' && b <= 'Z' ) ||
289
+ (b >= '0' && b <= '9' ) ||
290
+ b == '_' {
291
+ continue
292
+ } else {
293
+ if newKey == nil {
294
+ newKey = []byte (key )
295
+ }
296
+ newKey [i ] = SlsMetricstoreInvalidReplaceCharacter
297
+ }
298
+ }
299
+ if newKey == nil {
300
+ return key
301
+ }
302
+ return util .ZeroCopyBytesToString (newKey )
303
+ }
157
304
158
- builder := strings.Builder {}
159
- for index , l := range labels {
160
- if index != 0 {
161
- builder .WriteString ("|" )
305
+ func formatLabelValue (value string ) string {
306
+ if ! config .LogtailGlobalConfig .EnableSlsMetricsFormat {
307
+ return value
308
+ }
309
+ var newValue []byte
310
+ for i := 0 ; i < len (value ); i ++ {
311
+ b := value [i ]
312
+ if b != '|' {
313
+ continue
314
+ } else {
315
+ if newValue == nil {
316
+ newValue = []byte (value )
317
+ }
318
+ newValue [i ] = SlsMetricstoreInvalidReplaceCharacter
162
319
}
163
- builder .WriteString (l .Name )
164
- builder .WriteString ("#$#" )
165
- builder .WriteString (l .Value )
320
+ }
321
+ if newValue == nil {
322
+ return value
323
+ }
324
+ return util .ZeroCopyBytesToString (newValue )
325
+ }
166
326
327
+ func formatNewMetricName (name string ) string {
328
+ if ! config .LogtailGlobalConfig .EnableSlsMetricsFormat {
329
+ return name
330
+ }
331
+ newName := []byte (name )
332
+ for i , b := range newName {
333
+ if (b >= 'a' && b <= 'z' ) ||
334
+ (b >= 'A' && b <= 'Z' ) ||
335
+ (b >= '0' && b <= '9' ) ||
336
+ b == '_' ||
337
+ b == ':' {
338
+ continue
339
+ } else {
340
+ newName [i ] = SlsMetricstoreInvalidReplaceCharacter
341
+ }
167
342
}
168
- metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__labels__" , Value : builder .String ()})
343
+ return util .ZeroCopyBytesToString (newName )
344
+ }
169
345
170
- metric .Contents = append (metric .Contents , & protocol.Log_Content {Key : "__value__" , Value : value })
171
- return metric
346
+ // ReplaceInvalidChars analog of invalidChars = regexp.MustCompile("[^a-zA-Z0-9_]")
347
+ func ReplaceInvalidChars (in * string ) {
348
+ for charIndex , char := range * in {
349
+ charInt := int (char )
350
+ if ! ((charInt >= 97 && charInt <= 122 ) || // a-z
351
+ (charInt >= 65 && charInt <= 90 ) || // A-Z
352
+ (charInt >= 48 && charInt <= 57 ) || // 0-9
353
+ charInt == 95 || charInt == ':' ) { // _
354
+
355
+ * in = (* in )[:charIndex ] + "_" + (* in )[charIndex + 1 :]
356
+ }
357
+ }
172
358
}
0 commit comments