%%% %%% 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 %%% @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, 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 ]). -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) -> case handler_exists(Name) of true -> notify(Name, Event, Type, true); false -> notify(Name, Event, Type, false) end. %% 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}}] = ets:lookup(?FOLSOM_TABLE, Name), {Name, [{type, Type}]}; 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)]. %%%=================================================================== %%% 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 = 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, 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}.