Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ibm-mq-metrics/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ metrics:
enabled: true
"ibm.mq.heartbeat": # Queue manager heartbeat
enabled: true
"ibm.mq.queue_manager.uptime": # Queue manager uptime
enabled: true
"ibm.mq.archive.log.size": # Queue manager archive log size
enabled: true
"ibm.mq.manager.max.active.channels": # Queue manager max active channels
Expand Down
10 changes: 10 additions & 0 deletions ibm-mq-metrics/model/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,16 @@ groups:
attributes:
- ref: ibm.mq.queue.manager
requirement_level: required
- id: ibm.mq.queue_manager.uptime
type: metric
metric_name: ibm.mq.queue_manager.uptime
stability: development
brief: "Queue manager uptime"
instrument: counter
unit: "s"
attributes:
- ref: ibm.mq.queue.manager
requirement_level: required
- id: ibm.mq.archive.log.size
type: metric
metric_name: ibm.mq.archive.log.size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,14 @@ public static LongGauge createIbmMqHeartbeat(Meter meter) {
.build();
}

public static LongCounter createIbmMqQueueManagerUptime(Meter meter) {
return meter
.counterBuilder("ibm.mq.queue_manager.uptime")
.setUnit("s")
.setDescription("Queue manager uptime")
.build();
}

