Packages

Monitoring app that exports Erlang VM introspection data to Kafka

Current section

Files

Jump to
system_monitor src system_monitor_kafka.erl
Raw

src/system_monitor_kafka.erl

%%--------------------------------------------------------------------------------
%% Copyright 2020 Klarna Bank AB
%%
%% 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.
%%--------------------------------------------------------------------------------
%%% @doc
%%% Push monitoring events to Kafka
%%% @end
-module(system_monitor_kafka).
-behaviour(gen_server).
-include("sysmon_kafka.hrl").
-include("system_monitor.hrl").
-include_lib("brod/include/brod.hrl").
%% API
-export([start_link/0, produce/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state,
{ topic :: brod:topic()
}).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Best effort attempt to push event to Kafka.
%% @end
%%--------------------------------------------------------------------
-spec produce(tuple()) -> ok.
produce(Event) ->
gen_server:cast(?SERVER, {produce, Event}).
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%% @end
%%--------------------------------------------------------------------
-spec start_link() ->
{ok, pid()} | ignore | {error, term()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
{ok, Topic} = application:get_env(?APP, kafka_topic),
State = #state{topic = Topic},
gen_server:cast(?SERVER, post_init),
{ok, State}.
handle_call(_Msg, _From, State) ->
{reply, {error, bad_call}, State}.
handle_cast(post_init, State) ->
start_client(),
{noreply, State};
handle_cast({produce, Term}, State) ->
Payload = term_to_binary(Term),
publish(State, Payload),
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
stop_client().
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% (Re)start kafka client
%% @end
%%--------------------------------------------------------------------
-spec start_client() -> ok.
start_client() ->
stop_client(),
{ok, KafkaHosts} = application:get_env(?APP, kafka_hosts),
case KafkaHosts of
[] ->
ok;
_ ->
{ok, ClientConfig} = application:get_env(?APP, kafka_client_config),
ProducerConfig = [ {required_acks, 0}
, {retries, 0}
, {max_linger_ms, 1000}
, {max_linger_count, 100}
, {topic_restart_delay_seconds, 5}
],
Ret = brod:start_client( KafkaHosts
, ?SYSMON_KAFKA_CLIENT
, [ {auto_start_producers, true}
, {default_producer_config, ProducerConfig}
| ClientConfig]
),
case Ret of
ok ->
ok;
{error, {already_started, _}} ->
ok;
Error ->
error_logger:warning_msg( "~p:start_client Failed to start "
"kafka client: ~p~n"
, [?MODULE, Error])
end
end.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Ensure brod client stopped
%% @end
%%--------------------------------------------------------------------
-spec stop_client() -> ok.
stop_client() ->
brod:stop_client(?SYSMON_KAFKA_CLIENT).
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Push message to kafka
%%
%% @end
%%--------------------------------------------------------------------
-spec publish(#state{}, binary()) -> ok.
publish(State, Payload) ->
brod:produce( ?SYSMON_KAFKA_CLIENT
, State#state.topic
, fun part_fun/4
, <<"">>
, Payload
),
ok.
part_fun(_Topic, PartCount, Key, _Value) ->
{ok, erlang:phash2(Key, PartCount)}.
%%%_* Emacs ============================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2
%%% End: