%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(logger_backend).

-export([log_allowed/2]).

-include("logger_internal.hrl").

-define(OWN_KEYS,[level,filters,filter_default,handlers]).

%%%-----------------------------------------------------------------
%%% The default logger backend
log_allowed(Log, Tid) ->
    {ok,Config} = logger_config:get(Tid,primary),
    Filters = maps:get(filters,Config,[]),
    case apply_filters(primary,Log,Filters,Config) of
        stop ->
            ok;
        Log1 ->
            Handlers = maps:get(handlers,Config,[]),
            call_handlers(Log1,Handlers,Tid)
    end,
    ok.

call_handlers(#{level:=Level}=Log,[Id|Handlers],Tid) ->
    case logger_config:get(Tid,Id,Level) of
        {ok,{Module,Config}} ->
            Filters = maps:get(filters,Config,[]),
            case apply_filters(Id,Log,Filters,Config) of
                stop ->
                    ok;
                Log1 ->
                    Config1 = maps:without(?OWN_KEYS,Config),
                    try Module:log(Log1,Config1)
                    catch C:R:S ->
                            case logger:remove_handler(Id) of
                                ok ->
                                    logger:internal_log(
                                      error,{removed_failing_handler,Id}),
                                    ?LOG_INTERNAL(
                                       debug,
                                       [{logger,removed_failing_handler},
                                        {handler,{Id,Module}},
                                        {log_event,Log1},
                                        {config,Config1},
                                        {reason,{C,R,filter_stacktrace(S)}}]);
                                {error,{not_found,_}} ->
                                    %% Probably already removed by other client
                                    %% Don't report again
                                    ok;
                                {error,Reason} ->
                                    ?LOG_INTERNAL(
                                       debug,
                                       [{logger,remove_handler_failed},
                                        {reason,Reason}])
                            end
                    end
            end;
        _ ->
            ok
    end,
    call_handlers(Log,Handlers,Tid);
call_handlers(_Log,[],_Tid) ->
    ok.

apply_filters(Owner,Log,Filters,Config) ->
    case do_apply_filters(Owner,Log,Filters,ignore) of
        stop ->
            stop;
        ignore ->
            case maps:get(filter_default,Config) of
                log ->
                    Log;
                stop ->
                    stop
            end;
        Log1 ->
            Log1
    end.

do_apply_filters(Owner,Log,[{_Id,{FilterFun,FilterArgs}}=Filter|Filters],State) ->
    try FilterFun(Log,FilterArgs) of
        stop ->
            stop;
        ignore ->
            do_apply_filters(Owner,Log,Filters,State);
        Log1=#{level:=Level,msg:=Msg,meta:=Meta}
          when is_atom(Level), ?IS_MSG(Msg), is_map(Meta) ->
            do_apply_filters(Owner,Log1,Filters,log);
        Bad ->
            handle_filter_failed(Filter,Owner,Log,{bad_return_value,Bad})
    catch C:R:S ->
            handle_filter_failed(Filter,Owner,Log,{C,R,filter_stacktrace(S)})
    end;
do_apply_filters(_Owner,_Log,[],ignore) ->
    ignore;
do_apply_filters(_Owner,Log,[],log) ->
    Log.

handle_filter_failed({Id,_}=Filter,Owner,Log,Reason) ->
    case logger_server:remove_filter(Owner,Id) of
        ok ->
            logger:internal_log(error,{removed_failing_filter,Id}),
            ?LOG_INTERNAL(debug,
                          [{logger,removed_failing_filter},
                           {filter,Filter},
                           {owner,Owner},
                           {log_event,Log},
                           {reason,Reason}]);
        _ ->
            ok
    end,
    ignore.

filter_stacktrace(Stacktrace) ->
    logger:filter_stacktrace(?MODULE,Stacktrace).
