exporter.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package oc
  2. import (
  3. "github.com/sirupsen/logrus"
  4. "go.opencensus.io/trace"
  5. "google.golang.org/grpc/codes"
  6. "github.com/Microsoft/hcsshim/internal/log"
  7. "github.com/Microsoft/hcsshim/internal/logfields"
  8. )
  9. const spanMessage = "Span"
  10. var _errorCodeKey = logrus.ErrorKey + "Code"
  11. // LogrusExporter is an OpenCensus `trace.Exporter` that exports
  12. // `trace.SpanData` to logrus output.
  13. type LogrusExporter struct{}
  14. var _ trace.Exporter = &LogrusExporter{}
  15. // ExportSpan exports `s` based on the the following rules:
  16. //
  17. // 1. All output will contain `s.Attributes`, `s.SpanKind`, `s.TraceID`,
  18. // `s.SpanID`, and `s.ParentSpanID` for correlation
  19. //
  20. // 2. Any calls to .Annotate will not be supported.
  21. //
  22. // 3. The span itself will be written at `logrus.InfoLevel` unless
  23. // `s.Status.Code != 0` in which case it will be written at `logrus.ErrorLevel`
  24. // providing `s.Status.Message` as the error value.
  25. func (le *LogrusExporter) ExportSpan(s *trace.SpanData) {
  26. if s.DroppedAnnotationCount > 0 {
  27. logrus.WithFields(logrus.Fields{
  28. "name": s.Name,
  29. logfields.TraceID: s.TraceID.String(),
  30. logfields.SpanID: s.SpanID.String(),
  31. "dropped": s.DroppedAttributeCount,
  32. "maxAttributes": len(s.Attributes),
  33. }).Warning("span had dropped attributes")
  34. }
  35. entry := log.L.Dup()
  36. // Combine all span annotations with span data (eg, trace ID, span ID, parent span ID,
  37. // error, status code)
  38. // (OC) Span attributes are guaranteed to be strings, bools, or int64s, so we can
  39. // can skip overhead in entry.WithFields() and add them directly to entry.Data.
  40. // Preallocate ahead of time, since we should add, at most, 10 additional entries
  41. data := make(logrus.Fields, len(entry.Data)+len(s.Attributes)+10)
  42. // Default log entry may have prexisting/application-wide data
  43. for k, v := range entry.Data {
  44. data[k] = v
  45. }
  46. for k, v := range s.Attributes {
  47. data[k] = v
  48. }
  49. data[logfields.Name] = s.Name
  50. data[logfields.TraceID] = s.TraceID.String()
  51. data[logfields.SpanID] = s.SpanID.String()
  52. data[logfields.ParentSpanID] = s.ParentSpanID.String()
  53. data[logfields.StartTime] = s.StartTime
  54. data[logfields.EndTime] = s.EndTime
  55. data[logfields.Duration] = s.EndTime.Sub(s.StartTime)
  56. if sk := spanKindToString(s.SpanKind); sk != "" {
  57. data["spanKind"] = sk
  58. }
  59. level := logrus.InfoLevel
  60. if s.Status.Code != 0 {
  61. level = logrus.ErrorLevel
  62. // don't overwrite an existing "error" or "errorCode" attributes
  63. if _, ok := data[logrus.ErrorKey]; !ok {
  64. data[logrus.ErrorKey] = s.Status.Message
  65. }
  66. if _, ok := data[_errorCodeKey]; !ok {
  67. data[_errorCodeKey] = codes.Code(s.Status.Code).String()
  68. }
  69. }
  70. entry.Data = data
  71. entry.Time = s.StartTime
  72. entry.Log(level, spanMessage)
  73. }