ninenines / cowboy

Small, fast, modern HTTP server for Erlang/OTP.
https://ninenines.eu
ISC License
7.27k stars 1.16k forks source link

Adding metadata/labels to a route? #1647

Open rlipscombe opened 5 months ago

rlipscombe commented 5 months ago

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?

rlipscombe commented 5 months ago

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.

rlipscombe commented 5 months ago

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

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.