add tracing configuration switch for whole framework

This commit is contained in:
John Guo
2021-12-10 18:08:36 +08:00
parent 4769fbf383
commit 3f24159636
5 changed files with 99 additions and 46 deletions

View File

@ -16,9 +16,11 @@ import (
"net/http"
"net/http/cookiejar"
"net/url"
"os"
"strings"
"time"
"github.com/gogf/gf/v2/net/gtrace"
"golang.org/x/net/proxy"
"github.com/gogf/gf/v2"
@ -43,12 +45,13 @@ type Client struct {
}
var (
defaultClientAgent = fmt.Sprintf(`GoFrameHTTPClient %s`, gf.VERSION)
host, _ = os.Hostname()
defaultClientAgent = fmt.Sprintf(`GClient %s at %s`, gf.VERSION, host)
)
// New creates and returns a new HTTP client object.
func New() *Client {
client := &Client{
c := &Client{
Client: http.Client{
Transport: &http.Transport{
// No validation for https certification of the server in default.
@ -61,8 +64,12 @@ func New() *Client {
header: make(map[string]string),
cookies: make(map[string]string),
}
client.header["User-Agent"] = defaultClientAgent
return client
c.header["User-Agent"] = defaultClientAgent
// It enables OpenTelemetry for client if tracing feature is enabled.
if gtrace.IsEnabled() {
c.Use(MiddlewareTracing)
}
return c
}
// Clone deeply clones current client and returns a new one.

View File

@ -7,11 +7,13 @@
package gclient
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptrace"
"github.com/gogf/gf/v2/os/gctx"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
@ -27,25 +29,38 @@ import (
)
const (
tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client"
tracingAttrHttpAddressRemote = "http.address.remote"
tracingAttrHttpAddressLocal = "http.address.local"
tracingAttrHttpDnsStart = "http.dns.start"
tracingAttrHttpDnsDone = "http.dns.done"
tracingAttrHttpConnectStart = "http.connect.start"
tracingAttrHttpConnectDone = "http.connect.done"
tracingEventHttpRequest = "http.request"
tracingEventHttpRequestHeaders = "http.request.headers"
tracingEventHttpRequestBaggage = "http.request.baggage"
tracingEventHttpRequestBody = "http.request.body"
tracingEventHttpResponse = "http.response"
tracingEventHttpResponseHeaders = "http.response.headers"
tracingEventHttpResponseBody = "http.response.body"
tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client"
tracingAttrHttpAddressRemote = "http.address.remote"
tracingAttrHttpAddressLocal = "http.address.local"
tracingAttrHttpDnsStart = "http.dns.start"
tracingAttrHttpDnsDone = "http.dns.done"
tracingAttrHttpConnectStart = "http.connect.start"
tracingAttrHttpConnectDone = "http.connect.done"
tracingEventHttpRequest = "http.request"
tracingEventHttpRequestHeaders = "http.request.headers"
tracingEventHttpRequestBaggage = "http.request.baggage"
tracingEventHttpRequestBody = "http.request.body"
tracingEventHttpResponse = "http.response"
tracingEventHttpResponseHeaders = "http.response.headers"
tracingEventHttpResponseBody = "http.response.body"
tracingMiddlewareHandled gctx.StrKey = `MiddlewareClientTracingHandled`
)
// MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) {
tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
var (
ctx = r.Context()
)
// Mark this request is handled by server tracing middleware.
if ctx.Value(tracingMiddlewareHandled) != nil {
return c.Next(r)
}
ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1)
tr := otel.GetTracerProvider().Tracer(
tracingInstrumentName,
trace.WithInstrumentationVersion(gf.VERSION),
)
ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient))
defer span.End()

View File

@ -7,10 +7,11 @@
package ghttp
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"github.com/gogf/gf/v2/os/gctx"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
@ -20,38 +21,50 @@ import (
"github.com/gogf/gf/v2"
"github.com/gogf/gf/v2/internal/httputil"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
const (
tracingInstrumentName = "github.com/gogf/gf/v2/net/ghttp.Server"
tracingEventHttpRequest = "http.request"
tracingEventHttpRequestHeaders = "http.request.headers"
tracingEventHttpRequestBaggage = "http.request.baggage"
tracingEventHttpRequestBody = "http.request.body"
tracingEventHttpResponse = "http.response"
tracingEventHttpResponseHeaders = "http.response.headers"
tracingEventHttpResponseBody = "http.response.body"
tracingInstrumentName = "github.com/gogf/gf/v2/net/ghttp.Server"
tracingEventHttpRequest = "http.request"
tracingEventHttpRequestHeaders = "http.request.headers"
tracingEventHttpRequestBaggage = "http.request.baggage"
tracingEventHttpRequestBody = "http.request.body"
tracingEventHttpResponse = "http.response"
tracingEventHttpResponseHeaders = "http.response.headers"
tracingEventHttpResponseBody = "http.response.body"
tracingMiddlewareHandled gctx.StrKey = `MiddlewareServerTracingHandled`
)
// MiddlewareClientTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
func MiddlewareClientTracing(c *gclient.Client, r *http.Request) (*gclient.Response, error) {
return gclient.MiddlewareTracing(c, r)
}
// MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
func MiddlewareServerTracing(r *Request) {
var (
tr = otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
ctx, span = tr.Start(
otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),
r.URL.String(),
trace.WithSpanKind(trace.SpanKindServer),
ctx = r.Context()
)
// Mark this request is handled by server tracing middleware.
if ctx.Value(tracingMiddlewareHandled) != nil {
r.Middleware.Next()
return
}
ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1)
var (
span trace.Span
tr = otel.GetTracerProvider().Tracer(
tracingInstrumentName,
trace.WithInstrumentationVersion(gf.VERSION),
)
)
ctx, span = tr.Start(
otel.GetTextMapPropagator().Extract(
ctx,
propagation.HeaderCarrier(r.Header),
),
r.URL.String(),
trace.WithSpanKind(trace.SpanKindServer),
)
defer span.End()
span.SetAttributes(gtrace.CommonLabels()...)

View File

@ -16,6 +16,7 @@ import (
"strings"
"time"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/olekukonko/tablewriter"
"github.com/gogf/gf/v2/container/garray"
@ -115,6 +116,10 @@ func GetServer(name ...interface{}) *Server {
}
// Record the server to internal server mapping by name.
serverMapping.Set(serverName, s)
// It enables OpenTelemetry for server if tracing feature is enabled.
if gtrace.IsEnabled() {
s.Use(MiddlewareServerTracing)
}
return s
}

View File

@ -28,14 +28,16 @@ import (
const (
tracingCommonKeyIpIntranet = `ip.intranet`
tracingCommonKeyIpHostname = `hostname`
commandEnvKeyForMaxContentLogSize = "gf.gtrace.maxcontentlogsize"
commandEnvKeyForTracingInternal = "gf.gtrace.tracinginternal"
commandEnvKeyForTraceEnabled = "gf.trace.enabled" // Main switch for tracing feature.
commandEnvKeyForMaxContentLogSize = "gf.gtrace.max.content.log.size" // To avoid too big tracing content.
commandEnvKeyForTracingInternal = "gf.gtrace.tracing.internal" // For detailed controlling for tracing content.
)
var (
intranetIps, _ = gipv4.GetIntranetIpArray()
intranetIpStr = strings.Join(intranetIps, ",")
hostname, _ = os.Hostname()
traceEnabled = false // traceEnabled enables tracing feature for all.
tracingInternal = true // tracingInternal enables tracing for internal type spans.
tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body, especially for HTTP/RPC request.
// defaultTextMapPropagator is the default propagator for context propagation between peers.
@ -46,6 +48,7 @@ var (
)
func init() {
traceEnabled = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForTraceEnabled, "false"))
tracingInternal = gconv.Bool(command.GetOptWithEnv(commandEnvKeyForTracingInternal, "true"))
if maxContentLogSize := gconv.Int(command.GetOptWithEnv(commandEnvKeyForMaxContentLogSize)); maxContentLogSize > 0 {
tracingMaxContentLogSize = maxContentLogSize
@ -53,6 +56,21 @@ func init() {
CheckSetDefaultTextMapPropagator()
}
// SetEnabled enables or disables the tracing feature.
func SetEnabled(enabled bool) {
traceEnabled = enabled
}
// IsEnabled checks and returns if tracing feature is configured enabled.
func IsEnabled() bool {
return traceEnabled
}
// IsActivated checks given context and returns if tracing feature is actually activated in this context.
func IsActivated(ctx context.Context) bool {
return GetTraceID(ctx) != ""
}
// IsTracingInternal returns whether tracing spans of internal components.
func IsTracingInternal() bool {
return tracingInternal
@ -73,11 +91,6 @@ func CommonLabels() []attribute.KeyValue {
}
}
// IsActivated checks and returns if tracing feature is activated.
func IsActivated(ctx context.Context) bool {
return GetTraceID(ctx) != ""
}
// CheckSetDefaultTextMapPropagator sets the default TextMapPropagator if it is not set previously.
func CheckSetDefaultTextMapPropagator() {
p := otel.GetTextMapPropagator()