|
| 1 | +--- |
| 2 | +applyTo: "**/*MetricsParser.cs" |
| 3 | +description: "Pattern for developing metric parsers with proper units and consistency" |
| 4 | +--- |
| 5 | + |
| 6 | +# MetricsParser Development Guide |
| 7 | + |
| 8 | +## Class Structure |
| 9 | + |
| 10 | +Parsers inherit from `MetricsParser` → `TextParser<IList<Metric>>`: |
| 11 | + |
| 12 | +```csharp |
| 13 | +public class MyWorkloadMetricsParser : MetricsParser |
| 14 | +{ |
| 15 | + private static readonly Regex ValuePattern = new Regex( |
| 16 | + @"(\d+\.?\d*)\s+(ops/sec)", RegexOptions.Compiled); |
| 17 | + |
| 18 | + public MyWorkloadMetricsParser(string rawText) |
| 19 | + : base(rawText) |
| 20 | + { |
| 21 | + } |
| 22 | + |
| 23 | + public override IList<Metric> Parse() |
| 24 | + { |
| 25 | + try |
| 26 | + { |
| 27 | + this.Preprocess(); |
| 28 | + this.Sections = TextParsingExtensions.Sectionize(this.PreprocessedText, "SectionHeader"); |
| 29 | + List<Metric> metrics = new List<Metric>(); |
| 30 | + // Parse sections and extract metrics |
| 31 | + return metrics; |
| 32 | + } |
| 33 | + catch (Exception exc) |
| 34 | + { |
| 35 | + throw new WorkloadResultsException( |
| 36 | + "Failed to parse results.", exc, ErrorReason.WorkloadResultsParsingFailed); |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + protected override void Preprocess() |
| 41 | + { |
| 42 | + // Remove unwanted lines before parsing |
| 43 | + this.PreprocessedText = TextParsingExtensions.RemoveRows(this.RawText, @"^\s*$"); |
| 44 | + } |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +## Standard Units (from `MetricUnit` constants) |
| 49 | + |
| 50 | +Always use `MetricUnit.*` constants from `VirtualClient.Contracts/MetricUnit.cs` — never raw strings. |
| 51 | +Full list includes: `Bytes`, `Kilobytes`, `Megabytes`, `Gigabytes`, `Terabytes`, `Petabytes`, |
| 52 | +`BytesPerSecond`, `KilobytesPerSecond`, `MegabytesPerSecond`, `GigabytesPerSecond`, |
| 53 | +`KibibytesPerSecond`, `MebibytesPerSecond`, `GibibytesPerSecond`, |
| 54 | +`OperationsPerSec`, `RequestsPerSec`, `TransactionsPerSec`, |
| 55 | +`Nanoseconds`, `Microseconds`, `Milliseconds`, `Seconds`, `Minutes`, |
| 56 | +`Count`, `Operations`, `Watts`, `Amps`, `Celcius`, `Megahertz`, `BytesPerConnection`. |
| 57 | + |
| 58 | +## MetricRelativity |
| 59 | + |
| 60 | +Set relativity correctly on every metric: |
| 61 | + |
| 62 | +- `MetricRelativity.HigherIsBetter` — throughput, bandwidth, operations/sec |
| 63 | +- `MetricRelativity.LowerIsBetter` — latency, time, error counts |
| 64 | +- `MetricRelativity.Undefined` — informational metrics (default) |
| 65 | + |
| 66 | +```csharp |
| 67 | +new Metric("throughput", value, MetricUnit.OperationsPerSec, MetricRelativity.HigherIsBetter) |
| 68 | +new Metric("latency_p99", value, MetricUnit.Milliseconds, MetricRelativity.LowerIsBetter) |
| 69 | +``` |
| 70 | + |
| 71 | +## Regex Patterns |
| 72 | + |
| 73 | +Define as `private static readonly Regex` with `RegexOptions.Compiled`: |
| 74 | + |
| 75 | +```csharp |
| 76 | +private static readonly Regex ThroughputRegex = new Regex( |
| 77 | + @"Throughput:\s+(\d+\.?\d*)\s+ops/sec", RegexOptions.Compiled); |
| 78 | +``` |
| 79 | + |
| 80 | +## Error Handling |
| 81 | + |
| 82 | +Wrap `Parse()` body in try-catch, throwing `WorkloadResultsException`: |
| 83 | + |
| 84 | +```csharp |
| 85 | +catch (Exception exc) |
| 86 | +{ |
| 87 | + throw new WorkloadResultsException( |
| 88 | + "Failed to parse MyWorkload results.", exc, ErrorReason.WorkloadResultsParsingFailed); |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +## Text Parsing Utilities |
| 93 | + |
| 94 | +- `TextParsingExtensions.Sectionize(text, pattern)` — split text into named sections |
| 95 | +- `TextParsingExtensions.RemoveRows(text, pattern)` — strip unwanted lines in `Preprocess()` |
| 96 | +- `this.PreprocessedText` — normalized text for parsing (set in `Preprocess()`) |
| 97 | +- `this.Sections` — dictionary of parsed sections (set in `Parse()`) |
0 commit comments