enable OpenTelemetry in default for some core components

This commit is contained in:
John Guo 2021-12-16 23:09:37 +08:00
parent 000483cc6f
commit aee52fd56e
17 changed files with 211 additions and 96 deletions

View File

@ -38,7 +38,7 @@ const (
// addSqlToTracing adds sql information to tracer if it's enabled.
func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
if !gtrace.IsTracingInternal() || !gtrace.IsActivated(ctx) {
if gtrace.IsUsingDefaultProvider() || !gtrace.IsTracingInternal() {
return
}
tr := otel.GetTracerProvider().Tracer(

View File

@ -40,7 +40,7 @@ const (
// addTracingItem checks and adds redis tracing information to OpenTelemetry.
func (c *RedisConn) addTracingItem(ctx context.Context, item *tracingItem) {
if !gtrace.IsTracingInternal() || !gtrace.IsActivated(ctx) {
if gtrace.IsUsingDefaultProvider() || !gtrace.IsTracingInternal() {
return
}
tr := otel.GetTracerProvider().Tracer(

5
go.mod
View File

@ -12,8 +12,9 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/grokify/html-strip-tags-go v0.0.1
github.com/olekukonko/tablewriter v0.0.5
go.opentelemetry.io/otel v1.2.0
go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/otel v1.3.0
go.opentelemetry.io/otel/sdk v1.3.0
go.opentelemetry.io/otel/trace v1.3.0
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b

16
go.sum
View File

@ -15,6 +15,11 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1 h1:DX7uPQ4WgAWfoh+NGGlbJQswnYIVvz0SRlLS3rPZQDA=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
@ -69,10 +74,12 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ=
go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I=
go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0=
go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0=
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -104,6 +111,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -0,0 +1,56 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package tracing provides some utility functions for tracing functionality.
package tracing
import (
"math"
"time"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/util/grand"
"go.opentelemetry.io/otel/trace"
)
var (
randomInitSequence = int32(grand.Intn(math.MaxInt32))
sequence = gtype.NewInt32(randomInitSequence)
)
// NewIDs creates and returns a new trace and span ID.
func NewIDs() (traceID trace.TraceID, spanID trace.SpanID) {
var (
timestampNanoBytes = gbinary.EncodeInt64(time.Now().UnixNano())
sequenceBytes = gbinary.EncodeInt32(sequence.Add(1))
randomBytes = grand.B(4)
)
copy(traceID[:], timestampNanoBytes)
copy(traceID[8:], sequenceBytes)
copy(traceID[12:], randomBytes)
copy(spanID[:], timestampNanoBytes)
return
}
// NewTraceID creates and returns a trace ID.
func NewTraceID() (traceID trace.TraceID) {
var (
timestampNanoBytes = gbinary.EncodeInt64(time.Now().UnixNano())
sequenceBytes = gbinary.EncodeInt32(sequence.Add(1))
randomBytes = grand.B(4)
)
copy(traceID[:], timestampNanoBytes)
copy(traceID[8:], sequenceBytes)
copy(traceID[12:], randomBytes)
return
}
// NewSpanID creates and returns a span ID.
func NewSpanID() (spanID trace.SpanID) {
copy(spanID[:], gbinary.EncodeInt64(time.Now().UnixNano()))
return
}

View File

@ -20,7 +20,6 @@ import (
"strings"
"time"
"github.com/gogf/gf/v2/net/gtrace"
"golang.org/x/net/proxy"
"github.com/gogf/gf/v2"
@ -65,10 +64,8 @@ func New() *Client {
cookies: make(map[string]string),
}
c.header["User-Agent"] = defaultClientAgent
// It enables OpenTelemetry for client if tracing feature is enabled.
if gtrace.IsEnabled() {
c.Use(MiddlewareTracing)
}
// It enables OpenTelemetry for client in default.
c.Use(internalMiddlewareTracing)
return c
}

View File

@ -46,8 +46,8 @@ const (
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) {
// internalMiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
func internalMiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) {
var (
ctx = r.Context()
)
@ -70,6 +70,12 @@ func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err erro
// Inject tracing content into http header.
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header))
// If it is now using default trace provider, ot then does no complex tracing jobs.
if gtrace.IsUsingDefaultProvider() {
response, err = c.Next(r)
return
}
// Continue client handler executing.
response, err = c.Next(
r.WithContext(

View File

@ -38,8 +38,8 @@ const (
tracingMiddlewareHandled gctx.StrKey = `MiddlewareServerTracingHandled`
)
// MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
func MiddlewareServerTracing(r *Request) {
// internalMiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
func internalMiddlewareServerTracing(r *Request) {
var (
ctx = r.Context()
)
@ -73,6 +73,12 @@ func MiddlewareServerTracing(r *Request) {
// Inject tracing context.
r.SetCtx(ctx)
// If it is now using default trace provider, ot then does no complex tracing jobs.
if gtrace.IsUsingDefaultProvider() {
r.Middleware.Next()
return
}
// Request content logging.
reqBodyContentBytes, _ := ioutil.ReadAll(r.Body)
r.Body = utils.NewReadCloser(reqBodyContentBytes, false)

View File

@ -16,7 +16,6 @@ import (
"strings"
"time"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/olekukonko/tablewriter"
"github.com/gogf/gf/v2/container/garray"
@ -116,10 +115,8 @@ 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)
}
// It enables OpenTelemetry for server in default.
s.Use(internalMiddlewareServerTracing)
return s
}

View File

@ -12,7 +12,7 @@ import (
"os"
"strings"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/net/gtrace/internal/provider"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
@ -38,7 +38,6 @@ 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.
@ -49,29 +48,19 @@ 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
}
// Default trace provider.
otel.SetTracerProvider(provider.New())
CheckSetDefaultTextMapPropagator()
intlog.Printf(context.TODO(), `traceEnabled initialized as: %v`, traceEnabled)
}
// SetEnabled enables or disables the tracing feature.
func SetEnabled(enabled bool) {
traceEnabled = enabled
intlog.Printf(context.TODO(), `traceEnabled SetEnabled: %v`, 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) != ""
// IsUsingDefaultProvider checks and return if currently using default trace provider.
func IsUsingDefaultProvider() bool {
_, ok := otel.GetTracerProvider().(*provider.TracerProvider)
return ok
}
// IsTracingInternal returns whether tracing spans of internal components.

View File

@ -52,15 +52,17 @@ func TestNewCarrier(t *testing.T) {
SpanID: spanID,
TraceFlags: trace.FlagsSampled,
}))
sc := trace.SpanContextFromContext(ctx)
t.Assert(sc.TraceID().String(), traceID.String())
t.Assert(sc.SpanID().String(), "00f067aa0ba902b7")
ctx, _ = otel.Tracer("").Start(ctx, "inject")
carrier1 := gtrace.NewCarrier()
otel.GetTextMapPropagator().Inject(ctx, carrier1)
t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"}`)
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1)
gotSc := trace.SpanContextFromContext(ctx)
t.Assert(gotSc.TraceID().String(), traceID.String())
t.Assert(gotSc.SpanID().String(), "00f067aa0ba902b7")
// New span is created internally, so the SpanID is different.
})
}

View File

@ -0,0 +1,46 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gtrace_test
import (
"fmt"
"testing"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/gtrace"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Client_Server_Tracing(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
p := 8888
s := g.Server(p)
s.BindHandler("/", func(r *ghttp.Request) {
ctx := r.Context()
g.Log().Print(ctx, "GetTraceID:", gtrace.GetTraceID(ctx))
r.Response.Write(gtrace.GetTraceID(ctx))
})
s.SetPort(p)
s.SetDumpRouterMap(false)
t.AssertNil(s.Start())
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
ctx := gctx.New()
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
client := g.Client()
client.SetPrefix(prefix)
t.Assert(gtrace.IsUsingDefaultProvider(), true)
t.Assert(client.GetContent(ctx, "/"), gtrace.GetTraceID(ctx))
t.Assert(client.GetContent(ctx, "/"), gctx.CtxId(ctx))
})
}

View File

@ -0,0 +1,23 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package provider
import (
sdkTrace "go.opentelemetry.io/otel/sdk/trace"
)
type TracerProvider struct {
*sdkTrace.TracerProvider
}
func New() *TracerProvider {
return &TracerProvider{
TracerProvider: sdkTrace.NewTracerProvider(
sdkTrace.WithIDGenerator(NewIDGenerator()),
),
}
}

View File

@ -0,0 +1,30 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package provider
import (
"context"
"github.com/gogf/gf/v2/internal/tracing"
"go.opentelemetry.io/otel/trace"
)
type IDGenerator struct{}
func NewIDGenerator() *IDGenerator {
return &IDGenerator{}
}
// NewIDs creates and returns a new trace and span ID.
func (id *IDGenerator) NewIDs(ctx context.Context) (traceID trace.TraceID, spanID trace.SpanID) {
return tracing.NewIDs()
}
// NewSpanID returns an ID for a new span in the trace with traceID.
func (id *IDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) (spanID trace.SpanID) {
return tracing.NewSpanID()
}

View File

@ -10,7 +10,7 @@ package gctx
import (
"context"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/net/gtrace"
)
type (
@ -18,12 +18,6 @@ type (
StrKey string // StrKey is a type for warps basic type string as context key.
)
const (
// CtxKey is custom tracing context key for context id.
// The context id a unique string for certain context.
CtxKey StrKey = "GoFrameCtxId"
)
// New creates and returns a context which contains context id.
func New() context.Context {
return WithCtx(context.Background())
@ -31,31 +25,12 @@ func New() context.Context {
// WithCtx creates and returns a context containing context id upon given parent context `ctx`.
func WithCtx(ctx context.Context) context.Context {
return WithPrefix(ctx, "")
}
// WithPrefix creates and returns a context containing context id upon given parent context `ctx`.
// The generated context id has custom prefix string specified by parameter `prefix`.
func WithPrefix(ctx context.Context, prefix string) context.Context {
return WithCtxId(ctx, prefix+getUniqueID())
}
// WithCtxId creates and returns a context containing context id upon given parent context `ctx`.
// The generated context id value is specified by parameter `id`.
func WithCtxId(ctx context.Context, id string) context.Context {
if id == "" {
return New()
}
return context.WithValue(ctx, CtxKey, id)
ctx, span := gtrace.NewSpan(ctx, "gctx.WithCtx")
defer span.End()
return ctx
}
// CtxId retrieves and returns the context id from context.
func CtxId(ctx context.Context) string {
s, _ := ctx.Value(CtxKey).(string)
return s
}
// getUniqueID produces a global unique string.
func getUniqueID() string {
return guid.S()
return gtrace.GetTraceID(ctx)
}

View File

@ -12,14 +12,13 @@ import (
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
)
func Test_New(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ctx := gctx.New()
t.AssertNE(ctx, nil)
t.AssertNE(gctx.CtxId(ctx), "")
t.Assert(gctx.CtxId(ctx), "")
})
}
@ -27,25 +26,7 @@ func Test_WithCtx(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ctx := context.WithValue(context.TODO(), "TEST", 1)
ctx = gctx.WithCtx(ctx)
t.AssertNE(gctx.CtxId(ctx), "")
t.Assert(ctx.Value("TEST"), 1)
})
}
func Test_WithPrefix(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ctx := context.WithValue(context.TODO(), "TEST", 1)
ctx = gctx.WithPrefix(ctx, "H-")
t.Assert(gstr.Contains(gctx.CtxId(ctx), "H-"), true)
t.Assert(ctx.Value("TEST"), 1)
})
}
func Test_WithValue(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ctx := context.WithValue(context.TODO(), "TEST", 1)
ctx = gctx.WithCtxId(ctx, "123")
t.Assert(gctx.CtxId(ctx), "123")
t.Assert(gctx.CtxId(ctx), "")
t.Assert(ctx.Value("TEST"), 1)
})
}

View File

@ -15,7 +15,6 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
@ -52,7 +51,7 @@ func DefaultConfig() Config {
File: defaultFileFormat,
Flags: F_TIME_STD,
Level: LEVEL_ALL,
CtxKeys: []interface{}{gctx.CtxKey},
CtxKeys: []interface{}{},
StStatus: 1,
HeaderPrint: true,
StdoutPrint: true,
@ -167,7 +166,6 @@ func (l *Logger) SetStackFilter(filter string) {
// Note that multiple calls of this function will overwrite the previous set context keys.
func (l *Logger) SetCtxKeys(keys ...interface{}) {
l.config.CtxKeys = keys
l.config.CtxKeys = append(l.config.CtxKeys, gctx.CtxKey)
}
// AppendCtxKeys appends extra keys to logger.