Skip to content

Commit

Permalink
Replace Statix with Prometheus
Browse files Browse the repository at this point in the history
Prometheus pull method of metrics gathering suits better such
application as it doesn't require gatherer to be configure always. It
also provides metrics in much more "standardized" way as
prometheus_phoenix and prometheus_plugs are quite norm when coming to
the respective library metrics gathering.

There are also predefined Grafana's dashboards that can be found there:

https://github.com/deadtrickster/beam-dashboards
  • Loading branch information
hauleth committed Oct 9, 2018
1 parent 503f581 commit 1cb3619
Show file tree
Hide file tree
Showing 18 changed files with 125 additions and 135 deletions.
6 changes: 1 addition & 5 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use Mix.Config
config :imager, ImagerWeb.Endpoint,
url: [host: "localhost"],
render_errors: [view: ImagerWeb.Views.Error, accepts: ~w(json)],
instrumenters: [ImagerWeb.Instrumenter]
instrumenters: [Prometheus.PhoenixInstrumenter]

# Configures Elixir's Logger
config :logger, :console,
Expand All @@ -21,10 +21,6 @@ config :phoenix, :format_encoders, json: Jason
config :ex_aws,
json_codec: Jason

config :vmstats,
sink: Imager.Stats,
base_key: "erlang"

config :sentry,
included_environments: [:prod],
release: Mix.Project.config()[:version],
Expand Down
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ config :logger, :console, format: "[$level] $message\n"
config :imager, :stores, %{
"local" => %{
store: {Imager.Store.Local, dir: "test/fixtures/"},
cache: {Imager.Store.Local, dir: "tmp/cache/"}
cache: {Imager.Store.Blackhole, []}
}
}

Expand Down
17 changes: 7 additions & 10 deletions lib/imager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ defmodule Imager do

require Logger

alias Imager.Stats
alias Imager.Store
alias Imager.Tool
alias Imager.Instrumenter

@type size :: non_neg_integer() | :unknown
@type mime :: binary()
Expand All @@ -24,25 +24,22 @@ defmodule Imager do
def process(store, file_name, commands, opts \\ [])

def process(%{store: store}, file_name, [], opts) do
Stats.increment("imager.passthrough", 1, tags: Stats.tags())
Instrumenter.Cache.passthrough(store)

Store.retrieve(store, file_name, opts)
end

def process(%{store: store, cache: cache}, file_name, commands, opts) do
{mime, result_name} = Tool.result(file_name, commands)
args = Tool.build_command(commands)
tags = Stats.tags(~w(
store:#{elem(store, 0)}
cache:#{elem(cache, 0)}
))

Instrumenter.Processing.command(commands)

Logger.metadata(input: file_name, commands: inspect(commands))

with :error <- Store.retrieve(cache, result_name, opts),
{:ok, {_, _, stream}} <- Store.retrieve(store, file_name, opts) do
Logger.debug("Start processing")
Stats.increment("imager.process.started", 1, tags: tags)

