Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

Detailed pseudocode

johnabass edited this page Jun 11, 2018 · 2 revisions

Consider central types within the github.com/Comcast/golang-money/money.v1 package:

type Span struct {
    /* whatever money trace requires for a span */
}

// Result models the result fields of a span.  The zero value of this struct
// indicates a successful span execution.
type Result struct {
    // Code is an abstract value which is up to the span code to supply.
    // It is not necessary to enforce that this is an HTTP status code.
    // The translation into an HTTP status code should take place elsewhere.
    Code int

    // Err is just the error reported by the span
    Err error
    /* whatever else money trace needs for a result */
}

// Spanner is the core interface for this package.  It acts as the factory for spans
// for all downstream code.
type Spanner interface {
    Start(context.Context, Span) Tracker
}

// Tracker is the management interface for an active span.  It can be used to create
// child spans and to mark the current span as finished.
type Tracker interface {
    Spanner

    // Finish concludes this span with the given result
    Finish(Result)
}

Now consider types within the github.com/Comcast/golang-money/money.v1/moneyhttp package:

// SpanDecoder is a strategy for turning server requests into spans
type SpanDecoder func(*http.Request) Span

// HTTPSpanner implements money.Spanner and is the root factory
// for HTTP spans
type HTTPSpanner struct {
    sd SpanDecoder

    /* other stuff */
}

func (hs *HTTPSpanner) Start(ctx context.Context, s Span) money.Tracker {
    // create and return an HTTPTracker
}

func (hs *HTTPSpanner) Decorate(next http.Handler) http.Handler {
    if ht == nil {
        // allow DI of nil values to shut off money trace
        return next
    }

    return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
        span := hs.sd(request)
        tracker := ht.Start(request.Context(), span)
        next.ServeHTTP(
            simpleResponseWriter{response},
            request.WithContext(/* add tracker to the context */),
        )
    })
}

// New creates an HTTPSpanner based on options.
// See https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
type New(options) *HTTPSpanner {
}

// HTTPTracker is the management type for child spans
type HTTPTracker struct {
}

// simpleResponseWriter is the core decorated http.ResponseWriter.
// DO NOT blindly implement http.Flusher, etc, as this will lie to other infrastructure
type simpleResponseWriter struct {
    http.ResponseWriter
    code int
}

func (rw simpleResponseWriter) WriteHeader(code int) {
    if code < 1 {
        rw.code = code
        rw.WriteHeader(code)
    }
}

The above meets the (2) basic use cases:

(1) Using spans within http.Handler code

func MyHandler(_ http.ResponseWriter, request *http.Request) {
    // obtain the tracker from request.Context() if necessary
}

(2) Using spans in code that is unaware of HTTP semantics

func MyFunction(ctx context.Context) {
    child := trackerFromCtx.Start(...)
    defer child.Finish(Result{})
}
Clone this wiki locally