diff --git a/lib/sentry/application.ex b/lib/sentry/application.ex index e592ae04..8f373728 100644 --- a/lib/sentry/application.ex +++ b/lib/sentry/application.ex @@ -10,6 +10,11 @@ defmodule Sentry.Application do config = Config.validate!() :ok = Config.persist(config) + Config.put_config( + :in_app_module_allow_list, + Config.in_app_module_allow_list() ++ resolve_in_app_module_allow_list() + ) + http_client = Keyword.fetch!(config, :client) maybe_http_client_spec = @@ -71,4 +76,13 @@ defmodule Sentry.Application do Sentry.Integrations.Quantum.Cron.attach_telemetry_handler() end end + + defp resolve_in_app_module_allow_list do + Enum.flat_map(Config.in_app_otp_apps(), fn app -> + case :application.get_key(app, :modules) do + {:ok, modules} -> modules + _ -> [] + end + end) + end end diff --git a/lib/sentry/config.ex b/lib/sentry/config.ex index d57830c7..a173e0db 100644 --- a/lib/sentry/config.ex +++ b/lib/sentry/config.ex @@ -388,6 +388,20 @@ defmodule Sentry.Config do The number of lines of source code before and after the line that caused the exception to report. """ + ], + in_app_otp_apps: [ + type: {:list, :atom}, + default: [], + type_doc: "list of `t:atom/0`", + doc: """ + A list of OTP application names that will be used to populate additional modules for the + `:in_app_module_allow_list` option. List your application (or the applications in your + umbrella project) for them to show as "in-app" in stacktraces in Sentry. We recommend using + this option over `:in_app_module_allow_list`, unless you need more control over the exact + modules to consider as "in-app". + + *Available since v10.9.0*. + """ ] ] @@ -575,6 +589,9 @@ defmodule Sentry.Config do @spec report_deps?() :: boolean() def report_deps?, do: fetch!(:report_deps) + @spec in_app_otp_apps() :: [atom()] + def in_app_otp_apps, do: fetch!(:in_app_otp_apps) + @spec json_library() :: module() def json_library, do: fetch!(:json_library) diff --git a/mix.exs b/mix.exs index acfb35fa..aa2ab7fe 100644 --- a/mix.exs +++ b/mix.exs @@ -136,6 +136,7 @@ defmodule Sentry.Mixfile do defp run_integration_tests_if_supported(args) do if Version.match?(System.version(), ">= 1.16.0") do + run_integration_tests("umbrella", args) run_integration_tests("phoenix_app", args) else Mix.shell().info("Skipping integration tests for Elixir versions < 1.16") diff --git a/test_integrations/umbrella/.formatter.exs b/test_integrations/umbrella/.formatter.exs new file mode 100644 index 00000000..90a08535 --- /dev/null +++ b/test_integrations/umbrella/.formatter.exs @@ -0,0 +1,5 @@ +# Used by "mix format" +[ + inputs: ["mix.exs", "config/*.exs"], + subdirectories: ["apps/*"] +] diff --git a/test_integrations/umbrella/.gitignore b/test_integrations/umbrella/.gitignore new file mode 100644 index 00000000..013cd25e --- /dev/null +++ b/test_integrations/umbrella/.gitignore @@ -0,0 +1,20 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Temporary files, for example, from tests. +/tmp/ diff --git a/test_integrations/umbrella/README.md b/test_integrations/umbrella/README.md new file mode 100644 index 00000000..879708a7 --- /dev/null +++ b/test_integrations/umbrella/README.md @@ -0,0 +1,4 @@ +# PhoenixAppUmbrella + +**TODO: Add description** + diff --git a/test_integrations/umbrella/apps/admin/.formatter.exs b/test_integrations/umbrella/apps/admin/.formatter.exs new file mode 100644 index 00000000..d2cda26e --- /dev/null +++ b/test_integrations/umbrella/apps/admin/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/test_integrations/umbrella/apps/admin/.gitignore b/test_integrations/umbrella/apps/admin/.gitignore new file mode 100644 index 00000000..fab39365 --- /dev/null +++ b/test_integrations/umbrella/apps/admin/.gitignore @@ -0,0 +1,23 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +admin-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/test_integrations/umbrella/apps/admin/README.md b/test_integrations/umbrella/apps/admin/README.md new file mode 100644 index 00000000..198375ec --- /dev/null +++ b/test_integrations/umbrella/apps/admin/README.md @@ -0,0 +1,21 @@ +# Admin + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `admin` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:admin, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/test_integrations/umbrella/apps/admin/lib/admin.ex b/test_integrations/umbrella/apps/admin/lib/admin.ex new file mode 100644 index 00000000..800659cc --- /dev/null +++ b/test_integrations/umbrella/apps/admin/lib/admin.ex @@ -0,0 +1,18 @@ +defmodule Admin do + @moduledoc """ + Documentation for `Admin`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Admin.hello() + :world + + """ + def hello do + :world + end +end diff --git a/test_integrations/umbrella/apps/admin/lib/admin/settings.ex b/test_integrations/umbrella/apps/admin/lib/admin/settings.ex new file mode 100644 index 00000000..3ce90512 --- /dev/null +++ b/test_integrations/umbrella/apps/admin/lib/admin/settings.ex @@ -0,0 +1,3 @@ +defmodule Admin.Settings do + defstruct [:key, :value] +end diff --git a/test_integrations/umbrella/apps/admin/mix.exs b/test_integrations/umbrella/apps/admin/mix.exs new file mode 100644 index 00000000..34b00431 --- /dev/null +++ b/test_integrations/umbrella/apps/admin/mix.exs @@ -0,0 +1,33 @@ +defmodule Admin.MixProject do + use Mix.Project + + def project do + [ + app: :admin, + version: "0.1.0", + build_path: "../../_build", + config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.18", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}, + # {:sibling_app_in_umbrella, in_umbrella: true} + ] + end +end diff --git a/test_integrations/umbrella/apps/admin/test/admin_test.exs b/test_integrations/umbrella/apps/admin/test/admin_test.exs new file mode 100644 index 00000000..e1e0a781 --- /dev/null +++ b/test_integrations/umbrella/apps/admin/test/admin_test.exs @@ -0,0 +1,8 @@ +defmodule Admin.Sentry.ConfigTest do + use ExUnit.Case + + test "loads in_app_module_allow_list" do + assert Sentry.Config.in_app_module_allow_list() |> Enum.sort() == + [Admin, Admin.Settings, Public, Public.User] + end +end diff --git a/test_integrations/umbrella/apps/admin/test/test_helper.exs b/test_integrations/umbrella/apps/admin/test/test_helper.exs new file mode 100644 index 00000000..a1879af5 --- /dev/null +++ b/test_integrations/umbrella/apps/admin/test/test_helper.exs @@ -0,0 +1,5 @@ +Application.start(:nimble_options) +Application.start(:nimble_ownership) +Application.start(:sentry) + +ExUnit.start() diff --git a/test_integrations/umbrella/apps/public/.formatter.exs b/test_integrations/umbrella/apps/public/.formatter.exs new file mode 100644 index 00000000..d2cda26e --- /dev/null +++ b/test_integrations/umbrella/apps/public/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/test_integrations/umbrella/apps/public/.gitignore b/test_integrations/umbrella/apps/public/.gitignore new file mode 100644 index 00000000..c41d2839 --- /dev/null +++ b/test_integrations/umbrella/apps/public/.gitignore @@ -0,0 +1,23 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +public-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/test_integrations/umbrella/apps/public/README.md b/test_integrations/umbrella/apps/public/README.md new file mode 100644 index 00000000..689399f9 --- /dev/null +++ b/test_integrations/umbrella/apps/public/README.md @@ -0,0 +1,21 @@ +# Public + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `public` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:public, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/test_integrations/umbrella/apps/public/lib/public.ex b/test_integrations/umbrella/apps/public/lib/public.ex new file mode 100644 index 00000000..06632e11 --- /dev/null +++ b/test_integrations/umbrella/apps/public/lib/public.ex @@ -0,0 +1,18 @@ +defmodule Public do + @moduledoc """ + Documentation for `Public`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Public.hello() + :world + + """ + def hello do + :world + end +end diff --git a/test_integrations/umbrella/apps/public/lib/public/user.ex b/test_integrations/umbrella/apps/public/lib/public/user.ex new file mode 100644 index 00000000..abd9bfe1 --- /dev/null +++ b/test_integrations/umbrella/apps/public/lib/public/user.ex @@ -0,0 +1,3 @@ +defmodule Public.User do + defstruct [:id, :name] +end diff --git a/test_integrations/umbrella/apps/public/mix.exs b/test_integrations/umbrella/apps/public/mix.exs new file mode 100644 index 00000000..8f6e0d6e --- /dev/null +++ b/test_integrations/umbrella/apps/public/mix.exs @@ -0,0 +1,33 @@ +defmodule Public.MixProject do + use Mix.Project + + def project do + [ + app: :public, + version: "0.1.0", + build_path: "../../_build", + config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.18", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}, + # {:sibling_app_in_umbrella, in_umbrella: true} + ] + end +end diff --git a/test_integrations/umbrella/apps/public/test/public_test.exs b/test_integrations/umbrella/apps/public/test/public_test.exs new file mode 100644 index 00000000..9655c6d7 --- /dev/null +++ b/test_integrations/umbrella/apps/public/test/public_test.exs @@ -0,0 +1,8 @@ +defmodule Public.Sentry.ConfigTest do + use ExUnit.Case + + test "loads in_app_module_allow_list" do + assert Sentry.Config.in_app_module_allow_list() |> Enum.sort() == + [Admin, Admin.Settings, Public, Public.User] + end +end diff --git a/test_integrations/umbrella/apps/public/test/test_helper.exs b/test_integrations/umbrella/apps/public/test/test_helper.exs new file mode 100644 index 00000000..a1879af5 --- /dev/null +++ b/test_integrations/umbrella/apps/public/test/test_helper.exs @@ -0,0 +1,5 @@ +Application.start(:nimble_options) +Application.start(:nimble_ownership) +Application.start(:sentry) + +ExUnit.start() diff --git a/test_integrations/umbrella/config/config.exs b/test_integrations/umbrella/config/config.exs new file mode 100644 index 00000000..8c1d6cf0 --- /dev/null +++ b/test_integrations/umbrella/config/config.exs @@ -0,0 +1,27 @@ +# This file is responsible for configuring your umbrella +# and **all applications** and their dependencies with the +# help of the Config module. +# +# Note that all applications in your umbrella share the +# same configuration and dependencies, which is why they +# all use the same configuration file. If you want different +# configurations or dependencies per app, it is best to +# move said applications out of the umbrella. +import Config + +# Sample configuration: +# +# config :logger, :console, +# level: :info, +# format: "$date $time [$level] $metadata$message\n", +# metadata: [:user_id] +# + +config :sentry, + dsn: "http://public:secret@localhost:8080/1", + environment_name: Mix.env(), + enable_source_code_context: true, + root_source_code_paths: [File.cwd!()], + test_mode: true, + send_result: :sync, + in_app_otp_apps: [:public, :admin] diff --git a/test_integrations/umbrella/mix.exs b/test_integrations/umbrella/mix.exs new file mode 100644 index 00000000..1d88befa --- /dev/null +++ b/test_integrations/umbrella/mix.exs @@ -0,0 +1,25 @@ +defmodule Umbrella.MixProject do + use Mix.Project + + def project do + [ + apps_path: "apps", + version: "0.1.0", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Dependencies listed here are available only for this + # project and cannot be accessed from applications inside + # the apps folder. + # + # Run "mix help deps" for examples and options. + defp deps do + [ + {:hackney, "~> 1.18"}, + {:jason, "~> 1.4"}, + {:sentry, path: "../.."} + ] + end +end diff --git a/test_integrations/umbrella/mix.lock b/test_integrations/umbrella/mix.lock new file mode 100644 index 00000000..ff9454dd --- /dev/null +++ b/test_integrations/umbrella/mix.lock @@ -0,0 +1,12 @@ +%{ + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [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.4.1", [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", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, +}