seeplusplus

seeplusplus

Using Telemetry.Metrics counter/2 and :telemetry in general

I’m trying to get started exporting custom app metrics to prometheus on Fly. This is my first time using any Telemetry packages in Elixir, so any tips or suggestions on how to improve this are welcome.

I’m using the Telemetry.Metrics package (default with Phoenix, I think?) and telemetry_metrics_prometheus to export the metrics. I think the latter was necessary to have an endpoint that prometheus could scrape.

My application has a custom cache and I am trying to measure the hits vs misses. So here’s how I have MyApplicationWeb.Telemetry.metrics/0:

def metrics() do
[
      # a ton of default `summary`s that came out of the box,
      last_value("vm.memory.total", unit: {:byte, :kilobyte}), # added for testing
      counter("my_application.some_mod.cache.hit"),
      counter("my_application.some_mod.cache.miss")
]
end

My application.ex has my Telemetry module and TelemetryMetricsPrometheus in the children list for its Supervisor:

children = [
      MyApplicationWeb.Telemetry,
      {TelemetryMetricsPrometheus, [metrics: MyApplicationWeb.Telemetry.metrics()]},
      # Rest of children
    ]

And the code in the caching module:

def contains?(word) do
    with term when term != nil <- Repo.get_by(Term, word: word) do
      Logger.debug("cache hit")
      :telemetry.execute([:my_application, :some_mod, :cache], %{hit: 1}, %{})
      term.is_valid
    else
      nil ->
        Logger.debug("cache miss")
        is_valid? = Inclusive.contains?(word)
        :telemetry.execute([:my_application, :some_mod, :cache], %{miss: 1}, %{})
        add_to_cache(word, is_valid?)
        is_valid?
    end
  end

I have tested this. The metrics seem to work in the Phoenix Live Dashboard as expected correctly, but when I hit the /metrics endpoint provided by TelemetryMetricsPrometheus the hit and miss counters always increment in lockstep. I can read the logs and verify there have been no cache hits, but hit will always go up at the same time as misses.

Have I configured these metrics correctly? Am I using :telemetry.execute properly? I didn’t find the docs for these packages to be very clear, sadly.

Here are some examples of the graphs of my metrics in the dashboard:


at the same time if I scrape the /metrics endpoint, it looks like the counters are being summed?

my_application_some_mod_cache_miss 8
my_application_some_mod_cache_hit 8
vm_memory_total 79445.464

Most Liked

seeplusplus

seeplusplus

I found the issue. I’ll document in case anyone else runs into this. This was mostly due to me failing to read and understand the docs for :telemetry_metrics.

From the docs:

The first argument to all metric functions is the metric name. Metric name can be provided as a string (e.g. "http.request.stop.duration") or a list of atoms ([:http, :request, :stop, :duration]). The metric name is automatically used to infer the telemetry event and measurement. For example, In the "http.request.stop.duration" example, the source event name is [:http, :request, :stop] and metric values are drawn from :duration measurement. Like this:

[:http , :request, :stop]      :duration
<----- event name ------> <-- measurement -->

Source.

In configuring my counters as:

counter("my_application.some_mod.cache.hit"),
counter("my_application.some_mod.cache.miss")

I had mistakenly configured two counters, both of which were counting the total number of my_application.some_mod.cache events. The end result: every time I did a :telemetry.execute([:my_application, :some_mod, :cache], %{miss: 1}, %{}), I was incrementing both counters. The fix, two different measurements on two different event names:

counter("my_application.some_mod.cache.hit.count"),
counter("my_application.some_mod.cache.miss.count")

Where I increment with,

# :miss or :hit below
# value: 1 is meaningless, it can be anything, e.g., %{count:1} - I think it can just be an empty map.
:telemetry.execute([:my_application, :some_mod, :cache, :miss], %{value: 1}, %{})

I ended up finding this by cloning the of the prometheus telemetry exporter locally and adding some console logs on the telemetry dispatcher. I’m not sure why the incorrect config rendered “correctly” (read: as I expected it to) in the Phoenix LiveDashboard.

dimitarvp

dimitarvp

This is great. Instant bookmark with tagging for me.

Where Next?

Popular in Questions Top

ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mathew4509
I have a list say x = ["23gh", "56kh", "97mh"] I would like to pass each element to Val in each iteration. Say, in iteration 1 -------...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement