blob: 888000b25a424fd1c9a8ac12a4347eb4b992e1ae [file] [log] [blame]
%%%
%%% Copyright 2011, Boundary
%%%
%%% 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.
%%%
%%%-------------------------------------------------------------------
%%% File: folsom_ets.erl
%%% @author joe williams <j@boundary.com>
%%% @doc
%%% @end
%%%------------------------------------------------------------------
-module(folsom_ets).
%% API
-export([
add_handler/2,
add_handler/3,
add_handler/4,
add_handler/5,
tag_handler/2,
untag_handler/2,
delete_handler/1,
handler_exists/1,
notify/1,
notify/2,
notify/3,
tagged_notify/4,
notify_existing_metric/3,
get_handlers/0,
get_handlers_info/0,
get_info/1,
get_values/1,
get_history_values/2,
get_group_values/1,
get_group_values/2,
get_tags/1
]).
-record(metric, {
tags = sets:new(),
type,
history_size
}).
-include("folsom.hrl").
%%%===================================================================
%%% API
%%%===================================================================
add_handler(Type, Name) ->
maybe_add_handler(Type, Name, handler_exists(Name)).
add_handler(Type, Name, SampleSize) ->
maybe_add_handler(Type, Name, SampleSize, handler_exists(Name)).
add_handler(Type, Name, SampleType, SampleSize) ->
maybe_add_handler(Type, Name, SampleType, SampleSize, handler_exists(Name)).
add_handler(Type, Name, SampleType, SampleSize, Alpha) ->
maybe_add_handler(Type, Name, SampleType, SampleSize, Alpha, handler_exists(Name)).
tag_handler(Name, Tag) ->
case handler_exists(Name) of
true ->
add_tag(Name, Tag);
false ->
{error, Name, nonexistent_metric}
end.
untag_handler(Name, Tag) ->
case handler_exists(Name) of
true ->
rm_tag(Name, Tag);
false ->
{error, Name, nonexistent_metric}
end.
delete_handler(Name) ->
{_, Info} = get_info(Name),
ok = delete_metric(Name, proplists:get_value(type, Info)).
handler_exists(Name) ->
ets:member(?FOLSOM_TABLE, Name).
%% old tuple style notifications
notify({Name, Event}) ->
notify(Name, Event).
%% notify/2, checks metric type and makes sure metric exists
%% before notifying, returning error if not
notify(Name, Event) ->
case handler_exists(Name) of
true ->
{_, Info} = get_info(Name),
Type = proplists:get_value(type, Info),
notify(Name, Event, Type, true);
false ->
{error, Name, nonexistent_metric}
end.
%% notify/3, makes sure metric exist, if not creates metric
notify(Name, Event, Type) ->
notify(Name, Event, Type, handler_exists(Name)).
tagged_notify(Name, Event, Type, Tags) ->
R = notify(Name, Event, Type),
case get_tags(Name) of
{error, _, _} -> skip;
CurrentTags ->
[add_tag(Name, T) || T <- Tags, not sets:is_element(T, CurrentTags)]
end,
R.
%% assumes metric already exists, bypasses above checks
notify_existing_metric(Name, Event, Type) ->
notify(Name, Event, Type, true).
get_handlers() ->
proplists:get_keys(ets:tab2list(?FOLSOM_TABLE)).
get_handlers_info() ->
[get_info(Id) || Id <- get_handlers()].
get_info(Name) ->
case handler_exists(Name) of
true ->
[{_, #metric{type = Type, tags = Tags}}] = ets:lookup(?FOLSOM_TABLE, Name),
{Name, [{type, Type}, {tags, Tags}]};
false ->
{error, Name, nonexistent_metric}
end.
get_values(Name) ->
case handler_exists(Name) of
true ->
{_, Info} = get_info(Name),
get_values(Name, proplists:get_value(type, Info));
false ->
{error, Name, nonexistent_metric}
end.
get_values(Name, counter) ->
folsom_metrics_counter:get_value(Name);
get_values(Name, gauge) ->
folsom_metrics_gauge:get_value(Name);
get_values(Name, histogram) ->
folsom_metrics_histogram:get_values(Name);
get_values(Name, history) ->
folsom_metrics_history:get_events(Name);
get_values(Name, meter) ->
folsom_metrics_meter:get_values(Name);
get_values(Name, meter_reader) ->
folsom_metrics_meter_reader:get_values(Name);
get_values(Name, duration) ->
folsom_metrics_duration:get_values(Name);
get_values(Name, spiral) ->
folsom_metrics_spiral:get_values(Name);
get_values(_, Type) ->
{error, Type, unsupported_metric_type}.
get_history_values(Name, Count) ->
folsom_metrics_history:get_events(Name, Count).
get_group_values(Tag) ->
folsom_ets:get_group_values(Tag, '_').
get_group_values(Tag, Type) ->
Metrics = ets:match(?FOLSOM_TABLE, {'$1', {metric, '$2', Type, '_'}}),
[{Name, get_values(Name)} || [Name, Tags] <- Metrics, sets:is_element(Tag, Tags)].
get_tags(Name) ->
case handler_exists(Name) of
true ->
{_, Info} = get_info(Name),
proplists:get_value(tags, Info);
false ->
{error, Name, nonexistent_metric}
end.
%%%===================================================================
%%% Internal functions
%%%===================================================================
maybe_add_handler(counter, Name, false) ->
true = folsom_metrics_counter:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = counter}}),
ok;
maybe_add_handler(gauge, Name, false) ->
true = folsom_metrics_gauge:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = gauge}}),
ok;
maybe_add_handler(histogram, Name, false) ->
true = folsom_metrics_histogram:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = histogram}}),
ok;
maybe_add_handler(duration, Name, false) ->
true = folsom_metrics_histogram:new(Name),
true = folsom_metrics_duration:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = duration}}),
ok;
maybe_add_handler(history, Name, false) ->
ok = folsom_metrics_history:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = history, history_size = ?DEFAULT_SIZE}}),
ok;
maybe_add_handler(meter, Name, false) ->
ok = folsom_meter_timer_server:register(Name, folsom_metrics_meter),
true = folsom_metrics_meter:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = meter}}),
ok;
maybe_add_handler(meter_reader, Name, false) ->
ok = folsom_meter_timer_server:register(Name, folsom_metrics_meter_reader),
true = folsom_metrics_meter_reader:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = meter_reader}}),
ok;
maybe_add_handler(spiral, Name, false) ->
true = folsom_metrics_spiral:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = spiral}}),
ok;
maybe_add_handler(Type, _, false) ->
{error, Type, unsupported_metric_type};
maybe_add_handler(_, Name, true) ->
{error, Name, metric_already_exists}.
maybe_add_handler(histogram, Name, SampleType, false) ->
true = folsom_metrics_histogram:new(Name, SampleType),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = histogram}}),
ok;
maybe_add_handler(history, Name, SampleSize, false) ->
ok = folsom_metrics_history:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = history, history_size = SampleSize}}),
ok;
maybe_add_handler(Type, _, _, false) ->
{error, Type, unsupported_metric_type};
maybe_add_handler(_, Name, _, true) ->
{error, Name, metric_already_exists}.
maybe_add_handler(histogram, Name, SampleType, SampleSize, false) ->
true = folsom_metrics_histogram:new(Name, SampleType, SampleSize),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = histogram}}),
ok;
maybe_add_handler(Type, _, _, _, false) ->
{error, Type, unsupported_metric_type};
maybe_add_handler(_, Name, _, _, true) ->
{error, Name, metric_already_exists}.
maybe_add_handler(histogram, Name, SampleType, SampleSize, Alpha, false) ->
true = folsom_metrics_histogram:new(Name, SampleType, SampleSize, Alpha),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = histogram}}),
ok;
maybe_add_handler(duration, Name, SampleType, SampleSize, Alpha, false) ->
true = folsom_metrics_histogram:new(Name, SampleType, SampleSize, Alpha),
true = folsom_metrics_duration:new(Name),
true = ets:insert(?FOLSOM_TABLE, {Name, #metric{type = duration}}),
ok;
maybe_add_handler(Type, _, _, _, _, false) ->
{error, Type, unsupported_metric_type};
maybe_add_handler(_, Name, _, _, _, true) ->
{error, Name, metric_already_exists}.
add_tag(Name, Tag) ->
M = #metric{tags=Tags} = ets:lookup_element(?FOLSOM_TABLE, Name, 2),
true = ets:update_element(?FOLSOM_TABLE, Name, {2, M#metric{tags=sets:add_element(Tag, Tags)}}),
ok.
rm_tag(Name, Tag) ->
M = #metric{tags=Tags} = ets:lookup_element(?FOLSOM_TABLE, Name, 2),
true = ets:update_element(?FOLSOM_TABLE, Name, {2, M#metric{tags=sets:del_element(Tag, Tags)}}),
ok.
delete_metric(Name, history) ->
History = folsom_metrics_history:get_value(Name),
ok = delete_history(Name, History),
ok;
delete_metric(Name, histogram) ->
Metric = folsom_metrics_histogram:get_value(Name),
ok = delete_histogram(Name, Metric),
ok;
delete_metric(Name, duration) ->
Histo = folsom_metrics_histogram:get_value(Name),
ok = delete_histogram(Name, Histo),
true = ets:delete(?DURATION_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_metric(Name, counter) ->
ok = folsom_metrics_counter:delete(Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_metric(Name, gauge) ->
true = ets:delete(?GAUGE_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_metric(Name, meter) ->
ok = folsom_meter_timer_server:unregister(Name),
true = ets:delete(?METER_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_metric(Name, meter_reader) ->
ok = folsom_meter_timer_server:unregister(Name),
true = ets:delete(?METER_READER_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_metric(Name, spiral) ->
#spiral{tid=Tid, server=Pid} = folsom_metrics_spiral:get_value(Name),
folsom_sample_slide_server:stop(Pid),
ets:delete(?SPIRAL_TABLE, Name),
ets:delete(?FOLSOM_TABLE, Name),
ets:delete(Tid),
ok.
delete_histogram(Name, #histogram{type = uniform, sample = #uniform{reservoir = Reservoir}}) ->
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Reservoir),
ok;
delete_histogram(Name, #histogram{type = none, sample = #none{reservoir = Reservoir}}) ->
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Reservoir),
ok;
delete_histogram(Name, #histogram{type = slide_sorted, sample = #slide_sorted{reservoir = Reservoir}}) ->
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Reservoir),
ok;
delete_histogram(Name, #histogram{type = exdec}) ->
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
ok;
delete_histogram(Name, #histogram{type = slide, sample = #slide{reservoir = Reservoir, server=Pid}}) ->
folsom_sample_slide_server:stop(Pid),
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Reservoir),
ok;
delete_histogram(Name, #histogram{type = slide_uniform, sample = #slide_uniform{reservoir = Reservoir, server=Pid}}) ->
folsom_sample_slide_server:stop(Pid),
true = ets:delete(?HISTOGRAM_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Reservoir),
ok.
delete_history(Name, #history{tid = Tid}) ->
true = ets:delete(?HISTORY_TABLE, Name),
true = ets:delete(?FOLSOM_TABLE, Name),
true = ets:delete(Tid),
ok.
notify(Name, {inc, Value}, counter, true) ->
folsom_metrics_counter:inc(Name, Value),
ok;
notify(Name, {inc, Value}, counter, false) ->
add_handler(counter, Name),
folsom_metrics_counter:inc(Name, Value),
ok;
notify(Name, {dec, Value}, counter, true) ->
folsom_metrics_counter:dec(Name, Value),
ok;
notify(Name, {dec, Value}, counter, false) ->
add_handler(counter, Name),
folsom_metrics_counter:dec(Name, Value),
ok;
notify(Name, clear, counter, true) ->
folsom_metrics_counter:clear(Name),
ok;
notify(Name, clear, counter, false) ->
add_handler(counter, Name),
folsom_metrics_counter:clear(Name),
ok;
notify(Name, Value, gauge, true) ->
folsom_metrics_gauge:update(Name, Value),
ok;
notify(Name, Value, gauge, false) ->
add_handler(gauge, Name),
folsom_metrics_gauge:update(Name, Value),
ok;
notify(Name, Value, histogram, true) ->
folsom_metrics_histogram:update(Name, Value),
ok;
notify(Name, Value, histogram, false) ->
add_handler(histogram, Name),
folsom_metrics_histogram:update(Name, Value),
ok;
notify(Name, Value, history, true) ->
[{_, #metric{history_size = HistorySize}}] = ets:lookup(?FOLSOM_TABLE, Name),
folsom_metrics_history:update(Name, HistorySize, Value),
ok;
notify(Name, Value, history, false) ->
add_handler(history, Name),
[{_, #metric{history_size = HistorySize}}] = ets:lookup(?FOLSOM_TABLE, Name),
folsom_metrics_history:update(Name, HistorySize, Value),
ok;
notify(Name, Value, meter, true) ->
folsom_metrics_meter:mark(Name, Value),
ok;
notify(Name, Value, meter, false) ->
add_handler(meter, Name),
folsom_metrics_meter:mark(Name, Value),
ok;
notify(Name, Value, meter_reader, true) ->
folsom_metrics_meter_reader:mark(Name, Value),
ok;
notify(Name, Value, meter_reader, false) ->
add_handler(meter_reader, Name),
folsom_metrics_meter_reader:mark(Name, Value),
ok;
notify(Name, Value, duration, true) ->
folsom_metrics_duration:update(Name, Value),
ok;
notify(Name, Value, duration, false) ->
add_handler(duration, Name),
folsom_metrics_duration:update(Name, Value),
ok;
notify(Name, Value, spiral, true) ->
folsom_metrics_spiral:update(Name, Value),
ok;
notify(Name, Value, spiral, false) ->
add_handler(spiral, Name),
folsom_metrics_spiral:update(Name, Value),
ok;
notify(_, _, Type, _) ->
{error, Type, unsupported_metric_type}.