diff --git a/guides/Getting Started.md b/guides/Getting Started.md index 4725204..a5adf98 100644 --- a/guides/Getting Started.md +++ b/guides/Getting Started.md @@ -106,6 +106,8 @@ The `ErrorTracker.set_context/1` function stores the given context in the curren ErrorTracker.set_context(%{user_id: conn.assigns.current_user.id}) ``` +There are some requirements on the type of data that can be included in the context, so we recommend taking a look at `set_context/1` documentation + ## Manual error tracking If you want to report custom errors that fall outside the default integration scope, you may use `ErrorTracker.report/2`. This allows you to report an exception yourself: diff --git a/lib/error_tracker.ex b/lib/error_tracker.ex index b755c67..292569f 100644 --- a/lib/error_tracker.ex +++ b/lib/error_tracker.ex @@ -41,6 +41,9 @@ defmodule ErrorTracker do can gather, but aside from that, you can also add your own information. You can do this in a per-process basis or in a per-call basis (or both). + There are some requirements on the type of data that can be included in the + context, so we recommend taking a look at `set_context/1` documentation. + **Per process** This allows you to set a general context for the current process such as a Phoenix @@ -152,6 +155,17 @@ defmodule ErrorTracker do That means that any existing data on deep levels for he current context will be replaced if the first level key is received on the new contents. + + ## Content serialization + + The content stored on the context should be serializable using the JSON library + used by the application (usually `Jason`), so it is rather recommended to use + primitive types (strings, numbers, booleans...). + + If you still need to pass more complex data types to your context, please test + that they can be encoded to JSON or storing the errors will fail. In the case + of `Jason` that may require defining an Encoder for that data type if not + included by default. """ @spec set_context(context()) :: context() def set_context(params) when is_map(params) do @@ -188,16 +202,25 @@ defmodule ErrorTracker do existing_status = Repo.one(from e in Error, where: [fingerprint: ^error.fingerprint], select: e.status) - error = - Repo.insert!(error, - on_conflict: [set: [status: :unresolved, last_occurrence_at: DateTime.utc_now()]], - conflict_target: :fingerprint - ) - - occurrence = - error - |> Ecto.build_assoc(:occurrences, stacktrace: stacktrace, context: context, reason: reason) - |> Repo.insert!() + {:ok, {error, occurrence}} = + Repo.transaction(fn -> + error = + Repo.insert!(error, + on_conflict: [set: [status: :unresolved, last_occurrence_at: DateTime.utc_now()]], + conflict_target: :fingerprint + ) + + occurrence = + error + |> Ecto.build_assoc(:occurrences, + stacktrace: stacktrace, + context: context, + reason: reason + ) + |> Repo.insert!() + + {error, occurrence} + end) # If the error existed and was marked as resolved before this exception, # sent a Telemetry event diff --git a/lib/error_tracker/repo.ex b/lib/error_tracker/repo.ex index 9c99e11..fe9748a 100644 --- a/lib/error_tracker/repo.ex +++ b/lib/error_tracker/repo.ex @@ -29,6 +29,10 @@ defmodule ErrorTracker.Repo do dispatch(:aggregate, [queryable, aggregate], opts) end + def transaction(fun_or_multi, opts \\ []) do + dispatch(:transaction, [fun_or_multi], opts) + end + def with_adapter(fun) do adapter = case repo().__adapter__() do