Skip to content

Commit

Permalink
initial impl for connection telemetry (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
the-mikedavis authored Feb 3, 2021
1 parent 049901d commit 8f77ea4
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 15 deletions.
1 change: 1 addition & 0 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
{Credo.Check.Warning.ApplicationConfigInModuleAttribute, false},

#
# Controversial and experimental checks (opt-in, just remove `, false`)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a
Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.1.0 - 2021-02-03

### Added

- Initial implementation for publishing connection telemetry

## 0.0.0 - 2021-02-03

### Added
Expand Down
4 changes: 4 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use Mix.Config

if Mix.env() == :test do
import_config "test.exs"
end

# Generated by Elixir.Gaas.Generators.Simple.ConfigConfig
6 changes: 6 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use Mix.Config

config :phoenix, json_library: Jason

config :slipstream_honeycomb,
honeycomb_sender: HoneycombSenderMock
104 changes: 104 additions & 0 deletions lib/slipstream/honeycomb/connection.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
defmodule Slipstream.Honeycomb.Connection do
@moduledoc """
A GenServer that collects telemetry events from Slipstream connections and
emits them to Honeycomb
"""

@sender Application.get_env(
:slipstream_honeycomb,
:honeycomb_sender,
Opencensus.Honeycomb.Sender
)

alias Opencensus.Honeycomb.Event

@event_names [
~w[slipstream connection connect stop]a,
~w[slipstream connection handle stop]a
]

use GenServer

@doc false
def start_link(_args) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end

@doc false
@impl GenServer
def init(state) do
{:ok, state, {:continue, :telemetry_attach}}
end

@doc false
@impl GenServer
def handle_continue(:telemetry_attach, state) do
:telemetry.attach_many(
"slipstream-honeycomb-connection-exporter",
@event_names,
&handle_event/4,
state
)

{:noreply, state}
end

def handle_event(event, measurements, metadata, _state) do
GenServer.cast(__MODULE__, {event, measurements, metadata})
end

@impl GenServer
def handle_cast({event, measurements, metadata}, state) do
{event, measurements, metadata}
|> map_to_event()
|> send_event()

{:noreply, state}
end

defp map_to_event(
{~w[slipstream connection connect stop]a, %{duration: duration},
metadata}
) do
%Event{
time: metadata.start_time,
data: %{
state: inspect(metadata.state),
traceId: metadata.trace_id,
id: metadata.connection_id,
durationMs: convert_time(duration)
}
}
end

defp map_to_event(
{~w[slipstream connection handle stop]a, %{duration: duration},
metadata}
) do
%Event{
time: metadata.start_time,
data: %{
state: inspect(metadata.state),
traceId: metadata.trace_id,
parentId: metadata.connection_id,
id: metadata.span_id,
durationMs: convert_time(duration),
raw_message: inspect(metadata.raw_message),
message: inspect(metadata.message),
events: inspect(metadata.events),
built_events: inspect(metadata.built_events),
return: metadata.return
}
}
end

defp send_event(event) do
@sender.send_batch([event])
end

defp convert_time(time) do
# nanoseconds but with decimals!
# if we were to convert directly to msec, it'd be an integer :(
System.convert_time_unit(time, :native, :microsecond) / 1_000
end
end
7 changes: 0 additions & 7 deletions lib/slipstream_honeycomb.ex

This file was deleted.

10 changes: 7 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ defmodule SlipstreamHoneycomb.MixProject do
]
end

defp elixirc_paths(:test), do: ["lib", "test/fixtures"]
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

def application do
Expand All @@ -43,11 +43,15 @@ defmodule SlipstreamHoneycomb.MixProject do

