Table of Contents

Class ValidationMetrics

Namespace
Trellis
Assembly
Trellis.Core.dll

The Trellis.ValidationMetrics.Meter and instruments Trellis publishes for validation failures.

public static class ValidationMetrics
Inheritance
ValidationMetrics
Inherited Members

Remarks

Validation failures look like user error, and that label is why nobody looks at them. The reason to count them is that server-side validation is a backstop: when a client enforces the same rules before sending, this counter should sit near zero. That expected value is what makes it worth alerting on — a rising count does not mean users got worse at typing, it means client-side validation has drifted from the server's, or a client broke against you, or you tightened a rule and did not notice. All three are your defect, arriving disguised as theirs.

The validation.code tag is what makes that actionable, and it is the reason an HTTP-status metric is not a substitute: a 4xx rate tells you something drifted, the reason code tells you which rule did. It deliberately stops there. There is no field or route tag, because those are unbounded (see OtherCode for the same reasoning applied to codes), so the division of labour is detection here and diagnosis in the trace, whose JSON pointer names the offending field.

A second, narrower use is the framework's own: "is this rule dead?" — whether a reason code the framework can emit is ever actually emitted. Nothing else answers it. A trace answers it only for requests that were sampled, and a zero-volume code is indistinguishable from a code whose traces were all sampled away; a log answers it only if someone is aggregating the logs, which is the thing a counter is for. A code that never fires may also be shadowed by an earlier check rather than genuinely unused, which reads identically until you look.

A violation is counted where it is created. The counting site is the ReasonCode initializer on FieldViolation and RuleViolation — the violation itself, not the Error.InvalidInput that carries it. A failure can surface at the HTTP boundary, at the mediator pipeline, at both, or — in a worker — at neither, so no reporting site sees each failure exactly once. Coordinating the reporting sites does not work either: an AsyncLocal set inside an awaited Send is not visible to the caller afterwards, because a callee's assignment does not flow back up.

The carrying Error.InvalidInput is the wrong site for a subtler reason: it is rebuilt during re-projection. JsonValidationPathRebase re-roots pointers by constructing a fresh InvalidInput from an existing one's violations, and the ASP validation context aggregates collected violations into a final one — so counting there would count the same rule firing two or three times. The violation is the atom that is created once when a rule fires and only ever copied afterwards.

A with-expression does not recount. The synthesized copy constructor copies backing fields rather than re-running the initializers, which is what makes re-projection free: the rebase path rewrites a violation's pointer with violation with { Field = ... } and the count is unaffected.

Fields

FailuresInstrumentName

The name of the counter incremented once per violation carried by a validation failure.

public const string FailuresInstrumentName = "trellis.validation.failures"

Field Value

string

MeterName

The name of the Trellis.ValidationMetrics.Meter carrying Trellis validation instruments. Subscribe with AddTrellisValidationInstrumentation(MeterProviderBuilder).

public const string MeterName = "Trellis.Validation"

Field Value

string

OtherCode

The validation.code tag value substituted for any code outside the framework vocabulary.

public const string OtherCode = "other"

Field Value

string

Remarks

An application code reaches the wire verbatim — ValidationCodeProjection passes through anything it does not reserve — so an application is free to mint a code per entity, per tenant, or per request. Tagging those verbatim would let a caller create an unbounded number of time series, which is the expensive failure mode in a hosted metrics backend. Framework codes are a closed set, and they are the only ones the dead-rule question is about, so everything else is folded here. The total stays exact; only the breakdown is bucketed.