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 @@ -37,6 +37,7 @@ public final class MetricProducer implements io.opentelemetry.sdk.metrics.export
private final Map<Attributes, Long> counterIbmMqQueueDepthHighEvent;
private final Map<Attributes, Long> counterIbmMqQueueDepthLowEvent;
private final Map<Attributes, Long> counterIbmMqUnauthorizedEvent;
private final Map<Attributes, Long> counterIbmMqQueueManagerUptime;
private final Map<Attributes, Long> counterIbmMqConnectionErrors;

private long currentEpochNanos;
Expand All @@ -50,6 +51,7 @@ public MetricProducer(Resource resource, InstrumentationScopeInfo info) {
this.counterIbmMqQueueDepthHighEvent = new ConcurrentHashMap<>();
this.counterIbmMqQueueDepthLowEvent = new ConcurrentHashMap<>();
this.counterIbmMqUnauthorizedEvent = new ConcurrentHashMap<>();
this.counterIbmMqQueueManagerUptime = new ConcurrentHashMap<>();
this.counterIbmMqConnectionErrors = new ConcurrentHashMap<>();
}

Expand Down Expand Up @@ -668,6 +670,36 @@ public void recordIbmMqHeartbeat(long value, Attributes attributes) {
this.currentEpochNanos, Clock.getDefault().now(), attributes, value)))));
}

public void addIbmMqQueueManagerUptime(long value, Attributes attributes) {
long cumulativeValue =
this.counterIbmMqQueueManagerUptime.compute(
attributes,
(k, v) -> {
if (v == null) {
return value;
} else {
return v + value;
}
});
metricData.add(
createMetricData(
this.resource,
this.instrumentationScopeInfo,
"ibm.mq.queue_manager.uptime",
"Queue manager uptime",
"s",
MetricDataType.LONG_SUM,
SumData.createLongSumData(
/* isMonotonic= */ true,
AggregationTemporality.CUMULATIVE,
Collections.singletonList(
LongPointData.create(
this.currentEpochNanos,
Clock.getDefault().now(),
attributes,
cumulativeValue)))));
}

public void recordIbmMqArchiveLogSize(long value, Attributes attributes) {
metricData.add(
createMetricData(
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 @@ -79,6 +79,17 @@ public void accept(MetricsCollectorContext context) {
int maxActiveChannels = context.getQueueManager().getMaxActiveChannels();
producer.recordIbmMqManagerMaxActiveChannels(maxActiveChannels, attributes);
}
if (context.getMetricsConfig().isIbmMqQueueManagerUptimeEnabled()) {
try {
long uptimeSeconds = MessageBuddy.queueManagerUptime(responses.get(0));
producer.addIbmMqQueueManagerUptime(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 @@ -57,10 +57,18 @@ void testProcessPCFRequestAndPublishQMetricsForInquireQStatusCmd() throws Except
classUnderTest = new QueueManagerMetricsCollector(producer);
classUnderTest.accept(context);
List<String> metricsList = new ArrayList<>(singletonList("ibm.mq.manager.status"));
metricsList.add("ibm.mq.queue_manager.uptime");

for (MetricData metric : producer.produce(Resource.empty())) {
if (metricsList.remove(metric.getName())) {
assertThat(metric.getLongGaugeData().getPoints().iterator().next().getValue()).isEqualTo(2);
if ("ibm.mq.manager.status".equals(metric.getName())) {
assertThat(metric.getLongGaugeData().getPoints().iterator().next().getValue())
.isEqualTo(2);
}
if ("ibm.mq.queue_manager.uptime".equals(metric.getName())) {
assertThat(metric.getLongSumData().getPoints().iterator().next().getValue())
.isGreaterThan(0);
}
}
}
assertThat(metricsList).isEmpty();
Expand Down Expand Up @@ -113,6 +121,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