网站地图
首页
新闻
电影新闻
电视新闻
人物新闻
专题策划
影评
最新影评
新片热评
经典赏析
媒体评论
电影院
北京影讯
上海影讯
广州影讯
深圳影讯
成都影讯
电影库
典藏佳片
全球新片
即将上映
票房榜
社区
日志
相册
电影
好友
专辑
收藏
影视杂谈
明星时尚
文化休闲
群组
话题
达人
排行榜
电影榜
电视榜
人物榜
日志榜
话题榜

变奏曲,十一月……

永远不要说永远……

http://i.mtime.com/844165/

您当前的位置: 社区>> 博客>>

编辑 | 删除 erlang large日志系统代码阅读(七):lager_file_backend

GOK358405961 发布于:

-module(lager_file_backend).


-include("lager.hrl").


-behaviour(gen_event).


-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,

        code_change/3]).


-export([config_to_id/1]).


-record(state, {

        name :: string(),

        level :: {'mask', integer()},

        fd :: file:io_device(),

        inode :: integer(),

        flap=false :: boolean(),

        size = 0 :: integer(),

        date,

        count = 10,

        formatter,

        formatter_config

    }).


%% @private

-spec init([{string(), lager:log_level()},...]) -> {ok, #state{}}.

init([LogFile,{Formatter}]) ->

    init([LogFile,{Formatter,[]}]);

init([LogFile,{Formatter,FormatterConfig}]) ->

    case validate_logfile(LogFile) of%获取、解析logfile基本配置信息

        {Name, Level, Size, Date, Count} -> 

            schedule_rotation(Name, Date),%定时发送日志文件重新记录消息

            State = case lager_util:open_logfile(Name, true) of%创建/打开日志文件

                {ok, {FD, Inode, _}} ->

                    #state{name=Name, level=Level,

                        fd=FD, inode=Inode, size=Size, date=Date, count=Count, formatter=Formatter, formatter_config=FormatterConfig};

                {error, Reason} ->

                    ?INT_LOG(error, "Failed to open log file ~s with error ~s",

                        [Name, file:format_error(Reason)]),

                    #state{name=Name, level=Level,

                        flap=true, size=Size, date=Date, count=Count, formatter=Formatter, formatter_config=FormatterConfig}

            end,


            {ok, State};

        false ->

            ignore

    end;

init(LogFile) ->

    init([LogFile,{lager_default_formatter,[]}]).



%% @private

handle_call({set_loglevel, Level}, #state{name=Ident} = State) ->%重设log_level掩码

    case validate_loglevel(Level) of

        false ->

            {ok, {error, bad_loglevel}, State};

        Levels ->

            ?INT_LOG(notice, "Changed loglevel of ~s to ~p", [Ident, Level]),

            {ok, ok, State#state{level=Levels}}

    end;

handle_call(get_loglevel, #state{level=Level} = State) ->%获取log_level掩码

    {ok, Level, State};

handle_call(_Request, State) ->

    {ok, ok, State}.


%% @private

handle_event({log, Message},

    #state{name=Name, level=L,formatter=Formatter,formatter_config=FormatConfig} = State) ->

    case lager_util:is_loggable(Message,L,{lager_file_backend, Name}) of%判断信息是否需要从文件输出

        true ->

            {ok,write(State, lager_msg:severity_as_int(Message), Formatter:format(Message,FormatConfig)) };

        false ->

            {ok, State}

    end;

handle_event(_Event, State) ->

    {ok, State}.


%% @private


%日志文件重新记录消息处理


handle_info({rotate, File}, #state{name=File,count=Count,date=Date} = State) ->

    lager_util:rotate_logfile(File, Count),%日志重新记录

    schedule_rotation(File, Date),%定时发送日志文件重新记录消息

    {ok, State};

handle_info(_Info, State) ->

    {ok, State}.


%% @private

terminate(_Reason, #state{fd=FD}) ->

    %% flush and close any file handles

    _ = file:datasync(FD),

    _ = file:close(FD),

    ok.


%% @private

code_change(_OldVsn, State, _Extra) ->

    {ok, State}.


%% convert the config into a gen_event handler ID

config_to_id({Name,_Severity}) ->

    {?MODULE, Name};

config_to_id({Name,_Severity,_Size,_Rotation,_Count}) ->

    {?MODULE, Name};

config_to_id([{Name,_Severity,_Size,_Rotation,_Count}, _Format]) ->

    {?MODULE, Name}.



write(#state{name=Name, fd=FD, inode=Inode, flap=Flap, size=RotSize,

        count=Count} = State, Level, Msg) ->

    case lager_util:ensure_logfile(Name, FD, Inode, true) of%获取文件信息,如果文件信息有误则重新创建文件

        {ok, {_, _, Size}} when RotSize /= 0, Size > RotSize ->%日志文件大小超过配置最大值

            lager_util:rotate_logfile(Name, Count),%日志重新记录

            write(State, Level, Msg);

        {ok, {NewFD, NewInode, _}} ->

            %% delayed_write doesn't report errors

            _ = file:write(NewFD, Msg),

            case Level of

                _ when Level =< ?ERROR ->%error级别以上的log信息需要从buffer中立即刷入磁盘

                    %% force a sync on any message at error severity or above

                    Flap2 = case file:datasync(NewFD) of

                        {error, Reason2} when Flap == false ->

                            ?INT_LOG(error, "Failed to write log message to file ~s: ~s",

                                [Name, file:format_error(Reason2)]),

                            true;

                        ok ->

                            false;

                        _ ->

                            Flap

                    end,

                    State#state{fd=NewFD, inode=NewInode, flap=Flap2};

                _ -> 

                    State#state{fd=NewFD, inode=NewInode}

            end;

        {error, Reason} ->

            case Flap of

                true ->

                    State;

                _ ->

                    ?INT_LOG(error, "Failed to reopen log file ~s with error ~s",

                        [Name, file:format_error(Reason)]),

                    State#state{flap=true}

            end

    end.


validate_logfile({Name, Level}) ->

    case validate_loglevel(Level) of%计算log_level掩码

        false ->

            ?INT_LOG(error, "Invalid log level of ~p for ~s.",

                [Level, Name]),

            false;

        Levels ->

            {Name, Levels, 0, undefined, 0}

    end;

validate_logfile({Name, Level, Size, Date, Count}) ->

    ValidLevel = validate_loglevel(Level),%计算log_level掩码

    ValidSize = (is_integer(Size) andalso Size >= 0),%获取file size并验证有效性

    ValidCount = (is_integer(Count) andalso Count >= 0),%获取file count并验证有效性

    case {ValidLevel, ValidSize, ValidCount} of

        {false, _, _} ->

            ?INT_LOG(error, "Invalid log level of ~p for ~s.",

                [Level, Name]),

            false;

        {_, false, _} ->

            ?INT_LOG(error, "Invalid rotation size of ~p for ~s.",

                [Size, Name]),

            false;

        {_, _, false} ->

            ?INT_LOG(error, "Invalid rotation count of ~p for ~s.",

                [Count, Name]),

            false;

        {Levels, true, true} ->

            case lager_util:parse_rotation_date_spec(Date) of%日志文件重新记录时间解析

                {ok, Spec} ->

                    {Name, Levels, Size, Spec, Count};

                {error, _} when Date == "" ->

                    %% blank ones are fine.

                    {Name, Levels, Size, undefined, Count};

                {error, _} ->

                    ?INT_LOG(error, "Invalid rotation date of ~p for ~s.",

                        [Date, Name]),

                    false

            end

    end;

validate_logfile(H) ->

    ?INT_LOG(error, "Invalid log file config ~p.", [H]),

    false.


%计算log_level掩码


validate_loglevel(Level) ->

    try lager_util:config_to_mask(Level) of

        Levels ->

            Levels

    catch

        _:_ ->

            false

    end.


%定时发送日志文件重新记录消息


schedule_rotation(_, undefined) ->

    ok;

schedule_rotation(Name, Date) ->

    erlang:send_after(lager_util:calculate_next_rotation(Date) * 1000, self(), {rotate, Name}),

    ok.

回复 (0) | 收藏 (0) | 2841 次阅读 |

日志分类