question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

500 Errors showing as 200 in /metrics on ASP.NET Core 2.2

See original GitHub issue

I have an ASP.NET Core 2.2 project with one controller that works like this:

  • Get() method = 200
  • Post() method = 500 (throws exception)
  • Put() method = 400 (returns bad request)

When testing I am confirming the Post method returns a 500 via CURL, but in /metrics it shows up as a 200 for the ValuesController Post method.

Output of /metrics:

http_request_duration_seconds_sum{code="200",method="POST",controller="Values",action="Post"} 0.1010758
http_request_duration_seconds_count{code="200",method="POST",controller="Values",action="Post"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.001"} 0
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.002"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.004"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.008"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.016"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.032"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.064"} 2
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.128"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.256"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="0.512"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="1.024"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="2.048"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="4.096"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="8.192"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="16.384"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="32.768"} 3
http_request_duration_seconds_bucket{code="200",method="POST",controller="Values",action="Post",le="+Inf"} 3

Controller:

// GET api/values
        [HttpGet()]
        public ActionResult Get()
        {
            Ok();
        }

        [HttpPost()]
        public ActionResult<IEnumerable<string>> Post(string text)
        {
            _exceptionCount++;
            throw new Exception($"I am exception number {_exceptionCount}");
        }

        [HttpPut()]
        public ActionResult Put()
        {
            return BadRequest();
        }

Curl example shows 500 error:

C02XX041JGH7:~ lin.meyer$ echo '{"text": "Hello **world**!"}' | curl -X POST -d @- --verbose http://localhost:5000/api/values
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> POST /api/values HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 28
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 28 out of 28 bytes
< HTTP/1.1 500 Internal Server Error
< Date: Mon, 15 Jul 2019 17:47:06 GMT
< Server: Kestrel
< Content-Length: 0
<
* Connection #0 to host localhost left intact

Startup:

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseHttpMetrics(); // Adds basic HTTP metrics to Prometheus /metrics (must be before UseMvc)
            app.UseMetricServer();
            app.UseHealthChecks("/health");
            app.UseMvc();
        }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
mrich316commented, Jul 16, 2019

I always use the ExceptionHandlerMiddleware after prometheus-net and I don’t see this behaviour

app.UseMetricServer("/diagnostics/v1/metrics");
app.UseHttpMetrics();
app.UseExceptionHandler(...);

And prometheus-net seems to report the proper thing. Do I miss something ?

1reaction
sandersaarescommented, Jul 17, 2019

If you use an explicit exception handler middleware and configure it after the metrics middleware, that’s fine. However, the problem occurs when you either configure the exception handler before metrics or when you do not configure one at all (and let ASP.NET fallback to its default logic, which emits a 500).

After doing some digging in the ASP.NET Core source, I have not found a meaningful way to improve the behavior here, so I will fall back to documenting this aspect. In the end, ASP.NET Core is a pipeline and the nature of the pipelined processing must be understood for actual behavior to match expected behavior. This situation is one of the sufficiently nontrivial ones where it starts to matter.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Prometheus reports 200 but Response is actually 500
The logs and Postman clearly show a 500 response, so I am trying to understand why Prometheus doesn't record the status correctly.
Read more >
What is the solution to resolve the HTTP 500 Internal ...
NET 7 and deploying it on IIS with Windows 2019, I encounter the error 500. However, when using Core 2.2, the platform runs...
Read more >
Untitled
Asp.net core return internal server error 500 WebDec 20, 2022 · HTTP/1.1 500 Internal Server Error ... 500 Errors showing as 200 in...
Read more >
Using the Microsoft.AspNetCore.HealthChecks Package
Typically you will return a status code of 200 if everything is good, and any non-2xx code means something went wrong. For example,...
Read more >
Excluding health check endpoints from Serilog request ...
You can set up a simple, dumb, health check that returns a 200 OK response to every request, to let Kubernetes know your...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found