gtrace.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package gtrace provides convenience wrapping functionality for tracing feature using OpenTelemetry.
  7. package gtrace
  8. import (
  9. "context"
  10. "os"
  11. "strings"
  12. "go.opentelemetry.io/otel"
  13. "go.opentelemetry.io/otel/attribute"
  14. "go.opentelemetry.io/otel/propagation"
  15. semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
  16. "go.opentelemetry.io/otel/trace"
  17. "github.com/gogf/gf/v2/container/gmap"
  18. "github.com/gogf/gf/v2/container/gvar"
  19. "github.com/gogf/gf/v2/errors/gcode"
  20. "github.com/gogf/gf/v2/errors/gerror"
  21. "github.com/gogf/gf/v2/internal/command"
  22. "github.com/gogf/gf/v2/net/gipv4"
  23. "github.com/gogf/gf/v2/net/gtrace/internal/provider"
  24. "github.com/gogf/gf/v2/text/gstr"
  25. "github.com/gogf/gf/v2/util/gconv"
  26. )
  27. const (
  28. tracingCommonKeyIpIntranet = `ip.intranet`
  29. tracingCommonKeyIpHostname = `hostname`
  30. commandEnvKeyForMaxContentLogSize = "gf.gtrace.max.content.log.size" // To avoid too big tracing content.
  31. commandEnvKeyForTracingInternal = "gf.gtrace.tracing.internal" // For detailed controlling for tracing content.
  32. )
  33. var (
  34. intranetIps, _ = gipv4.GetIntranetIpArray()
  35. intranetIpStr = strings.Join(intranetIps, ",")
  36. hostname, _ = os.Hostname()
  37. tracingInternal = true // tracingInternal enables tracing for internal type spans.
  38. tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body, especially for HTTP/RPC request.
  39. // defaultTextMapPropagator is the default propagator for context propagation between peers.
  40. defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
  41. propagation.TraceContext{},
  42. propagation.Baggage{},
  43. )
  44. )
  45. func init() {
  46. tracingInternal = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForTracingInternal, "true"))
  47. if maxContentLogSize := gconv.Int(command.GetOptWithEnv(commandEnvKeyForMaxContentLogSize)); maxContentLogSize > 0 {
  48. tracingMaxContentLogSize = maxContentLogSize
  49. }
  50. // Default trace provider.
  51. otel.SetTracerProvider(provider.New())
  52. CheckSetDefaultTextMapPropagator()
  53. }
  54. // IsUsingDefaultProvider checks and return if currently using default trace provider.
  55. func IsUsingDefaultProvider() bool {
  56. _, ok := otel.GetTracerProvider().(*provider.TracerProvider)
  57. return ok
  58. }
  59. // IsTracingInternal returns whether tracing spans of internal components.
  60. func IsTracingInternal() bool {
  61. return tracingInternal
  62. }
  63. // MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request.
  64. func MaxContentLogSize() int {
  65. return tracingMaxContentLogSize
  66. }
  67. // CommonLabels returns common used attribute labels:
  68. // ip.intranet, hostname.
  69. func CommonLabels() []attribute.KeyValue {
  70. return []attribute.KeyValue{
  71. attribute.String(tracingCommonKeyIpHostname, hostname),
  72. attribute.String(tracingCommonKeyIpIntranet, intranetIpStr),
  73. semconv.HostNameKey.String(hostname),
  74. }
  75. }
  76. // CheckSetDefaultTextMapPropagator sets the default TextMapPropagator if it is not set previously.
  77. func CheckSetDefaultTextMapPropagator() {
  78. p := otel.GetTextMapPropagator()
  79. if len(p.Fields()) == 0 {
  80. otel.SetTextMapPropagator(GetDefaultTextMapPropagator())
  81. }
  82. }
  83. // GetDefaultTextMapPropagator returns the default propagator for context propagation between peers.
  84. func GetDefaultTextMapPropagator() propagation.TextMapPropagator {
  85. return defaultTextMapPropagator
  86. }
  87. // GetTraceID retrieves and returns TraceId from context.
  88. // It returns an empty string is tracing feature is not activated.
  89. func GetTraceID(ctx context.Context) string {
  90. if ctx == nil {
  91. return ""
  92. }
  93. traceID := trace.SpanContextFromContext(ctx).TraceID()
  94. if traceID.IsValid() {
  95. return traceID.String()
  96. }
  97. return ""
  98. }
  99. // GetSpanID retrieves and returns SpanId from context.
  100. // It returns an empty string is tracing feature is not activated.
  101. func GetSpanID(ctx context.Context) string {
  102. if ctx == nil {
  103. return ""
  104. }
  105. spanID := trace.SpanContextFromContext(ctx).SpanID()
  106. if spanID.IsValid() {
  107. return spanID.String()
  108. }
  109. return ""
  110. }
  111. // SetBaggageValue is a convenient function for adding one key-value pair to baggage.
  112. // Note that it uses attribute.Any to set the key-value pair.
  113. func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context {
  114. return NewBaggage(ctx).SetValue(key, value)
  115. }
  116. // SetBaggageMap is a convenient function for adding map key-value pairs to baggage.
  117. // Note that it uses attribute.Any to set the key-value pair.
  118. func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context {
  119. return NewBaggage(ctx).SetMap(data)
  120. }
  121. // GetBaggageMap retrieves and returns the baggage values as map.
  122. func GetBaggageMap(ctx context.Context) *gmap.StrAnyMap {
  123. return NewBaggage(ctx).GetMap()
  124. }
  125. // GetBaggageVar retrieves value and returns a *gvar.Var for specified key from baggage.
  126. func GetBaggageVar(ctx context.Context, key string) *gvar.Var {
  127. return NewBaggage(ctx).GetVar(key)
  128. }
  129. // WithUUID injects custom trace id with UUID into context to propagate.
  130. func WithUUID(ctx context.Context, uuid string) (context.Context, error) {
  131. return WithTraceID(ctx, gstr.Replace(uuid, "-", ""))
  132. }
  133. // WithTraceID injects custom trace id into context to propagate.
  134. func WithTraceID(ctx context.Context, traceID string) (context.Context, error) {
  135. generatedTraceID, err := trace.TraceIDFromHex(traceID)
  136. if err != nil {
  137. return ctx, gerror.WrapCodef(
  138. gcode.CodeInvalidParameter,
  139. err,
  140. `invalid custom traceID "%s", a traceID string should be composed with [0-f] and fixed length 32`,
  141. traceID,
  142. )
  143. }
  144. sc := trace.SpanContextFromContext(ctx)
  145. if !sc.HasTraceID() {
  146. var span trace.Span
  147. ctx, span = NewSpan(ctx, "gtrace.WithTraceID")
  148. defer span.End()
  149. sc = trace.SpanContextFromContext(ctx)
  150. }
  151. ctx = trace.ContextWithRemoteSpanContext(ctx, trace.NewSpanContext(trace.SpanContextConfig{
  152. TraceID: generatedTraceID,
  153. SpanID: sc.SpanID(),
  154. TraceFlags: sc.TraceFlags(),
  155. TraceState: sc.TraceState(),
  156. Remote: sc.IsRemote(),
  157. }))
  158. return ctx, nil
  159. }