Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Configuration for correlation plugin #245

Merged
merged 3 commits into from Dec 27, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 69 additions & 6 deletions src/plugins/nova_correlation_plugin.erl
@@ -1,3 +1,41 @@
%%%-------------------------------------------------------------------
%%% @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>&lt;&lt;"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).

Expand All @@ -7,21 +45,46 @@

-include_lib("kernel/include/logger.hrl").

pre_request(Req, _) ->
UUID = uuid:uuid_to_string(uuid:get_v4()),
%%--------------------------------------------------------------------
%% @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
logger:update_process_metadata(#{<<"correlation-id">> => UUID}),
Req1 = cowboy_req:set_resp_header(<<"X-Correlation-ID">>, UUID, Req),
{ok, Req1}.
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.1.0">>,
<<"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}).