-
Notifications
You must be signed in to change notification settings - Fork 278
Add .NET metrics #283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add .NET metrics #283
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
92d66de
dotnet http metrics
fc9d3c2
asp.net core switch to error.type and rebase
48d2f8a
update according to .net changes
64fa683
nits
e704a37
rebase and regen md
1981c8a
review: part1
b6b0dd3
review: part2 and unify client attrs
ebb2b48
review: part3
29754ab
lint
30ae552
Add missing hugo titles and minor updates
8a47d03
regenerate md
662efb8
feedback
465bf37
clean up attributes
dd37d3b
more feedback
bc3ce34
more feedback and mark as stable
9b0cf6f
up
704113a
is_fallback_route -> is_fallback
46d72fe
time_in_queue excten connection, not request
e95fde0
Merge branch 'main' into dotnet8-metrics
2e3d5ee
Merge branch 'main' into dotnet8-metrics
8a354eb
Merge branch 'main' into dotnet8-metrics
d87e78a
Merge branch 'main' into dotnet8-metrics
5f02517
Merge branch 'main' into dotnet8-metrics
d9ff990
Update CODEOWNERS
b79feff
Merge branch 'main' into dotnet8-metrics
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <!--- Hugo front matter used to generate the website version of this page: | ||
| linkTitle: .NET | ||
| path_base_for_github_subdir: | ||
| from: content/en/docs/specs/semconv/dotnet/_index.md | ||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| to: dotnet/README.md | ||
| ---> | ||
|
|
||
| # Semantic Conventions for .NET metrics | ||
|
|
||
| **Status**: [Stable][DocumentStatus] | ||
|
|
||
| This article documents semantic conventions for metrics emitted by the .NET runtime and individual components in the .NET ecosystem. | ||
|
|
||
| The following metrics are currently supported: | ||
|
|
||
| * [ASP.NET Core](dotnet-aspnetcore-metrics.md): Semantic Conventions for ASP.NET Core routing, exceptions, and rate-limiting *metrics*. | ||
| * [DNS](dotnet-dns-metrics.md): Semantic Conventions for client-side DNS lookups associated with *metrics*. | ||
| * [HTTP](dotnet-http-metrics.md): Semantic Conventions for HTTP client and server *metrics*. | ||
| * [Kestrel](dotnet-kestrel-metrics.md): Semantic Conventions for Kestrel web server *metrics*. | ||
| * [SignalR](dotnet-signalr-metrics.md): Semantic Conventions for SignalR server *metrics*. | ||
|
|
||
| [DocumentStatus]: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/document-status.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| <!--- Hugo front matter used to generate the website version of this page: | ||
| linkTitle: ASP.NET Core | ||
| ---> | ||
|
|
||
| # Semantic Conventions for ASP.NET Core metrics | ||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| **Status**: [Stable][DocumentStatus] | ||
|
|
||
| This article defines semantic conventions for ASP.NET Core metrics. | ||
|
|
||
| <!-- toc --> | ||
|
|
||
| - [Server](#server) | ||
| - [Routing](#routing) | ||
| * [Metric: `aspnetcore.routing.match_attempts`](#metric-aspnetcoreroutingmatch_attempts) | ||
| - [Exceptions](#exceptions) | ||
| * [Metric: `aspnetcore.diagnostics.exceptions`](#metric-aspnetcorediagnosticsexceptions) | ||
| - [Rate-limiting](#rate-limiting) | ||
| * [Metric: `aspnetcore.rate_limiting.active_request_leases`](#metric-aspnetcorerate_limitingactive_request_leases) | ||
| * [Metric: `aspnetcore.rate_limiting.request_lease.duration`](#metric-aspnetcorerate_limitingrequest_leaseduration) | ||
| * [Metric: `aspnetcore.rate_limiting.queued_requests`](#metric-aspnetcorerate_limitingqueued_requests) | ||
| * [Metric: `aspnetcore.rate_limiting.request.time_in_queue`](#metric-aspnetcorerate_limitingrequesttime_in_queue) | ||
| * [Metric: `aspnetcore.rate_limiting.requests`](#metric-aspnetcorerate_limitingrequests) | ||
|
|
||
| <!-- tocstop --> | ||
|
|
||
| ## Server | ||
|
|
||
| ## Routing | ||
|
|
||
| All routing metrics are reported by the `Microsoft.AspNetCore.Routing` meter. | ||
|
|
||
| ### Metric: `aspnetcore.routing.match_attempts` | ||
|
|
||
| <!-- semconv metric.aspnetcore.routing.match_attempts(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.routing.match_attempts` | Counter | `{match_attempt}` | Number of requests that were attempted to be matched to an endpoint. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.Routing`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.routing.match_attempts(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.routing.is_fallback` | boolean | A value that indicates whether the matched route is a fallback route. | `True` | Conditionally Required: if and only if a route was successfully matched. | | ||
| | `aspnetcore.routing.match_status` | string | Match result - success or failure | `success`; `failure` | Required | | ||
| | [`http.route`](../attributes-registry/http.md) | string | The matched route, that is, the path template in the format used by the respective server framework. [1] | `/users/:userID?`; `{controller}/{action}/{id?}` | Conditionally Required: if and only if a route was successfully matched. | | ||
|
|
||
| **[1]:** MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. | ||
| SHOULD include the [application root](/docs/http/http-spans.md#http-server-definitions) if there is one. | ||
|
|
||
| `aspnetcore.routing.match_status` has the following list of well-known values. If one of them applies, then the respective value MUST be used, otherwise a custom value MAY be used. | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `success` | Match succeeded | | ||
| | `failure` | Match failed | | ||
| <!-- endsemconv --> | ||
|
|
||
| ## Exceptions | ||
|
|
||
| Exceptions Metric is reported by the `Microsoft.AspNetCore.Diagnostics` meter. | ||
|
|
||
| ### Metric: `aspnetcore.diagnostics.exceptions` | ||
|
|
||
| <!-- semconv metric.aspnetcore.diagnostics.exceptions(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.diagnostics.exceptions` | Counter | `{exception}` | Number of exceptions caught by exception handling middleware. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.Diagnostics`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.diagnostics.exceptions(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.diagnostics.exception.result` | string | ASP.NET Core exception middleware handling result | `handled`; `unhandled` | Required | | ||
| | `aspnetcore.diagnostics.handler.type` | string | Full type name of the [`IExceptionHandler`](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.diagnostics.iexceptionhandler) implementation that handled the exception. | `Contoso.MyHandler` | Conditionally Required: [1] | | ||
| | [`error.type`](../attributes-registry/error.md) | string | The full name of exception type. [2] | `System.OperationCanceledException`; `Contoso.MyException` | Required | | ||
|
|
||
| **[1]:** if and only if the exception was handled by this handler. | ||
|
|
||
| **[2]:** The `error.type` SHOULD be predictable and SHOULD have low cardinality. | ||
| Instrumentations SHOULD document the list of errors they report. | ||
|
|
||
| The cardinality of `error.type` within one instrumentation library SHOULD be low. | ||
| Telemetry consumers that aggregate data from multiple instrumentation libraries and applications | ||
| should be prepared for `error.type` to have high cardinality at query time when no | ||
| additional filters are applied. | ||
|
|
||
| If the operation has completed successfully, instrumentations SHOULD NOT set `error.type`. | ||
|
|
||
| If a specific domain defines its own set of error identifiers (such as HTTP or gRPC status codes), | ||
| it's RECOMMENDED to: | ||
|
|
||
| * Use a domain-specific attribute | ||
| * Set `error.type` to capture all errors, regardless of whether they are defined within the domain-specific set or not. | ||
|
|
||
| `aspnetcore.diagnostics.exception.result` MUST be one of the following: | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `handled` | Exception was handled by the exception handling middleware. | | ||
| | `unhandled` | Exception was not handled by the exception handling middleware. | | ||
| | `skipped` | Exception handling was skipped because the response had started. | | ||
| | `aborted` | Exception handling didn't run because the request was aborted. | | ||
|
|
||
| `error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used, otherwise a custom value MAY be used. | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | | ||
| <!-- endsemconv --> | ||
|
|
||
| ## Rate-limiting | ||
|
|
||
| All rate-limiting metrics are reported by the `Microsoft.AspNetCore.RateLimiting` meter. | ||
|
|
||
| ### Metric: `aspnetcore.rate_limiting.active_request_leases` | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.active_request_leases(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.rate_limiting.active_request_leases` | UpDownCounter | `{request}` | Number of requests that are currently active on the server that hold a rate limiting lease. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.RateLimiting`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.active_request_leases(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.rate_limiting.policy` | string | Rate limiting policy name. | `fixed`; `sliding`; `token` | Conditionally Required: [1] | | ||
|
|
||
| **[1]:** if the matched endpoint for the request had a rate-limiting policy. | ||
| <!-- endsemconv --> | ||
|
|
||
| ### Metric: `aspnetcore.rate_limiting.request_lease.duration` | ||
|
|
||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this metric SHOULD be specified with | ||
| [`ExplicitBucketBoundaries`](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/metrics/api.md#instrument-advisory-parameters) | ||
| of `[ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 ]`. | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.request_lease.duration(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.rate_limiting.request_lease.duration` | Histogram | `s` | The duration of rate limiting lease held by requests on the server. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.RateLimiting`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.request_lease.duration(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.rate_limiting.policy` | string | Rate limiting policy name. | `fixed`; `sliding`; `token` | Conditionally Required: [1] | | ||
|
|
||
| **[1]:** if the matched endpoint for the request had a rate-limiting policy. | ||
| <!-- endsemconv --> | ||
|
|
||
| ### Metric: `aspnetcore.rate_limiting.queued_requests` | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.queued_requests(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.rate_limiting.queued_requests` | UpDownCounter | `{request}` | Number of requests that are currently queued, waiting to acquire a rate limiting lease. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.RateLimiting`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.queued_requests(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.rate_limiting.policy` | string | Rate limiting policy name. | `fixed`; `sliding`; `token` | Conditionally Required: [1] | | ||
|
|
||
| **[1]:** if the matched endpoint for the request had a rate-limiting policy. | ||
| <!-- endsemconv --> | ||
|
|
||
| ### Metric: `aspnetcore.rate_limiting.request.time_in_queue` | ||
|
|
||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this metric SHOULD be specified with | ||
| [`ExplicitBucketBoundaries`](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/metrics/api.md#instrument-advisory-parameters) | ||
| of `[ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 ]`. | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.request.time_in_queue(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.rate_limiting.request.time_in_queue` | Histogram | `s` | The time the request spent in a queue waiting to acquire a rate limiting lease. [1] | | ||
|
|
||
| **[1]:** Meter name: `Microsoft.AspNetCore.RateLimiting`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.request.time_in_queue(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.rate_limiting.policy` | string | Rate limiting policy name. | `fixed`; `sliding`; `token` | Conditionally Required: [1] | | ||
| | `aspnetcore.rate_limiting.result` | string | Rate-limiting result, shows whether the lease was acquired or contains a rejection reason | `acquired`; `request_canceled` | Required | | ||
|
|
||
| **[1]:** if the matched endpoint for the request had a rate-limiting policy. | ||
|
|
||
| `aspnetcore.rate_limiting.result` has the following list of well-known values. If one of them applies, then the respective value MUST be used, otherwise a custom value MAY be used. | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `acquired` | Lease was acquired | | ||
| | `endpoint_limiter` | Lease request was rejected by the endpoint limiter | | ||
| | `global_limiter` | Lease request was rejected by the global limiter | | ||
| | `request_canceled` | Lease request was canceled | | ||
| <!-- endsemconv --> | ||
|
|
||
| ### Metric: `aspnetcore.rate_limiting.requests` | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.requests(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `aspnetcore.rate_limiting.requests` | Counter | `{request}` | Number of requests that tried to acquire a rate limiting lease. [1] | | ||
|
|
||
| **[1]:** Requests could be: | ||
|
|
||
| * Rejected by global or endpoint rate limiting policies | ||
| * Canceled while waiting for the lease. | ||
|
|
||
| Meter name: `Microsoft.AspNetCore.RateLimiting`; Added in: ASP.NET Core 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.aspnetcore.rate_limiting.requests(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `aspnetcore.rate_limiting.policy` | string | Rate limiting policy name. | `fixed`; `sliding`; `token` | Conditionally Required: [1] | | ||
| | `aspnetcore.rate_limiting.result` | string | Rate-limiting result, shows whether the lease was acquired or contains a rejection reason | `acquired`; `request_canceled` | Required | | ||
|
|
||
| **[1]:** if the matched endpoint for the request had a rate-limiting policy. | ||
|
|
||
| `aspnetcore.rate_limiting.result` has the following list of well-known values. If one of them applies, then the respective value MUST be used, otherwise a custom value MAY be used. | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `acquired` | Lease was acquired | | ||
| | `endpoint_limiter` | Lease request was rejected by the endpoint limiter | | ||
| | `global_limiter` | Lease request was rejected by the global limiter | | ||
| | `request_canceled` | Lease request was canceled | | ||
| <!-- endsemconv --> | ||
|
|
||
| [DocumentStatus]: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/document-status.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| <!--- Hugo front matter used to generate the website version of this page: | ||
| linkTitle: DNS | ||
| ---> | ||
|
|
||
| # Semantic Conventions for DNS metrics emitted by .NET | ||
|
|
||
| **Status**: [Stable][DocumentStatus] | ||
|
|
||
| This article defines semantic conventions for DNS metrics emitted by .NET. | ||
|
|
||
| <!-- toc --> | ||
|
|
||
| - [DNS metrics](#dns-metrics) | ||
| * [Metric: `dns.lookup.duration`](#metric-dnslookupduration) | ||
|
|
||
| <!-- tocstop --> | ||
|
|
||
| ## DNS metrics | ||
|
|
||
| ### Metric: `dns.lookup.duration` | ||
|
|
||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this metric SHOULD be specified with | ||
| [`ExplicitBucketBoundaries`](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/metrics/api.md#instrument-advisory-parameters) | ||
| of `[ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 ]`. | ||
|
|
||
| <!-- semconv metric.dotnet.dns.lookup.duration(metric_table) --> | ||
| | Name | Instrument Type | Unit (UCUM) | Description | | ||
| | -------- | --------------- | ----------- | -------------- | | ||
| | `dns.lookup.duration` | Histogram | `s` | Measures the time taken to perform a DNS lookup. [1] | | ||
|
|
||
| **[1]:** Meter name: `System.Net.NameResolution`; Added in: .NET 8.0 | ||
| <!-- endsemconv --> | ||
|
|
||
| <!-- semconv metric.dotnet.dns.lookup.duration(full) --> | ||
| | Attribute | Type | Description | Examples | Requirement Level | | ||
| |---|---|---|---|---| | ||
| | `dns.question.name` | string | The name being queried. [1] | `www.example.com`; `dot.net` | Required | | ||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | [`error.type`](../attributes-registry/error.md) | string | One of the resolution errors or the full name of exception type. [2] | `host_not_found`; `no_recovery`; `System.Net.Sockets.SocketException` | Conditionally Required: if and only if an error has occurred. | | ||
|
|
||
| **[1]:** The name being queried. | ||
| If the name field contains non-printable characters (below 32 or above 126), those characters should be represented as escaped base 10 integers (\DDD). Back slashes and quotes should be escaped. Tabs, carriage returns, and line feeds should be converted to \t, \r, and \n respectively. | ||
|
|
||
| **[2]:** The following errors codes are reported: | ||
|
|
||
| - "host_not_found" | ||
| - "try_again" | ||
| - "address_family_not_supported" | ||
| - "no_recovery" | ||
|
|
||
| See [SocketError](https://learn.microsoft.com/dotnet/api/system.net.sockets.socketerror) | ||
| for more details. | ||
|
|
||
| `error.type` has the following list of well-known values. If one of them applies, then the respective value MUST be used, otherwise a custom value MAY be used. | ||
|
|
||
| | Value | Description | | ||
| |---|---| | ||
| | `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | | ||
lmolkova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <!-- endsemconv --> | ||
|
|
||
| [DocumentStatus]: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.26.0/specification/document-status.md | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.