process =
Porcelain.spawn(executable(), args,
Expand All @@ -53,7 +50,7 @@ defmodule Imager do

case Porcelain.Process.await(process) do
{:ok, %{status: 0}} ->
Stats.increment("imager.process.succeeded", 1, tags: tags)
Instrumenter.Processing.succeeded(store)

stream =
process.out
Expand All @@ -62,15 +59,15 @@ defmodule Imager do
{:ok, {:unknown, mime, stream}}

_ ->
Stats.increment("imager.process.failed", 1, tags: tags)
Instrumenter.Processing.failed(store)
Logger.error(process.err)

:failed
end
else
{:ok, {_, _, _}} = result ->
Logger.debug("Cache hit")
Stats.increment("imager.process.cache_hit", 1, tags: tags)
Instrumenter.Cache.cache_hit(cache)

result

Expand Down
4 changes: 3 additions & 1 deletion lib/imager/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Imager.Application do

use Application

require Prometheus.Registry

def start(_type, _args) do
children = [
ImagerWeb.Endpoint
Expand All @@ -14,8 +16,8 @@ defmodule Imager.Application do
Application.get_env(:imager, :sentry_dsn)
)

Imager.Stats.start()
{:ok, _} = Logger.add_backend(Sentry.LoggerBackend)
Prometheus.Registry.register_collector(:prometheus_process_collector)

opts = [strategy: :one_for_one, name: Imager.Supervisor]
Supervisor.start_link(children, opts)
Expand Down
15 changes: 15 additions & 0 deletions lib/imager/instrumenter/cache.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Imager.Instrumenter.Cache do
use Prometheus.Metric

@counter [
name: :cache_hits_total,
help: "Count of cached images hits",
labels: [:type, :store]
]

def passthrough({store, _}),
do: Counter.inc(name: :cache_hits_total, labels: [:passthrough, store])

def cache_hit({store, _}),
do: Counter.inc(name: :cache_hits_total, labels: [:cache, store])
end
29 changes: 29 additions & 0 deletions lib/imager/instrumenter/processing.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule Imager.Instrumenter.Processing do
use Prometheus.Metric

@counter [
name: :processed_total,
help: "Count of processed images",
labels: [:status, :store]
]
@counter [
name: :process_commands_total,
help: "Processing commands defined",
labels: [:command, :argument]
]

def succeeded({store, _}),
do: Counter.inc(name: :processed_total, labels: ["ok", store])

def failed({store, _}),
do: Counter.inc(name: :processed_total, labels: ["failed", store])

def command({command, argument}) do
Counter.inc(
name: :process_commands_total,
labels: [command, argument]
)
end

def command(list) when is_list(list), do: Enum.each(list, &command/1)
end
37 changes: 37 additions & 0 deletions lib/imager/instrumenter/storage.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Imager.Instrumenter.Storage do
use Prometheus.Metric

@counter [
name: :store_retrieved_total,
help: "Count of retrieves from the given store",
labels: [:store]
]
@counter [
name: :store_saved_total,
help: "Count of saves into the given store",
labels: [:store]
]

@summary [
name: :store_retrieved_bytes,
help: "Bytes retrieved from the store",
labels: [:store]
]
@summary [
name: :store_saved_bytes,
help: "Saved bytes to given store",
labels: [:store]
]

def retrieved(stream, store) do
Counter.inc(name: :store_retrieved_total, labels: [store])

Stream.each(stream, &Summary.observe([name: :store_retrieved_bytes, labels: [store]], byte_size(&1)))
end

def saved(stream, store) do
Counter.inc(name: :store_saved_total, labels: [store])

Stream.each(stream, &Summary.observe([name: :store_saved_bytes, labels: [store]], byte_size(&1)))
end
end
42 changes: 0 additions & 42 deletions lib/imager/stats.ex

This file was deleted.

14 changes: 5 additions & 9 deletions lib/imager/store.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Imager.Store do
in application.
"""

alias Imager.Stats
alias Imager.Instrumenter

@type size :: non_neg_integer()
@type stream :: Enumerable.t()
Expand All @@ -31,21 +31,17 @@ defmodule Imager.Store do
@spec retrieve(store, binary, keyword) ::
{:ok, {size, mime, stream}} | :error
def retrieve({store, glob_opts}, path, options) do
Stats.increment(
"imager.store.retrieve",
1,
Stats.tags(~w(module:#{store}))
)

store.retrieve(path, Keyword.merge(glob_opts, options))
with {:ok, {size, mime, stream}} <-
store.retrieve(path, Keyword.merge(glob_opts, options)),
do: {:ok, {size, mime, Instrumenter.Storage.retrieved(stream, store)}}
end

@doc """
Save file in store.
"""
@spec store(stream, store, mime, binary, keyword) :: stream
def store(stream, {store, glob_opts}, mime, path, options) do
Stats.increment("imager.store.store", 1, Stats.tags(~w(module:#{store})))
store.store(path, mime, stream, Keyword.merge(glob_opts, options))
|> Instrumenter.Storage.saved(store)
end
end
4 changes: 3 additions & 1 deletion lib/imager_web/controllers/health.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ defmodule ImagerWeb.Controllers.Health do
def get(conn, _params) do
json(conn, %{
status: "pass",
version: Imager.Stats.version(),
version: version(),
description: @description
})
end

defp version, do: Application.spec(:imager, :vsn) |> List.to_string()
end
7 changes: 7 additions & 0 deletions lib/imager_web/endpoint.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
defmodule ImagerWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :imager
# use Prometheus.PlugPipelineInstrumenter
use Sentry.Phoenix.Endpoint

plug(ImagerWeb.Plug.PipelineInstrumenter)
plug(ImagerWeb.Plug.MetricsExporter)

# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
Expand All @@ -27,6 +31,9 @@ defmodule ImagerWeb.Endpoint do
configuration should be loaded from the system environment.
"""
def init(_key, config) do
ImagerWeb.Plug.PipelineInstrumenter.setup()
ImagerWeb.Plug.MetricsExporter.setup()

port =
System.get_env("PORT") || Application.get_env(:imager, :port) ||
raise "expected the PORT environment variable to be set"
Expand Down
30 changes: 0 additions & 30 deletions lib/imager_web/instrumenter.ex

This file was deleted.

3 changes: 3 additions & 0 deletions lib/imager_web/plug/metrics_exporter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule ImagerWeb.Plug.MetricsExporter do
use Prometheus.PlugExporter
end
3 changes: 3 additions & 0 deletions lib/imager_web/plug/pipeline_instrumenter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
defmodule ImagerWeb.Plug.PipelineInstrumenter do
use Prometheus.PlugPipelineInstrumenter
end
9 changes: 5 additions & 4 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ defmodule Imager.Mixfile do
compilers: [:phoenix] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
deps: deps(),
dialyzer: [plt_add_apps: [:vmstats]],
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
Expand All @@ -36,7 +35,7 @@ defmodule Imager.Mixfile do
[
mod: {Imager.Application, []},
extra_applications: [:logger, :inets, :runtime_tools],
included_applications: [:vmstats]
included_applications: []
]
end

Expand All @@ -53,13 +52,15 @@ defmodule Imager.Mixfile do
{:phoenix_pubsub, "~> 1.0"},
{:cowboy, "~> 1.0"},
{:distillery, "~> 2.0"},
{:statix, ">= 0.0.0"},
{:prometheus_ex, "~> 3.0"},
{:prometheus_phoenix, "~> 1.2.0"},
{:prometheus_plugs, "~> 1.1.1"},
{:prometheus_process_collector, github: "deadtrickster/prometheus_process_collector", override: true, manager: :rebar3},
{:ex_aws, "~> 2.0"},
{:ex_aws_s3, "~> 2.0"},
{:hackney, "~> 1.9"},
{:sweet_xml, "~> 0.6"},
{:jason, ">= 0.0.0"},
{:vmstats, "~> 2.2", runtime: false},
{:sentry, "~> 7.0"},
{:porcelain, "~> 2.0.3"},
{:jose, "~> 1.8"},
Expand Down

0 comments on commit 1cb3619

Please sign in to comment.