UNPKG

autotel

Version:
87 lines 3.75 kB
//#region src/analysis.d.ts /** * Core Analysis Loop * * Debugging from first principles: take the events you care about, take a * comparable normal population, and ask which recorded field separates them. * The loop is mechanical, so run it in code instead of clicking through a * dashboard. * * `compareCohorts()` scores every field/value pair by how much more often it * appears in the outlier group than in the baseline. The top result names the * cohort behind a regression, which is the hypothesis you then verify against * individual traces. * * The input is any array of flat records: wide events from * `getRequestLogger()`, `TestSpan.attributes` from `autotel/testing`, or rows * returned by a backend query. * * @example Find the cohort behind a latency regression * ```typescript * import { compareCohorts } from 'autotel/analysis' * * const slow = events.filter((event) => event['duration_ms'] >= 800) * const normal = events.filter((event) => event['duration_ms'] < 800) * * const [top] = compareCohorts({ outlier: slow, baseline: normal }) * // { field: 'payment.provider', value: 'bank-beta', difference: 0.94, ... } * ``` */ /** A flat telemetry record. Span attributes and wide events both satisfy it. */ type AnalysisEvent = Record<string, unknown>; /** How strongly one field/value pair separates the outlier group from the baseline. */ interface CohortDifference { /** Attribute name, such as `payment.provider`. */ field: string; /** The value, rendered as a string for display and grouping. */ value: string; /** Share of outlier events carrying this value, 0-1. */ outlierFraction: number; /** Share of baseline events carrying this value, 0-1. */ baselineFraction: number; /** `outlierFraction - baselineFraction`. Positive means over-represented in the outliers. */ difference: number; outlierCount: number; baselineCount: number; } interface CompareCohortsOptions { /** The events you are investigating: the errors, the slow requests, the failed checkouts. */ outlier: readonly AnalysisEvent[]; /** A comparable normal population from the same time range. */ baseline: readonly AnalysisEvent[]; /** Restrict the scan to these fields. Defaults to every field seen in either group. */ fields?: readonly string[]; /** Fields to skip, such as identifiers you already know are unique. */ ignoreFields?: readonly string[]; /** * Skip a field once it holds more distinct values than this across both * groups. Default 50. */ maxValuesPerField?: number; /** * Skip a field whose distinct values outnumber this share of the combined * events. A request id or a raw duration takes a near-unique value per * event, so its values never repeat and it cannot describe a cohort. The * ratio catches those fields in small populations, where an absolute cap * does not. Bucket numeric fields at instrumentation time if you want them * ranked. Default 0.5. */ maxUniqueRatio?: number; /** Drop pairs whose absolute difference falls below this. Default 0.1. */ minDifference?: number; /** Maximum results to return, strongest first. Default 20. */ limit?: number; } /** * Rank the field/value pairs that separate an outlier group from a baseline. * * Returns an empty array when either group is empty, because a fraction over * zero events carries no information. * * @param options - The two populations and the scan limits * @returns Differences sorted by absolute strength, strongest first */ declare function compareCohorts(options: CompareCohortsOptions): CohortDifference[]; //#endregion export { AnalysisEvent, CohortDifference, CompareCohortsOptions, compareCohorts }; //# sourceMappingURL=analysis.d.cts.map