Slide 1

Slide 1 text

What is telemetry all about by Qing Wu

Slide 2

Slide 2 text

What's covered • Why telemetry • Journey to understanding • Further references

Slide 3

Slide 3 text

Background ! Ecto 3 moved my cheese!

Slide 4

Slide 4 text

In Ecto 2 config :my_app, MyApp.Repo, url: System.get_env("DATABASE_URL"), loggers: [Ecto.LogEntry]

Slide 5

Slide 5 text

Custom Logger for Ecto 2 config :my_app, MyApp.Repo, url: System.get_env("DATABASE_URL"), loggers: [Ecto.LogEntry, {SpandexEcto.EctoLogger, :trace, ["my_repo"]}]

Slide 6

Slide 6 text

Telemetry Dynamic dispatching library for metrics and instrumentations

Slide 7

Slide 7 text

Telemetry telemetry allows you to invoke certain functions whenever a particular event is emitted.

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Telemetry API attach(HandlerId, EventName, Function, Config) -> ok | {error, already_exists} HandlerId = handler_id() EventName = event_name() Function = handler_function() Config = handler_config() execute(EventName, Measurements, Metadata) -> ok EventName = event_name() Measurements = event_measurements() | event_value() Metadata = event_metadata()

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Leap year defmodule Leapyear do def leapyear?(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end end

Slide 13

Slide 13 text

Leap year defmodule Leapyear do def leapyear?(year) when is_integer(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end end

Slide 14

Slide 14 text

Leap year defmodule Leapyear do require Logger def leapyear?(year) when is_integer(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end def leapyear?(year) do Logger.warn("Invalid year: #{inspect(year)}") false end end

Slide 15

Slide 15 text

Sharing as hex package def deps do [ {:leapyear, "~> 0.1.0"} ] end

Slide 16

Slide 16 text

Sharing as hex package • hex.pm: https:/ /hex.pm/packages/leapyear • https:/ /github.com/wiserfirst/leapyear

Slide 17

Slide 17 text

! defmodule Leapyear do require Logger def leapyear?(year) when is_integer(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end def leapyear?(year) do Logger.warn("Invalid year: #{inspect(year)}") Appsignal.send_error(%RuntimeError{}, "Invalid year: #{inspect(year)}") false end end

Slide 18

Slide 18 text

defmodule Leapyear do require Logger def leapyear?(year, _) when is_integer(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end def leapyear?(year, handler) do Logger.warn("Invalid year: #{inspect(year)}") handler.("Invalid year: #{inspect(year)}") false end end

Slide 19

Slide 19 text

handler = fn msg -> Appsignal.send_error(%RuntimeError{}, msg) end # client code Leapyear.leapyear?(2009, handler) Leapyear.leapyear?("whatever", handler)

Slide 20

Slide 20 text

Ad hoc function passing

Slide 21

Slide 21 text

Ad hoc function passing • Damage to library code !

Slide 22

Slide 22 text

Ad hoc function passing • Damage to library code ! • Damage to client code "

Slide 23

Slide 23 text

Ad hoc function passing • Damage to library code ! • Damage to client code " • Consistency #

Slide 24

Slide 24 text

What do we want here? • data from the library • function from end user • clean API

Slide 25

Slide 25 text

With telemetry defmodule Leapyear do def leapyear?(year) when is_integer(year) do (rem(year, 4) == 0 and rem(year, 100) != 0) or rem(year, 400) == 0 end def leapyear?(year) do metrics = %{message: "Invalid year: #{inspect(year)}} :telemetry.execute([:leapyear, :invalid_input], metrics) false end end

Slide 26

Slide 26 text

# in application.ex :telemetry.attach( "leapyear_events", [:leapyear, :invalid_input], &MyApp.TelemetryAdapter.handler/4, nil ) Leapyear.leapyear?(2009) Leapyear.leapyear?("whatever")

Slide 27

Slide 27 text

defmodule MyApp.TelemetryAdapter do def handler([:leapyear, :invalid_input], %{message: message}, _meta, _config) Logger.warn(message) Appsignal.send_error(%RuntimeError{}, message) end end

Slide 28

Slide 28 text

Example for Ecto and Datadog # in application.ex :telemetry.attach( "spandex-ecto", [:my_app, :repo, :query], &SpandexEcto.TelemetryAdapter.handle_event/4, nil )

Slide 29

Slide 29 text

Example for Ecto and Datadog defmodule SpandexEcto.TelemetryAdapter do alias SpandexEcto.EctoLogger def handle_event([_, repo_name, :query], measurements, metadata, _config) do log_entry = %{ query: metadata.query, source: metadata.source, params: metadata.params, query_time: Map.get(measurements, :query_time, 0), decode_time: Map.get(measurements, :decode_time, 0), queue_time: Map.get(measurements, :queue_time, 0), result: wrap_result(metadata.result) } EctoLogger.trace(log_entry, "#{repo_name}_database") end end

Slide 30

Slide 30 text

Summary • Recommended for library author • Overkill for application code?

Slide 31

Slide 31 text

References Official Documentation Telemetry and metrics for all by Arkadiusz Gil The "How"s, "What"s, and "Why"s of Elixir Telemetry

Slide 32

Slide 32 text

Thank you! @wiserfirst