%%%-------------------------------------------------------------------
%%% @doc
%%% <h2> Plugin Configuration</h2>
%%%
%%% To not break backwards compatibility in a minor release, some behavior is behind configuration items.
%%%
%%%
%%% <h3> Request Correlation Header Name </h3>
%%%
%%% Set <code>request_correlation_header</code> in the plugin config to read the correlation ID from the request headers.
%%%
%%% Notice: Cowboy request headers are always in lowercase.
%%%
%%% <h3> Default Correlation ID Generation </h3>
%%%
%%% If the header name is not defined or the request lacks a correlation ID header, then the plugin generates
%%% a v4 UUID automatically.
%%%
%%% <h3> Logger Metadata Key Override </h3>
%%%
%%% Use <code>logger_metadata_key</code> to customize the correlation ID key in OTP logger process metadata. By default it is set to <code><<"correlation-id">></code>.
%%%
%%% <h3> Correlation ID in Request Object </h3>
%%%
%%% The plugin defines a field called <code>correlation_id</code> in the request object for controller use if it makes further requests that it want to pass on the correlation id to.
%%%
%%% <h3> Example configuration </h3>
%%% <pre lang="erlang"><![CDATA[
%%% {plugins, [
%%% {pre_request, nova_correlation_plugin, #{
%%% request_correlation_header => <<"x-correlation-id">>,
%%% logger_metadata_key => correlation_id
%%% }}
%%% ]}
%%% ]]></pre>
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(nova_correlation_plugin).
-behaviour(nova_plugin).
-export([pre_request/2,
post_request/2,
plugin_info/0]).
-include_lib("kernel/include/logger.hrl").
%%--------------------------------------------------------------------
%% @doc
%% Pre-request callback to either pick up correlation id from request headers
%% or generate a new uuid correlation id.
%% @end
%%--------------------------------------------------------------------
pre_request(Req0, Opts) ->
CorrId = get_correlation_id(Req0, Opts),
%% Update the loggers metadata with correlation-id
ok = update_logger_metadata(CorrId, Opts),
Req1 = cowboy_req:set_resp_header(<<"X-Correlation-ID">>, CorrId, Req0),
Req = Req1#{correlation_id => CorrId},
{ok, Req}.
post_request(Req, _) ->
{ok, Req}.
plugin_info() ->
{
<<"nova_correlation_plugin">>,
<<"0.2.0">>,
<<"Nova team <info@novaframework.org">>,
<<"Add X-Correlation-ID headers to response">>,
[]
}.
get_correlation_id(Req, #{ request_correlation_header := CorrelationHeader }) ->
case cowboy_req:header(CorrelationHeader, Req) of
undefined ->
uuid();
CorrId ->
CorrId
end;
get_correlation_id(_Req, _Opts) ->
uuid().
uuid() ->
uuid:uuid_to_string(uuid:get_v4()).
update_logger_metadata(CorrId, Opts) ->
LoggerKey = maps:get(logger_metadata_key, Opts, <<"correlation-id">>),
logger:update_process_metadata(#{LoggerKey => CorrId}).