github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/log_helper.go (about) 1 // Copyright 2021 iLogtail Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package helper 16 17 import ( 18 "fmt" 19 "math" 20 "sort" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/alibaba/ilogtail/pkg/config" 26 "github.com/alibaba/ilogtail/pkg/protocol" 27 "github.com/alibaba/ilogtail/pkg/selfmonitor" 28 "github.com/alibaba/ilogtail/pkg/util" 29 ) 30 31 const ( 32 // StaleNaN is a signaling NaN, due to the MSB of the mantissa being 0. 33 // This value is chosen with many leading 0s, so we have scope to store more 34 // complicated values in the future. It is 2 rather than 1 to make 35 // it easier to distinguish from the NormalNaN by a human when debugging. 36 StaleNaN uint64 = 0x7ff0000000000002 37 StaleNan = "__STALE_NAN__" 38 SlsMetricstoreInvalidReplaceCharacter = '_' 39 ) 40 41 func CreateLog(t time.Time, enableTimestampNano bool, configTag map[string]string, logTags map[string]string, fields map[string]string) (*protocol.Log, error) { 42 var slsLog protocol.Log 43 slsLog.Contents = make([]*protocol.Log_Content, 0, len(configTag)+len(logTags)+len(fields)) 44 for key, val := range configTag { 45 cont := &protocol.Log_Content{ 46 Key: key, 47 Value: val, 48 } 49 slsLog.Contents = append(slsLog.Contents, cont) 50 } 51 52 for key, val := range logTags { 53 cont := &protocol.Log_Content{ 54 Key: key, 55 Value: val, 56 } 57 slsLog.Contents = append(slsLog.Contents, cont) 58 } 59 60 for key, val := range fields { 61 cont := &protocol.Log_Content{ 62 Key: key, 63 Value: val, 64 } 65 slsLog.Contents = append(slsLog.Contents, cont) 66 } 67 if enableTimestampNano { 68 protocol.SetLogTimeWithNano(&slsLog, uint32(t.Unix()), uint32(t.Nanosecond())) 69 } else { 70 protocol.SetLogTime(&slsLog, uint32(t.Unix())) 71 } 72 return &slsLog, nil 73 } 74 75 func CreateLogByArray(t time.Time, enableTimestampNano bool, configTag map[string]string, logTags map[string]string, columns []string, values []string) (*protocol.Log, error) { 76 var slsLog protocol.Log 77 slsLog.Contents = make([]*protocol.Log_Content, 0, len(configTag)+len(logTags)+len(columns)) 78 79 for key, val := range configTag { 80 cont := &protocol.Log_Content{ 81 Key: key, 82 Value: val, 83 } 84 slsLog.Contents = append(slsLog.Contents, cont) 85 } 86 87 for key, val := range logTags { 88 cont := &protocol.Log_Content{ 89 Key: key, 90 Value: val, 91 } 92 slsLog.Contents = append(slsLog.Contents, cont) 93 } 94 95 if len(columns) != len(values) { 96 return nil, fmt.Errorf("columns and values not equal") 97 } 98 99 for index := range columns { 100 cont := &protocol.Log_Content{ 101 Key: columns[index], 102 Value: values[index], 103 } 104 slsLog.Contents = append(slsLog.Contents, cont) 105 } 106 if enableTimestampNano { 107 protocol.SetLogTimeWithNano(&slsLog, uint32(t.Unix()), uint32(t.Nanosecond())) 108 } else { 109 protocol.SetLogTime(&slsLog, uint32(t.Unix())) 110 } 111 return &slsLog, nil 112 } 113 114 // Label for metric label 115 type MetricLabel struct { 116 Name string 117 Value string 118 } 119 120 // Labels for metric labels 121 type MetricLabels struct { 122 keyValues []*MetricLabel 123 sorted bool 124 formatStr string 125 } 126 127 func (kv *MetricLabels) clearCache() { 128 kv.sorted = false 129 kv.formatStr = "" 130 } 131 132 func (kv *MetricLabels) Len() int { 133 return len(kv.keyValues) 134 } 135 136 func (kv *MetricLabels) Swap(i int, j int) { 137 kv.keyValues[i], kv.keyValues[j] = kv.keyValues[j], kv.keyValues[i] 138 } 139 140 func (kv *MetricLabels) Less(i int, j int) bool { 141 return kv.keyValues[i].Name < kv.keyValues[j].Name 142 } 143 144 func (kv *MetricLabels) Replace(key, value string) { 145 sort.Sort(kv) 146 findIndex := sort.Search(len(kv.keyValues), func(index int) bool { 147 return kv.keyValues[index].Name >= key 148 }) 149 if findIndex < len(kv.keyValues) && kv.keyValues[findIndex].Name == key { 150 kv.keyValues[findIndex].Value = value 151 } else { 152 kv.Append(key, value) 153 } 154 kv.clearCache() 155 } 156 157 func (kv *MetricLabels) Clone() *MetricLabels { 158 if kv == nil { 159 return &MetricLabels{} 160 } 161 var newKeyValues MetricLabels 162 kv.CloneInto(&newKeyValues) 163 return &newKeyValues 164 } 165 166 func (kv *MetricLabels) CloneInto(dst *MetricLabels) *MetricLabels { 167 if kv == nil { 168 return &MetricLabels{} 169 } 170 if dst == nil { 171 return kv.Clone() 172 } 173 if len(kv.keyValues) < cap(dst.keyValues) { 174 dst.keyValues = dst.keyValues[:len(kv.keyValues)] 175 } else { 176 dst.keyValues = make([]*MetricLabel, len(kv.keyValues)) 177 } 178 dst.sorted = kv.sorted 179 dst.formatStr = kv.formatStr 180 for i, value := range kv.keyValues { 181 cp := *value 182 dst.keyValues[i] = &cp 183 } 184 return dst 185 } 186 187 // AppendMap ... 188 func (kv *MetricLabels) AppendMap(mapVal map[string]string) { 189 for key, value := range mapVal { 190 kv.Append(key, value) 191 } 192 kv.clearCache() 193 } 194 195 // Append ... 196 func (kv *MetricLabels) Append(key, value string) { 197 kv.keyValues = append(kv.keyValues, &MetricLabel{ 198 formatLabelKey(key), 199 formatLabelValue(value), 200 }) 201 kv.clearCache() 202 } 203 204 func (kv *MetricLabels) SubSlice(begin, end int) { 205 kv.keyValues = kv.keyValues[begin:end] 206 kv.clearCache() 207 } 208 209 func (kv *MetricLabels) String() string { 210 if kv == nil { 211 return "" 212 } 213 if !kv.sorted || kv.formatStr == "" { 214 sort.Sort(kv) 215 var builder strings.Builder 216 for index, label := range kv.keyValues { 217 builder.WriteString(label.Name) 218 builder.WriteString("#$#") 219 builder.WriteString(label.Value) 220 if index != len(kv.keyValues)-1 { 221 builder.WriteByte('|') 222 } 223 } 224 kv.formatStr = builder.String() 225 kv.sorted = true 226 } 227 return kv.formatStr 228 } 229 230 // DefBucket ... 231 type DefBucket struct { 232 Le float64 233 Count int64 234 } 235 236 // HistogramData ... 237 type HistogramData struct { 238 Buckets []DefBucket 239 Count int64 240 Sum float64 241 } 242 243 // ToMetricLogs .. 244 func (hd *HistogramData) ToMetricLogs(name string, timeMs int64, labels *MetricLabels) []*protocol.Log { 245 logs := make([]*protocol.Log, 0, len(hd.Buckets)+2) 246 logs = append(logs, NewMetricLog(name+"_count", timeMs, float64(hd.Count), labels)) 247 logs = append(logs, NewMetricLog(name+"_sum", timeMs, hd.Sum, labels)) 248 for _, v := range hd.Buckets { 249 labels.Replace("le", strconv.FormatFloat(v.Le, 'g', -1, 64)) 250 logs = append(logs, NewMetricLog(name+"_bucket", timeMs, float64(v.Count), labels)) 251 } 252 253 return logs 254 } 255 256 // NewMetricLog create a metric log, time support unix milliseconds and unix nanoseconds. 257 // Note: must pass safe string 258 func NewMetricLog(name string, t int64, value float64, labels *MetricLabels) *protocol.Log { 259 var valStr string 260 if math.Float64bits(value) == StaleNaN { 261 valStr = StaleNan 262 } else { 263 valStr = strconv.FormatFloat(value, 'g', -1, 64) 264 } 265 return NewMetricLogStringVal(name, t, valStr, labels) 266 } 267 268 // NewMetricLogStringVal create a metric log with val string, time support unix milliseconds and unix nanoseconds. 269 // Note: must pass safe string 270 func NewMetricLogStringVal(name string, t int64, value string, labels *MetricLabels) *protocol.Log { 271 strTime := strconv.FormatInt(t, 10) 272 metric := &protocol.Log{} 273 switch len(strTime) { 274 case 13: 275 protocol.SetLogTimeWithNano(metric, uint32(t/1000), uint32((t*1e6)%1e9)) 276 strTime += "000000" 277 case 19: 278 protocol.SetLogTimeWithNano(metric, uint32(t/1e9), uint32(t%1e9)) 279 default: 280 t = int64(float64(t) * math.Pow10(19-len(strTime))) 281 strTime = strconv.FormatInt(t, 10) 282 protocol.SetLogTimeWithNano(metric, uint32(t/1e9), uint32(t%1e9)) 283 } 284 metric.Contents = make([]*protocol.Log_Content, 0, 4) 285 metric.Contents = append(metric.Contents, &protocol.Log_Content{Key: "__name__", Value: formatNewMetricName(name)}) 286 metric.Contents = append(metric.Contents, &protocol.Log_Content{Key: "__time_nano__", Value: strTime}) 287 metric.Contents = append(metric.Contents, &protocol.Log_Content{Key: "__labels__", Value: labels.String()}) 288 metric.Contents = append(metric.Contents, &protocol.Log_Content{Key: "__value__", Value: value}) 289 return metric 290 } 291 292 func formatLabelKey(key string) string { 293 if !config.LoongcollectorGlobalConfig.EnableSlsMetricsFormat { 294 return key 295 } 296 var newKey []byte 297 for i := 0; i < len(key); i++ { 298 b := key[i] 299 if (b >= 'a' && b <= 'z') || 300 (b >= 'A' && b <= 'Z') || 301 (b >= '0' && b <= '9') || 302 b == '_' { 303 continue 304 } else { 305 if newKey == nil { 306 newKey = []byte(key) 307 } 308 newKey[i] = SlsMetricstoreInvalidReplaceCharacter 309 } 310 } 311 if newKey == nil { 312 return key 313 } 314 return util.ZeroCopyBytesToString(newKey) 315 } 316 317 func formatLabelValue(value string) string { 318 if !config.LoongcollectorGlobalConfig.EnableSlsMetricsFormat { 319 return value 320 } 321 var newValue []byte 322 for i := 0; i < len(value); i++ { 323 b := value[i] 324 if b != '|' { 325 continue 326 } else { 327 if newValue == nil { 328 newValue = []byte(value) 329 } 330 newValue[i] = SlsMetricstoreInvalidReplaceCharacter 331 } 332 } 333 if newValue == nil { 334 return value 335 } 336 return util.ZeroCopyBytesToString(newValue) 337 } 338 339 func formatNewMetricName(name string) string { 340 if !config.LoongcollectorGlobalConfig.EnableSlsMetricsFormat { 341 return name 342 } 343 var newName []byte 344 for i := 0; i < len(name); i++ { 345 b := name[i] 346 if (b >= 'a' && b <= 'z') || 347 (b >= 'A' && b <= 'Z') || 348 (b >= '0' && b <= '9') || 349 b == '_' || 350 b == ':' { 351 continue 352 } else { 353 if newName == nil { 354 newName = []byte(name) 355 } 356 newName[i] = SlsMetricstoreInvalidReplaceCharacter 357 } 358 } 359 if newName == nil { 360 return name 361 } 362 return util.ZeroCopyBytesToString(newName) 363 } 364 365 // ReplaceInvalidChars analog of invalidChars = regexp.MustCompile("[^a-zA-Z0-9_]") 366 func ReplaceInvalidChars(in *string) { 367 for charIndex, char := range *in { 368 charInt := int(char) 369 if !((charInt >= 97 && charInt <= 122) || // a-z 370 (charInt >= 65 && charInt <= 90) || // A-Z 371 (charInt >= 48 && charInt <= 57) || // 0-9 372 charInt == 95 || charInt == ':') { // _ 373 374 *in = (*in)[:charIndex] + "_" + (*in)[charIndex+1:] 375 } 376 } 377 } 378 379 func GetMetricName(log *protocol.Log) string { 380 for _, cnt := range log.Contents { 381 if cnt.GetKey() == selfmonitor.SelfMetricNameKey { 382 return cnt.GetValue() 383 } 384 } 385 return "" 386 } 387 388 func LogContentsToMap(contents []*protocol.Log_Content) map[string]string { 389 result := make(map[string]string) 390 for _, content := range contents { 391 result[content.GetKey()] = content.GetValue() 392 } 393 return result 394 }