Gumdrop includes a native OpenTelemetry implementation for distributed tracing, metrics, and observability. The telemetry system integrates with Gumdrop's event-driven architecture and exports data via OTLP/HTTP or OTLP/gRPC to any OpenTelemetry Collector, or to local JSONL files for offline analysis and debugging. Built-in instrumentation is provided for HTTP, SMTP, IMAP, POP3, and FTP servers, as well as the session replication cluster. The API enables custom instrumentation for any Gumdrop-based service.
The telemetry system provides:
Telemetry is configured via the TelemetryConfig component in
gumdroprc. HTTPS is strongly recommended for OTLP export to protect
telemetry data in transit:
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<!-- Service identification -->
<property name="service-name">my-mail-server</property>
<property name="service-version">1.0.0</property>
<property name="service-namespace">production</property>
<property name="deployment-environment">prod-east</property>
<!-- OTLP endpoint (OpenTelemetry Collector) - use HTTPS in production -->
<!-- For HTTP/protobuf use port 4318; for gRPC use port 4317 -->
<property name="endpoint">https://otel-collector:4318</property>
<property name="protocol">http/protobuf</property>
<!-- TLS configuration for OTLP export -->
<property name="truststore-file">/etc/gumdrop/otlp-truststore.p12</property>
<property name="truststore-pass">changeit</property>
<!-- Metrics configuration -->
<property name="metrics-temporality-name">cumulative</property>
<property name="metrics-interval-ms">60000</property>
</component>
The presence of a TelemetryConfig on a server enables telemetry;
there is no separate "enabled" flag. Individual signal types (traces, logs,
metrics) can be disabled if needed.
| Property | Default | Description |
|---|---|---|
traces-enabled | true | Enable trace collection |
logs-enabled | true | Enable log collection |
metrics-enabled | true | Enable metrics collection |
service-name | "gumdrop" | Service name in resource attributes |
service-version | - | Service version |
service-namespace | - | Service namespace |
service-instance-id | - | Unique instance identifier |
deployment-environment | - | Environment (prod, staging, dev) |
endpoint | - | Base OTLP endpoint URL (use https:// in production) |
traces-endpoint | endpoint + /v1/traces | Traces-specific endpoint |
logs-endpoint | endpoint + /v1/logs | Logs-specific endpoint |
metrics-endpoint | endpoint + /v1/metrics | Metrics-specific endpoint |
truststore-file | - | PKCS12 truststore for HTTPS endpoints |
truststore-pass | - | Password for the truststore |
metrics-temporality-name | "cumulative" | Aggregation: "delta" or "cumulative" |
metrics-interval-ms | 60000 | Metrics collection interval |
headers | - | Extra HTTP headers (key1=value1,key2=value2) |
timeout-ms | 10000 | Export timeout in milliseconds |
batch-size | 512 | Spans per export batch |
flush-interval-ms | 5000 | Maximum time between exports |
max-queue-size | 2048 | Maximum queued spans before dropping |
protocol | "http/protobuf" | Export protocol: "http/protobuf" (default, port 4318) or "grpc" (port 4317) |
exporter-type | "otlp" | Exporter type: "otlp" for OTLP export, "file" for JSONL file export |
file-traces-path | - | File path for traces JSONL output (null = stdout) |
file-logs-path | - | File path for logs JSONL output (null = stdout) |
file-metrics-path | - | File path for metrics JSONL output (null = stdout) |
file-buffer-size | 8192 | I/O buffer size in bytes for file exporter |
jmx-bridge-enabled | true | Expose metrics via JMX MBeans for jconsole, VisualVM, Prometheus JMX exporter |
include-exception-details | false | When false, span exception records include only the exception class name (exception.type); when true, include full message and stack trace for observability. Set to true only when telemetry export is trusted (e.g. internal collector). |
To enable telemetry for a server, reference the configuration:
<service id="smtp" class="org.bluezoo.gumdrop.smtp.SimpleRelayService">
<property name="telemetry-config" ref="#telemetry"/>
<listener class="org.bluezoo.gumdrop.smtp.SMTPListener" port="25"/>
</service>
<service id="http" class="org.bluezoo.gumdrop.servlet.ServletService">
<property name="telemetry-config" ref="#telemetry"/>
<listener class="org.bluezoo.gumdrop.http.HTTPListener">
<property name="port">8080</property>
</listener>
</service>
When telemetry is enabled, the HTTP server automatically creates spans for each request:
http.method - request methodhttp.url - request URLhttp.status_code - response statushttp.client.address - client IPtraceparent headertraceparent header in responseMetrics:
| Metric | Type | Description |
|---|---|---|
http.server.requests | Counter | Total HTTP requests received |
http.server.active_requests | UpDownCounter | Requests currently being processed |
http.server.active_connections | UpDownCounter | Active HTTP connections |
http.server.request.duration | Histogram | Request duration (ms) |
http.server.request.size | Histogram | Request body size (bytes) |
http.server.response.size | Histogram | Response body size (bytes) |
The SMTP server creates hierarchical traces for connections and sessions:
net.peer.ip - client IP addressnet.transport - TCPsmtp.session_number - session indexsmtp.client_hostname - HELO/EHLO hostnamesmtp.mail_from - sender addresssmtp.auth.mechanism - authentication methodsmtp.auth.user - authenticated userMetrics:
| Metric | Type | Description |
|---|---|---|
smtp.server.connections | Counter | Total SMTP connections |
smtp.server.active_connections | UpDownCounter | Active SMTP connections |
smtp.server.messages | Counter | Total messages received |
smtp.server.message.size | Histogram | Message size (bytes) |
smtp.server.recipients | Histogram | Recipients per message |
smtp.server.session.duration | Histogram | Session duration (ms) |
smtp.server.authentications | Counter | Authentication attempts |
smtp.server.authentications.success | Counter | Successful authentications |
smtp.server.authentications.failure | Counter | Failed authentications |
smtp.server.starttls | Counter | STARTTLS upgrades |
The IMAP server creates hierarchical traces for connections and sessions:
net.peer.ip - client IP addressnet.transport - TCPenduser.id - authenticated usernameimap.auth.mechanism - authentication methodMetrics:
| Metric | Type | Description |
|---|---|---|
imap.server.connections | Counter | Total IMAP connections |
imap.server.active_connections | UpDownCounter | Active IMAP connections |
imap.server.idle_connections | UpDownCounter | Connections in IDLE state |
imap.server.commands | Counter | Commands executed (by type) |
imap.server.command.duration | Histogram | Command execution time (ms) |
imap.server.session.duration | Histogram | Session duration (ms) |
imap.server.authentications | Counter | Authentication attempts |
imap.server.authentications.success | Counter | Successful authentications |
imap.server.authentications.failure | Counter | Failed authentications |
imap.server.messages.fetched | Counter | Messages fetched |
imap.server.messages.appended | Counter | Messages appended |
imap.server.messages.deleted | Counter | Messages deleted |
imap.server.messages.copied | Counter | Messages copied |
imap.server.starttls | Counter | STARTTLS upgrades |
The POP3 server creates traces similar to IMAP:
enduser.id - authenticated usernamepop3.mailbox.count - number of messagespop3.mailbox.size - total mailbox sizeMetrics:
| Metric | Type | Description |
|---|---|---|
pop3.server.connections | Counter | Total POP3 connections |
pop3.server.active_connections | UpDownCounter | Active POP3 connections |
pop3.server.session.duration | Histogram | Session duration (ms) |
pop3.server.commands | Counter | Commands executed (by type) |
pop3.server.authentications | Counter | Authentication attempts |
pop3.server.authentications.success | Counter | Successful authentications |
pop3.server.authentications.failure | Counter | Failed authentications |
pop3.server.messages.retrieved | Counter | Messages retrieved |
pop3.server.messages.deleted | Counter | Messages deleted |
pop3.server.bytes.transferred | Counter | Bytes transferred |
pop3.server.starttls | Counter | STLS upgrades |
The FTP server creates traces for connections and file operations:
enduser.id - authenticated usernameftp.cwd - current working directoryMetrics:
| Metric | Type | Description |
|---|---|---|
ftp.server.connections | Counter | Total FTP control connections |
ftp.server.active_connections | UpDownCounter | Active control connections |
ftp.server.active_data_connections | UpDownCounter | Active data connections |
ftp.server.session.duration | Histogram | Session duration (ms) |
ftp.server.commands | Counter | Commands executed (by type) |
ftp.server.authentications | Counter | Authentication attempts |
ftp.server.authentications.success | Counter | Successful authentications |
ftp.server.authentications.failure | Counter | Failed authentications |
ftp.server.transfers | Counter | Total file transfers |
ftp.server.uploads | Counter | Files uploaded (STOR) |
ftp.server.downloads | Counter | Files downloaded (RETR) |
ftp.server.bytes.uploaded | Counter | Bytes uploaded |
ftp.server.bytes.downloaded | Counter | Bytes downloaded |
ftp.server.transfer.duration | Histogram | Transfer duration (ms) |
ftp.server.transfer.size | Histogram | Transfer size (bytes) |
ftp.server.directory.listings | Counter | Directory listings |
ftp.server.directory.created | Counter | Directories created |
ftp.server.files.deleted | Counter | Files deleted |
ftp.server.auth_tls | Counter | AUTH TLS upgrades |
The DNS server creates spans for incoming queries and upstream proxy operations.
Metrics are tagged with dns.transport to distinguish UDP, DoT, and
DoQ listeners.
Metrics:
| Metric | Type | Description | Attributes |
|---|---|---|---|
dns.server.queries | Counter | Queries received | dns.question.type, dns.transport |
dns.server.responses | Counter | Responses sent | dns.response.code, dns.transport |
dns.server.query.duration | Histogram | Query processing time (ms) | dns.transport |
dns.server.cache.hits | Counter | Cache hits | |
dns.server.cache.misses | Counter | Cache misses | |
dns.server.upstream.queries | Counter | Queries forwarded upstream | |
dns.server.upstream.duration | Histogram | Upstream query duration (ms) | |
dns.server.upstream.failures | Counter | Upstream query failures |
When telemetry is configured on the parent HTTP listener,
WebSocketServerMetrics records connection and frame-level metrics.
Metrics:
| Metric | Type | Description | Attributes |
|---|---|---|---|
websocket.server.connections | Counter | Total WebSocket connections | |
websocket.server.session.duration | Histogram | Session duration (ms) | |
websocket.server.messages.received | Counter | Messages received | type |
websocket.server.messages.sent | Counter | Messages sent | type |
websocket.server.frames.received | Counter | Frames received | opcode |
websocket.server.frames.sent | Counter | Frames sent | opcode |
websocket.server.errors | Counter | WebSocket errors |
The session cluster provides metrics for distributed session replication when clustering is enabled for web applications:
Metrics:
| Metric | Type | Description | Attributes |
|---|---|---|---|
cluster.nodes.active | UpDownCounter | Active nodes in cluster | |
cluster.sessions.replicated | Counter | Sessions replicated to cluster | context |
cluster.sessions.received | Counter | Sessions received from cluster | context |
cluster.sessions.passivated | Counter | Sessions removed via cluster | context |
cluster.messages.sent | Counter | Messages sent to cluster | type |
cluster.messages.received | Counter | Messages received from cluster | type |
cluster.bytes.sent | Counter | Bytes sent to cluster | type |
cluster.bytes.received | Counter | Bytes received from cluster | type |
cluster.deltas.sent | Counter | Delta updates sent (incremental) | |
cluster.deltas.received | Counter | Delta updates received | |
cluster.fragments.sent | Counter | Message fragments sent | |
cluster.fragments.received | Counter | Message fragments received | |
cluster.fragments.reassembled | Counter | Fragmented messages reassembled | |
cluster.fragments.timed_out | Counter | Fragment sets that timed out | |
cluster.errors.decrypt | Counter | Decryption errors | |
cluster.errors.replay | Counter | Replay attacks detected | |
cluster.errors.timestamp | Counter | Timestamp validation failures | |
cluster.replication.duration | Histogram | Replication duration (ms) | type |
Attribute values:
context: The servlet context name (e.g., "/myapp")type (messages/bytes): "ping", "replicate", "passivate", "delta", or "fragment"type (replication duration): "full" or "delta"Custom servers and handlers can add their own instrumentation using the telemetry API.
// Get telemetry config (from server or connection)
TelemetryConfig config = server.getTelemetryConfig();
// Create a new trace with root span
Trace trace = config.createTrace("Process Order", SpanKind.SERVER);
// Or continue from incoming traceparent
String traceparent = request.getHeader("traceparent");
Trace trace = config.createTraceFromTraceparent(traceparent, "Process Order", SpanKind.SERVER);
// Add attributes to current span
trace.addAttribute("order.id", orderId);
trace.addAttribute("customer.id", customerId);
trace.addAttribute("order.total", 149.99);
// Add events to mark significant points
trace.addEvent("Payment validated");
trace.addEvent("Inventory reserved");
// Start a child span for a sub-operation
Span childSpan = trace.startSpan("Query Database", SpanKind.CLIENT);
try {
// ... database operation ...
childSpan.setStatusOk();
} catch (Exception e) {
childSpan.recordException(e);
} finally {
childSpan.end();
}
// End the trace (exports automatically)
trace.end();
// Set status explicitly
span.setStatusOk();
span.setStatusError("Validation failed");
span.setStatus(SpanStatus.UNSET);
// Record exception (sets ERROR status and adds event)
span.recordException(exception);
public class MyProtocolHandler implements ProtocolHandler {
private Trace endpointTrace;
@Override
public void connected(Endpoint endpoint) {
// Create endpoint-level trace
TelemetryConfig config = endpoint.getServerEndpoint().getTelemetryConfig();
if (config != null && config.isTracesEnabled()) {
endpointTrace = config.createTrace("MyProtocol endpoint", SpanKind.SERVER);
endpointTrace.addAttribute("net.peer.ip", endpoint.getRemoteAddress());
}
}
protected void processRequest(MyRequest request) {
// Start a span for this request
Span requestSpan = null;
if (endpointTrace != null) {
requestSpan = endpointTrace.startSpan("Process request");
requestSpan.addAttribute("request.type", request.getType());
}
try {
// Process request...
if (requestSpan != null) {
requestSpan.addEvent("Request processed");
requestSpan.setStatusOk();
}
} catch (Exception e) {
if (requestSpan != null) {
requestSpan.recordException(e);
}
throw e;
} finally {
if (requestSpan != null) {
requestSpan.end();
}
}
}
@Override
public void disconnected() {
if (endpointTrace != null) {
endpointTrace.end();
}
}
}
public class InstrumentedServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Get the stream's span (created automatically by HTTP server)
Span httpSpan = ((GumdropHttpServletRequest) req).getSpan();
if (httpSpan != null) {
// Add application-specific attributes
httpSpan.addAttribute("app.action", req.getParameter("action"));
httpSpan.addAttribute("app.user", req.getRemoteUser());
// Start child span for business logic
Span bizSpan = httpSpan.startChild("Business Logic", SpanKind.INTERNAL);
try {
processBusinessLogic(req, resp);
bizSpan.setStatusOk();
} catch (Exception e) {
bizSpan.recordException(e);
throw e;
} finally {
bizSpan.end();
}
} else {
// Telemetry disabled, just process normally
processBusinessLogic(req, resp);
}
}
}
Gumdrop provides OpenTelemetry-compatible metrics collection with synchronous and asynchronous instruments.
| Instrument | Type | Description | Use Case |
|---|---|---|---|
LongCounter | Sync | Monotonically increasing | Request count, bytes sent |
LongUpDownCounter | Sync | Can increase or decrease | Active connections, queue size |
DoubleHistogram | Sync | Distribution of values | Request latency, response size |
ObservableGauge | Async | Point-in-time value | Memory usage, temperature |
ObservableCounter | Async | Async monotonic counter | CPU time, page faults |
ObservableUpDownCounter | Async | Async bidirectional | Thread count, file handles |
// Get a meter from TelemetryConfig
Meter meter = config.getMeter("org.bluezoo.gumdrop.http");
// Synchronous counter
LongCounter requestCounter = meter.counterBuilder("http.server.requests")
.setDescription("Total HTTP requests received")
.setUnit("requests")
.build();
// Synchronous histogram with explicit buckets
DoubleHistogram latencyHistogram = meter.histogramBuilder("http.server.duration")
.setDescription("HTTP request latency")
.setUnit("ms")
.setExplicitBuckets(5, 10, 25, 50, 100, 250, 500, 1000)
.build();
// Up-down counter for current state
LongUpDownCounter activeConnections = meter.upDownCounterBuilder("http.server.active_connections")
.setDescription("Currently active connections")
.build();
// Record with attributes
requestCounter.add(1, Attributes.of(
"http.method", "GET",
"http.status_code", 200
));
latencyHistogram.record(45.2, Attributes.of("http.method", "GET"));
// Track active connections
activeConnections.add(1); // Connection opened
activeConnections.add(-1); // Connection closed
Observable instruments use callbacks invoked at collection time, ideal for values maintained externally:
// Observable gauge for memory usage
meter.gaugeBuilder("process.memory.used")
.setDescription("Used memory in bytes")
.setUnit("bytes")
.buildWithCallback(new ObservableCallback() {
public void observe(ObservableMeasurement measurement) {
Runtime rt = Runtime.getRuntime();
measurement.record(rt.totalMemory() - rt.freeMemory());
}
});
// Observable counter for OS statistics
meter.observableCounterBuilder("process.cpu.time")
.setDescription("CPU time used by process")
.setUnit("ns")
.buildWithCallback(new ObservableCallback() {
public void observe(ObservableMeasurement measurement) {
measurement.record(getProcessCpuTime());
}
});
Metrics can be exported with different temporalities:
<property name="metrics-temporality-name">delta</property>
Gumdrop supports W3C Trace Context for distributed tracing across services.
When a request includes a traceparent header, the server continues
that trace rather than starting a new one. The format is:
traceparent: 00-{trace-id}-{span-id}-{flags}
Example: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
When making outbound requests with the HTTP client, call
client.setTrace(state.getTrace()) before
connect(). The traceparent header is then added automatically
to every request, so the distributed trace remains connected without
manual header handling:
HTTPClient client = new HTTPClient(loop, downstreamHost, downstreamPort);
client.setTrace(state.getTrace()); // Propagates traceparent automatically
client.connect(new HTTPClientHandler() {
public void onConnected(Endpoint endpoint) {
HTTPRequest request = client.post("/api");
request.header("Content-Type", "application/json");
request.send(responseHandler);
}
// ...
});
If you omit setTrace(), no traceparent header is sent and the
downstream service will start a new trace. You can still add the header
manually via request.header("traceparent", trace.getTraceparent())
if needed, but automatic propagation is preferred.
This creates a connected chain of spans across services, visible in trace visualization tools.
| Kind | Use Case |
|---|---|
| SERVER | Handling an incoming request |
| CLIENT | Making an outbound request |
| PRODUCER | Creating a message for async processing |
| CONSUMER | Processing a message from a queue |
| INTERNAL | Internal operation (no network) |
The OTLP exporter sends telemetry data to an OpenTelemetry Collector. Two protocols are supported:
protocol="http/protobuf", uses port 4318,
streams protobuf directly to HTTP endpoints. Maximally efficient for HTTP-based
collectors.protocol="grpc", uses port 4317, sends gRPC-framed
protobuf to collector gRPC services. Use when the collector expects gRPC.HTTPS should be used in production to protect sensitive telemetry data including trace context, service identifiers, and custom attributes.
For HTTPS endpoints, configure a truststore containing the CA certificate(s) that signed the collector's certificate:
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<property name="service-name">my-service</property>
<property name="endpoint">https://otel-collector:4318</property>
<!-- Truststore containing the CA certificate for the collector -->
<property name="truststore-file">/etc/gumdrop/otlp-truststore.p12</property>
<property name="truststore-pass">changeit</property>
</component>
Create a PKCS12 truststore containing your CA certificate:
# Import a CA certificate into a new PKCS12 truststore
keytool -importcert -alias otel-ca \
-file /path/to/ca-cert.pem \
-keystore otlp-truststore.p12 \
-storetype PKCS12 \
-storepass changeit
If no truststore is configured and the endpoint uses HTTPS, the default JVM truststore will be used. This is suitable when the collector uses a certificate signed by a well-known CA.
Example OpenTelemetry Collector configuration with TLS to receive Gumdrop traces:
# otel-collector-config.yaml (HTTP receiver on 4318)
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
tls:
cert_file: /etc/otel/server.crt
key_file: /etc/otel/server.key
# For gRPC (port 4317), add:
# grpc:
# endpoint: 0.0.0.0:4317
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
# Or export to other backends
# zipkin:
# endpoint: http://zipkin:9411/api/v2/spans
# otlp:
# endpoint: tempo:4317
service:
pipelines:
traces:
receivers: [otlp]
exporters: [jaeger]
For authenticated endpoints, use the headers property:
<property name="headers">Authorization=Bearer token123,X-Org-Id=my-org</property>
<!-- Telemetry configuration with HTTPS -->
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<property name="service-name">mail-gateway</property>
<property name="service-version">2.1.0</property>
<property name="deployment-environment">production</property>
<property name="service-instance-id">mail-gw-01</property>
<!-- HTTPS endpoint (recommended for production) -->
<property name="endpoint">https://otel-collector.monitoring:4318</property>
<property name="truststore-file">/etc/gumdrop/otlp-truststore.p12</property>
<property name="truststore-pass">changeit</property>
<property name="batch-size">256</property>
<property name="flush-interval-ms">3000</property>
<property name="metrics-interval-ms">30000</property>
</component>
<!-- SMTP service with telemetry -->
<service id="smtp" class="org.bluezoo.gumdrop.smtp.SimpleRelayService">
<property name="telemetry-config" ref="#telemetry"/>
<property name="handler-factory" ref="#smtpHandler"/>
<listener class="org.bluezoo.gumdrop.smtp.SMTPListener" port="25"/>
</service>
<!-- HTTP servlet service with telemetry -->
<service id="http" class="org.bluezoo.gumdrop.servlet.ServletService">
<property name="telemetry-config" ref="#telemetry"/>
<property name="web-app" ref="#webapp"/>
<listener class="org.bluezoo.gumdrop.http.HTTPListener">
<property name="port">8080</property>
</listener>
</service>
Gumdrop ensures telemetry data is not lost during connection errors or JVM shutdown:
OTLPExporter.forceFlush() method can be
called programmatically to immediately export all pending data
The shutdown hook ensures that telemetry data buffered for batching is exported
when the server is stopped gracefully. For immediate data visibility during
development, reduce flush-interval-ms to export more frequently.
The OTLPFileExporter writes telemetry data to local files in
OpenTelemetry OTLP JSON Lines format. Each line is a complete OTLP JSON
object (ExportTraceServiceRequest,
ExportLogsServiceRequest, or
ExportMetricsServiceRequest) followed by a newline.
The file exporter is useful for:
jq, grep, custom scripts).jsonl files for each signal type (traces, logs,
metrics) as required by the specification
Set exporter-type to file and provide paths for each
signal type. Any signal without a configured path writes to stdout.
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<property name="service-name">my-service</property>
<property name="exporter-type">file</property>
<!-- Separate files per signal type -->
<property name="file-traces-path">/var/log/otel/traces.jsonl</property>
<property name="file-logs-path">/var/log/otel/logs.jsonl</property>
<property name="file-metrics-path">/var/log/otel/metrics.jsonl</property>
<!-- Optional: tune I/O buffer size -->
<property name="file-buffer-size">16384</property>
<property name="metrics-interval-ms">60000</property>
</component>
When no file paths are set, the exporter writes all signals to stdout. This is the simplest way to see telemetry output during development:
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<property name="service-name">my-service</property>
<property name="exporter-type">file</property>
<!-- No file paths = all output goes to stdout -->
</component>
The output files use standard JSON Lines format, one JSON object per line. You can process them with common tools:
# View traces with jq cat /var/log/otel/traces.jsonl | jq . # Find slow spans (duration > 1s) cat /var/log/otel/traces.jsonl | jq ' .resourceSpans[].scopeSpans[].spans[] | select(.endTimeUnixNano - .startTimeUnixNano > 1000000000)' # Count metrics by name cat /var/log/otel/metrics.jsonl | jq -r ' .resourceMetrics[].scopeMetrics[].metrics[].name' | sort | uniq -c # Tail traces in real time tail -f /var/log/otel/traces.jsonl | jq .
When metrics are enabled, Gumdrop exposes OpenTelemetry metrics via JMX MBeans. This allows JMX-based monitoring tools (jconsole, VisualVM, Prometheus JMX exporter) to access the same metrics without requiring OTLP export. OpenTelemetry remains the single source of truth; the JMX bridge reads from it on each attribute access.
Metrics are exposed under the domain org.bluezoo.gumdrop with type
Telemetry:
org.bluezoo.gumdrop:type=Telemetry
Metric names use underscores instead of dots (e.g., http.server.requests
becomes http_server_requests). Counters and gauges appear as single
numeric attributes. Histograms expose four attributes per metric:
| Suffix | Description |
|---|---|
_count | Total number of recorded values |
_sum | Sum of all recorded values |
_min | Minimum recorded value |
_max | Maximum recorded value |
Example attributes for HTTP server metrics:
http_server_requests - Total requests (counter)http_server_active_requests - Requests in progress (gauge)http_server_active_connections - Active connections (gauge)http_server_request_duration_count, _sum, _min, _max - Request duration histogramThe JMX bridge is enabled by default when metrics are enabled. Disable it with:
<component id="telemetry" class="org.bluezoo.gumdrop.telemetry.TelemetryConfig">
<property name="metrics-enabled">true</property>
<property name="jmx-bridge-enabled">false</property>
</component>
The bridge registers automatically during TelemetryConfig.init() and
unregisters on shutdown. No OTLP or file exporter is required; the JMX bridge
works with metrics alone.
jconsole / VisualVM: Connect to the Gumdrop process and navigate to
org.bluezoo.gumdrop → Telemetry to view metric values.
Prometheus JMX Exporter: Configure the
Prometheus JMX Exporter
to scrape the Gumdrop JVM. The exporter will discover the Telemetry
MBean and expose its attributes as Prometheus metrics.
← Back to Main Page | HTTP Server & Client | SMTP Server & Client | IMAP Server | POP3 Server
Gumdrop Telemetry