Holds a single ForgeOps DSN plus everything else the client needs to build and deliver events.
Mirrors gems/forge_ops_tracker's Configuration: a single DSN string carries
both the ingestion URL and the project's api_key: https://<api_key>@host/api/v1/events.
Backed by a GenServer (started under ForgeOpsTracker.Application's supervision tree, the
same "shared mutable state" role every other SDK in this repo's own Configuration class
plays) rather than a plain struct a caller has to thread through every call: Elixir has no
language-level singleton the way Ruby's module-level state or a Kotlin object gives other
clients in this repo for free, so a supervised process is the idiomatic BEAM equivalent.
Summary
Functions
The DSN's userinfo component, percent-decoded: nil if the DSN is unset or malformed.
Same derivation again, swapping the trailing /events for /change_snapshots.
Same derivation again, swapping the trailing /events for /changes.
Returns a specification to start this module under a supervisor.
Same derivation as performance_samples_uri/1, swapping the trailing /events for /custom_metrics.
The one-line warning ForgeOpsTracker.init/1 logs when a DSN is configured but the environment
isn't one of enabled_environments, so nothing would be sent; nil when there's nothing to warn
about (no DSN, or the environment is enabled).
The current configuration.
Same derivation again, swapping the trailing /events for /infrastructure_metrics.
The ingestion URL with credentials stripped out (they travel as the Authorization header instead).
Same host and api_key as ingestion_uri/1, a sibling path under the same DSN rather than a
second DSN to configure: the DSN's own path is always /api/v1/events (what ForgeOps issues to
every project), so the performance-samples endpoint is derived by substitution, the same
approach gems/forge_ops_tracker's own Configuration#performance_samples_uri already takes.
Whether an outgoing request to host should carry a traceparent header; see
propagate_traces/trace_propagation_targets above. Case-insensitive, since hostnames are.
Same derivation again, swapping the trailing /events for /query_plans.
The environment the client starts with, given the OS environment variables (System.get_env/0),
the app's config :forge_ops_tracker, environment: value, and Mix.env/0 when this VM is running
under Mix (nil otherwise), in this order
Same derivation as performance_samples_uri/1, swapping the trailing /events for /spans.
Updates the configuration via a function that receives the current struct and returns the new
one (ForgeOpsTracker.Configuration.update(&%{&1 | dsn: "..."})) the closest Elixir
equivalent to every other SDK's own mutable Configuration object, without actually mutating
anything (this replaces the GenServer's state, doesn't mutate a shared struct in place).
Types
@type t() :: %ForgeOpsTracker.Configuration{ app_module_prefix: String.t() | nil, capture_source_context: boolean(), capture_sql_objects: boolean(), capture_sql_statement: boolean(), detect_changes: boolean(), dsn: String.t() | nil, enabled_environments: MapSet.t(String.t()), environment: String.t(), explain_slow_queries: boolean(), explain_threshold_ms: non_neg_integer(), infrastructure_metric_flush_interval: pos_integer(), logger: (String.t() -> any()) | nil, max_breadcrumbs: pos_integer(), metric_flush_interval: pos_integer(), performance_flush_interval: pos_integer(), propagate_traces: boolean(), queue_size: pos_integer(), release: String.t() | nil, scrub_pii: boolean(), server_name: String.t() | nil, timeout: pos_integer(), trace_capture_threshold: non_neg_integer(), trace_propagation_targets: [String.t() | Regex.t()] | nil, track_breadcrumbs: boolean(), track_env_var_names: boolean(), track_performance: boolean(), track_tracing: boolean() }
Functions
The DSN's userinfo component, percent-decoded: nil if the DSN is unset or malformed.
Same derivation again, swapping the trailing /events for /change_snapshots.
Same derivation again, swapping the trailing /events for /changes.
Returns a specification to start this module under a supervisor.
See Supervisor.
Same derivation as performance_samples_uri/1, swapping the trailing /events for /custom_metrics.
The one-line warning ForgeOpsTracker.init/1 logs when a DSN is configured but the environment
isn't one of enabled_environments, so nothing would be sent; nil when there's nothing to warn
about (no DSN, or the environment is enabled).
@spec get() :: t()
The current configuration.
Same derivation again, swapping the trailing /events for /infrastructure_metrics.
The ingestion URL with credentials stripped out (they travel as the Authorization header instead).
Same host and api_key as ingestion_uri/1, a sibling path under the same DSN rather than a
second DSN to configure: the DSN's own path is always /api/v1/events (what ForgeOps issues to
every project), so the performance-samples endpoint is derived by substitution, the same
approach gems/forge_ops_tracker's own Configuration#performance_samples_uri already takes.
Whether an outgoing request to host should carry a traceparent header; see
propagate_traces/trace_propagation_targets above. Case-insensitive, since hostnames are.
Same derivation again, swapping the trailing /events for /query_plans.
@spec resolve_environment(%{optional(String.t()) => String.t()}, term(), atom() | nil) :: String.t()
The environment the client starts with, given the OS environment variables (System.get_env/0),
the app's config :forge_ops_tracker, environment: value, and Mix.env/0 when this VM is running
under Mix (nil otherwise), in this order:
FORGE_OPS_ENVIRONMENTconfig :forge_ops_tracker, environment: ...MIX_ENV, withprodread as"production",devas"development"andtestas"test"(any other value as written)Mix.env/0, mapped the same way:mixdoesn't put its default:devintoMIX_ENV, so this is what keepsiex -S mixormix phx.serveron a developer's machine from reporting as production"production": a Mix release, an escript or anelixirscript, where Mix isn't running and nothing above is set, so an app deployed with nothing but a DSN still sends its errors.
An empty value counts as unset.
Same derivation as performance_samples_uri/1, swapping the trailing /events for /spans.
Updates the configuration via a function that receives the current struct and returns the new
one (ForgeOpsTracker.Configuration.update(&%{&1 | dsn: "..."})) the closest Elixir
equivalent to every other SDK's own mutable Configuration object, without actually mutating
anything (this replaces the GenServer's state, doesn't mutate a shared struct in place).
If fun raises, that's re-raised here, in the calling process, with its original stacktrace:
not left to crash this shared, process-wide GenServer (which every other part of a host
app calling ForgeOpsTracker also depends on) over what's usually just a caller bug, like an
unknown configuration key.