UNPKG

@zendesk/retrace

Version:

define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API

404 lines (358 loc) 11.1 kB
import type { ErrorInfo } from 'react' import type { BeaconConfig } from './hooksTypes' import type { ParentSpanMatcher, SpanAndAnnotationForMatching, } from './matchSpan' import type { TICK_META, TICK_META_END, TickMeta } from './TickParentResolver' import type { TraceRecording } from './traceRecordingTypes' import type { DraftTraceContext, MapSchemaToTypes, RelationSchemasBase, RelationsOnASpan, Timestamp, } from './types' import type { OpenPick, Prettify } from './typeUtils' export type NativePerformanceEntryType = | 'element' | 'event' | 'first-input' | 'largest-contentful-paint' | 'layout-shift' | 'long-animation-frame' | 'longtask' | 'mark' | 'measure' | 'navigation' | 'paint' | 'resource' | 'taskattribution' | 'visibility-state' export type ComponentLifecycleSpanType = | 'component-render-start' | 'component-render' | 'component-unmount' | 'hook-render-start' | 'hook-render' | 'hook-unmount' export interface DraftTraceConfig<RelationSchemaT, VariantsT extends string> { id?: string parentTraceId?: string startTime?: Partial<Timestamp> variant: VariantsT /** * any attributes that are relevant to the entire trace */ attributes?: Attributes relatedTo: MapSchemaToTypes<RelationSchemaT> | undefined /** * Any additional data that can be used by the tooling to identify the trace * It will *not* be a part of the trace recording. */ // TODO: add typing baggage?: unknown } export interface StartTraceConfig<RelationSchemaT, VariantsT extends string> extends DraftTraceConfig<RelationSchemaT, VariantsT> { relatedTo: MapSchemaToTypes<RelationSchemaT> } export interface DraftTraceInput<RelationSchemaT, VariantsT extends string> extends DraftTraceConfig<RelationSchemaT, VariantsT> { id: string startTime: Timestamp } export interface ActiveTraceInput<RelationSchemaT, VariantsT extends string> extends DraftTraceInput<RelationSchemaT, VariantsT> { relatedTo: MapSchemaToTypes<RelationSchemaT> } export interface ActiveTraceConfig< SelectedRelationNameT extends keyof RelationSchemasT, RelationSchemasT, VariantsT extends string, > extends DraftTraceInput<RelationSchemasT[SelectedRelationNameT], VariantsT> { relatedTo: MapSchemaToTypes<RelationSchemasT[SelectedRelationNameT]> } // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style export interface Attributes { [key: string]: unknown } export type SpanStatus = 'ok' | 'error' export type GetParentSpanFn< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > = ( context: GetParentSpanContext<RelationSchemasT>, /** * Whether to attempt to resolve parents recursively. * Enabling this "bakes-in" the PARENT_SPAN reference onto the span */ recursive?: boolean, ) => Span<RelationSchemasT> | undefined export const PARENT_SPAN = Symbol('parentSpan') export interface SpanBase< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > { /** * providing an id is optional, but it will always be present in the recording * if not provided, it will be generated automatically */ id: string // TODO: allow defining custom spans that extend this SpanBase type: SpanType | (string & {}) /** * The common name of the span. * If converted from a PerformanceEntry, this is a sanitized version of the name. */ name: string startTime: Timestamp // for non performance entries, relatedTo is set explicitly in the span, something like ticket id or user id // performance entries can derive relatedTo based using `deriveRelationFromPerformanceEntry` relatedTo?: RelationsOnASpan<RelationSchemasT> attributes: Attributes /** * The duration of this span. * If this span is just an event (a point in time), this will be 0. * On the other hand, spans will have duration > 0. */ duration: number /** * Status of the span ('error' or 'ok'). */ status?: SpanStatus /** * The original PerformanceEntry from which the Span was created */ performanceEntry?: PerformanceEntry /** * if status is error, optionally provide the Error object with additional metadata */ error?: ErrorLike /** * Optional parent span, if known. * Non-enumerable (symbol). */ [PARENT_SPAN]?: Span<RelationSchemasT> /** * Resolve parentSpanId after the Trace is completed, or on demand. * Set internally. */ getParentSpan: GetParentSpanFn<RelationSchemasT> /** * The ID of the tick in which the span was created. * This is used to group spans created in the same event loop tick. */ tickId?: string /** * Metadata about the tick in which the span was created (if tick-tracking functionality enabled). * Not enumerable (symbol). * This is used to resolve parent spans across ticks. */ [TICK_META]?: TickMeta<RelationSchemasT> /** * Metadata about the tick in which the span was ended (if tick-tracking functionality enabled). * Not enumerable (symbol). * This is used to resolve parent spans across ticks. */ [TICK_META_END]?: TickMeta<RelationSchemasT> /** * If true, this span will only be present for matching while the trace is being recorded, * but will not be included in the final trace recording, unless it is a parent of another span. * This is useful for internal spans that are not relevant to the final trace. */ internalUse?: boolean } export interface WithParentSpanMatcher< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > { /** * Optional parent span, if known. Takes precedence over getParentSpan. * Non-enumerable. */ parentSpan?: Span<RelationSchemasT> /** * A matcher that can be used to find the parent span of this span after the trace is completed. */ parentSpanMatcher?: ParentSpanMatcher< keyof RelationSchemasT, RelationSchemasT, // eslint-disable-next-line @typescript-eslint/no-explicit-any any > } export interface ConvenienceSpanProperties< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > extends WithParentSpanMatcher<RelationSchemasT> { startTime?: Partial<Timestamp> } export interface GetParentSpanContext< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > { thisSpanAndAnnotation: SpanAndAnnotationForMatching<RelationSchemasT> // TODO: improve types here by requiring SelectedRelationNameT and VariantsT: // eslint-disable-next-line @typescript-eslint/no-explicit-any traceContext: DraftTraceContext<any, RelationSchemasT, any> | undefined } export interface ComponentRenderSpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > // it would be more correct to use 'relatedTo' from BeaconConfig, // but we'd need to solve some type issues extends Omit<SpanBase<RelationSchemasT>, 'attributes'>, Omit<BeaconConfig<RelationSchemasT>, 'relatedTo'> { type: ComponentLifecycleSpanType isIdle: boolean errorInfo?: ErrorInfo renderCount: number attributes: NonNullable<BeaconConfig<RelationSchemasT>['attributes']> } export type InitiatorType = | 'audio' | 'beacon' | 'body' | 'css' | 'early-hint' | 'embed' | 'fetch' | 'frame' | 'iframe' | 'icon' | 'image' | 'img' | 'input' | 'link' | 'navigation' | 'object' | 'ping' | 'script' | 'track' | 'video' | 'xmlhttprequest' | 'other' export interface ResourceSpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > extends SpanBase<RelationSchemasT> { type: 'resource' resourceDetails: { initiatorType: InitiatorType query: Record<string, string | string[]> hash: string } } export interface PerformanceEntrySpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > extends SpanBase<RelationSchemasT> { type: Exclude<NativePerformanceEntryType, 'resource'> } /** * Represents a child operation span within a trace. * The shape is the same as TraceRecording */ export interface ChildOperationSpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > extends Omit<SpanBase<RelationSchemasT>, 'id'>, Omit< TraceRecording<keyof RelationSchemasT, RelationSchemasT>, // these come from the SpanBase type: 'duration' | 'status' | 'relatedTo' | 'entries' > { type: 'operation' } export interface ErrorLike { message: string name?: string stack?: string cause?: unknown } export interface ErrorSpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > extends SpanBase<RelationSchemasT> { type: 'error' error: ErrorLike status: 'error' } /** * All possible trace entries */ export type Span< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > = | PerformanceEntrySpan<RelationSchemasT> | ComponentRenderSpan<RelationSchemasT> | ResourceSpan<RelationSchemasT> | ChildOperationSpan<RelationSchemasT> | ErrorSpan<RelationSchemasT> export type SpanType = | NativePerformanceEntryType | ComponentLifecycleSpanType | 'operation' | 'error' export type AutoAddedSpanProperties = | 'id' | 'startTime' | 'attributes' | 'duration' | 'getParentSpan' export type ConvenienceSpan< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, SpanT extends Span<RelationSchemasT>, > = Prettify< Omit<SpanT, AutoAddedSpanProperties> & Partial<Pick<SpanT, 'id' | 'attributes' | 'duration'>> & ConvenienceSpanProperties<RelationSchemasT> > export type ErrorSpanInput< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > = Prettify< Omit< ErrorSpan<RelationSchemasT>, 'status' | 'type' | 'name' | AutoAddedSpanProperties > & Partial< Pick< ErrorSpan<RelationSchemasT>, 'type' | 'name' | 'id' | 'attributes' | 'duration' > & { startTime: Partial<Timestamp> } > & ConvenienceSpanProperties<RelationSchemasT> > export type PerformanceEntrySpanInput< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > = Prettify< Omit<PerformanceEntrySpan<RelationSchemasT>, AutoAddedSpanProperties> & Partial< Pick< PerformanceEntrySpan<RelationSchemasT>, 'id' | 'attributes' | 'duration' > & { startTime?: Partial<Timestamp> } > & ConvenienceSpanProperties<RelationSchemasT> > export type RenderSpanInput< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, > = Prettify< Omit<ComponentRenderSpan<RelationSchemasT>, AutoAddedSpanProperties> & Partial< Pick< ComponentRenderSpan<RelationSchemasT>, 'id' | 'attributes' | 'duration' > > & ConvenienceSpanProperties<RelationSchemasT> > export type UpdatableSpanProperties = | 'attributes' | 'relatedTo' | 'renderedOutput' | 'isIdle' export type SpanUpdateFunction< RelationSchemasT extends RelationSchemasBase<RelationSchemasT>, SpanT extends Span<RelationSchemasT>, > = ( spanUpdates: Partial<OpenPick<SpanT, UpdatableSpanProperties>> & { reprocess?: boolean }, ) => void