trace

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2023 License: Unlicense Imports: 16 Imported by: 0

Documentation

Overview

This module makes it easy to register a Span (and associated Trace) in GCP (Google Cloud Platform) CloudTrace (API v2).

References to "ct2." refer to items imported from the "google.golang.org/api/cloudtrace/v2" module. References to "lager." refer to items imported from "github.com/Unity-Technologies/go-lager-internal". "spans." refers to "github.com/Unity-Technologies/go-lager-internal/gcp-spans".

Index

Constants

View Source
const ZuluTime = "2006-01-02T15:04:05.999999Z"

Variables

This section is empty.

Functions

func ContextPushSpan

func ContextPushSpan(
	ctx context.Context, name string,
) (context.Context, spans.Factory)

ContextPushSpan() takes a Context which should already be decorated with a span Factory [see spans.ContextStoreSpan()]. If so, it calls NewSpan() on that span, calls 'SetDisplayName(name)' on the new child span, and returns both a Context (decorated with the new span) and the new span.

If not, it logs the lack of a span in the Context (including a stack trace) and returns an empty Factory that is mostly useless other than not being 'nil' (and the original Context).

Example usage:

ctx2, span := trace.ContextPushSpan(ctx, "span.name")
defer span.Finish()

If you do not need to retain access to the prior 'ctx', then you may want to use PushSpan() instead.

func EnvInteger

func EnvInteger(tacit int, envvar string) int

EnvInteger() gets a configuration 'int' value from the specified environment variable, returning the 'tacit' value if not set.

func NewSpanID

func NewSpanID(oldSpanID uint64) (spanID uint64)

NewSpanID() just generates a random uint64 value. You are never expected to call this directly. It prefers to use cryptographically strong random values but will resort to math/rand.Uint64() if that fails. Such a failure will be logged via lager.Warn() only once but the attempt is always made.

func NewTraceID

func NewTraceID(oldTraceID string) string

NewTraceID() returns a new trace ID that can be used with GCP CloudTrace. It is just a randomly generated sequence of 32 hexadecimal digits. You are not expected to call this directly.