defp deps do
[
{:slipstream, "~> 0.3"},
{:telemetry, "~> 0.4"},
{:opencensus_honeycomb, "~> 0.2.0"},
# docs
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
# test
{:bless, "~> 1.0"},
{:convene, "~> 0.2", organization: "cuatro", only: [:dev, :test]},
{:bless, "~> 1.0", only: [:dev, :test]},
{:mox, "~> 1.0", only: :test},
{:credo, "~> 1.0", only: [:dev, :test]},
{:excoveralls, "~> 0.7", only: :test}
]
end
Expand Down
16 changes: 16 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,36 @@
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.5.3", "70bdd7e7188c804f3a30ee0e7c99655bc35d8ac41c23e12325f36ab449b70651", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "ed516acb3929b101208a9d700062d520f3953da3b6b918d866106ffa980e1c10"},
"convene": {:hex, :convene, "0.2.2", "2495b032aff9a8d5b87b5db83c64e65a90e61d438dda2f72fc951d2093ceaea3", [:mix], [{:credo, "~> 1.4", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm:cuatro", "83e0a113b38315091e5d65fc12f02f1f518b317eee9bf9dfe3dd01efb64ece51"},
"counters": {:hex, :counters, "0.2.1", "aa3d97e88f92573488987193d0f48efce0f3b2cd1443bf4ee760bc7f99322f0c", [:mix, :rebar3], [], "hexpm", "a020c714992f7db89178d27e1f39909e5d77da3b80daa4d6015877ed0c94b8ab"},
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm", "1e1a3d176d52daebbecbbcdfd27c27726076567905c2a9d7398c54da9d225761"},
"credo": {:hex, :credo, "1.5.4", "9914180105b438e378e94a844ec3a5088ae5875626fc945b7c1462b41afc3198", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cf51af45eadc0a3f39ba13b56fdac415c91b34f7b7533a13dc13550277141bc4"},
"ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"},
"earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"},
"ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"},
"excoveralls": {:hex, :excoveralls, "0.13.4", "7b0baee01fe150ef81153e6ffc0fc68214737f54570dc257b3ca4da8e419b812", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "faae00b3eee35cdf0342c10b669a7c91f942728217d2a7c7f644b24d391e6190"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"gun": {:hex, :gun, "1.3.3", "cf8b51beb36c22b9c8df1921e3f2bc4d2b1f68b49ad4fbc64e91875aa14e16b4", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "3106ce167f9c9723f849e4fb54ea4a4d814e3996ae243a1c828b256e749041e0"},
"hackney": {:hex, :hackney, "1.17.0", "717ea195fd2f898d9fe9f1ce0afcc2621a41ecfe137fae57e7fe6e9484b9aa99", [:rebar3], [{:certifi, "~>2.5", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "64c22225f1ea8855f584720c0e5b3cd14095703af1c9fbc845ba042811dc671c"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
"makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"},
"nimble_options": {:hex, :nimble_options, "0.3.5", "a4f6820cdcb4ee444afd78635f323e58e8a5ddf2fbbe9b9d283a99f972034bae", [:mix], [], "hexpm", "f5507cc90033a8d12769522009c80aa9164af6bab245dbd4ad421d008455f1e1"},
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
"opencensus": {:hex, :opencensus, "0.9.3", "a7bb2771b40593f8513fc14eb70d822e88bea1b2fb552e19a1e2e6f98371427a", [:rebar3], [{:counters, "~> 0.2.1", [hex: :counters, repo: "hexpm", optional: false]}, {:ctx, "~> 0.5", [hex: :ctx, repo: "hexpm", optional: false]}, {:wts, "~> 0.3", [hex: :wts, repo: "hexpm", optional: false]}], "hexpm", "9495b4fa817f10a7cdf8b69cca929470aeda0633a04ea18cff6a0a9ea03a03e9"},
"opencensus_honeycomb": {:hex, :opencensus_honeycomb, "0.2.2", "71ed8039fe573be25100777b47afc2893e58d64ee9164d8b69156166bd9bd751", [:mix], [{:hackney, "~> 1.15", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:opencensus, "~> 0.9.2", [hex: :opencensus, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5b08dff93f34a4d56f2038382bd1c4ddcd2661c14e90b80a501e874f169a39d3"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.5.7", "2923bb3af924f184459fe4fa4b100bd25fa6468e69b2803dfae82698269aa5e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "774cd64417c5a3788414fdbb2be2eb9bcd0c048d9e6ad11a0c1fd67b7c0d0978"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
"plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"},
"plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"},
"slipstream": {:hex, :slipstream, "0.3.0", "e2e86234c8a7347360138fa3423df2ebcc3ab21662a61c3e3df4fb36cb90d5c0", [:mix], [{:gun, "~> 1.0", [hex: :gun, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.1", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "09d80ea7193e7ba559cb2d263fbc6b20e16a76a192442bf5a860ca6df4585e06"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"wts": {:hex, :wts, "0.4.0", "62d9dc400ad29f0d233f0665b9c75c8f8eb0a8af75eec921056ba4a73b0605a2", [:rebar3], [], "hexpm", "711bb675de2ce2b3ebab80a613ac93b994f74df44af4cff7970dc9eebe724869"},
}
72 changes: 72 additions & 0 deletions test/slipstream/honeycomb/connection_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
defmodule Slipstream.Honeycomb.ConnectionTest do
use ExUnit.Case

import Mox
setup :verify_on_exit!
@sender Application.fetch_env!(:slipstream_honeycomb, :honeycomb_sender)

test "honeycomb events are emitted on telemetry events" do
pid = start_supervised!(Slipstream.Honeycomb.Connection)
test_proc = self()

expect(@sender, :send_batch, 2, fn [event] ->
send(test_proc, {:send_event, event})

{:ok, 1}
end)
|> allow(self(), pid)

# ideally we'd have slipstream do the actual emitting here, but I don't
# wanna go through all the melarky to set up a phoenix endpoint just for
# this test
metadata = %{
state: %{},
connection_id: "foo",
trace_id: "bar",
start_time: DateTime.utc_now()
}

duration = System.monotonic_time() - System.monotonic_time()

:telemetry.execute(
[:slipstream, :connection, :connect, :stop],
%{duration: duration},
metadata
)

assert_receive {:send_event, event}

assert event.time == metadata.start_time
assert event.data.state == "%{}"
assert event.data.traceId == metadata.trace_id
assert event.data.id == metadata.connection_id

metadata = %{
state: %{},
connection_id: "foo",
span_id: "baz",
trace_id: "bar",
start_time: DateTime.utc_now(),
raw_message: :connect,
message: :connect,
events: [],
built_events: [],
return: {:noreply, %{}}
}

:telemetry.execute(
[:slipstream, :connection, :handle, :stop],
%{duration: duration},
metadata
)

assert_receive {:send_event, event}

assert event.time == metadata.start_time
assert event.data.state == "%{}"
assert event.data.traceId == metadata.trace_id
assert event.data.parentId == metadata.connection_id
assert event.data.id == metadata.span_id
assert event.data.raw_message == ":connect"
end
end
5 changes: 0 additions & 5 deletions test/slipstream_honeycomb_test.exs

This file was deleted.

11 changes: 11 additions & 0 deletions test/support/honeycomb_sender_behaviour.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Slipstream.Honeycomb.SenderBehaviour do
@moduledoc """
A behaviour for `Opencensus.Honeycomb.Sender` that defines the `send_batch/1`
function
Used by mox to allow us to test emitting these events without side effects
"""

@callback send_batch([Opencensus.Honeycomb.Event.t()]) ::
{:ok, integer()} | {:error, Exception.t()}
end
1 change: 1 addition & 0 deletions test/support/mocks.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Mox.defmock(HoneycombSenderMock, for: Slipstream.Honeycomb.SenderBehaviour)

0 comments on commit 8f77ea4

Please sign in to comment.