Skip to content

Instantly share code, notes, and snippets.

@bernardd
Created December 19, 2016 06:07
Show Gist options
  • Save bernardd/ea03b2535b9d71a752ca215b7bda1d4b to your computer and use it in GitHub Desktop.
Save bernardd/ea03b2535b9d71a752ca215b7bda1d4b to your computer and use it in GitHub Desktop.
AWS CloudWatch reporter plugin for exometer
%% -------------------------------------------------------------------
%%
%% Copyright (c) 2016 Hippware Inc. All Rights Reserved.
%%
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at http://mozilla.org/MPL/2.0/.
%%
%% Required Parameters:
%%
%% access_key_id - An access key ID for an AWS user with appropriate
%% put_metrics permissions
%% secret_access_key - The secret access key for the same user
%% region - The region to which to report metrics
%% dimensions - At least one dimention pair ([{"Key", "Value"}]) on which to
%% report metrics
%%
%% Optional Parameters:
%%
%% host - Overrides the default CloudWatch host
%% namespace - Specify a namespace for reported metrics
%%
%% -------------------------------------------------------------------
-module(exometer_report_cloudwatch).
-behaviour(exometer_report).
%% exometer_report callbacks
-export(
[
exometer_init/1,
exometer_info/2,
exometer_cast/2,
exometer_call/3,
exometer_report/5,
exometer_subscribe/5,
exometer_unsubscribe/4,
exometer_newentry/2,
exometer_setopts/4,
exometer_terminate/2
]).
-import(exometer_util, [get_opt/3, get_opt/2]).
-define(DEFAULT_NS, "App/Exometer").
-define(aws_auth, 'Elixir.ExAws.Auth').
-record(state, {
host :: string(),
access_key_id :: string(),
secret_access_key :: string(),
region :: string(),
namespace :: string(),
dimensions :: [{string(), string()}]
}).
%%%===================================================================
%%% exometer_report callbacks
%%%===================================================================
exometer_init(Opts) ->
Region = get_opt(region, Opts),
DefaultHost = "monitoring." ++ Region ++ ".amazonaws.com",
State = #state{
host = get_opt(host, Opts, DefaultHost),
access_key_id = get_opt(access_key_id, Opts),
secret_access_key = get_opt(secret_access_key, Opts),
region = Region,
namespace = get_opt(namespace, Opts, ?DEFAULT_NS),
dimensions = get_opt(dimensions, Opts, [])
},
{ok, State}.
exometer_report(Probe, DataPoint, _Extra, Value,
State = #state{host = Host,
namespace = Namespace,
dimensions = Dimensions}) ->
Params =
[{"Action", "PutMetricData"},
{"Version", "2010-08-01"},
{"Namespace", Namespace},
{"MetricData.member.1.MetricName", name(Probe, DataPoint)},
{"MetricData.member.1.Value", value(Value)}
| make_dimensions(Dimensions)],
send_metric(make_url_and_headers(Host, Params, State)),
{ok, State}.
exometer_subscribe(_Metric, _DataPoint, _Extra, _Interval, State) ->
{ok, State}.
exometer_unsubscribe(_Metric, _DataPoint, _Extra, State) ->
{ok, State}.
exometer_call(_Unknown, _From, State) ->
{ok, State}.
exometer_cast(_Unknown, State) ->
{ok, State}.
exometer_info(_Unknown, State) ->
{ok, State}.
exometer_newentry(_Entry, State) ->
{ok, State}.
exometer_setopts(_Metric, _Options, _Status, State) ->
{ok, State}.
exometer_terminate(_, _) ->
ignore.
%%%===================================================================
%%% private helpers
%%%===================================================================
make_url_and_headers(Host, Params, State) ->
URL = ["http://", Host, "/?" | params(Params)],
Headers = ?aws_auth:headers(get, iolist_to_binary(URL),
monitoring, make_aws_config(State),
[], []),
{lists:flatten(URL), to_string_headers(Headers)}.
to_string_headers(Headers) ->
lists:map(fun({A, B}) -> {binary_to_list(A), binary_to_list(B)} end,
Headers).
params([Param|Tail]) ->
[param(Param) |
lists:map(fun(P) -> ["&", param(P)] end, Tail)].
param({Key, Val}) ->
[Key, "=", Val].
make_aws_config(#state{access_key_id = AccessKey,
secret_access_key = SecretKey,
region = Region}) ->
[{access_key_id, list_to_binary(AccessKey)},
{secret_access_key, list_to_binary(SecretKey)},
{region, list_to_binary(Region)}].
make_dimensions(Dimensions) ->
{Result, _} = lists:mapfoldl(fun make_dimension/2, 1, Dimensions),
lists:flatten(Result).
make_dimension({Key, Value}, Count) ->
Prefix = ["MetricData.member.1.Dimensions.member.",
integer_to_list(Count), "."],
{[{[Prefix, "Name"], Key},
{[Prefix, "Value"], Value}],
Count +1}.
datapoint(V) when is_integer(V) -> integer_to_list(V);
datapoint(V) when is_atom(V) -> atom_to_list(V).
%% Add value, int or float, converted to list
value(V) when is_integer(V) -> integer_to_list(V);
value(V) when is_float(V) -> float_to_list(V);
value(_) -> 0.
send_metric({URL, Headers}) ->
httpc:request(get,
{URL, Headers},
[], []).
name(Probe, DataPoint) ->
[[[metric_elem_to_list(I), $.] || I <- Probe], datapoint(DataPoint)].
metric_elem_to_list(V) when is_atom(V) -> atom_to_list(V);
metric_elem_to_list(V) when is_binary(V) -> binary_to_list(V);
metric_elem_to_list(V) when is_integer(V) -> integer_to_list(V);
metric_elem_to_list(V) when is_list(V) -> V.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment