logsdata_to_logservice.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package tencentcloudlogserviceexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/tencentcloudlogserviceexporter"
  4. import (
  5. "encoding/json"
  6. "strconv"
  7. "time"
  8. "go.opentelemetry.io/collector/pdata/pcommon"
  9. "go.opentelemetry.io/collector/pdata/plog"
  10. conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
  11. "google.golang.org/protobuf/proto"
  12. cls "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/tencentcloudlogserviceexporter/proto"
  13. "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/traceutil"
  14. )
  15. const (
  16. traceIDField = "traceID"
  17. spanIDField = "spanID"
  18. clsLogTimeUnixNano = "timeUnixNano"
  19. clsLogSeverityNumber = "severityNumber"
  20. clsLogSeverityText = "severityText"
  21. clsLogContent = "content"
  22. clsLogAttribute = "attribute"
  23. clsLogFlags = "flags"
  24. clsLogResource = "resource"
  25. clsLogHost = "host"
  26. clsLogService = "service"
  27. // shortcut for "otlp.instrumentation.library.name" "otlp.instrumentation.library.version"
  28. clsLogInstrumentationName = "otlp.name"
  29. clsLogInstrumentationVersion = "otlp.version"
  30. )
  31. func convertLogs(ld plog.Logs) []*cls.Log {
  32. clsLogs := make([]*cls.Log, 0, ld.LogRecordCount())
  33. rls := ld.ResourceLogs()
  34. for i := 0; i < rls.Len(); i++ {
  35. rl := rls.At(i)
  36. ills := rl.ScopeLogs()
  37. resource := rl.Resource()
  38. resourceContents := resourceToLogContents(resource)
  39. for j := 0; j < ills.Len(); j++ {
  40. ils := ills.At(j)
  41. instrumentationLibraryContents := instrumentationLibraryToLogContents(ils.Scope())
  42. logs := ils.LogRecords()
  43. for j := 0; j < logs.Len(); j++ {
  44. clsLog := mapLogRecordToLogService(logs.At(j), resourceContents, instrumentationLibraryContents)
  45. if clsLog != nil {
  46. clsLogs = append(clsLogs, clsLog)
  47. }
  48. }
  49. }
  50. }
  51. return clsLogs
  52. }
  53. func resourceToLogContents(resource pcommon.Resource) []*cls.Log_Content {
  54. attrs := resource.Attributes()
  55. var hostname, serviceName string
  56. if host, ok := attrs.Get(conventions.AttributeHostName); ok {
  57. hostname = host.AsString()
  58. }
  59. if service, ok := attrs.Get(conventions.AttributeServiceName); ok {
  60. serviceName = service.AsString()
  61. }
  62. fields := map[string]any{}
  63. attrs.Range(func(k string, v pcommon.Value) bool {
  64. if k == conventions.AttributeServiceName || k == conventions.AttributeHostName {
  65. return true
  66. }
  67. fields[k] = v.AsString()
  68. return true
  69. })
  70. attributeBuffer, err := json.Marshal(fields)
  71. if err != nil {
  72. return nil
  73. }
  74. return []*cls.Log_Content{
  75. {
  76. Key: proto.String(clsLogHost),
  77. Value: proto.String(hostname),
  78. },
  79. {
  80. Key: proto.String(clsLogService),
  81. Value: proto.String(serviceName),
  82. },
  83. {
  84. Key: proto.String(clsLogResource),
  85. Value: proto.String(string(attributeBuffer)),
  86. },
  87. }
  88. }
  89. func instrumentationLibraryToLogContents(scope pcommon.InstrumentationScope) []*cls.Log_Content {
  90. return []*cls.Log_Content{
  91. {
  92. Key: proto.String(clsLogInstrumentationName),
  93. Value: proto.String(scope.Name()),
  94. },
  95. {
  96. Key: proto.String(clsLogInstrumentationVersion),
  97. Value: proto.String(scope.Version()),
  98. },
  99. }
  100. }
  101. func mapLogRecordToLogService(lr plog.LogRecord,
  102. resourceContents,
  103. instrumentationLibraryContents []*cls.Log_Content) *cls.Log {
  104. if lr.Body().Type() == pcommon.ValueTypeEmpty {
  105. return nil
  106. }
  107. var clsLog cls.Log
  108. // pre alloc, refine if logContent's len > 16
  109. preAllocCount := 16
  110. clsLog.Contents = make([]*cls.Log_Content, 0, preAllocCount+len(resourceContents)+len(instrumentationLibraryContents))
  111. fields := map[string]any{}
  112. lr.Attributes().Range(func(k string, v pcommon.Value) bool {
  113. fields[k] = v.AsString()
  114. return true
  115. })
  116. attributeBuffer, err := json.Marshal(fields)
  117. if err != nil {
  118. return nil
  119. }
  120. contentsBuffer := []*cls.Log_Content{
  121. {
  122. Key: proto.String(clsLogTimeUnixNano),
  123. Value: proto.String(strconv.FormatUint(uint64(lr.Timestamp()), 10)),
  124. },
  125. {
  126. Key: proto.String(clsLogSeverityNumber),
  127. Value: proto.String(strconv.FormatInt(int64(lr.SeverityNumber()), 10)),
  128. },
  129. {
  130. Key: proto.String(clsLogSeverityText),
  131. Value: proto.String(lr.SeverityText()),
  132. },
  133. {
  134. Key: proto.String(clsLogAttribute),
  135. Value: proto.String(string(attributeBuffer)),
  136. },
  137. {
  138. Key: proto.String(clsLogContent),
  139. Value: proto.String(lr.Body().AsString()),
  140. },
  141. {
  142. Key: proto.String(clsLogFlags),
  143. Value: proto.String(strconv.FormatUint(uint64(lr.Flags()), 16)),
  144. },
  145. {
  146. Key: proto.String(traceIDField),
  147. Value: proto.String(traceutil.TraceIDToHexOrEmptyString(lr.TraceID())),
  148. },
  149. {
  150. Key: proto.String(spanIDField),
  151. Value: proto.String(traceutil.SpanIDToHexOrEmptyString(lr.SpanID())),
  152. },
  153. }
  154. clsLog.Contents = append(clsLog.Contents, resourceContents...)
  155. clsLog.Contents = append(clsLog.Contents, instrumentationLibraryContents...)
  156. clsLog.Contents = append(clsLog.Contents, contentsBuffer...)
  157. if lr.Timestamp() > 0 {
  158. // convert time nano to time seconds
  159. clsLog.Time = proto.Int64(int64(lr.Timestamp() / 1000000000))
  160. } else {
  161. clsLog.Time = proto.Int64(time.Now().Unix())
  162. }
  163. return &clsLog
  164. }