diff --git a/config/test.exs b/config/test.exs index 28daf46fb..e9a6560a6 100644 --- a/config/test.exs +++ b/config/test.exs @@ -49,3 +49,5 @@ config :logger, ], level: :error ] + +config :tesla, Logflare.Backends.Adaptor.WebhookAdaptor.Client, adapter: Tesla.Mock diff --git a/lib/logflare/backends.ex b/lib/logflare/backends.ex index 12a6c8a14..b9ea40a51 100644 --- a/lib/logflare/backends.ex +++ b/lib/logflare/backends.ex @@ -5,6 +5,7 @@ defmodule Logflare.Backends do alias Logflare.Backends.Adaptor.WebhookAdaptor alias Logflare.Backends.Adaptor.PostgresAdaptor alias Logflare.Backends.Adaptor.BigQueryAdaptor + alias Logflare.Backends.Adaptor.DatadogAdaptor alias Logflare.Backends.Backend alias Logflare.Backends.SourceDispatcher alias Logflare.Backends.SourceRegistry @@ -25,7 +26,8 @@ defmodule Logflare.Backends do @adaptor_mapping %{ webhook: WebhookAdaptor, postgres: PostgresAdaptor, - bigquery: BigQueryAdaptor + bigquery: BigQueryAdaptor, + datadog: DatadogAdaptor } defdelegate child_spec(arg), to: __MODULE__.Supervisor diff --git a/lib/logflare/backends/adaptor.ex b/lib/logflare/backends/adaptor.ex index 36b9c01e0..7ab07f9ab 100644 --- a/lib/logflare/backends/adaptor.ex +++ b/lib/logflare/backends/adaptor.ex @@ -29,6 +29,7 @@ defmodule Logflare.Backends.Adaptor do @spec get_adaptor(Backend.t()) :: module() def get_adaptor(%Backend{type: type}) do case type do + :datadog -> __MODULE__.DatadogAdaptor :webhook -> __MODULE__.WebhookAdaptor :postgres -> __MODULE__.PostgresAdaptor :bigquery -> __MODULE__.BigQueryAdaptor diff --git a/lib/logflare/backends/adaptor/datadog_adaptor.ex b/lib/logflare/backends/adaptor/datadog_adaptor.ex new file mode 100644 index 000000000..1e3a30ace --- /dev/null +++ b/lib/logflare/backends/adaptor/datadog_adaptor.ex @@ -0,0 +1,86 @@ +defmodule Logflare.Backends.Adaptor.DatadogAdaptor do + @moduledoc """ + Wrapper module for `Logflare.Backends.Adaptor.WebhookAdaptor` to provide API + for DataDog logs ingestion endpoint. + """ + + use TypedStruct + + alias Logflare.Backends.Adaptor.WebhookAdaptor + + # https://docs.datadoghq.com/api/latest/logs/#send-logs + @api_url_mapping %{ + "US1" => "https://http-intake.logs.datadoghq.com/api/v2/logs", + "US3" => "https://http-intake.logs.us3.datadoghq.com/api/v2/logs", + "US5" => "https://http-intake.logs.us5.datadoghq.com/api/v2/logs", + "EU" => "https://http-intake.logs.datadoghq.eu/api/v2/logs", + "AP1" => "https://http-intake.logs.ap1.datadoghq.com/api/v2/logs", + "US1-FED" => "https://http-intake.logs.ddog-gov.com/api/v2/logs" + } + + def api_url_mapping, do: @api_url_mapping + + typedstruct enforce: true do + field(:api_key, String.t()) + end + + @behaviour Logflare.Backends.Adaptor + + def child_spec(arg) do + %{ + id: __MODULE__, + start: {__MODULE__, :start_link, [arg]} + } + end + + @impl Logflare.Backends.Adaptor + def start_link({source, backend}) do + backend = %{ + backend + | config: %{ + url: Map.get(@api_url_mapping, backend.config.region), + headers: %{"dd-api-key" => backend.config.api_key} + } + } + + WebhookAdaptor.start_link({source, backend}) + end + + @impl Logflare.Backends.Adaptor + def ingest(pid, log_events, opts) do + new_events = + Enum.map(log_events, &translate_event/1) + + WebhookAdaptor.ingest(pid, new_events, opts) + end + + @impl Logflare.Backends.Adaptor + def execute_query(_ident, _query), do: {:error, :not_implemented} + + @impl Logflare.Backends.Adaptor + def cast_config(params) do + {%{}, %{api_key: :string, region: :string}} + |> Ecto.Changeset.cast(params, [:api_key, :region]) + end + + @impl Logflare.Backends.Adaptor + def validate_config(changeset) do + changeset + |> Ecto.Changeset.validate_required([:api_key, :region]) + |> Ecto.Changeset.validate_inclusion(:region, Map.keys(@api_url_mapping)) + end + + defp translate_event(%Logflare.LogEvent{} = le) do + formatted_ts = + DateTime.from_unix!(le.body["timestamp"], :microsecond) |> DateTime.to_iso8601() + + %Logflare.LogEvent{ + le + | body: %{ + message: formatted_ts <> " " <> Jason.encode!(le.body), + ddsource: "Logflare by Supabase", + service: le.source.name + } + } + end +end diff --git a/lib/logflare/backends/backend.ex b/lib/logflare/backends/backend.ex index 242d879ce..34b3c6269 100644 --- a/lib/logflare/backends/backend.ex +++ b/lib/logflare/backends/backend.ex @@ -10,7 +10,7 @@ defmodule Logflare.Backends.Backend do alias Logflare.User alias Logflare.Rule - @adaptor_types [:bigquery, :webhook, :postgres] + @adaptor_types [:bigquery, :webhook, :postgres, :datadog, :elastic] typed_schema "backends" do field(:name, :string) diff --git a/lib/logflare_web/live/backends/components/backend_form.heex b/lib/logflare_web/live/backends/components/backend_form.heex index 475ca7422..00e4b461d 100644 --- a/lib/logflare_web/live/backends/components/backend_form.heex +++ b/lib/logflare_web/live/backends/components/backend_form.heex @@ -31,7 +31,7 @@
- <%= select(f, :type, ["Select a backend type...", Webhook: :webhook, Postgres: :postgres, BigQuery: :bigquery], + <%= select(f, :type, ["Select a backend type...", Webhook: :webhook, Postgres: :postgres, BigQuery: :bigquery, Datadog: :datadog], phx_change: :change_form_type, class: "form-control form-control-margin", id: "type" @@ -129,6 +129,26 @@ A BigQuery Dataset ID where data will be stored.
+ <% "datadog" -> %> +
+ <%= label(f_config, :api_key, "API Key") %> + <%= text_input(f_config, :api_key, class: "form-control", type: "password") %> + + The API Key obtained from the Datadog dashboard. + https://us1.datadoghq.com/organization-settings/api-keys + +
+ +
+ <%= label(f_config, :region, "Region") %> + <%= select(f_config, :region, ["Select a region..." | Map.keys(Logflare.Backends.Adaptor.DatadogAdaptor.api_url_mapping())], + class: "form-control form-control-margin", + id: "datadog-region" + ) %> + + Region that the Datadog account is in. + +
<% _ -> %>
Select a Backend Type
<% end %> diff --git a/lib/logflare_web/live/source_backends_live.ex b/lib/logflare_web/live/source_backends_live.ex index a65c657c5..a9194d1ce 100644 --- a/lib/logflare_web/live/source_backends_live.ex +++ b/lib/logflare_web/live/source_backends_live.ex @@ -12,7 +12,7 @@ defmodule LogflareWeb.SourceBackendsLive do connected: <%= Enum.count(@attached_backend_ids) %> <.form :let={f} as={:source} for={%{}} action="#" phx-submit="save"> <% grouped = Enum.group_by(@backends, & &1.type) %> - <%= for type <- [:bigquery, :postgres, :webhook], + <%= for type <- [:bigquery, :postgres, :webhook, :datadog], backends = Map.get(grouped, type, []) do %>
@@ -20,6 +20,7 @@ defmodule LogflareWeb.SourceBackendsLive do :bigquery -> "BigQuery" :postgres -> "PostgreSQL" :webhook -> "Webhook" + :datadog -> "Datadog" end %> diff --git a/mix.lock b/mix.lock index cdaba4520..8732544d0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "atomic_map": {:hex, :atomic_map, "0.9.3", "3c7f1302e0590164732d08ca999708efbb2cd768abf2911cf140280ce2dc499d", [:mix], [], "hexpm", "c237babf301bd2435bd85b96cffc973022b4cbb7721537059ee0dd3bb74938d2"}, - "bandit": {:hex, :bandit, "1.1.0", "1414e65916229d4ee0914f6d4e7f8ec16c6f2d90e01ad5174d89e90baa577625", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4891fb2f48a83445da70a4e949f649a9b4032310f1f640f4a8a372bc91cece18"}, + "bandit": {:hex, :bandit, "1.4.2", "a1475c8dcbffd1f43002797f99487a64c8444753ff2b282b52409e279488e1f5", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3db8bacea631bd926cc62ccad58edfee4252d1b4c5cccbbad9825df2722b884f"}, "benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"}, "benchee_async": {:hex, :benchee_async, "0.1.2", "38e686bd9cdf7ae8767a08c0df6932b625b166fd97267d0437c715f9ff424bc1", [:mix], [], "hexpm", "73b0bd95173f86c61d41570adaeb151efe70c0cc273eec6501d391b35e3599dd"}, "bertex": {:hex, :bertex, "1.3.0", "0ad0df9159b5110d9d2b6654f72fbf42a54884ef43b6b651e6224c0af30ba3cb", [:mix], [], "hexpm", "0a5d5e478bb5764b7b7bae37cae1ca491200e58b089df121a2fe1c223d8ee57a"}, @@ -15,7 +15,7 @@ "configcat": {:hex, :configcat, "2.0.1", "cffd7e6ba7a4c41e1e6bbb706379192a1be7cd848bb6b098d4ed054b13c18f9d", [:mix], [{:elixir_uuid, "~> 1.2", [hex: :elixir_uuid, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.7", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "3e4a239a613d2acbcee7103a6a426c4ae52882ae65bf48cdb5c1247877b65112"}, "contex": {:hex, :contex, "0.3.0", "d390713efee604702600ba801a481bcb8534a9af43e118b29d9d37fe4495fcba", [:mix], [{:nimble_strftime, "~> 0.1.0", [hex: :nimble_strftime, repo: "hexpm", optional: false]}], "hexpm", "3fa7535cc3b265691a4eabc2707fe8622aa60a2565145a14da9aebd613817652"}, "cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"}, - "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, + "cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"}, "credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [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", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"}, @@ -102,7 +102,7 @@ "phoenix_view": {:hex, :phoenix_view, "2.0.3", "4d32c4817fce933693741deeb99ef1392619f942633dde834a5163124813aad3", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "cd34049af41be2c627df99cd4eaa71fc52a328c0c3d8e7d4aa28f880c30e7f64"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, "plug_caisson": {:hex, :plug_caisson, "0.2.1", "aa6a45a4f0e674459b8881d742cc0e8c7d5d0e008a29fe84dc10ab95d6fcfa74", [:mix], [{:brotli, "~> 0.3.2", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "661887bca916122c31717842fa6496c5a4d92c22b5dbef6bd6973d28188939cc"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.6.2", "753611b23b29231fb916b0cdd96028084b12aff57bfd7b71781bd04b1dbeb5c9", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "951ed2433df22f4c97b85fdb145d4cee561f36b74854d64c06d896d7cd2921a7"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.7.1", "87677ffe3b765bc96a89be7960f81703223fe2e21efa42c125fcd0127dd9d6b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "02dbd5f9ab571b864ae39418db7811618506256f6d13b4a45037e5fe78dc5de3"}, "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, "poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"}, "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, @@ -128,6 +128,7 @@ "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"}, "tesla": {:hex, :tesla, "1.9.0", "8c22db6a826e56a087eeb8cdef56889731287f53feeb3f361dec5d4c8efb6f14", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "7c240c67e855f7e63e795bf16d6b3f5115a81d1f44b7fe4eadbf656bae0fef8a"}, "thousand_island": {:hex, :thousand_island, "1.2.0", "4f548ae771ab5f96bc7e199f9824c0c2ce6d365f8c93f5f64dbbb33988e484bf", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "521671fea179672addb6af46455fc2a77be1edda4c0ed351633e0ef37a4b3584"}, + "test_server": {:hex, :test_server, "0.1.16", "403d6cebaa7ad1d08c0ca9475af48836c45ae0f64cda9f4e88b0f17310c5c452", [:mix], [{:bandit, ">= 1.4.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 2.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:x509, "~> 0.6", [hex: :x509, repo: "hexpm", optional: false]}], "hexpm", "f27cfc858d494e4df413a962fbe4d7aa3daf1a3824307f0b046bb8cec6ac8e42"}, "timber_logfmt": {:git, "https://github.com/Logflare/logfmt-elixir.git", "9766367ccb3014b47b0a621a489261011dcea769", []}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, @@ -144,4 +145,5 @@ "warpath": {:hex, :warpath, "0.5.0", "906ca2efe1bbd07f2dafc2159897e2cb3068e567210c81c2d9858addc172df50", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a9b4f518bfce91818fd52bc8e7656259844167436f14c0c1a01a6ca7e061f671"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.5", "9dfeee8269b27e958a65b3e235b7e447769f66b5b5925385f5a569269164a210", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4b977ba4a01918acbf77045ff88de7f6972c2a009213c515a445c48f224ffce9"}, + "x509": {:hex, :x509, "0.8.8", "aaf5e58b19a36a8e2c5c5cff0ad30f64eef5d9225f0fd98fb07912ee23f7aba3", [:mix], [], "hexpm", "ccc3bff61406e5bb6a63f06d549f3dba3a1bbb456d84517efaaa210d8a33750f"}, } diff --git a/test/logflare/backends/adaptor/datadog_adaptor_test.exs b/test/logflare/backends/adaptor/datadog_adaptor_test.exs new file mode 100644 index 000000000..c5bc22410 --- /dev/null +++ b/test/logflare/backends/adaptor/datadog_adaptor_test.exs @@ -0,0 +1,91 @@ +defmodule Logflare.Backends.Adaptor.DatadogAdaptorTest do + use Logflare.DataCase, async: false + + alias Logflare.Backends.Adaptor + + @subject Logflare.Backends.Adaptor.DatadogAdaptor + + doctest @subject + + describe "cast and validate" do + test "API key is required" do + changeset = Adaptor.cast_and_validate_config(@subject, %{}) + + refute changeset.valid? + + assert Adaptor.cast_and_validate_config(@subject, %{ + "api_key" => "foobarbaz", + "region" => "US1" + }).valid? + + refute Adaptor.cast_and_validate_config(@subject, %{ + "api_key" => "foobarbaz" + }).valid? + end + end + + describe "logs ingestion" do + setup do + user = insert(:user) + source = insert(:source, user: user) + + backend = + insert(:backend, + type: :datadog, + sources: [source], + config: %{api_key: "foo-bar", region: "US1"} + ) + + pid = start_supervised!({@subject, {source, backend}}) + :timer.sleep(500) + [pid: pid, backend: backend, source: source] + end + + test "sent logs are delivered", %{pid: pid, source: source, backend: backend} do + this = self() + ref = make_ref() + + Tesla.Mock.mock_global(fn _req -> + send(this, ref) + %Tesla.Env{status: 200, body: ""} + end) + + le = build(:log_event, source: source) + + assert :ok == @subject.ingest(pid, [le], source_id: source.id, backend_id: backend.id) + assert_receive ^ref, 2000 + end + + test "service field is set to source name", %{pid: pid, source: source, backend: backend} do + this = self() + ref = make_ref() + + Tesla.Mock.mock_global(fn req -> + send(this, {ref, Jason.decode!(req.body)}) + %Tesla.Env{status: 200, body: ""} + end) + + le = build(:log_event, source: source) + + assert :ok == @subject.ingest(pid, [le], source_id: source.id, backend_id: backend.id) + assert_receive {^ref, [log_entry]}, 2000 + assert log_entry["service"] == source.name + end + + test "message is JSON encoded log event", %{pid: pid, source: source, backend: backend} do + this = self() + ref = make_ref() + + Tesla.Mock.mock_global(fn req -> + send(this, {ref, Jason.decode!(req.body)}) + %Tesla.Env{status: 200, body: ""} + end) + + le = build(:log_event, source: source) + + assert :ok == @subject.ingest(pid, [le], source_id: source.id, backend_id: backend.id) + assert_receive {^ref, [log_entry]}, 2000 + assert log_entry["message"] =~ Jason.encode!(le.body) + end + end +end