---
title: "Scala"
description: "Learn how to configure Middleware APM for Scala applications to monitor performance, track exceptions, and optimize system resources."
section: "APM"
breadcrumb: "APM > Scala"
canonical: "https://docs.middleware.io/get-started/apm/scala"
---
# Scala

Use Middleware APM for Scala to collect traces, metrics and application logs from Scala services running on the JVM. The table shows which data types it collects.

| Traces | Metrics | App Logs | Custom Logs | Profiling |
| --- | --- | --- | --- | --- |
| ✅ | ✅ | ✅ | ✅ | ✅ |

## 1. Prerequisites

- Middleware Host Agent running on the same machine (or reachable). In containers, point apps to the agent with MW_AGENT_SERVICE: Docker 172.17.0.1, Kubernetes mw-service.mw-agent-ns.svc.cluster.local.
- Scala/SBT project (Scala 2.11.6+ is fine) and JDK 8+. Check using:

  ```bash
    scala --version
  java --version
  ```

  ![Check Scala and Java versions for Middleware APM setup](/images/v2/apm/scala-version.png)

#### Host-based

#### 1 Download the Middleware Java agent JAR

Grab the latest release of the Middleware Java agent JAR from GitHub and place it in your project (e.g., project root or a lib/ directory).

> **Tip:** Keep the filename versioned (e.g., `middleware-javaagent-1.x.y.jar`) and commit it or fetch it during CI.

#### 2 Wire the agent in build.sbt

Enable a forked JVM and pass `-javaagent` + service name via `javaOptions`. (Forking is required for SBT to apply `javaOptions` to your app’s run.)

```java
  // build.sbt
ThisBuild / scalaVersion := "2.13.14"

// ensure SBT forks for run so javaOptions apply
(run / fork) := true

(run / javaOptions) ++= Seq(
  // point to where you stored the agent jar
  "-javaagent:middleware-javaagent-1.6.0.jar",
  // logical service name shown in Middleware APM
  "-Dotel.service.name={APM-SERVICE-NAME}"
)
```

(Your existing Scala page uses the same javaOptions structure; we’re just adding the fork so it takes effect.)
Alternative: if you prefer, you can manage javaagents [via the community sbt-javaagent plugin](https://github.com/sbt/sbt-javaagent) instead of manual `javaOptions`.

#### 3 Run your app with your account key

Your Middleware API key is required; export it and run the app via SBT:

```bash
  MW_API_KEY="<API-key>" sbt run
```

#### Serverless

If you’re not running a Host Agent, point the agent straight to Middleware’s ingest and keep the rest identical:

```java
  // build.sbt (add one more property)
(run / javaOptions) ++= Seq(
  "-Dotel.exporter.otlp.endpoint=https://ruplp.middleware.io" // serverless ingest URL
)
```

OpenTelemetry Java agents accept the standard `otel.exporter.otlp.endpoint` property; the SDK constructs the signal-specific paths as needed for OTLP/HTTP.
You’ll still export `MW_API_KEY=...` alongside sbt run.

## (Optional) Custom logs from your Scala code

If you want to emit custom logs (INFO/WARN/DEBUG/ERROR) that correlate with traces, add the agent-apm-java library to your build and use its **Logger**:

- Add dependency (SBT):

  ```bash
    // build.sbt
  libraryDependencies += "io.github.middleware-labs" % "agent-apm-java" % "0.0.18"
  ```
- Use the logger (Scala/Java interop):

  ```js
    // anywhere in your app code
  import io.github.middlewarelabs.agentapmjava.Logger

  Logger.info("info message")
  Logger.debug("debug message")
  Logger.warn("warn message")
  Logger.error("error message")
  ```
- Record stack errors
  Call `recordError` from a catch block to attach stack traces to the active span:

  ```js
    try {
    // ...
  } catch {
    case e: Exception =>
      io.github.middlewarelabs.agentapmjava.Logger.recordError(e)
      throw e
  }
  ```

## View your data

After you start the app, wait 3–5 minutes, then open Traces, Logs, and Profiling in Middleware to verify data is flowing. (This mirrors your Scala page’s flow.)

## SBT notes & tips

- Forking is required for `javaOptions` (like `-javaagent`) to apply to `sbt run`. Configure `(run / fork) := true`.
- If you package with sbt-native-packager, you can also push Java options into start scripts via `Universal / javaOptions` or add extra defines to scripts.
- When sending data without a local collector, `otel.exporter.otlp.endpoint` is the standard knob; OTLP exporters also support header-based auth via `otel.exporter.otlp.headers` if you ever need it.

## Environment variables

| Key/Option | Where it's used | Purpose | Example |
| --- | --- | --- | --- |
| `MW_AGENT_SERVICE` | Containerized host-based | Route app → Host Agent | `172.17.0.1` (Docker), `mw-service.mw-agent-ns.svc.cluster.local` (K8s). |
| `MW_API_KEY` | Runtime env | Your Middleware account key, required for export | `export MW_API_KEY="…"`; then `sbt run`. |
| `-Dotel.service.name` | `javaOptions` | Logical service name shown in APM | `-Dotel.service.name=payments-scala` |
| `-Dotel.exporter.otlp.endpoint` | `javaOptions`(serverless) | Send telemetry directly to Middleware ingest (no Host Agent) | `-Dotel.exporter.otlp.endpoint=https://ruplp.middleware.io.` |

## Troubleshooting

- No telemetry appears → Confirm `(run / fork) := true` and the `-javaagent:` path is correct. SBT only applies javaOptions to a forked run.
- Still no data on serverless → Ensure the endpoint property is set (`otel.exporter.otlp.endpoint`) and that your key is present. OTLP HTTP exporters derive `/v1/{signal}` paths automatically when you set a base endpoint.
- Packaging instead of `sbt run` → Use sbt-native-packager’s `Universal / javaOptions` or script defines so the agent flags end up in the generated start scripts.

Need assistance or want to learn more about Middleware? Get in touch with us via our Contact Us page or join our Slack channel.
