Documentation
¶
Overview ¶
Package telemetry gives the gateway OpenTelemetry tracing.
Why the exporter is hand-rolled ¶
The spans, the context and the propagation are real OpenTelemetry: the API and SDK are imported and used as intended, so anything that understands OTel understands this. What is written here is only the exporter, because OTLP permits a JSON body over HTTP and every collector accepts it. Taking that option keeps the module tree at twenty-two rather than ninety-six; the protobuf exporter brings grpc, protobuf and grpc-gateway, which is the same stack internal/metrics declined for the same reason. Carriers audit this tree.
What goes in a span, and what must not ¶
Spans leave the building. They go to a collector that is very often operated by somebody else, and they are kept for longer than anybody intends. So no passenger data goes in one: no names, no contacts, no documents, no frequent flyer numbers. Locators and carrier codes do, because they are the identifiers an operator needs to follow a booking through the system and are meaningless without the record they point at.
The attributes here are deliberately useful to two audiences at once. An operator wants to know which link is slow and what is failing; the commercial side wants to know how often a carrier refuses, how much of a booking sells without asking, and what ancillary revenue was issued against what. Both questions are answered by the same spans.
Index ¶
- Constants
- func Fail(span trace.Span, err error)
- func FromHTTP(ctx context.Context, h http.Header) context.Context
- func IDs(ctx context.Context) (traceID, spanID string)
- func Setup(ctx context.Context, cfg Config) (shutdown func(context.Context) error, err error)
- func Start(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, trace.Span)
- func ToHTTP(ctx context.Context, h http.Header)
- func Tracer() trace.Tracer
- type Config
Constants ¶
const ( // Link and message shape: what arrived, from whom, on what. AttrPeer = attribute.Key("jetway.peer") AttrCarrier = attribute.Key("jetway.carrier") AttrFormat = attribute.Key("jetway.format") AttrTransport = attribute.Key("jetway.transport") AttrMessageID = attribute.Key("jetway.message.id") AttrMessageKind = attribute.Key("jetway.message.kind") AttrMessageSize = attribute.Key("jetway.message.bytes") AttrDirection = attribute.Key("jetway.direction") AttrStatus = attribute.Key("jetway.status") AttrDuplicate = attribute.Key("jetway.duplicate") // The record a message touched. AttrLocator = attribute.Key("jetway.record.locator") AttrRecordID = attribute.Key("jetway.record.id") AttrSegmentCount = attribute.Key("jetway.record.segments") AttrPaxCount = attribute.Key("jetway.record.passengers") AttrInterline = attribute.Key("jetway.record.interline") // Selling. The commercial questions live here: how much goes out under // free sale rather than as a request, and what the carrier said back. AttrSeats = attribute.Key("jetway.seats") AttrFreeSale = attribute.Key("jetway.free_sale") AttrOutcome = attribute.Key("jetway.outcome") AttrActionCode = attribute.Key("jetway.action_code") AttrChannel = attribute.Key("jetway.channel") // Documents and ancillary revenue. RFIC is the revenue category, which is // the point of carrying it. AttrDocumentType = attribute.Key("jetway.document.type") AttrDocumentNumber = attribute.Key("jetway.document.number") AttrCouponCount = attribute.Key("jetway.document.coupons") AttrRFIC = attribute.Key("jetway.document.rfic") AttrRFISC = attribute.Key("jetway.document.rfisc") AttrAmount = attribute.Key("jetway.document.amount") AttrCurrency = attribute.Key("jetway.document.currency") // Work and disagreement. AttrQueue = attribute.Key("jetway.queue") AttrQueueCode = attribute.Key("jetway.queue.code") AttrQueuePlaced = attribute.Key("jetway.queue.placed") AttrDivergence = attribute.Key("jetway.divergence") AttrUnreachable = attribute.Key("jetway.carriers.unreachable") AttrNotified = attribute.Key("jetway.carriers.notified") // Diagnostics the decoder raised, so a dialect problem is visible as a // count before it is visible as a support ticket. AttrDiagnostics = attribute.Key("jetway.diagnostics") AttrDecodeError = attribute.Key("jetway.decode_error") )
The jetway attribute vocabulary.
Two audiences read these. An operator asks which link is slow, what is failing, and how deep the backlog is. The commercial side asks how often a carrier refuses, how much of a booking sells without having to ask, how long a carrier takes to answer, and what ancillary revenue was issued against what. Both are answerable from the same spans, which is why there is one vocabulary rather than an operational one and a business one that drift.
Nothing here carries passenger data. Spans leave the building for a collector somebody else very likely operates, and they outlive the intention to keep them. Locators and carrier codes are fine: they are the identifiers needed to follow a booking, and they mean nothing without the record they point at. Names, contacts, documents and frequent flyer numbers are not, and there is a test that the booking span carries no passenger name.
const ( OutcomeConfirmed = "confirmed" OutcomeWaitlisted = "waitlisted" OutcomeRefused = "refused" OutcomePending = "pending" )
Outcome values for AttrOutcome. They are the commercial answer to a sell, collapsed to the four cases anybody reports on.
const ( ChannelAPI = "api" ChannelNDC = "ndc" ChannelTypeB = "typeb" ChannelEDIFACT = "edifact" )
Channel values for AttrChannel: how a booking reached this node.
const ScopeName = "github.com/adamf/jetway"
ScopeName names the instrumentation scope in exported spans.
Variables ¶
This section is empty.
Functions ¶
func FromHTTP ¶
FromHTTP returns a context carrying any trace the caller propagated.
Teletype and EDIFACT links carry no place to put a traceparent, so a message arriving on one starts a new trace. HTTP does carry one, so an agent or an NDC client that is already tracing keeps a single trace across the boundary.
func IDs ¶
IDs returns the trace and span identifiers on a context, or empty strings.
They are recorded against the message in the log, which is what turns "what happened to this message" into a link rather than a search.
func Setup ¶
Setup installs a tracer provider and returns a shutdown function.
With no endpoint it installs a no-op provider and returns immediately, so every call site can create spans unconditionally and pay nothing.
func Start ¶
func Start(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, trace.Span)
Start begins a span. It is a thin wrapper so call sites need not import the OTel packages directly.
Types ¶
type Config ¶
type Config struct {
// Endpoint is an OTLP HTTP traces endpoint, e.g.
// http://collector:4318/v1/traces. Empty disables tracing entirely, which
// is the default: a gateway with nowhere to send spans should not pay to
// make them.
Endpoint string
// Headers are sent with every export, for collectors behind an API key.
Headers map[string]string
// ServiceName identifies this node. Defaults to "jetway".
ServiceName string
// Environment is an optional deployment label.
Environment string
// SampleRatio is the head sampling ratio, 0 to 1. Zero means one, because
// a gateway that has been told where to send spans wants them.
SampleRatio float64
// Timeout bounds one export. Zero uses ten seconds.
Timeout time.Duration
}
Config configures tracing.