If 'oldTraceID' is a valid trace ID, then it is used to add more randomness to the new trace ID (and can't return that same trace ID).

func PushSpan

func PushSpan(
	pReq **http.Request, pCtx *context.Context, name string,
) spans.Factory

PushSpan() takes pointers to an *http.Request and to a Context and takes a name to give to a new span. If 'pCtx' or '*pCtx' is 'nil', then '(*pReq).Context()' is used. If 'pReq' or '*pReq' is also 'nil' or the Context is not decorated [see spans.ContextStoreSpan()] with a Factory, then the lack of a span is logged (with a stack trace) and an empty Factory that is mostly useless other than not being 'nil'.

Otherwise, it calls 'NewSpan().SetDisplayName(name)' on the span and returns the new span. If 'pCtx' is not 'nil', then '*pCtx' is set to a copy of the Context decorated with the new span. If 'pReq' and '*pReq' are not 'nil', then '*pReq' is set to a *shallow* copy of '*pReq' (with the new Context).

Example usage:

defer trace.PushSpan(&req, &ctx, "span.name").AddPairs(
    "user", user,
).Finish()

func RequestPushSpan

func RequestPushSpan(
	req *http.Request, ctx context.Context, name string,
) (*http.Request, context.Context, spans.Factory)

RequestPushSpan() takes an *http.Request and a Context which should already be decorated with a span Factory [see spans.ContextStoreSpan()]. If so, it calls NewSpan() on that span, calls 'SetDisplayName(name)' on the new child span, and returns (in reverse order) the new span, a copy of the Context decorated with the new span, and a *deep* copy of the Request (with the new Context).

If not, it logs the lack of a span in the Context (including a stack trace) and returns an empty Factory that is mostly useless other than not being 'nil' along with the original Context and a *deep* copy of the original Request (with the original Context).

Passing in a separate Context can allow you to avoid making yet another (shallow) copy of the Request by calling 'req.WithContext(ctx)'. If you pass in 'ctx' as 'nil', then 'req.Context()' is used.

Example usage:

// req2 is a *deep* copy to avoid simultaneous access to 'req'
req2, ctx2, span := trace.RequestPushSpan(req, ctx, "span.name")
go func() {
    defer span.Finish()
    resp, err := transport.RoundTrip(req2)
    ...
}()
resp, err := transport.RoundTrip(req)
...

If you do not need a *deep* copy to be made, then you may want to use PushSpan() instead (you can save the value of the prior Request and/or Context before you call it, if needed).

func StartServer

func StartServer(
	pCtx *context.Context, pRegs ...**Registrar,
) func()

StartServer() is the simplest start-up code to include in a server to enable GCP-based tracing, usually called like:

ctx := context.Background()
defer trace.StartServer(&ctx)()
// Have 'ctx' returned by the http.Server.BaseContext func.

This assumes that the calling function will not exit until the server is shutting down.

You can also add an extra argument that is a pointer to a variable of type '*Registrar' to have that variable set to the span Registrar (mostly useful when testing).

func TimeAsString

func TimeAsString(when time.Time) string

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

See NewClient().

func MustNewClient

func MustNewClient(ctx context.Context, svc *ct2.Service) Client

MustNewClient() calls NewClient(). If that fails, then lager.Exit() is used to log the error and abort the process.

func NewClient

func NewClient(ctx context.Context, svc *ct2.Service) (Client, error)

NewClient() creates a new client capable of registering Spans in the GCP CloudTrace API v2. This client has no methods but should be passed in when starting the Registrar.

To get a default connection, pass in 'nil' for 'svc'. Otherwise you can use ct2.NewService() or ct2.New() to create a base service to use and pass the result in as 'svc'.

If 'svc' is 'nil', then 'ctx' is the Context used when creating the base service using default options. If 'svc' is not 'nil', then 'ctx' is ignored.

type Registrar

type Registrar struct {
	// contains filtered or unexported fields
}

Registrar is mostly just an object to use to Halt() the registration runners that got started when you created the Registrar.

It also can create an empty spans.Factory that can be used to create and manipulate spans.

func MustNewRegistrar

func MustNewRegistrar(project string, client Client) *Registrar

MustNewRegistrar() calls NewRegistrar() and, if that fails, uses lager.Exit() to abort the process.

func NewRegistrar

func NewRegistrar(project string, client Client) (*Registrar, error)

NewRegistrar() starts a number of go-routines that wait to receive Finish()ed Spans and then register them with GCP Cloud Trace.

func (*Registrar) Halt

func (r *Registrar) Halt()

Halt() tells the runners to terminate and waits for them all to finish before returning.

Halt() should only be called after you are sure that no more spans will be Finish()ed. Any spans Finish()ed after Halt() has been called may cause a panic(). Not waiting for Halt() to return can mean that recently Finish()ed spans might not be registered.

func (*Registrar) NewFactory

func (r *Registrar) NewFactory() spans.Factory

NewFactory() returns a spans.Factory that can be used to create and manipulate spans and eventually register them with GCP Cloud Trace.

func (*Registrar) WaitForIdleRunners

func (r *Registrar) WaitForIdleRunners()

WaitForIdleRunners() is only meant to be used by tests. It allows you to ensure that all prior Finish()ed Spans have been processed so the test can check for any errors that were logged.

It works by sending one request per runner that will cause that runner to wait when it receives it. Then it reads the responses from all of the runners (which ends their waiting) and then returns.

func (*Registrar) WaitForRunnerRead

func (r *Registrar) WaitForRunnerRead()

WaitForRunnerRead() is only meant to be used by tests. It allows you to ensure that a prior Finish()ed Spans has been read by the only runner.

type Span

type Span struct {
	spans.ROSpan
	// contains filtered or unexported fields
}

Span tracks a span inside of a trace and can be used to create new child spans within it. It also registers the span with GCP when Finish() is called on it [unless it was created via Import()].

A Span object is expected to be modified only from a single goroutine and so no locking is implemented. Creation of sub-spans does implement locking so that multiple go routines can safely create sub-spans from the same span without additional locking.

func (*Span) AddAttribute

func (s *Span) AddAttribute(key string, val interface{}) error

AddAttribute() adds an attribute key/value pair to the contained span. Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed (even returning a 'nil' error).

'val' can be a 'string', 'int64', or a 'bool'. 'int' values will be promoted to 'int64'. Other values that have a String() or an Error() method will have that method used to convert them to a string. If 'key' is empty or 'val' is not one of the listed types, then an error is returned and the attribute is not added.

func (*Span) AddPairs

func (s *Span) AddPairs(pairs ...interface{}) spans.Factory

AddPairs() takes a list of attribute key/value pairs. For each pair, AddAttribute() is called and any returned error is logged (including a reference to the line of code that called AddPairs). Always returns the calling Factory so further method calls can be chained.

AddPairs() silently ignores 'zero' values except "" ('0', 'false', 'nil') rather than either logging an error or adding them only to have the value show up as "undefined".

Does nothing except log a single failure with a stack trace if the Factory is empty or Import()ed.

func (*Span) Finish

func (s *Span) Finish() time.Duration

Finish() notifies the Factory that the contained span is finished. The Factory will be read-only (as if empty) afterward. The Factory will arrange for the span to be registered.

The returned value is the duration of the span's life. If the Factory was already empty or the contained span was Import()ed, then a failure with a stack trace is logged and a 0 duration is returned.

func (Span) GetDuration

func (s Span) GetDuration() time.Duration

GetDuration() returns a negative duration if the Factory is empty or if the span has not been Finish()ed yet. Otherwise, it returns the span's duration.

func (Span) GetStart

func (s Span) GetStart() time.Time

GetStart() returns the time at which the span began. Returns a zero time if the Factory is empty or the contained span was Import()ed.

func (Span) Import

func (s Span) Import(traceID string, spanID uint64) (spans.Factory, error)

Import() returns a new Factory containing a span created somewhere else. If the traceID or spanID is invalid, then a 'nil' Factory and an error are returned. The usual reason to do this is so that you can then call NewSubSpan().

func (Span) ImportFromHeaders

func (s Span) ImportFromHeaders(headers http.Header) spans.Factory

ImportFromHeaders() returns a new Factory containing a span created somewhere else based on the "X-Cloud-Trace-Context:" header. If the header does not contain a valid CloudContext value, then a valid but empty Factory is returned.

func (*Span) NewSpan

func (s *Span) NewSpan() spans.Factory

NewSpan() returns a new Factory holding a new span; either NewTrace() or NewSubSpan(), depending on whether the invoking Factory is empty.

func (*Span) NewSubSpan

func (s *Span) NewSubSpan() spans.Factory

NewSubSpan() returns a new Factory holding a new span that is a sub-span of the span contained in the invoking Factory. If the invoking Factory was empty, then a failure with a stack trace is logged and a new, empty Factory is returned.

NewSubSpan() locks the calling span so that you can safely call NewSubSpan() on the same parent span from multiple go routines.

Only for NewSubSpan(), a Finish()ed span is not considered to be empty. The ability to call NewSubSpan() from a different go routine means that it is not hard to come up with scenarios where a race could lead to NewSubSpan() being called after Finish().

For example, say operation X is accomplished by trying both operations X1 and X2 simultaneously and using the results of the first one to finish. If X2 finishes first, then a request to cancel X1 is initiated and the result from X2 is immediately returned... and the span for X is Finish()ed. It will probably not take long for the cancelation of X1 to happen, but in that short window, it is easily possible for a span for X1 to be created.

func (Span) NewTrace

func (s Span) NewTrace() spans.Factory

NewTrace() returns a new Factory holding a new span, part of a new trace. Any span held in the invoking Factory is ignored.

func (*Span) SetDisplayName

func (s *Span) SetDisplayName(desc string) spans.Factory

SetDisplayName() sets the display name on the contained span. Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetIsClient

func (s *Span) SetIsClient() spans.Factory

Sets the span kind to "CLIENT". Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetIsPublisher

func (s *Span) SetIsPublisher() spans.Factory

Sets the span kind to "PRODUCER". Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetIsServer

func (s *Span) SetIsServer() spans.Factory

Sets the span kind to "SERVER". Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetIsSubscriber

func (s *Span) SetIsSubscriber() spans.Factory

Sets the span kind to "CONSUMER". Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetStatusCode

func (s *Span) SetStatusCode(code int64) spans.Factory

SetStatusCode() sets the status code on the contained span. 'code' is expected to be a value from google.golang.org/genproto/googleapis/rpc/code but this is not verified. HTTP status codes are also understood by the library. Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

func (*Span) SetStatusMessage

func (s *Span) SetStatusMessage(msg string) spans.Factory

SetStatusMessage() sets the status message string on the contained span. By convention, only a failure should set a status message. Does nothing except log a failure with a stack trace if the Factory is empty or Import()ed. Always returns the calling Factory so further method calls can be chained.

type Stringer

type Stringer interface {
	String() string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL