rwdaigle / metrix

Elixir library to log custom application metrics, in a well-structured, human and machine readable format, for use by downstream log processing systems (Librato, Reimann, etc...)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Metrix global context conflicts when setting local context with a keyword list

jknipp opened this issue · comments

When trying to set the context locally for metrics on count with a keyword list, Metrix throws an error when merging the values with the global context. If the global context is removed, a keyword list is allowed and works as expected.

Global source configuration via config.exs

config :metrix,
  context: %{"source" => "my_source"}

Example Call

Metrics.count(source: "value", name)

Stack Trace

     stacktrace:
       (elixir) lib/keyword.ex:728: Keyword.update([], "source", "my_source", #Function<3.82657468/1 in Dict.do_merge/4>)
       (elixir) lib/keyword.ex:733: Keyword.update/4
       (elixir) lib/keyword.ex:733: Keyword.update/4
       (elixir) lib/dict.ex:293: anonymous fn/4 in Dict.do_merge/4
       (elixir) lib/enum.ex:3103: Enumerable.Map.do_reduce/3
       (elixir) lib/dict.ex:292: Dict.do_merge/4
       (metrix) lib/metrix.ex:87: Metrix.log/1
       (metrix) lib/metrix.ex:56: Metrix.count/3

This doesn't appear to be isolated to count.

After more research, I tracked this down to Dict.merge and how it merges keyword lists. If Strings are used as keys and the key does not exist in the map, Elixir throws an error.

Reversing the args to Dict.merge(%{"meta" => "data"}, [meta: "data"]}) works as expected and allows for us to override the global context locally (which we aren't doing now).

iex(4)> [meta: "data"] |> Dict.merge( %{})                                             
[meta: "data"]
iex(5)> [meta: "data"] |> Dict.merge( %{meta: "data_global"})    
[meta: "data_global"]
iex(6)> [meta: "data"] |> Dict.merge( %{"meta": "data_global"})
[meta: "data_global"]

iex(7)> [meta: "data"] |> Dict.merge( %{"meta" => "data_global"})
** (FunctionClauseError) no function clause matching in Keyword.update/4
    (elixir) lib/keyword.ex:692: Keyword.update([], "meta", "data_global", #Function<3.97265964/1 in Dict.do_merge/4>)
    (elixir) lib/keyword.ex:697: Keyword.update/4
    (elixir) lib/dict.ex:293: anonymous fn/4 in Dict.do_merge/4
    (elixir) lib/enum.ex:2865: Enumerable.Map.do_reduce/3
    (elixir) lib/dict.ex:292: Dict.do_merge/4

# Key does not exist, and only Strings are used as keys
iex(18)> [meta: "data"] |> Dict.merge( %{"other" => "data_global"})   
** (FunctionClauseError) no function clause matching in Keyword.update/4
    (elixir) lib/keyword.ex:692: Keyword.update([], "other", "data_global", #Function<3.97265964/1 in Dict.do_merge/4>)
    (elixir) lib/keyword.ex:697: Keyword.update/4
    (elixir) lib/dict.ex:293: anonymous fn/4 in Dict.do_merge/4
    (elixir) lib/enum.ex:2865: Enumerable.Map.do_reduce/3
    (elixir) lib/dict.ex:292: Dict.do_merge/4