Skip to content

Commit

Permalink
Merge pull request #78 from muir/sixtysix
Browse files Browse the repository at this point in the history
http middleware (inbound)
  • Loading branch information
muir authored Sep 28, 2022
2 parents 02fd35a + 508df5e commit bef2d48
Show file tree
Hide file tree
Showing 10 changed files with 440 additions and 244 deletions.
15 changes: 14 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

# required before ready to use in production

- misc

- rename trace.Bundle.TraceParent to Bundle.ParentTrace
- remame xoptest.Request.Trace to Bundle

- repo

- create xop org, move repo there
Expand All @@ -15,14 +20,22 @@
- integrations

- resty
- remove dependency on mux in middleware

- documentation

- propogation (add to README)

# Just do it (build ready)

- sampling can be based on Boring() in which case the flags need to
change before the "traceresponse" is set. That means top logger
must know if base loggers honored the boring. Change xopbase.Boring
to return a bool. Propagate upwards. Add log.IsBoring()

- add flag to honor sampling flag by defaulting to Boring

- change Boring to be request-level only

- grab and modify the the zerolog linter to make sure that log lines don't get dropped

- make deepcopy function configurable
Expand Down
74 changes: 0 additions & 74 deletions rest/middleware.go

This file was deleted.

84 changes: 0 additions & 84 deletions rest/request.go

This file was deleted.

17 changes: 17 additions & 0 deletions trace/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package trace

import (
"encoding/hex"
"regexp"
)

// TraceState represents W3C tracing headers.
Expand Down Expand Up @@ -61,10 +62,26 @@ type Trace struct {

func NewTrace() Trace {
var trace Trace
trace.flags.b[0] = 1
trace.initialize()
return trace
}

var traceRE = regexp.MustCompile(`^([a-fA-F0-9]{2})-([a-fA-F0-9]{32})-([a-fA-F0-9]{16})-([a-fA-F0-9]{2})$`)

func TraceFromString(s string) Trace {
m := traceRE.FindStringSubmatch(s)
if m == nil {
return NewTrace()
}
var trace Trace
trace.Version().SetString(m[1])
trace.TraceID().SetString(m[2])
trace.SpanID().SetString(m[3])
trace.Flags().SetString(m[4])
return trace
}

func (t *Trace) Version() WrappedHexBytes1 {
t.initialize()
return WrappedHexBytes1{
Expand Down
8 changes: 4 additions & 4 deletions xopjson/jsonlogger.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions xopjson/jsonlogger.zzzgo
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,17 @@ func (s *span) addRequestStartData(rq *builder) {
rq.AddSafeString(s.trace.Trace.TraceID().String())
rq.AddSafeKey("span.id")
rq.AddSafeString(s.trace.Trace.SpanID().String())
if !s.trace.TraceParent.IsZero() {
if !s.trace.TraceParent.TraceID().IsZero() {
rq.AddSafeKey("trace.parent")
rq.AppendString(s.trace.TraceParent.String())
rq.AddSafeString(s.trace.TraceParent.String())
}
if !s.trace.State.IsZero() {
rq.AddSafeKey("trace.state")
rq.AppendString(s.trace.State.String())
rq.AddSafeString(s.trace.State.String())
}
if !s.trace.Baggage.IsZero() {
rq.AddSafeKey("trace.baggage")
rq.AppendString(s.trace.Baggage.String())
rq.AddSafeString(s.trace.Baggage.String())
}
rq.AddSafeKey("span.name")
rq.AddString(s.name)
Expand Down
115 changes: 115 additions & 0 deletions xopmiddle/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package xopmiddle

import (
"context"
"net/http"

"github.com/muir/xop-go"
"github.com/muir/xop-go/xopconst"
)

type Inbound struct {
requestToName func(*http.Request) string
seed xop.Seed
}

func New(seed xop.Seed, requestToName func(*http.Request) string) Inbound {
return Inbound{
seed: seed,
requestToName: requestToName,
}
}

func (i Inbound) HandlerFuncMiddleware() func(http.HandlerFunc) http.HandlerFunc {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log, ctx := i.makeChildSpan(w, r)
defer log.Done()
r = r.WithContext(log.IntoContext(ctx))
next(w, r)
}
}
}

func (i Inbound) HandlerMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log, ctx := i.makeChildSpan(w, r)
defer log.Done()
r = r.WithContext(log.IntoContext(ctx))
next.ServeHTTP(w, r)
})
}
}

// InjectorWithContext is compatible with https://github.com/muir/nject/nvelope and
// provides a *xop.Log to the injection chain. It also puts the log in
// the request context.
func (i Inbound) InjectorWithContext() func(inner func(*xop.Log, *http.Request), w http.ResponseWriter, r *http.Request) {
return func(inner func(*xop.Log, *http.Request), w http.ResponseWriter, r *http.Request) {
log, ctx := i.makeChildSpan(w, r)
defer log.Done()
r = r.WithContext(log.IntoContext(ctx))
inner(log, r)
}
}

// InjectorWithContext is compatible with https://github.com/muir/nject/nvelope and
// provides a *xop.Log to the injection chain.
func (i Inbound) Injector() func(inner func(*xop.Log), w http.ResponseWriter, r *http.Request) {
return func(inner func(*xop.Log), w http.ResponseWriter, r *http.Request) {
log, _ := i.makeChildSpan(w, r)
defer log.Done()
inner(log)
}
}

func (i Inbound) makeChildSpan(w http.ResponseWriter, r *http.Request) (*xop.Log, context.Context) {
name := i.requestToName(r)
if name == "" {
name = r.URL.String()
}

bundle := i.seed.Bundle()

if b3 := r.Header.Get("b3"); b3 != "" {
SetByB3Header(&bundle, b3)
} else if tp := r.Header.Get("traceparent"); tp != "" {
SetByTraceParentHeader(&bundle, tp)
} else if b3TraceID := r.Header.Get("X-B3-TraceId"); b3TraceID != "" {
bundle.Trace.TraceID().SetString(b3TraceID)
if b3Sampling := r.Header.Get("X-B3-Sampled"); b3Sampling != "" {
SetByB3Sampled(&bundle.Trace, b3Sampling)
}
bundle.TraceParent = bundle.Trace
if b3ParentSpanID := r.Header.Get("X-B3-ParentSpanId"); b3ParentSpanID != "" {
bundle.TraceParent.SpanID().SetString(b3ParentSpanID)
} else {
// Uh oh, no parent span id
bundle.TraceParent.SpanID().SetZero()
}
if b3SpanID := r.Header.Get("X-B3-SpanId"); b3SpanID != "" {
bundle.Trace.SpanID().SetString(b3SpanID)
} else {
bundle.Trace.SpanID().SetRandom()
}
}
if bundle.Trace.TraceID().IsZero() {
bundle.Trace.TraceID().SetRandom()
}
if bundle.Trace.SpanID().IsZero() {
bundle.Trace.SpanID().SetRandom()
}

ctx := r.Context()
log := i.seed.Copy(
xop.WithContext(ctx),
xop.WithBundle(bundle),
).Request(r.Method + " " + name)

w.Header().Set("traceresponse", log.Span().Trace().String())
log.Span().Enum(xopconst.SpanKind, xopconst.SpanKindClient)
log.Span().EmbeddedEnum(xopconst.SpanTypeHTTPClientRequest)
log.Span().String(xopconst.URL, r.URL.String())
return log, ctx
}
Loading

0 comments on commit bef2d48

Please sign in to comment.