[#11] adding Stats Collector
This commit is contained in:
parent
1815f1aabe
commit
16acef3f1a
|
@ -7,9 +7,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
|
|||
mining?: boolean,
|
||||
hashrate: non_neg_integer,
|
||||
peers: non_neg_integer,
|
||||
pending: non_neg_integer,
|
||||
gas_price: Literal.Decimal.t(),
|
||||
block: POAAgent.Entity.Ethereum.Block.t(),
|
||||
syncing?: boolean,
|
||||
uptime: non_neg_integer
|
||||
}
|
||||
|
@ -19,9 +17,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
|
|||
mining?: nil,
|
||||
hashrate: nil,
|
||||
peers: nil,
|
||||
pending: nil,
|
||||
gas_price: nil,
|
||||
block: %POAAgent.Entity.Ethereum.Block{},
|
||||
syncing?: nil,
|
||||
uptime: nil
|
||||
]
|
||||
|
@ -34,8 +30,7 @@ defmodule POAAgent.Entity.Ethereum.Statistics do
|
|||
gas_price: :gasPrice,
|
||||
syncing?: :syncing
|
||||
]
|
||||
x = Enum.reduce(mapping, x, &POAAgent.Entity.Name.change/2)
|
||||
Map.update!(x, :block, &POAAgent.Entity.NameConvention.from_elixir_to_node/1)
|
||||
Enum.reduce(mapping, x, &POAAgent.Entity.Name.change/2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -81,11 +81,14 @@ defmodule POAAgent.Plugins.Collector do
|
|||
@doc """
|
||||
A callback executed when the Collector Plugin starts.
|
||||
The argument is retrieved from the configuration file when the Collector is defined
|
||||
It must return `{:ok, state}`, that `state` will be keept as in `GenServer` and can be
|
||||
It can return `{:ok, state}`, that `state` will be keept as in `GenServer` and can be
|
||||
retrieved in the `collect/1` function.
|
||||
There are some cases where we want to send data to the transfer after initialize the
|
||||
Collector, if that is the case you must return `{:transfer, data, state}` where the data is the
|
||||
metrics we want to send to the transfer
|
||||
"""
|
||||
@callback init_collector(args :: term()) ::
|
||||
{:ok, any()}
|
||||
@callback init_collector(args :: term()) :: {:ok, state :: any()}
|
||||
| {:transfer, data :: any(), state :: any()}
|
||||
|
||||
@doc """
|
||||
In this callback is where the metrics collection logic must be placed.
|
||||
|
@ -113,7 +116,14 @@ defmodule POAAgent.Plugins.Collector do
|
|||
|
||||
@doc false
|
||||
def init(state) do
|
||||
{:ok, internal_state} = init_collector(state[:args])
|
||||
internal_state =
|
||||
case init_collector(state[:args]) do
|
||||
{:ok, internal_state} ->
|
||||
internal_state
|
||||
{:transfer, _, _} = transfer ->
|
||||
transfer |> transfer(state.label, state.transfers)
|
||||
end
|
||||
|
||||
set_collector_timer(state.frequency)
|
||||
{:ok, Map.put(state, :internal_state, internal_state)}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
defmodule POAAgent.Plugins.Collectors.Eth.Stats do
|
||||
use POAAgent.Plugins.Collector
|
||||
|
||||
@moduledoc """
|
||||
This is a Collector's Plugin which makes requests to a Ethereum node in order to know if
|
||||
the stats has been changed.
|
||||
|
||||
This Collector needs the url of the node to iteract. That url must be placed in the args field
|
||||
in the config file. For example:
|
||||
|
||||
{:eth_stats, POAAgent.Plugins.Collectors.Eth.Stats, 5000, :eth_stats, [url: "http://localhost:8545"]}
|
||||
|
||||
In this example, the Collector will check with the Ethereum node every 5 seconds if the stats of the
|
||||
node has changed. If that is the case it will send it to the Transfers
|
||||
encapsulated in a `POAAgent.Entity.Ethereum.Statistics` struct
|
||||
|
||||
"""
|
||||
|
||||
@typep internal_state :: %{last_stats: POAAgent.Entity.Ethereum.Statistics.t | nil,
|
||||
tries: non_neg_integer,
|
||||
down: non_neg_integer}
|
||||
|
||||
@doc false
|
||||
@spec init_collector(term()) :: {:ok, internal_state()}
|
||||
def init_collector(args) do
|
||||
:ok = config(args)
|
||||
|
||||
case get_stats(0, 0) do
|
||||
{:ok, stats, tries, down} ->
|
||||
{:transfer, stats, %{last_stats: stats, tries: tries, down: down}}
|
||||
{:error, tries, down} ->
|
||||
{:ok, %{last_stats: nil, tries: tries, down: down}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec collect(internal_state()) :: term()
|
||||
def collect(%{last_stats: last_stats, tries: tries, down: down} = state) do
|
||||
case get_stats(tries, down) do
|
||||
{:ok, ^last_stats, tries, down} ->
|
||||
{:notransfer, %{state | tries: tries, down: down}}
|
||||
{:ok, stats, tries, down} ->
|
||||
{:transfer, stats, %{state | last_stats: stats, tries: tries, down: down}}
|
||||
{:error, tries, down} ->
|
||||
{:notransfer, %{state | tries: tries, down: down}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec terminate(internal_state()) :: :ok
|
||||
def terminate(_state) do
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc false
|
||||
defp config([url: url]) do
|
||||
Application.put_env(:ethereumex, :url, url)
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc false
|
||||
defp get_stats(tries, down) do
|
||||
tries = tries + 1
|
||||
|
||||
with {:ok, peers} <- Ethereumex.HttpClient.net_peer_count(),
|
||||
{:ok, mining} <- Ethereumex.HttpClient.eth_mining(),
|
||||
{:ok, hashrate} <- Ethereumex.HttpClient.eth_hashrate(),
|
||||
{:ok, syncing} <- Ethereumex.HttpClient.eth_syncing(),
|
||||
{:ok, gas_price} <- Ethereumex.HttpClient.eth_gas_price()
|
||||
do
|
||||
peers = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(peers))
|
||||
hashrate = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(hashrate))
|
||||
gas_price = String.to_integer(POAAgent.Format.Literal.Hex.decimalize(gas_price))
|
||||
|
||||
down =
|
||||
if peers == 0 do
|
||||
down + 1
|
||||
else
|
||||
down
|
||||
end
|
||||
|
||||
stats =
|
||||
%POAAgent.Entity.Ethereum.Statistics{
|
||||
active?: peers > 0,
|
||||
mining?: mining,
|
||||
hashrate: hashrate,
|
||||
peers: peers,
|
||||
gas_price: gas_price,
|
||||
syncing?: syncing,
|
||||
uptime: uptime(tries, down)
|
||||
}
|
||||
|
||||
{:ok, stats, tries, down}
|
||||
else
|
||||
_error -> {:error, tries, down}
|
||||
end
|
||||
end
|
||||
|
||||
defp uptime(tries, down) do
|
||||
((tries - down) / tries) * 100
|
||||
end
|
||||
|
||||
end
|
3
mix.exs
3
mix.exs
|
@ -47,7 +47,8 @@ defmodule POAAgent.MixProject do
|
|||
POAAgent.Plugins.Transfer,
|
||||
],
|
||||
"Ethereum Plugins": [
|
||||
POAAgent.Plugins.Collectors.Eth.LatestBlock
|
||||
POAAgent.Plugins.Collectors.Eth.LatestBlock,
|
||||
POAAgent.Plugins.Collectors.Eth.Stats
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
defmodule POAAgent.Plugins.Collectors.Eth.StatsTest do
|
||||
use ExUnit.Case
|
||||
import Mock
|
||||
|
||||
test "stats sent to the transfer" do
|
||||
with_mock Ethereumex.HttpClient, [
|
||||
net_peer_count: fn() -> {:ok, "0x3"} end,
|
||||
eth_mining: fn() -> {:ok, false} end,
|
||||
eth_hashrate: fn() -> {:ok, "0x0"} end,
|
||||
eth_syncing: fn() -> {:ok, syncing()} end,
|
||||
eth_gas_price: fn() -> {:ok, "0x0"} end
|
||||
] do
|
||||
|
||||
{:transfer, stats, _} = POAAgent.Plugins.Collectors.Eth.Stats.collect(%{last_stats: nil, tries: 0, down: 0})
|
||||
assert stats == expected_stats()
|
||||
end
|
||||
end
|
||||
|
||||
def syncing() do
|
||||
%{
|
||||
"currentBlock" => "0x44ee0",
|
||||
"highestBlock" => "0x44ee2",
|
||||
"startingBlock" => "0x40ad3",
|
||||
"warpChunksAmount" => nil,
|
||||
"warpChunksProcessed" => nil
|
||||
}
|
||||
end
|
||||
|
||||
def expected_stats() do
|
||||
%POAAgent.Entity.Ethereum.Statistics{
|
||||
active?: true,
|
||||
gas_price: 0,
|
||||
hashrate: 0,
|
||||
mining?: false,
|
||||
peers: 3,
|
||||
syncing?: syncing(),
|
||||
uptime: 100.0}
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue