anchor.diff:  category_bin(_)    -> <<"info">>.
   
   build_alerts(Params) ->
  -    create_new_alert(),
  -    [{alerts, Alerts}] = call_simple_cache(lookup, [alerts]),
  -    LastNumber = case proplists:get_value("lastNumber", Params) of
  +    {MinTStamp, Limit} = common_params(Params),
  +    AlertConfig = get_alert_config(),
  +    Alerts = proplists:get_value(alerts, AlertConfig, []),
  +    LogEntries = ns_log:recent(),
  +    LogEntries2 = lists:filter(
  +                    fun(#log_entry{module = Module,
  +                                   code = Code}) ->
  +                            lists:member(alert_key(Module, Code), Alerts)
  +                    end,
  +                    LogEntries),
  +    build_log_structs(LogEntries2, MinTStamp, Limit).
  +
  +% The defined alert_key() responses are...
  +%
  +%  server_down
  +%  server_up
  +%  server_joined
  +%  memory_low
  +%  bucket_created
  +%  config_changed
  +
  +is_alert_key("server_down")    -> true;
  +is_alert_key("server_up")      -> true;
  +is_alert_key("server_joined")  -> true;
  +is_alert_key("memory_low")     -> true;
  +is_alert_key("bucket_created") -> true;
  +is_alert_key("config_changed") -> true;
  +is_alert_key(_) -> false.
  +
  +alert_key(_Module, _Code) -> all.
  +
  +default_alert_config() ->
  +    [{email, ""},
  +     {email_alerts, false},
  +     {alerts, []}].
  +
  +get_alert_config() ->
  +    case ns_config:search(ns_config:get(), alerts) of
  +        {value, X} -> X;
  +        false      -> default_alert_config()
  +    end.
  +
  +set_alert_config(AlertConfig) ->
  +    ns_config:set(alerts, AlertConfig).
  +
  +common_params(Params) ->
  +    MinTStamp = case proplists:get_value("sinceTime", Params) of
                        undefined -> 0;
                        V -> list_to_integer(V)
                    end,
  -    Limit = ?ALERTS_LIMIT,
  -    CutAlerts = stateful_takewhile(fun ({struct, [{number, N} | _]}, Index) ->
  -                                           {(N > LastNumber) andalso
  -                                            (Index < Limit), Index+1}
  -                                   end,
  -                                   Alerts,
  -                                   0),
  -    CutAlerts.
  -
  -fetch_alert_settings() ->
  -    caching_result(alert_settings,
  -                   fun () ->
  -                           [{email, <<"alk@tut.by">>},
  -                            {sendAlerts, <<"1">>},
  -                            {sendForLowSpace, <<"1">>},
  -                            {sendForLowMemory, <<"1">>},
  -                            {sendForNotResponding, <<"1">>},
  -                            {sendForJoinsCluster, <<"1">>},
  -                            {sendForOpsAboveNormal, <<"1">>},
  -                            {sendForSetsAboveNormal, <<"1">>}]
  -                   end).
  -
  --define(INITIAL_ALERTS, [{struct, [{number, 3},
  -                                   {type, <<"info">>},
  -                                   {tstamp, 1259836260000},
  -                                   {shortText, <<"Above Average Operations per Second">>},
  -                                   {text, <<"Licensing, capacity, NorthScale issues, etc.">>}]},
  -                         {struct, [{number, 2},
  -                                   {type, <<"attention">>},
  -                                   {tstamp, 1259836260000},
  -                                   {shortText, <<"New Node Joined Pool">>},
  -                                   {text, <<"A new node is now online">>}]},
  -                         {struct, [{number, 1},
  -                                   {type, <<"warning">>},
  -                                   {tstamp, 1259836260000},
  -                                   {shortText, <<"Server Node Down">>},
  -                                   {text, <<"Server node is no longer available">>}]}]).
  -
  -fetch_alerts() ->
  -    caching_result(alerts,
  -                   fun () -> ?INITIAL_ALERTS end).
  -
  -create_new_alert() ->
  -    fetch_alerts(), %% side effect
  -    [{alerts, OldAlerts}] = call_simple_cache(lookup, [alerts]),
  -    [{struct, [{number, LastNumber} | _]} | _] = OldAlerts,
  -    NewAlerts = [{struct, [{number, LastNumber+1},
  -                           {type, <<"attention">>},
  -                           {tstamp, java_date()},
  -                           {shortText, <<"Lorem ipsum">>},
  -                           {text, <<"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas egestas dictum iaculis.">>}]}
  -                 | OldAlerts],
  -    call_simple_cache(insert, [{alerts, NewAlerts}]),
  -    nil.
  +    Limit = case proplists:get_value("limit", Params) of
  +                undefined -> ?DEFAULT_LIMIT;
  +                L -> list_to_integer(L)
  +            end,
  +    {MinTStamp, Limit}.
   
   -ifdef(EUNIT).
  -
  -reset_alerts() ->
  -    call_simple_cache(delete, [alerts]).
  -
  -fetch_default_alerts_test() ->
  -    reset_alerts(),
  -    ?assertEqual(?INITIAL_ALERTS,
  -                 fetch_alerts()),
  -
  -    ?assertEqual(?INITIAL_ALERTS,
  -                 fetch_alerts()).
  -
  -create_new_alert_test() ->
  -    reset_alerts(),
  -    create_new_alert(),
  -    List1 = fetch_alerts(),
  -    ?assertEqual(?INITIAL_ALERTS,
  -                 tl(List1)),
  -    ?assertMatch({struct, [{number, 4}|_]},
  -                 lists:nth(1, List1)),
  -    ?assertEqual([], lists:nthtail(4, List1)),
  -
  -    create_new_alert(),
  -    List2 = fetch_alerts(),
  -    ?assertEqual(?INITIAL_ALERTS,
  -                 tl(tl(List2))),
  -    ?assertMatch({struct, [{number, 5}|_]},
  -                 lists:nth(1, List2)),
  -    ?assertMatch({struct, [{number, 4}|_]},
  -                 lists:nth(2, List2)),
  -    ?assertEqual([], lists:nthtail(5, List2)).
  -
  -build_alerts_test() ->
  -    reset_alerts(),
  -    ?assertEqual(?INITIAL_ALERTS,
  -                 tl(build_alerts([]))),
  -
  -    reset_alerts(),
  -    ?assertMatch([{struct, [{number, 4} | _]}],
  -                 build_alerts([{"lastNumber", "3"}])).
   
   test() ->
       eunit:test(wrap_tests_with_cache_setup({module, ?MODULE}),
  
positive.code: 
  %% External API
  
  -define(ALERTS_LIMIT, 15).
  
  handle_logs(Req) ->
      reply_json(Req, {struct, [{list, build_logs(Req:parse_qs())}]}).
  
  handle_alerts(Req) ->
      reply_json(Req, {struct, [{limit, ?ALERTS_LIMIT},
                                {settings, {struct, [{updateUri,
                                                      <<"/alerts/settings">>}
                                                     | fetch_alert_settings()]}},
                                {list, build_alerts(Req:parse_qs())}]}).
  
  handle_alerts_settings_post(Req) ->
      PostArgs = Req:parse_post(),
      call_simple_cache(insert,
                        [{alert_settings,
                          lists:map(fun ({K,V}) ->
                                            {list_to_atom(K), list_to_binary(V)}
                                    end,
                                    PostArgs)}]),
      %% TODO: make it more RESTful
      Req:respond({200, [], []}).
  
  build_logs(Params) ->
      MinTStamp = case proplists:get_value("minTimeStamp", Params) of
                       undefined -> 0;
                       V -> list_to_integer(V)
                   end,
      LogEntries = lists:filter(
                     fun(#log_entry{tstamp = TStamp}) ->
                             TStamp > MinTStamp
                     end,
                     ns_log:recent()),
      build_log_structs(LogEntries).
  
  build_log_structs(LogEntries) ->
      LogStructs =
          lists:foldl(
            fun(#log_entry{module = Module,
  
negative.code:                   end
            end,
            [],
            LogEntries3),
      LogStructs.
  
  category_bin(info) -> <<"info">>;
  