# Scout Elixir Performance Monitoring Agent
`scout_apm` monitors the performance of Elixir applications in production and provides an in-browser profiler during development. Metrics are
reported to [Scout](https://scoutapm.com), a hosted application monitoring service.

## Monitoring Usage
1. Signup for a [free Scout account](https://scoutapp.com/info/pricing).
2. Follow our install instructions within the UI.
[See our docs](http://docs.scoutapm.com/#elixir-agent) for detailed information.
## DevTrace (Development Profiler) Usage
DevTrace, Scout's in-browser development profiler, may be used without signup.

To use:
1. [Follow the same installation steps as monitoring](http://docs.scoutapm.com/#elixir-install), but skip downloading the config file.
2. In your `config/dev.exs` file, add:
```elixir
# config/dev.exs
config :scout_apm,
dev_trace: true
```
3. Restart your app.
4. Refresh your browser window and look for the speed badge.
## Instrumentation
See [our docs](http://docs.scoutapm.com/#elixir-instrumented-libaries) for information on libraries we auto-instrument (like Phoenix controller-actions) and guides for instrumenting Phoenix channels, Task, HTTPoison, GenServer, and more.
### Supported Template Engines
Scout APM automatically instruments the following Phoenix template engines:
- **EEx** (`.eex`) - Standard Embedded Elixir templates
- **ExS** (`.exs`) - ExScript templates
- **HEEx** (`.heex`) - HTML-aware Embedded Elixir (requires `phoenix_live_view ~> 0.17`)
- **Slime** (`.slim`, `.slime`) - Slim-like templates (requires `phoenix_slime`)
Template engines are enabled automatically based on your project's dependencies. To use Scout's instrumented engines, add to your `config/config.exs`:
```elixir
config :phoenix, :template_engines,
eex: ScoutApm.Instruments.EExEngine,
exs: ScoutApm.Instruments.ExsEngine,
heex: ScoutApm.Instruments.HEExEngine # Only if phoenix_live_view is installed
```
## Error Tracking
Scout APM captures errors automatically and allows manual error capture.
### Automatic Error Capture
Errors are automatically captured from:
- Phoenix controller exceptions (via telemetry)
- LiveView exceptions (mount, handle_event, handle_params)
- Oban job failures
- Code wrapped in `transaction` blocks
To enable Phoenix router-level error capture, add to your application startup:
```elixir
# In your application.ex start/2
def start(_type, _args) do
ScoutApm.Instruments.PhoenixErrorTelemetry.attach()
# ... rest of supervision tree
end
```
### Manual Error Capture
Capture errors manually with `ScoutApm.Error.capture/2`:
```elixir
try do
risky_operation()
rescue
e ->
ScoutApm.Error.capture(e, stacktrace: __STACKTRACE__)
reraise e, __STACKTRACE__
end
```
With additional context:
```elixir
ScoutApm.Error.capture(exception,
stacktrace: __STACKTRACE__,
context: %{user_id: user.id},
request_path: conn.request_path,
request_params: conn.params
)
```
### Configuration
```elixir
config :scout_apm,
# Enable/disable error capture (default: true)
errors_enabled: true,
# Exceptions to ignore (default: [])
errors_ignored_exceptions: [Phoenix.Router.NoRouteError],
# Additional parameter keys to filter (default: [])
# Built-in: password, token, secret, api_key, auth, credentials, etc.
errors_filter_parameters: ["credit_card", "cvv"]
```
## OTLP Logging
Scout APM can capture Elixir Logger messages, enrich them with request context, and send them to an OpenTelemetry collector via OTLP (OpenTelemetry Protocol).
### Setup
1. Enable logging in your configuration:
```elixir
# config/config.exs
config :scout_apm,
logs_enabled: true,
logs_ingest_key: "your-logs-ingest-key" # Or falls back to :key
```
2. Attach the log handler in your application startup:
```elixir
# In your application.ex start/2
def start(_type, _args) do
ScoutApm.Logging.attach()
# ... rest of supervision tree
end
```
### Automatic Context Enrichment
Logs captured within Scout-tracked requests are automatically enriched with:
- `scout_transaction_id` - Unique request identifier for correlation
- `controller_entrypoint` - Controller action name (e.g., "UsersController#show")
- `job_entrypoint` - Background job name (e.g., "Job/EmailWorker")
- `scout_current_operation` - Current span type/name (e.g., "Ecto/MyApp.Repo")
- `scout_start_time` - Request start time (ISO 8601)
- `scout_end_time` - Request end time, if completed
- `scout_duration` - Request duration in seconds, if completed
- `scout_tag_{key}` - Custom tags from `ScoutApm.Context.add/2`
- `user.{key}` - User context from `ScoutApm.Context.add_user/2`
- `service.name` - Application name from config
Entrypoint and transaction ID attributes persist through the entire request lifecycle, including logs emitted after the Scout transaction completes (e.g., Phoenix endpoint logs).
Example - logs are automatically enriched when inside a Scout-tracked request:
```elixir
def show(conn, %{"id" => id}) do
user = Repo.get!(User, id)
Logger.info("Fetched user", user_id: id) # Includes Scout context automatically
render(conn, :show, user: user)
end
```
### Custom Context
Add custom context that appears in your logs:
```elixir
ScoutApm.Context.add("feature_flag", "new_checkout")
ScoutApm.Context.add_user("id", current_user.id)
Logger.info("Processing checkout") # Includes scout_tag_feature_flag and user.id
```
### Configuration
```elixir
config :scout_apm,
# Enable/disable log capture (default: false, opt-in)
logs_enabled: true,
# OTLP collector endpoint
logs_endpoint: "https://otlp.scoutotel.com:4318",
# Authentication key (falls back to :key if not set)
logs_ingest_key: "your-logs-key",
# Minimum log level to capture (default: :info)
logs_level: :info,
# Batching settings
logs_batch_size: 100, # Logs per batch (default: 100)
logs_max_queue_size: 5000, # Max queued logs (default: 5000)
logs_flush_interval_ms: 5_000, # Flush interval in ms (default: 5000)
# Modules to exclude from capture (default: [])
logs_filter_modules: [MyApp.VerboseModule]
```
### Programmatic Control
```elixir
# Attach with custom options
ScoutApm.Logging.attach(level: :warning, filter_modules: [MyApp.Noisy])
# Check if attached
ScoutApm.Logging.attached?() # => true
# Force flush queued logs
ScoutApm.Logging.flush()
# Wait for queue to drain (useful for graceful shutdown)
ScoutApm.Logging.drain(5_000)
# Detach the handler
ScoutApm.Logging.detach()
```
### Log Levels
The following Elixir log levels are mapped to OTLP severity:
| Elixir Level | OTLP Severity | OTLP Number |
|--------------|---------------|-------------|
| `:debug` | DEBUG | 5 |
| `:info` | INFO | 9 |
| `:notice` | INFO2 | 11 |
| `:warning` | WARN | 13 |
| `:error` | ERROR | 17 |
| `:critical` | FATAL | 21 |
| `:alert` | FATAL | 21 |
| `:emergency` | FATAL | 21 |
## Development
See [TESTING.md](TESTING.md) for information on testing with different template engine configurations.
## Releasing
Releases are published to [Hex.pm](https://hex.pm/packages/scout_apm) automatically via GitHub Actions when a version tag is pushed.
### Standard Release
1. Update the version in `mix.exs`:
```elixir
version: "2.0.0",
```
2. Commit the version bump:
```bash
git commit -am "Bump version to 2.0.0"
```
3. Tag and push:
```bash
git tag v2.0.0
git push origin v2.0.0
```
The workflow will run tests, verify the tag matches `mix.exs`, publish to Hex.pm, and create a GitHub Release.
### Pre-release / RC Version
Use Elixir's pre-release version format. The tag must match `mix.exs` exactly.
1. Update `mix.exs`:
```elixir
version: "2.0.0-rc.1",
```
2. Tag and push:
```bash
git tag v2.0.0-rc.1
git push origin v2.0.0-rc.1
```
Pre-release versions (tags containing `-rc`, `-alpha`, `-beta`, or `-dev`) are automatically marked as pre-release on GitHub. On Hex.pm, pre-release versions are not installed by default -- users must opt in with `{:scout_apm, "~> 2.0.0-rc.1"}`.
### Requirements
- A `HEX_API_KEY` secret must be configured in the repository settings. Generate one with:
```bash
mix hex.user key generate --key-name github-actions
```