public static LongGauge createIbmMqArchiveLogSize(Meter meter) {
return meter
.gaugeBuilder("ibm.mq.archive.log.size")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ public boolean isIbmMqHeartbeatEnabled() {
return isEnabled("ibm.mq.heartbeat");
}

public boolean isIbmMqQueueManagerUptimeEnabled() {
return isEnabled("ibm.mq.queue_manager.uptime");
Comment thread
harshitt13 marked this conversation as resolved.
}

public boolean isIbmMqArchiveLogSizeEnabled() {
return isEnabled("ibm.mq.archive.log.size");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import com.ibm.mq.headers.pcf.PCFException;
import com.ibm.mq.headers.pcf.PCFMessage;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

public final class MessageBuddy {

Expand Down Expand Up @@ -72,4 +74,29 @@ public static long channelStartTime(PCFMessage message) throws PCFException {
public static String jobName(PCFMessage message) throws PCFException {
return message.getStringParameterValue(CMQCFC.MQCACH_MCA_JOB_NAME).trim();
}

/**
* Calculate the queue manager uptime in seconds.
*
* <p>Fetches the queue manager start date and time from the PCF response, parses them, and
* calculates the difference from the current system time.
*
* @param message the PCF response message containing queue manager information
* @return uptime in seconds since the queue manager started
* @throws PCFException if the required attributes cannot be retrieved from the PCF message
*/
public static long queueManagerUptime(PCFMessage message) throws PCFException {
String date = message.getStringParameterValue(CMQCFC.MQCACF_Q_MGR_START_DATE).trim();
String time = message.getStringParameterValue(CMQCFC.MQCACF_Q_MGR_START_TIME).trim();

// Parse the date (format: yyyy-MM-dd) and time (format: HH.mm.ss)
// The queue manager start timestamp does not include timezone information in this PCF response,
// so we normalize to UTC for a stable and predictable baseline across regions.
LocalDateTime qmgrStartLocal = LocalDateTime.parse(date + "T" + time.replaceAll("\\.", ":"));
Instant qmgrStartInstant = qmgrStartLocal.toInstant(ZoneOffset.UTC);
Instant now = Instant.now();

// Calculate uptime in seconds
return now.getEpochSecond() - qmgrStartInstant.getEpochSecond();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.ibm.mq.constants.CMQCFC;
import com.ibm.mq.headers.pcf.PCFMessage;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.LongGauge;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.ibm.mq.metrics.Metrics;
Expand All @@ -30,6 +31,7 @@ public final class QueueManagerMetricsCollector implements Consumer<MetricsColle
private final LongGauge reuseLogSizeGauge;
private final LongGauge archiveLogSizeGauge;
private final LongGauge maxActiveChannelsGauge;
private final LongCounter queueManagerUptimeCounter;

public QueueManagerMetricsCollector(Meter meter) {
this.statusGauge = Metrics.createIbmMqManagerStatus(meter);
Expand All @@ -38,6 +40,7 @@ public QueueManagerMetricsCollector(Meter meter) {
this.reuseLogSizeGauge = Metrics.createIbmMqReusableLogSize(meter);
this.archiveLogSizeGauge = Metrics.createIbmMqArchiveLogSize(meter);
this.maxActiveChannelsGauge = Metrics.createIbmMqManagerMaxActiveChannels(meter);
this.queueManagerUptimeCounter = Metrics.createIbmMqQueueManagerUptime(meter);
}

@Override
Expand Down Expand Up @@ -91,6 +94,17 @@ public void accept(MetricsCollectorContext context) {
int maxActiveChannels = context.getQueueManager().getMaxActiveChannels();
maxActiveChannelsGauge.set(maxActiveChannels, attributes);
}
if (context.getMetricsConfig().isIbmMqQueueManagerUptimeEnabled()) {
try {
long uptimeSeconds = MessageBuddy.queueManagerUptime(responses.get(0));
queueManagerUptimeCounter.add(uptimeSeconds, attributes);
} catch (Exception e) {
logger.debug(
"Unable to calculate queue manager uptime for {}: {}",
context.getQueueManagerName(),
e.getMessage());
}
}
Comment thread
harshitt13 marked this conversation as resolved.
} catch (Exception e) {
logger.error(e.getMessage());
throw new IllegalStateException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.ibm.mq.metricscollector;

import static org.assertj.core.api.Assertions.assertThat;

import com.ibm.mq.constants.CMQCFC;
import com.ibm.mq.headers.pcf.PCFException;
import com.ibm.mq.headers.pcf.PCFMessage;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.jupiter.api.Test;

class MessageBuddyTest {

private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ROOT);
private static final DateTimeFormatter TIME_FORMATTER =
DateTimeFormatter.ofPattern("HH.mm.ss", Locale.ROOT);

@Test
void queueManagerUptimeUsesUtcAndDoesNotDriftWithSystemTimezone() throws PCFException {
TimeZone originalTz = TimeZone.getDefault();
try {
Instant startInstant = Instant.now().minusSeconds(600);
LocalDateTime startUtc = LocalDateTime.ofInstant(startInstant, ZoneOffset.UTC);
String startDate = startUtc.format(DATE_FORMATTER);
String startTime = startUtc.format(TIME_FORMATTER);

PCFMessage message = new PCFMessage(2, CMQCFC.MQCMD_INQUIRE_Q_MGR_STATUS, 1, true);
message.addParameter(CMQCFC.MQCACF_Q_MGR_START_DATE, startDate);
message.addParameter(CMQCFC.MQCACF_Q_MGR_START_TIME, startTime);

TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Kiritimati"));
long beforeFirst = Instant.now().getEpochSecond();
long firstResult = MessageBuddy.queueManagerUptime(message);
long afterFirst = Instant.now().getEpochSecond();

TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Honolulu"));
long beforeSecond = Instant.now().getEpochSecond();
long secondResult = MessageBuddy.queueManagerUptime(message);
long afterSecond = Instant.now().getEpochSecond();

long expectedLowerFirst = beforeFirst - startInstant.getEpochSecond();
long expectedUpperFirst = afterFirst - startInstant.getEpochSecond();
assertThat(firstResult).isBetween(expectedLowerFirst, expectedUpperFirst);

long expectedLowerSecond = beforeSecond - startInstant.getEpochSecond();
long expectedUpperSecond = afterSecond - startInstant.getEpochSecond();
assertThat(secondResult).isBetween(expectedLowerSecond, expectedUpperSecond);

// Changing the JVM default timezone should not materially change uptime when UTC is used.
assertThat(Math.abs(firstResult - secondResult)).isLessThanOrEqualTo(2);
} finally {
TimeZone.setDefault(originalTz);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

package io.opentelemetry.ibm.mq.metricscollector;

import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
Expand All @@ -20,8 +19,6 @@
import io.opentelemetry.ibm.mq.opentelemetry.ConfigWrapper;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -58,14 +55,18 @@ void testProcessPCFRequestAndPublishQMetricsForInquireQStatusCmd() throws Except
new QueueManagerMetricsCollector(
otelTesting.getOpenTelemetry().getMeter("opentelemetry.io/mq"));
classUnderTest.accept(context);
List<String> metricsList = new ArrayList<>(singletonList("ibm.mq.manager.status"));

long managerStatus = Long.MIN_VALUE;
long queueManagerUptime = Long.MIN_VALUE;
for (MetricData metric : otelTesting.getMetrics()) {
if (metricsList.remove(metric.getName())) {
assertThat(metric.getLongGaugeData().getPoints().iterator().next().getValue()).isEqualTo(2);
if ("ibm.mq.manager.status".equals(metric.getName())) {
managerStatus = metric.getLongGaugeData().getPoints().iterator().next().getValue();
}
if ("ibm.mq.queue_manager.uptime".equals(metric.getName())) {
queueManagerUptime = metric.getLongSumData().getPoints().iterator().next().getValue();
}
}
assertThat(metricsList).isEmpty();
assertThat(managerStatus).isEqualTo(2);
assertThat(queueManagerUptime).isGreaterThan(0);
}

/* Request
Expand Down Expand Up @@ -115,6 +116,8 @@ private static PCFMessage[] createPCFResponseForInquireQMgrStatusCmd() {
response1.addParameter(CMQCFC.MQIACF_RESTART_LOG_SIZE, 42);
response1.addParameter(CMQCFC.MQIACF_REUSABLE_LOG_SIZE, 42);
response1.addParameter(CMQCFC.MQIACF_ARCHIVE_LOG_SIZE, 42);
response1.addParameter(CMQCFC.MQCACF_Q_MGR_START_DATE, "2024-01-01");
response1.addParameter(CMQCFC.MQCACF_Q_MGR_START_TIME, "12.00.00");

return new PCFMessage[] {response1};
}
Expand Down
2 changes: 2 additions & 0 deletions ibm-mq-metrics/src/test/resources/conf/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ metrics:
enabled: true
"ibm.mq.heartbeat": # Queue manager heartbeat
enabled: true
"ibm.mq.queue_manager.uptime": # Queue manager uptime
enabled: true
"ibm.mq.archive.log.size": # Queue manager archive log size
enabled: true
"ibm.mq.manager.max.active.channels": # Queue manager max active channels
Expand Down
Loading