1

Have a question regarding Prometheus metrics in Tapir and ZIO. I have a simple code:

val metrics = PrometheusMetrics.default[Task]()
val options: ZioHttpServerOptions[Any] = ZioHttpServerOptions
    .customiseInterceptors
    .metricsInterceptor(metrics.metricsInterceptor())
    .options

and it works correct when I call localhost:8080/metrics, I see metrics.

But when I added default error handler:

val metrics = PrometheusMetrics.default[Task]()
def failureResponse(msg: String): ValuedEndpointOutput[_]=
   ValuedEndpointOutput(jsonBody[MyFailure], MyFailure(msg))
val options: ZioHttpServerOptions[Any] = ZioHttpServerOptions
    .customiseInterceptors
    .metricsInterceptor(metrics.metricsInterceptor())
    .defaultHandlers(failureResponse, notFoundWhenRejected = true)
    .options

It doesn't work. Instead of metrics I see now error (404) which was caught during request to localhost:8080/metrics. Honestly, don't know why. Is it possible to fix it somehow and keep error handler along with metrics interceptor?

EDIT: Metrics endpoint:

def metricsEndpoint = ZioHttpInterpreter(options).toHttp(metrics.metricsEndpoint)
Developus
  • 1,400
  • 2
  • 14
  • 50
  • Are you sure you are adding the metrics endpoint in both cases? – adamw Sep 20 '22 at 17:48
  • 1
    Hm maybe you are interpreting your endpoints and the metrics endpoint separately - that is, adding the `zhttp.http.Http` twice? If so, combined with `notFoundWhenRejected = true`, would return a 404 when the first `Http` is run. – adamw Sep 20 '22 at 17:52
  • Yes, I use this options in `ZioHttpInterpreter(options).toHttp(...)`. I edited post and added it – Developus Sep 20 '22 at 17:52

1 Answers1

1

This problem is most probably due to separately interpreting the "main" endpoints and the metrics endpoint as ZIO Http's Http value.

Consider the following:

val mainHttp = ZioHttpInterpreter(options).toHttp(mainEndpoints)
val metricsHttp = ZioHttpInterpreter(options).toHttp(metricsEndpoints)

Server.start(8080, mainHttp <> metricsHttp)

If the notFoundWhenRejected = true option is used, when the request /metrics comes in, it is first handled by mainHttp. However, that value doesn't know how to handle this request - hence it is rejected. But as we specified the mentioned option, rejections are turned into 404s, hence the answer.

The default value for that option is false. In this situation, the /metrics request is rejected by the mainHttp value, but this isn't converted into a 404 response, instead processing continues with metricsHttp.

The proper solution, to have both /metrics working, and the notFoundWhenRejected = true option, is to interpret all endpoints at once. Then, the 404 will be returned only when none of the endpoint (neither the main, nor the metrics one) matches the request:

val http = ZioHttpInterpreter(options)
  .toHttp(mainEndpoints ++ metricsEndpoints)

Server.start(8080, http)
adamw
  • 8,038
  • 4
  • 28
  • 32
  • So `mainEndpoints` and `metricsEndpoints` here are `zServerLogic` for every interpreters? – Developus Sep 20 '22 at 18:42
  • 1
    yes, these are your server endpoints; you can combine them in a list and interpret in one go – adamw Sep 20 '22 at 18:50
  • Ok, it works no correctly. Metrics can be seen and not existing urls are correctly caught. It is what I needed. Thank you. – Developus Sep 20 '22 at 18:57