ninenines / cowboy

Small, fast, modern HTTP server for Erlang/OTP.

Home Page:https://ninenines.eu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Adding metadata/labels to a route?

rlipscombe opened this issue · comments

One question that occasionally arises at work is "how do we attach telemetry labels to a cowboy route?". We'd like to be able to attach arbitrary metadata to a route, and have it available in the cowboy_metrics_h callback.

Ideally, it would be an extra element in the route tuple, something like this:

    Dispatch = cowboy_router:compile([
        {'_', [
            {"/", cowboy_static, {priv_file, ?APPLICATION, "index.html"}},
            {"/users/:id", [{id, int}], the_user_h, [], #{labels => #{route => "/users"}}}
        ]}
    ]),

...and would be picked up by the cowboy_router middleware and put in the env along with handler and handler_opts.

(to avoid ambiguity, if you wanted this, you'd have to explicitly specify constraints, possibly empty -- or maybe {Handler, Opts} would need to be a tuple in this case).

Does this seem like a good idea? Is there another way to do it right now?

I've been wracking my brains for ways to wrap cowboy_router:compile or the cowboy_router middleware, or to wrap the individual handlers, but I can't come up with anything that doesn't feel like a hack.

After some thought, I've come up with the following, which doesn't require any cowboy changes:

  • Replace the current Handler with a tuple, {Handler, HandlerOpts}
  • Replace the current HandlerOpts with HandlerMetadata.

Add a middleware that goes in between cowboy_router and cowboy_handler that does this:

execute(Req, Env = #{handler := {Handler, HandlerOpts}, handler_opts := Metadata}) ->
    Env2 = Env#{handler => Handler, handler_opts => HandlerOpts, handler_metadata => Metadata},
    {ok, Req, Env2}.

Getting handler_metadata into user_data where the metrics callback can see it is left as an exercise for the reader.