{"slug": "tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide", "title": "Tomcat Performance Monitoring with OpenTelemetry - Key Metrics and Setup Guide", "summary": "Apache Tomcat performance monitoring can be enhanced using OpenTelemetry, which provides a vendor-neutral pipeline for collecting traces, metrics, and logs. The OpenTelemetry Java agent attaches to Tomcat with zero code changes, enabling historical retention, alerting, and correlation of metrics with request traces. This guide outlines setting up Tomcat monitoring with OpenTelemetry, including Dockerized deployment and verification in SigNoz Cloud.", "body_md": "# Tomcat Performance Monitoring with OpenTelemetry - Key Metrics and Setup Guide\n\nApache Tomcat exposes performance data via JMX (Java Management Extensions), giving you access to thread pool usage, request throughput, session counts, and JVM metrics such as heap memory usage and garbage collection. While built-in tools like the Tomcat Manager and JConsole can display this data in real time, they lack historical retention, alerting, and the ability to correlate metrics with request traces. OpenTelemetry solves this by giving you a single, vendor-neutral pipeline for collecting traces, metrics, and logs from Tomcat. The OpenTelemetry Java agent attaches to Tomcat with zero code changes and exports telemetry directly to your observability backend.\n\nIn this guide, we will set up Tomcat performance monitoring using the OpenTelemetry Java agent, build a Dockerized Tomcat application with demo endpoints, instrument it, and verify traces, metrics, and logs flowing into the observability backend.\n\nPrerequisites\n\nBefore starting, make sure you have the following ready:\n\n- Docker and Docker Compose are installed on your machine\n- A SigNoz account (\n[SigNoz Cloud](https://signoz.io/teams/)) - Basic familiarity with Tomcat (you don't need a running Tomcat instance; we will set one up)\n- You will also need Ingestion Key and Ingestion URL from your SigNoz account. For a detailed walkthrough on getting these credentials, see\n[how to get the ingestion key and ingestion url from SigNoz Cloud](https://signoz.io/docs/ingestion/signoz-cloud/keys/).\n\nImplementation Roadmap\n\nSetting up Tomcat monitoring with OpenTelemetry involves four steps:\n\n- Create the project structure with a Dockerfile and demo endpoints\n- Configure OpenTelemetry environment variables to send telemetry to SigNoz Cloud\n- Start the stack with Docker Compose\n- Verify data in SigNoz Cloud\n\nThe architecture follows the [SigNoz Tomcat instrumentation docs](https://signoz.io/docs/instrumentation/java/opentelemetry-tomcat/). Because Tomcat runs on the JVM, the cleanest way to instrument it is the OpenTelemetry [Java agent](https://signoz.io/blog/opentelemetry-agent/#java-bytecode-manipulation) attached with `-javaagent`\n\n: it auto-instruments servlets and common libraries with no code changes, which is why the steps below rely on it instead of a manual SDK setup. The agent handles all three signals (traces, metrics, and logs) and exports them directly to SigNoz Cloud via [OTLP](https://signoz.io/blog/what-is-otlp/). Let's walk through each step.\n\nStep 1: Create the Project Structure\n\nCreate a project directory with the Dockerfile, demo web application, and supporting files:\n\n```\nmkdir -p tomcat-monitoring/webapps/ROOT\ncd tomcat-monitoring\n```\n\nCreate Dockerfile\n\nThe Dockerfile uses a multi-stage build: an Alpine stage downloads the OpenTelemetry Java agent, and the Tomcat stage copies it in and attaches it via `CATALINA_OPTS`\n\n.\n\n```\n# tomcat-monitoring/Dockerfile\nFROM alpine:3.20 AS otel\nARG OTEL_JAVA_AGENT_VERSION=2.13.3\nRUN apk add --no-cache curl \\\n    && mkdir -p /otel \\\n    && curl -fL \"https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar\" \\\n    -o /otel/opentelemetry-javaagent.jar\n\nFROM tomcat:10.1-jdk17-temurin\nCOPY --from=otel /otel/opentelemetry-javaagent.jar /opt/otel/opentelemetry-javaagent.jar\nCOPY webapps/ROOT /usr/local/tomcat/webapps/ROOT\n\n# Attach the OTel Java agent at Tomcat startup.\nENV CATALINA_OPTS=\"-javaagent:/opt/otel/opentelemetry-javaagent.jar\"\n```\n\nThe agent version is pinned via a build argument (`OTEL_JAVA_AGENT_VERSION=2.13.3`\n\n). This makes upgrades explicit and reproducible. The agent attaches to Tomcat through the standard `-javaagent`\n\nJVM flag, which Tomcat picks up from `CATALINA_OPTS`\n\n(an environment variable that Tomcat reads at startup to pass extra JVM arguments specifically to the Tomcat process).\n\nDemo Web Application\n\nCreate three JSP pages that give you different types of telemetry to verify in SigNoz: normal requests, slow requests, and errors.\n\n** /webapps/ROOT/index.jsp** is the landing page, which generates standard request traces:\n\n```\n<%@ page language=\"java\" contentType=\"text/html; charset=UTF-8\" pageEncoding=\"UTF-8\"%>\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Tomcat + OpenTelemetry + SigNoz</title>\n</head>\n<body>\n  <h1>Apache Tomcat Monitoring Demo</h1>\n  <p>This app is instrumented using the OpenTelemetry Java agent.</p>\n  <ul>\n    <li><a href=\"/slow.jsp?delayMs=500\">Slow endpoint (500 ms)</a></li>\n    <li><a href=\"/slow.jsp?delayMs=1200\">Slow endpoint (1200 ms)</a></li>\n    <li><a href=\"/error.jsp\">Error endpoint</a></li>\n  </ul>\n</body>\n</html>\n```\n\n** /webapps/ROOT/slow.jsp** has the sow endpoint that generates traces with configurable latency, useful for testing latency alerts:\n\n```\n<%@ page language=\"java\" contentType=\"text/plain; charset=UTF-8\" pageEncoding=\"UTF-8\"%>\n<%\n  String delayParam = request.getParameter(\"delayMs\");\n  int delayMs = 250;\n  if (delayParam != null) {\n    try {\n      delayMs = Integer.parseInt(delayParam);\n    } catch (NumberFormatException ignored) {\n      delayMs = 250;\n    }\n  }\n  Thread.sleep(Math.max(delayMs, 0));\n  out.println(\"Slept for \" + delayMs + \" ms\");\n%>\n```\n\n** /webapps/ROOT/error.jsp** exposes the error endpoint to generate traces with error status, useful for testing error rate alerts:\n\n```\n<%-- webapps/ROOT/error.jsp --%>\n<%@ page language=\"java\" contentType=\"text/plain; charset=UTF-8\" pageEncoding=\"UTF-8\"%>\n<%\n  throw new RuntimeException(\"Intentional demo error for telemetry\");\n%>\n```\n\nStep 2: Configure OpenTelemetry Environment Variables\n\nThe OpenTelemetry Java agent can be configured via environment variables, system properties, or a properties configuration file. The key variables for SigNoz Cloud are `OTEL_EXPORTER_OTLP_ENDPOINT`\n\nand `OTEL_EXPORTER_OTLP_HEADERS`\n\n.\n\nCreate a `.env`\n\nfile to store your SigNoz Cloud credentials:\n\n```\n# tomcat-monitoring/.env\nSIGNOZ_INGESTION_ENDPOINT=https://ingest.<region>.signoz.cloud:443\nSIGNOZ_INGESTION_KEY=<your-ingestion-key>\n\n# Optional overrides\nOTEL_SERVICE_NAME=tomcat-monitoring-demo\nOTEL_RESOURCE_ATTRIBUTES=deployment.environment=demo,service.version=1.0.0\nOTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf\n```\n\nReplace `<region>`\n\nwith your SigNoz Cloud region (`us`\n\n, `in`\n\n, or `eu`\n\n) and `<your-ingestion-key>`\n\nwith your [SigNoz ingestion key](https://signoz.io/docs/ingestion/signoz-cloud/keys/).\n\nNow create the**`docker-compose.yml`\n\n**file that wires the environment variables to the Tomcat container:\n\n```\n# tomcat-monitoring/docker-compose.yml\nservices:\n  tomcat:\n    build:\n      context: .\n    container_name: tomcat-otel-signoz-demo\n    ports:\n      - \"8080:8080\"\n    environment:\n      OTEL_SERVICE_NAME: \"${OTEL_SERVICE_NAME:-tomcat-monitoring-demo}\"\n      OTEL_RESOURCE_ATTRIBUTES: \"${OTEL_RESOURCE_ATTRIBUTES:-deployment.environment=demo,service.version=1.0.0}\"\n      OTEL_TRACES_EXPORTER: \"otlp\"\n      OTEL_METRICS_EXPORTER: \"otlp\"\n      OTEL_LOGS_EXPORTER: \"otlp\"\n      OTEL_EXPORTER_OTLP_PROTOCOL: \"${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf}\"\n      OTEL_EXPORTER_OTLP_ENDPOINT: \"${SIGNOZ_INGESTION_ENDPOINT}\"\n      OTEL_EXPORTER_OTLP_HEADERS: \"signoz-ingestion-key=${SIGNOZ_INGESTION_KEY}\"\n```\n\nThe Java agent enables all three signals (traces, metrics, and logs) and sends them directly to SigNoz Cloud.\n\nYour project directory should look like this:\n\n```\ntomcat-monitoring/\n├── Dockerfile\n├── docker-compose.yml\n├── .env\n└── webapps/\n    └── ROOT/\n        ├── index.jsp\n        ├── slow.jsp\n        └── error.jsp\n```\n\nStep 3: Start the Stack\n\nBuild and start the container:\n\n```\ndocker compose up --build -d\n```\n\nVerify Tomcat is running:\n\n```\ncurl http://localhost:8080\n```\n\nYou should see the demo landing page HTML with links to the slow and error endpoints.\n\nCheck that the OTel Java agent attached successfully:\n\n```\ndocker logs tomcat-otel-signoz-demo 2>&1 | grep -i \"opentelemetry\"\n```\n\nYou should see a line like `[otel.javaagent] opentelemetry-javaagent - version: 2.13.3`\n\n.\n\nGenerate Traffic\n\nTo produce meaningful telemetry data across all three endpoint types, create a load generation script:\n\n``` bash\n#!/usr/bin/env bash\n# tomcat-monitoring/scripts/generate-load.sh\nset -euo pipefail\n\nBASE_URL=\"${1:-http://localhost:8080}\"\nROUNDS=\"${2:-20}\"\n\necho \"Generating traffic against ${BASE_URL} for ${ROUNDS} rounds...\"\nfor i in $(seq 1 \"${ROUNDS}\"); do\n  curl -fsS \"${BASE_URL}/\" >/dev/null\n  curl -fsS \"${BASE_URL}/slow.jsp?delayMs=$((200 + (RANDOM % 1000)))\" >/dev/null\n  # Intentionally ignore failures from the error endpoint.\n  curl -s \"${BASE_URL}/error.jsp\" >/dev/null || true\ndone\n\necho \"Done. Check SigNoz Cloud for traces/metrics/logs from service: tomcat-monitoring-demo\"\n```\n\nRun it:\n\n```\nchmod +x scripts/generate-load.sh\n./scripts/generate-load.sh\n```\n\nThis sends 20 rounds of normal, slow (200-1200ms random delay), and error requests.\n\nStep 4: Verify in SigNoz\n\nNavigate to the**Services** tab (under APM) in your SigNoz dashboard. You should see `tomcat-monitoring-demo`\n\nlisted with the automatically generated RED metrics (Rate, Errors, Duration).\n\nClick on `tomcat-monitoring-demo`\n\nto view\n\n-\n**Application Metrics**: Request rate, average latency, error percentage, and p99 latencyThis gives you a high-level view of request rate, latency distribution, and error percentage for your Tomcat service.\n\n-\n**Traces**: Click any trace to see the span waterfall. You should see spans for`GET /`\n\n,`GET /slow.jsp`\n\n, and`GET /error.jsp`\n\n.Each span represents a single HTTP request handled by Tomcat, including slow and error endpoints.\n\nIf you want a broader view, switch to the `Traces`\n\ntab to see all captured requests.\n\nFinally, move to the `Logs`\n\ntab to inspect correlated application logs.\n\nApache Tomcat Performance Key Metrics\n\nNow that data is flowing, here are the metrics that matter most. The OpenTelemetry Java agent exports JVM runtime metrics automatically when `OTEL_METRICS_EXPORTER=otlp`\n\nis set. You can use these metric names directly in the SigNoz**Metrics**→** Query Builder**to create dashboard panels.\n\nJVM Metrics (Exported by Java Agent)\n\n| Metric | Description | What to Watch For |\n|---|---|---|\n`jvm.memory.used{jvm.memory.type=\"heap\"}` | Current heap memory usage in bytes | Baseline climbing toward 90%+ of max after full GC cycles indicates a memory leak |\n`jvm.memory.committed{jvm.memory.type=\"heap\"}` | Heap memory committed by the JVM | Should stay well below `jvm.memory.max` |\n`jvm.memory.limit{jvm.memory.type=\"heap\"}` | Maximum heap memory available | Use with `jvm.memory.used` to calculate utilization percentage |\n`jvm.memory.used{jvm.memory.type=\"nonheap\"}` | Non-heap memory (metaspace, code cache) | Steady growth may indicate classloader leaks |\n`jvm.thread.count` | Current number of live JVM threads | A sustained climb without traffic increase suggests thread leaks |\n`jvm.gc.duration.sum` | Cumulative GC pause time | Use `rate(jvm.gc.duration.sum[5m])` to track GC overhead; pauses above 200ms cause request timeouts |\n`jvm.gc.duration.count` | Number of GC events | Sudden spikes in GC frequency often precede OOM errors |\n`jvm.class.loaded` | Number of currently loaded classes | Should stabilize after startup; continuous growth indicates classloader leaks |\n\nDashboards and Alerts\n\nSigNoz provides a built-in RED metrics dashboard (Rate, Errors, Duration) out of the box for any service instrumented with the OpenTelemetry Java agent. You can find this under the `Services`\n\ntab → click on `tomcat-monitoring-demo`\n\nto see request rate, error percentage, and latency percentiles without any manual configuration.\n\nIf you want to create custom dashboards for JVM metrics or Tomcat-specific panels, follow the [SigNoz dashboard creation guide](https://signoz.io/docs/userguide/manage-dashboards/).\n\nSigNoz also supports threshold-based alerts on any metric. For Tomcat monitoring, configure alerts for these conditions:\n\n| Condition | Threshold | Severity |\n|---|---|---|\n| Error rate spike | `error_rate > 5%` for 3 min | Critical |\n| Request latency P95 | `> 2000ms` for 5 min | Warning |\n| Heap memory pressure | `heap_used / heap_max > 0.9` after GC | Warning |\n| Thread pool saturation | `busy_threads / max_threads > 0.85` for 5 min | Warning |\n| Active sessions growing | `delta(active_sessions[1h]) > 500` without traffic increase | Info |\n\nFor instructions on configuring notification channels (Slack, email, PagerDuty), see the [SigNoz alerts documentation](https://signoz.io/docs/alerts/).\n\nRelated reading: the same agent-based approach extends to broader [Java application monitoring](https://signoz.io/guides/monitoring-java-applications/), and the patterns here mirror [Node.js performance monitoring](https://signoz.io/blog/nodejs-performance-monitoring/) on the metrics side. If you're new to reading the trace data, this primer on [traces vs spans](https://signoz.io/comparisons/opentelemetry-trace-vs-span/) is a good starting point, and our roundup of [APM tools](https://signoz.io/blog/apm-tools/) covers how SigNoz compares to other options.\n\nFrequently Asked Questions\n\nWhy is my Tomcat server running out of memory (Heap)?\n\nThis is usually due to improper heap size settings or a memory leak in the application. Monitoring tools focus on JVM Garbage Collection (GC) activity to assess whether memory is reclaimed efficiently.\n\nHow much overhead does the Java agent add?\n\nThe Overhead varies by workload and configuration. Benchmark in staging, then reduce cost/overhead using sampling and by disabling unnecessary instrumentations.\n\nHow do I monitor multiple Tomcat instances?\n\nEach Tomcat instance runs its own Java agent and exports directly to SigNoz Cloud. Use `OTEL_SERVICE_NAME`\n\nand `OTEL_RESOURCE_ATTRIBUTES`\n\nto distinguish between instances:\n\n```\n# Instance 1\nOTEL_SERVICE_NAME: \"tomcat-app-1\"\nOTEL_RESOURCE_ATTRIBUTES: \"deployment.environment=production,host.name=server-1\"\n\n# Instance 2\nOTEL_SERVICE_NAME: \"tomcat-app-2\"\nOTEL_RESOURCE_ATTRIBUTES: \"deployment.environment=production,host.name=server-2\"\n```\n\nWhat are the most important Tomcat metrics to track?\n\nIf you're setting up a dashboard, these are the \"Golden Signals\":\n\n- **Request Latency:** Average processing time per request.\n- **Error Counts:** Tracking 4xx and 5xx HTTP status codes.\n- **Throughput:** Requests per second (RPS).\n**JVM Metrics:** Heap usage, non-heap usage, and GC overhead.", "url": "https://wpnews.pro/news/tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide", "canonical_source": "https://signoz.io/guides/tomcat-performance-monitoring", "published_at": "2026-06-29 00:00:00+00:00", "updated_at": "2026-06-29 13:20:56.924457+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Apache Tomcat", "OpenTelemetry", "SigNoz", "Java", "Docker"], "alternates": {"html": "https://wpnews.pro/news/tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide", "markdown": "https://wpnews.pro/news/tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide.md", "text": "https://wpnews.pro/news/tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide.txt", "jsonld": "https://wpnews.pro/news/tomcat-performance-monitoring-with-opentelemetry-key-metrics-and-setup-guide.jsonld"}}