github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/converter/converter.go (about) 1 // Copyright 2022 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 protocol 16 17 import ( 18 "fmt" 19 "strings" 20 "sync" 21 22 "github.com/alibaba/ilogtail/pkg/config" 23 "github.com/alibaba/ilogtail/pkg/flags" 24 "github.com/alibaba/ilogtail/pkg/models" 25 "github.com/alibaba/ilogtail/pkg/protocol" 26 ) 27 28 const ( 29 ProtocolCustomSingle = "custom_single" 30 ProtocolCustomSingleFlatten = "custom_single_flatten" 31 ProtocolOtlpV1 = "otlp_v1" 32 ProtocolInfluxdb = "influxdb" 33 ProtocolJsonline = "jsonline" 34 ProtocolRaw = "raw" 35 ) 36 37 const ( 38 EncodingNone = "none" 39 EncodingJSON = "json" 40 EncodingProtobuf = "protobuf" 41 EncodingCustom = "custom" 42 ) 43 44 const ( 45 tagPrefix = "__tag__:" 46 targetContentPrefix = "content." 47 targetTagPrefix = "tag." 48 49 targetGroupMetadataPrefix = "metadata." 50 ) 51 52 const ( 53 tagHostIP = "host.ip" 54 tagLogTopic = "log.topic" 55 tagLogFilePath = "log.file.path" 56 tagHostname = "host.name" 57 tagK8sNodeIP = "k8s.node.ip" 58 tagK8sNodeName = "k8s.node.name" 59 tagK8sNamespace = "k8s.namespace.name" 60 tagK8sPodName = "k8s.pod.name" 61 tagK8sPodIP = "k8s.pod.ip" 62 tagK8sPodUID = "k8s.pod.uid" 63 tagContainerName = "container.name" 64 tagContainerIP = "container.ip" 65 tagContainerImageName = "container.image.name" 66 tagK8sContainerName = "k8s.container.name" 67 tagK8sContainerIP = "k8s.container.ip" 68 tagK8sContainerImageName = "k8s.container.image.name" 69 ) 70 71 // todo: make multiple pools for different size levels 72 var byteBufPool = sync.Pool{ 73 New: func() interface{} { 74 buf := make([]byte, 0, 1024) 75 return &buf 76 }, 77 } 78 79 var tagConversionMap = map[string]string{ 80 "__path__": tagLogFilePath, 81 "__hostname__": tagHostname, 82 "_node_ip_": tagK8sNodeIP, 83 "_node_name_": tagK8sNodeName, 84 "_namespace_": tagK8sNamespace, 85 "_pod_name_": tagK8sPodName, 86 "_pod_ip_": tagK8sPodIP, 87 "_pod_uid_": tagK8sPodUID, 88 "_container_name_": tagContainerName, 89 "_container_ip_": tagContainerIP, 90 "_image_name_": tagContainerImageName, 91 } 92 93 // When in k8s, the following tags should be renamed to k8s-specific names. 94 var specialTagConversionMap = map[string]string{ 95 "_container_name_": tagK8sContainerName, 96 "_container_ip_": tagK8sContainerIP, 97 "_image_name_": tagK8sContainerImageName, 98 } 99 100 var supportedEncodingMap = map[string]map[string]bool{ 101 ProtocolCustomSingle: { 102 EncodingJSON: true, 103 EncodingProtobuf: false, 104 }, 105 ProtocolCustomSingleFlatten: { 106 EncodingJSON: true, 107 EncodingProtobuf: false, 108 }, 109 ProtocolOtlpV1: { 110 EncodingNone: true, 111 }, 112 ProtocolInfluxdb: { 113 EncodingCustom: true, 114 }, 115 ProtocolJsonline: { 116 EncodingJSON: true, 117 }, 118 ProtocolRaw: { 119 EncodingCustom: true, 120 }, 121 } 122 123 type Converter struct { 124 Protocol string 125 Encoding string 126 Separator string 127 IgnoreUnExpectedData bool 128 OnlyContents bool 129 TagKeyRenameMap map[string]string 130 ProtocolKeyRenameMap map[string]string 131 GlobalConfig *config.GlobalConfig 132 } 133 134 func NewConverterWithSep(protocol, encoding, sep string, ignoreUnExpectedData bool, tagKeyRenameMap, protocolKeyRenameMap map[string]string, globalConfig *config.GlobalConfig) (*Converter, error) { 135 converter, err := NewConverter(protocol, encoding, tagKeyRenameMap, protocolKeyRenameMap, globalConfig) 136 if err != nil { 137 return nil, err 138 } 139 converter.Separator = sep 140 converter.IgnoreUnExpectedData = ignoreUnExpectedData 141 return converter, nil 142 } 143 144 func NewConverter(protocol, encoding string, tagKeyRenameMap, protocolKeyRenameMap map[string]string, globalConfig *config.GlobalConfig) (*Converter, error) { 145 enc, ok := supportedEncodingMap[protocol] 146 if !ok { 147 return nil, fmt.Errorf("unsupported protocol: %s", protocol) 148 } 149 if supported, existed := enc[encoding]; !existed || !supported { 150 return nil, fmt.Errorf("unsupported encoding: %s for protocol %s", encoding, protocol) 151 } 152 return &Converter{ 153 Protocol: protocol, 154 Encoding: encoding, 155 TagKeyRenameMap: tagKeyRenameMap, 156 ProtocolKeyRenameMap: protocolKeyRenameMap, 157 GlobalConfig: globalConfig, 158 }, nil 159 } 160 161 func (c *Converter) Do(logGroup *protocol.LogGroup) (logs interface{}, err error) { 162 logs, _, err = c.DoWithSelectedFields(logGroup, nil) 163 return 164 } 165 166 func (c *Converter) DoWithSelectedFields(logGroup *protocol.LogGroup, targetFields []string) (logs interface{}, values []map[string]string, err error) { 167 switch c.Protocol { 168 case ProtocolCustomSingle: 169 return c.ConvertToSingleProtocolLogs(logGroup, targetFields) 170 case ProtocolCustomSingleFlatten: 171 return c.ConvertToSingleProtocolLogsFlatten(logGroup, targetFields) 172 case ProtocolOtlpV1: 173 return c.ConvertToOtlpResourseLogs(logGroup, targetFields) 174 default: 175 return nil, nil, fmt.Errorf("unsupported protocol: %s", c.Protocol) 176 } 177 } 178 179 func (c *Converter) ToByteStream(logGroup *protocol.LogGroup) (stream interface{}, err error) { 180 stream, _, err = c.ToByteStreamWithSelectedFields(logGroup, nil) 181 return 182 } 183 184 func (c *Converter) ToByteStreamWithSelectedFields(logGroup *protocol.LogGroup, targetFields []string) (stream interface{}, values []map[string]string, err error) { 185 switch c.Protocol { 186 case ProtocolCustomSingle: 187 return c.ConvertToSingleProtocolStream(logGroup, targetFields) 188 case ProtocolCustomSingleFlatten: 189 return c.ConvertToSingleProtocolStreamFlatten(logGroup, targetFields) 190 case ProtocolInfluxdb: 191 return c.ConvertToInfluxdbProtocolStream(logGroup, targetFields) 192 case ProtocolJsonline: 193 return c.ConvertToJsonlineProtocolStreamFlatten(logGroup) 194 default: 195 return nil, nil, fmt.Errorf("unsupported protocol: %s", c.Protocol) 196 } 197 } 198 199 func (c *Converter) ToByteStreamWithSelectedFieldsV2(groupEvents *models.PipelineGroupEvents, targetFields []string) (stream interface{}, values []map[string]string, err error) { 200 switch c.Protocol { 201 case ProtocolRaw: 202 return c.ConvertToRawStream(groupEvents, targetFields) 203 case ProtocolInfluxdb: 204 return c.ConvertToInfluxdbProtocolStreamV2(groupEvents, targetFields) 205 default: 206 return nil, nil, fmt.Errorf("unsupported protocol: %s", c.Protocol) 207 } 208 } 209 210 func GetPooledByteBuf() *[]byte { 211 return byteBufPool.Get().(*[]byte) 212 } 213 214 func PutPooledByteBuf(buf *[]byte) { 215 *buf = (*buf)[:0] 216 byteBufPool.Put(buf) 217 } 218 219 func TrimPrefix(str string) string { 220 switch { 221 case strings.HasPrefix(str, targetContentPrefix): 222 return strings.TrimPrefix(str, targetContentPrefix) 223 case strings.HasPrefix(str, targetTagPrefix): 224 return strings.TrimPrefix(str, targetTagPrefix) 225 default: 226 return str 227 } 228 } 229 230 func convertLogToMap(log *protocol.Log, logTags []*protocol.LogTag, src, topic string, tagKeyRenameMap map[string]string) (map[string]string, map[string]string) { 231 contents, tags := make(map[string]string), make(map[string]string) 232 for _, logContent := range log.Contents { 233 switch logContent.Key { 234 case "__log_topic__": 235 addTagIfRequired(tags, tagKeyRenameMap, tagLogTopic, logContent.Value) 236 case tagPrefix + "__user_defined_id__": 237 continue 238 default: 239 var tagName string 240 if strings.HasPrefix(logContent.Key, tagPrefix) { 241 tagName = logContent.Key[len(tagPrefix):] 242 if _, ok := specialTagConversionMap[tagName]; *flags.K8sFlag && ok { 243 tagName = specialTagConversionMap[tagName] 244 } else if _, ok := tagConversionMap[tagName]; ok { 245 tagName = tagConversionMap[tagName] 246 } 247 } else { 248 if _, ok := specialTagConversionMap[logContent.Key]; *flags.K8sFlag && ok { 249 tagName = specialTagConversionMap[logContent.Key] 250 } else if _, ok := tagConversionMap[logContent.Key]; ok { 251 tagName = tagConversionMap[logContent.Key] 252 } 253 } 254 if len(tagName) != 0 { 255 addTagIfRequired(tags, tagKeyRenameMap, tagName, logContent.Value) 256 } else { 257 contents[logContent.Key] = logContent.Value 258 } 259 } 260 } 261 262 for _, logTag := range logTags { 263 if logTag.Key == "__user_defined_id__" || logTag.Key == "__pack_id__" { 264 continue 265 } 266 267 tagName := logTag.Key 268 if _, ok := specialTagConversionMap[logTag.Key]; *flags.K8sFlag && ok { 269 tagName = specialTagConversionMap[logTag.Key] 270 } else if _, ok := tagConversionMap[logTag.Key]; ok { 271 tagName = tagConversionMap[logTag.Key] 272 } 273 addTagIfRequired(tags, tagKeyRenameMap, tagName, logTag.Value) 274 } 275 276 addTagIfRequired(tags, tagKeyRenameMap, tagHostIP, src) 277 if topic != "" { 278 addTagIfRequired(tags, tagKeyRenameMap, tagLogTopic, topic) 279 } 280 281 return contents, tags 282 } 283 284 func findTargetValues(targetFields []string, contents, tags, tagKeyRenameMap map[string]string) (map[string]string, error) { 285 if len(targetFields) == 0 { 286 return nil, nil 287 } 288 289 desiredValue := make(map[string]string, len(targetFields)) 290 for _, field := range targetFields { 291 switch { 292 case strings.HasPrefix(field, targetContentPrefix): 293 if value, ok := contents[field[len(targetContentPrefix):]]; ok { 294 desiredValue[field] = value 295 } 296 case strings.HasPrefix(field, targetTagPrefix): 297 if value, ok := tags[field[len(targetTagPrefix):]]; ok { 298 desiredValue[field] = value 299 } else if value, ok := tagKeyRenameMap[field[len(targetTagPrefix):]]; ok { 300 desiredValue[field] = tags[value] 301 } 302 default: 303 return nil, fmt.Errorf("unsupported field: %s", field) 304 } 305 } 306 return desiredValue, nil 307 } 308 309 func addTagIfRequired(tags, tagKeyRenameMap map[string]string, key, value string) { 310 if newKey, ok := tagKeyRenameMap[key]; ok && len(newKey) != 0 { 311 tags[newKey] = value 312 } else if !ok { 313 tags[key] = value 314 } 315 }