Skip to content

Commit

Permalink
refactors ot to otel trace
Browse files Browse the repository at this point in the history
Signed-off-by: Afzal Ansari <afzal442@gmail.com>
  • Loading branch information
afzal442 committed Jun 27, 2023
1 parent b2a2648 commit f9a595d
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 36 deletions.
73 changes: 69 additions & 4 deletions examples/hotrod/pkg/tracing/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,44 @@ import (
"errors"
"io"
"net/http"
"net/http/httptrace"
"net/url"

"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
)

type contextKey int

const (
keyTracer contextKey = iota
)

// HTTPClient wraps an http.Client with tracing instrumentation.
type HTTPClient struct {
Tracer opentracing.Tracer
Tracer trace.TracerProvider
Client *http.Client
}

// Tracer holds tracing details for one HTTP request.
type Tracer struct {
tp trace.TracerProvider
root trace.Span
sp trace.Span
opts *clientOptions
}

type clientOptions struct {
operationName string
componentName string
urlTagFunc func(u *url.URL) string
disableClientTrace bool
disableInjectSpanContext bool
spanObserver func(span trace.Span, r *http.Request)
}

// ClientOption contols the behavior of TraceRequest.
type ClientOption func(*clientOptions)

// GetJSON executes HTTP GET against specified url and tried to parse
// the response into out object.
func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, out interface{}) error {
Expand All @@ -40,7 +67,7 @@ func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, o
return err
}
req = req.WithContext(ctx)
req, ht := nethttp.TraceRequest(c.Tracer, req, nethttp.OperationName("HTTP GET: "+endpoint))
req, ht := c.TraceRequest(c.Tracer, req, c.OperationName("HTTP GET: "+endpoint))
defer ht.Finish()

res, err := c.Client.Do(req)
Expand All @@ -60,3 +87,41 @@ func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, o
decoder := json.NewDecoder(res.Body)
return decoder.Decode(out)
}

func (c *HTTPClient) TraceRequest(tp trace.TracerProvider, req *http.Request, options ...ClientOption) (*http.Request, *Tracer) {
opts := &clientOptions{
urlTagFunc: func(u *url.URL) string {
return u.String()
},
spanObserver: func(_ trace.Span, _ *http.Request) {},
}
for _, opt := range options {
opt(opts)
}
ht := &Tracer{tp: tp, opts: opts}
ctx := req.Context()
if !opts.disableClientTrace {
ctx = httptrace.WithClientTrace(ctx, ht.clientTrace())
}
req = req.WithContext(context.WithValue(ctx, keyTracer, ht))
return req, ht
}

// OperationName returns a ClientOption that sets the operation
// name for the client-side span.
func (c *HTTPClient) OperationName(operationName string) ClientOption {
return func(options *clientOptions) {
options.operationName = operationName
}
}

// Finish finishes the span of the traced request.
func (h *Tracer) Finish() {
if h.root != nil {
h.root.End()
}
}

func (h *Tracer) clientTrace() *httptrace.ClientTrace {
return &httptrace.ClientTrace{}
}
264 changes: 264 additions & 0 deletions examples/hotrod/pkg/tracing/statusCodeTraker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tracing

import (
"io"
"net/http"
)

type statusCodeTracker struct {
http.ResponseWriter
status int
}

func (w *statusCodeTracker) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
}

func (w *statusCodeTracker) Write(b []byte) (int, error) {
return w.ResponseWriter.Write(b)
}

// wrappedResponseWriter returns a wrapped version of the original
// ResponseWriter and only implements the same combination of additional
// interfaces as the original. This implementation is based on
// https://github.com/felixge/httpsnoop.
func (w *statusCodeTracker) wrappedResponseWriter() http.ResponseWriter {
var (
hj, i0 = w.ResponseWriter.(http.Hijacker)
cn, i1 = w.ResponseWriter.(http.CloseNotifier)
pu, i2 = w.ResponseWriter.(http.Pusher)
fl, i3 = w.ResponseWriter.(http.Flusher)
rf, i4 = w.ResponseWriter.(io.ReaderFrom)
)

switch {
case !i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
}{w}
case !i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
io.ReaderFrom
}{w, rf}
case !i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
}{w, fl}
case !i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{w, fl, rf}
case !i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Pusher
}{w, pu}
case !i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Pusher
io.ReaderFrom
}{w, pu, rf}
case !i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Pusher
http.Flusher
}{w, pu, fl}
case !i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Pusher
http.Flusher
io.ReaderFrom
}{w, pu, fl, rf}
case !i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
}{w, cn}
case !i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
}{w, cn, rf}
case !i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Flusher
}{w, cn, fl}
case !i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Flusher
io.ReaderFrom
}{w, cn, fl, rf}
case !i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
}{w, cn, pu}
case !i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
io.ReaderFrom
}{w, cn, pu, rf}
case !i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
http.Flusher
}{w, cn, pu, fl}
case !i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
http.Flusher
io.ReaderFrom
}{w, cn, pu, fl, rf}
case i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
}{w, hj}
case i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
}{w, hj, rf}
case i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Flusher
}{w, hj, fl}
case i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Flusher
io.ReaderFrom
}{w, hj, fl, rf}
case i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
}{w, hj, pu}
case i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
io.ReaderFrom
}{w, hj, pu, rf}
case i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
http.Flusher
}{w, hj, pu, fl}
case i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
http.Flusher
io.ReaderFrom
}{w, hj, pu, fl, rf}
case i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
}{w, hj, cn}
case i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
io.ReaderFrom
}{w, hj, cn, rf}
case i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
}{w, hj, cn, fl}
case i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
io.ReaderFrom
}{w, hj, cn, fl, rf}
case i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
}{w, hj, cn, pu}
case i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
io.ReaderFrom
}{w, hj, cn, pu, rf}
case i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
http.Flusher
}{w, hj, cn, pu, fl}
case i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
http.Flusher
io.ReaderFrom
}{w, hj, cn, pu, fl, rf}
default:
return struct {
http.ResponseWriter
}{w}
}
}

0 comments on commit f9a595d

Please sign in to comment.