src/support/z_exometer_mqtt.erl

%% @author Maas-Maarten Zeeman <mmzeeman@xs4all.nl>
%% @copyright 2015 Maas-Maarten Zeeman
%% @doc Exometer reporter which publishes exometer stats to mqtt topics.
%% @end

%% Copyright 2015 Maas-Maarten Zeeman
%%
%% 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.
-module(z_exometer_mqtt).

%% exometer_report callback API
-export([
    exometer_init/1,
    exometer_info/2,
    exometer_cast/2,
    exometer_call/3,
    exometer_report/5,
    exometer_report_bulk/3,
    exometer_subscribe/5,
    exometer_unsubscribe/4,
    exometer_terminate/2,
    exometer_newentry/2,
    exometer_setopts/4
]).

-include("zotonic.hrl").
-include_lib("exometer_core/include/exometer.hrl").

-record(state, {
    topic_prefix = [],
    context % The context
}).

exometer_init(Opts) ->
    {context, Context} = proplists:lookup(context, Opts),
    TopicPrefix = proplists:get_value(topic_prefix, Opts, []),
    {ok, #state{topic_prefix=TopicPrefix, context=Context}}.

exometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, State) ->
    {ok, State}.

exometer_unsubscribe(_Metric, _DataPoint, _Extra, State) ->
    {ok, State}.

exometer_report(Metric, DataPoint, _Extra, Value, State) ->
    Topic = make_topic(Metric, DataPoint, State#state.topic_prefix),
    z_mqtt:publish(Topic, Value, State#state.context),
    {ok, State}.

exometer_report_bulk(Found, _Extra, State) ->
    [ begin
          Topic = make_topic(Metric, State#state.topic_prefix),
          z_mqtt:publish(Topic, DataPoint, #{ retain => true, qos => 0 }, State#state.context)
      end || {Metric, DataPoint} <- Found],

    {ok, State}.

exometer_call(Unknown, From, State) ->
    ?LOG_DEBUG("Unknown call ~p from ~p", [Unknown, From]),
    {ok, State}.

exometer_cast(Unknown, State) ->
    ?LOG_DEBUG("Unknown cast: ~p", [Unknown]),
    {ok, State}.

exometer_info(Unknown, State) ->
    ?LOG_DEBUG("Unknown info: ~p", [Unknown]),
    {ok, State}.

exometer_newentry(_Entry, State) ->
    {ok, State}.

exometer_setopts(_Metric, _Options, _Status, State) ->
    {ok, State}.

exometer_terminate(_, _State) ->
    ok.

%%
%% Helpers
%%


make_topic(Metric, Prefix) ->
    % io:format("~p~n", [ Metric ]),
    Topic = [ Prefix, [z_convert:to_binary(M) || M <- Metric]],
    join_topic(Topic).

make_topic(Metric, DataPoint, Prefix) ->
    Topic = [ Prefix, [z_convert:to_binary(M) || M <- Metric],
              z_convert:to_binary(DataPoint)],
    join_topic(Topic).

join_topic(Topic) ->
    join(lists:flatten(Topic), <<"/">>).

join(L, Sep) ->
    join(L, Sep, <<>>).

join([], _Sep, Acc) -> Acc;
join([B| Rest], Sep, <<>>) ->
    join(Rest, Sep, B);
join([B| Rest], Sep, Acc) ->
    join(Rest, Sep, <<Acc/binary, Sep/binary, B/binary>>).