Skip to content

Instantly share code, notes, and snippets.

@ibmmqmet
Created November 20, 2017 09:21
Show Gist options
  • Save ibmmqmet/3196d9da2d37ef17aa61370cfcd2d92e to your computer and use it in GitHub Desktop.
Save ibmmqmet/3196d9da2d37ef17aa61370cfcd2d92e to your computer and use it in GitHub Desktop.
Adding resource statistics to your applications.
/*
Copyright (c) IBM Corporation 2017
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific
Contributors:
Mark Taylor - Initial Contribution
*/
package amqjrua;
/*
* This is a demonstration of how user applications can produce resource usage data in the same format used
* by amqsrua that can then be collected by monitoring programs.
* MQ V9.0.2 is needed as the minimum to run this program as the Java PCF classes in previous levels of MQ do not
* correctly build MQCFGR objects. That is also the version of MQ where amqsrua was enhanced to support arbitrary
* metadata prefix values, needed for application-supplied topics.
*
* This program uses JMS as the core messaging API.
*/
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Random;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.headers.pcf.MQCFGR;
import com.ibm.mq.headers.pcf.MQCFH;
import com.ibm.mq.headers.pcf.PCFMessage;
import com.ibm.mq.jms.MQTopic;
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsConstants;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.wmq.WMQConstants;
public class MQMonitor {
// Values for connectivity to my queue manager.
static String qmgrName = "QM1";
static String channel = "SYSTEM.DEF.SVRCONN";
static String connName = "localhost(1414)";
// This is used for the metadata prefix and must be unique
final static String APPLICATION_NAME = "DemoMQMonitor";
// amqsrua should be started with "-p $SYS/Application/DemoMQMonitor" to match these topics
final static String monitorTopicRealRoot = "$SYS/Application/" + APPLICATION_NAME + "/INFO/QMGR/" + qmgrName + "/Monitor";
final static String monitorTopicMetaRoot = monitorTopicRealRoot + "/METADATA";
private Connection connection;
private Session session;
final static int delaySeconds = 4; // Frequency of generation of data
private MessageProducer monitorT1Producer = null; // Used to publish to the statistics topics
private MessageProducer monitorT2Producer = null;
// Data structure building the tree of classes, types and elements
private HashMap<Integer, MQMonitorResource> monitorClasses = new HashMap<Integer, MQMonitorResource>();
private long previousTime = System.nanoTime(); // Used to calculate monitoring interval
// Constants to insert and access elements in the tree of metrics.
static final private int CLASS_SYSTEM = 0;
static final private int CLASS_OBJECTS = 1;
static final private int TYPE_SYSTEM_T1 = 0;
static final private int TYPE_SYSTEM_T2 = 1;
static final private int TYPE_OBJECTS_SPECIFIC = 0;
static final private int ELEM_TOTAL = MQConstants.MQIA_USER_LIST + 42; // Pick an arbitrary number that is not in current MQIA range
static final private int ELEM_SUCCESS = ELEM_TOTAL + 1;
static final private int ELEM_FAIL = ELEM_TOTAL + 2;
static final private int ELEM_RANDOM = ELEM_TOTAL + 3;
static final private int ELEM_ITERS = ELEM_TOTAL + 4;
static private Random r = new Random(); // for demo data generation
static private HashMap<Integer, DemoObject> demoObjects = new HashMap<Integer, DemoObject>();
// Create some objects whose statistics we will
// later make available. A hashmap is just a convenient way of
// building and walking through this list.
static {
demoObjects.put(1, new DemoObject("A"));
demoObjects.put(2, new DemoObject("B"));
demoObjects.put(3, new DemoObject("C"));
}
int iterations = 0;
// amqsrua will look for translated descriptions in a set of known
// locales. Only if no messages are found on the locale topic will it
// look at the "base" topic name.
// This code does not translate the descriptions. But we will publish
// the CLASSES metadata on each locale anyway, always pointing at English text
// and the common metadata for TYPES and ELEMENTS. That can help speed up
// the initial invocation of amqsrua.
private static final String[] locales = { "",
"En_US", "en_US", "Cs_CZ", "cs_CZ",
"De_DE", "de_DE", "Es_ES", "es_ES",
"Fr_FR", "fr_FR", "Hu_HU", "hu_HU",
"It_IT", "it_IT", "Ja_JP", "ja_JP",
"Ko_KR", "ko_KR", "Pl_PL", "pl_PL",
"Pt_BR", "pt_BR", "Ru_RU", "ru_RU",
"Zh_CN", "zh_CN", "Zh_TW", "zh_TW"
};
/**
* The public main routine for this program.
* Can use positional parms to set qmgr, connName and channelName
*/
public static void main(String[] args) {
if (args.length > 0) qmgrName = args[0];
if (args.length > 1) connName = args[1];
if (args.length > 2) channel = args[2];
new MQMonitor().run();
}
/**
* The real main function of the program
*/
void run() {
boolean keepRunning = true;
// Connect to the queue manager as a client
JmsFactoryFactory ff;
try {
ff = JmsFactoryFactory.getInstance(JmsConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, qmgrName);
cf.setStringProperty(WMQConstants.WMQ_CONNECTION_NAME_LIST, connName);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channel);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
connection = cf.createConnection();
connection.start();
session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
System.out.println("Connected to queue manager " + qmgrName);
} catch (JMSException e) {
e.printStackTrace();
System.exit(1);
}
// Publish the metadata describing the statistics that will be produced
try {
publishMetaData();
} catch (JMSException | IOException e) {
e.printStackTrace();
System.exit(1);
}
// Then go into a permanent loop writing the real statistics
while (keepRunning) {
try {
iterations++;
publish();
Thread.sleep(delaySeconds * 1000);
} catch (InterruptedException e) {
} catch (JMSException | IOException e) {
e.printStackTrace();
keepRunning = false;
}
}
}
/*
* Publish the metadata describing the classes, types and specific metric elements.
*/
void publishMetaData() throws JMSException, IOException {
String metaTopic;
PCFMessage m;
MQMonitorResource c;
MQMonitorResource t;
Destination metaTopicDestination;
System.out.println("Writing monitor metadata");
/*
* Build the tree of Class/Type/Element objects that define the monitoring resources and then get published in metadata topics for
* amqsrua etc to display.
* In this example, there are two classes of data, SYSTEM and OBJECTS. The SYSTEM class has two sub-types, each of which
* (in this case) produce the same metric elements - "random" and "iteration count".
* The OBJECT class only has a single subtype, which contains 3 metrics along with the object's name. The flags value shows
* whether the monitoring program must incorporate a specific object name (eg a queue or topic as appropriate).
*
* In a real system, it's likely that every type will have a different set of data elements but I've just used a couple of basic
* values here.
*/
monitorClasses.put(CLASS_SYSTEM, new MQMonitorResource("SYSTEM", "System Data", MQConstants.MQIAMO_MONITOR_FLAGS_NONE));
monitorClasses.put(CLASS_OBJECTS, new MQMonitorResource("OBJECTS", "Object Data", MQConstants.MQIAMO_MONITOR_FLAGS_OBJNAME));
c = monitorClasses.get(CLASS_SYSTEM);
c.children.put(TYPE_SYSTEM_T1, new MQMonitorResource("T1", "Data about system resource T1"));
c.children.put(TYPE_SYSTEM_T2, new MQMonitorResource("T2", "Data about system resource T2"));
t = c.children.get(TYPE_SYSTEM_T1);
t.children.put(ELEM_RANDOM, new MQMonitorResource("Random", "A random number"));
t.children.put(ELEM_ITERS, new MQMonitorResource("Iterations", "How many loops generated"));
t = c.children.get(TYPE_SYSTEM_T2);
t.children.put(ELEM_RANDOM, new MQMonitorResource("Random", "A random number"));
t.children.put(ELEM_ITERS, new MQMonitorResource("Iterations", "How many loops generated"));
// The per-object stats
c = monitorClasses.get(CLASS_OBJECTS);
c.children.put(TYPE_OBJECTS_SPECIFIC, new MQMonitorResource("OBJECTNAME", "Object Specific Data"));
t = c.children.get(TYPE_OBJECTS_SPECIFIC);
t.children.put(ELEM_SUCCESS, new MQMonitorResource("Success", "Successful invocations"));
t.children.put(ELEM_FAIL, new MQMonitorResource("Failure", "Failed invocations"));
t.children.put(ELEM_TOTAL, new MQMonitorResource("Total", "Total invocations"));
// We publish the CLASS data on a range of locales, but none of the other
// components of the metadata are locale-based. The CLASSES all point to the same
// TYPE topic, regardless of language.
for (String locale : locales) {
metaTopic = monitorTopicMetaRoot + "/CLASSES";
if (locale.length() > 0)
metaTopic += "/" + locale;
metaTopicDestination = session.createTopic(metaTopic);
m = createClassesMessage(monitorClasses);
publishPCFMessage(m, null, metaTopicDestination, true);
}
// Iterate through the parent/child relationships to get the name and description
// for each metric, and publish the details.
for (int i : monitorClasses.keySet()) {
metaTopic = monitorTopicMetaRoot + "/TYPES/" + monitorClasses.get(i).name;
metaTopicDestination = session.createTopic(metaTopic);
m = createTypesMessage(monitorClasses.get(i), i);
publishPCFMessage(m, null, metaTopicDestination, true);
for (int j : monitorClasses.get(i).children.keySet()) {
metaTopic = monitorTopicMetaRoot + "/ELEMS/" + monitorClasses.get(i).name + "/" + monitorClasses.get(i).children.get(j).name;
metaTopicDestination = session.createTopic(metaTopic);
m = createElementsMessage(monitorClasses.get(i), monitorClasses.get(i).children.get(j), i, monitorClasses.get(i).flags, j);
publishPCFMessage(m, null, metaTopicDestination, true);
}
}
// Commit the delivery of all of the metadata messages
session.commit();
}
// This method publishes the actual statistics for each interval.
private void publish() throws JMSException, IOException {
PCFMessage m;
String metaTopic;
long currentTime = System.nanoTime();
System.out.println("Writing monitor data");
Object[] demoObjectNames = demoObjects.keySet().toArray();
for (Object demoObjectName : demoObjectNames) {
// Create some random values for the monitor to display
int successCount = r.nextInt(20);
int failCount = r.nextInt(20);
DemoObject demoObject = demoObjects.get(demoObjectName);
// Every object has its own producer object (like the hObj) because it will get
// reused on each iteration. This should be more efficient than getting the
// the topic reopened each time for each object. But if you have LOTS of objects, then
// watch out for the MAXHANDS attribute on the qmgr.
if (demoObject.producer == null) {
String monitorTopicName = monitorTopicRealRoot + "/" + monitorClasses.get(CLASS_OBJECTS).name + "/"
+ monitorClasses.get(CLASS_OBJECTS).children.get(TYPE_OBJECTS_SPECIFIC).name + "/" + demoObject.name;
Topic t = session.createTopic(monitorTopicName);
demoObject.producer = session.createProducer(t);
}
// Build the PCF message containing the stats for this object. The first group of
// fields are always required; the object-specific elements follow later.
PCFMessage monMsg = new PCFMessage(0);
monMsg.setHeader(new MQCFH());
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, CLASS_OBJECTS);
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_TYPE, TYPE_OBJECTS_SPECIFIC);
monMsg.addParameter(MQConstants.MQIAMO64_MONITOR_INTERVAL, (long) ((currentTime - previousTime) / 1000));
monMsg.addParameter(MQConstants.MQCA_TOPIC_NAME, demoObject.name);
monMsg.addParameter(MQConstants.MQIACF_OBJECT_TYPE, MQConstants.MQOT_TOPIC);
monMsg.addParameter(ELEM_SUCCESS, successCount);
monMsg.addParameter(ELEM_FAIL, failCount);
monMsg.addParameter(ELEM_TOTAL, successCount + failCount);
publishPCFMessage(monMsg, demoObject.producer, demoObject.producer.getDestination(), false);
}
// Then we publish the overall stats for each subtype of the system, T1 and T2.
// Again, there are topic producer objects associated with the publications that we keep open until
// termination.
if (monitorT1Producer == null) {
Topic t = session.createTopic(monitorTopicRealRoot + "/" + monitorClasses.get(CLASS_SYSTEM).name + "/"
+ monitorClasses.get(CLASS_SYSTEM).children.get(TYPE_SYSTEM_T1).name);
monitorT1Producer = session.createProducer(t);
}
PCFMessage monMsg = new PCFMessage(0);
monMsg.setHeader(new MQCFH());
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, CLASS_SYSTEM);
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_TYPE, TYPE_SYSTEM_T1);
monMsg.addParameter(MQConstants.MQIAMO64_MONITOR_INTERVAL, (long) ((currentTime - previousTime) / 1000));
monMsg.addParameter(ELEM_ITERS, iterations);
monMsg.addParameter(ELEM_RANDOM, r.nextInt(20));
publishPCFMessage(monMsg, monitorT1Producer, monitorT1Producer.getDestination(), false);
if (monitorT2Producer == null) {
Topic t = session.createTopic(monitorTopicRealRoot + "/" + monitorClasses.get(CLASS_SYSTEM).name + "/"
+ monitorClasses.get(CLASS_SYSTEM).children.get(TYPE_SYSTEM_T2).name);
monitorT2Producer = session.createProducer(t);
}
monMsg = new PCFMessage(0);
monMsg.setHeader(new MQCFH());
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, CLASS_SYSTEM);
monMsg.addParameter(MQConstants.MQIAMO_MONITOR_TYPE, TYPE_SYSTEM_T2);
monMsg.addParameter(MQConstants.MQIAMO64_MONITOR_INTERVAL, (long) ((currentTime - previousTime) / 1000));
monMsg.addParameter(ELEM_ITERS, iterations);
monMsg.addParameter(ELEM_RANDOM, r.nextInt(20));
publishPCFMessage(monMsg, monitorT2Producer, monitorT2Producer.getDestination(), false);
previousTime = currentTime;
// Commit the delivery of all the statistics in this batch.
session.commit();
}
// Build the metadata message describing which classes are offered.
private PCFMessage createClassesMessage(HashMap<Integer, MQMonitorResource> monitorClasses) {
PCFMessage m = new PCFMessage(0);
MQCFH cfh = new MQCFH();
cfh.setType(MQConstants.MQCFT_STATISTICS);
cfh.setVersion(MQConstants.MQCFH_VERSION_3);
cfh.setCommand(MQConstants.MQCMD_NONE);
cfh.setMsgSeqNumber(1);
cfh.setControl(MQConstants.MQCFC_LAST);
cfh.setCompCode(0);
cfh.setReason(0);
cfh.setParameterCount(0);
m.setHeader(cfh);
for (int i : monitorClasses.keySet()) {
MQCFGR cfgr = new MQCFGR();
cfgr.setParameter(MQConstants.MQGACF_MONITOR_CLASS);
cfgr.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, i);
cfgr.addParameter(MQConstants.MQIAMO_MONITOR_FLAGS, monitorClasses.get(i).flags);
cfgr.addParameter(MQConstants.MQCAMO_MONITOR_CLASS, monitorClasses.get(i).name);
cfgr.addParameter(MQConstants.MQCAMO_MONITOR_DESC, monitorClasses.get(i).desc);
cfgr.addParameter(MQConstants.MQCA_TOPIC_STRING, monitorTopicMetaRoot + "/TYPES/" + monitorClasses.get(i).name);
m.addParameter(cfgr);
}
return m;
}
// Build the metadata message describing which types are offered for this class.
private PCFMessage createTypesMessage(MQMonitorResource monitorTypes, int classNumber) {
MQMonitorResource elem;
PCFMessage m = new PCFMessage(0);
MQCFH cfh = new MQCFH();
cfh.setType(MQConstants.MQCFT_STATISTICS);
cfh.setVersion(MQConstants.MQCFH_VERSION_3);
cfh.setCommand(MQConstants.MQCMD_NONE);
cfh.setMsgSeqNumber(1);
cfh.setControl(MQConstants.MQCFC_LAST);
cfh.setCompCode(0);
cfh.setReason(0);
cfh.setParameterCount(0);
m.setHeader(cfh);
m.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, classNumber);
m.addParameter(MQConstants.MQIAMO_MONITOR_FLAGS, monitorTypes.flags);
for (int i : monitorTypes.children.keySet()) {
MQCFGR cfgr = new MQCFGR();
cfgr.setParameter(MQConstants.MQGACF_MONITOR_TYPE);
elem = monitorTypes.children.get(i);
cfgr.addParameter(MQConstants.MQIAMO_MONITOR_TYPE, i);
cfgr.addParameter(MQConstants.MQCAMO_MONITOR_TYPE, elem.name);
cfgr.addParameter(MQConstants.MQCAMO_MONITOR_DESC, elem.desc);
cfgr.addParameter(MQConstants.MQCA_TOPIC_STRING, monitorTopicMetaRoot + "/ELEMS/" + monitorTypes.name + "/" + elem.name);
m.addParameter(cfgr);
}
return m;
}
// Build the metadata message describing which elements (metrics) are available for each subtype. If
// the class refers to a named resource, then add the %s to the topic so that amqsrua can subscribe to the
// correct topic later.
private PCFMessage createElementsMessage(MQMonitorResource monitorType, MQMonitorResource monitorElements, int classNumber, int classFlags, int typeNumber) {
MQMonitorResource elem;
PCFMessage m = new PCFMessage(0);
MQCFH cfh = new MQCFH();
cfh.setType(MQConstants.MQCFT_STATISTICS);
cfh.setVersion(MQConstants.MQCFH_VERSION_3);
cfh.setCommand(MQConstants.MQCMD_NONE);
cfh.setMsgSeqNumber(1);
cfh.setControl(MQConstants.MQCFC_LAST);
cfh.setCompCode(0);
cfh.setReason(0);
cfh.setParameterCount(0);
m.setHeader(cfh);
m.addParameter(MQConstants.MQIAMO_MONITOR_CLASS, classNumber);
m.addParameter(MQConstants.MQIAMO_MONITOR_TYPE, typeNumber);
m.addParameter(MQConstants.MQIAMO_MONITOR_FLAGS, monitorType.flags);
for (int i : monitorElements.children.keySet()) {
MQCFGR cfgr = new MQCFGR();
cfgr.setParameter(MQConstants.MQGACF_MONITOR_ELEMENT);
elem = monitorElements.children.get(i);
cfgr.addParameter(MQConstants.MQIAMO_MONITOR_ELEMENT, i);
// For some metrics, this may not be appropriate - for example, if the
// value is meant to replace a previous value rather than being combined. Using
// the DELTA flag means that a monitoring program knows to combine the values, if
// it gets multiple publications for the same resource in a single one of its polling
// iterations which is not necessarily the same as the generation period (delaySeconds in
// this program). There are also some scale options (hundredths etc) but I prefer to
// always work with the raw unit value - can then scale in the monitoring display program
int dataType = MQConstants.MQIAMO_MONITOR_DELTA;
cfgr.addParameter(MQConstants.MQIAMO_MONITOR_DATATYPE, dataType);
cfgr.addParameter(MQConstants.MQCAMO_MONITOR_DESC, elem.desc);
m.addParameter(cfgr);
}
// The %s will be replaced by the monitoring product with the name of the specific object to monitor.
String realTopic = monitorTopicRealRoot + "/" + monitorType.name + "/" + monitorElements.name;
if (classNumber == CLASS_OBJECTS && typeNumber == TYPE_OBJECTS_SPECIFIC)
realTopic += "/%s";
m.addParameter(MQConstants.MQCA_TOPIC_STRING, realTopic);
return m;
}
// This method sends the publication to the queue manager as a PCF message. Some
// special processing is needed when doing PCF in a JMS program, to ensure datatypes
// are correctly converted, and that the right header fields are set.
// The method either uses an existing producer object to write the publication, or creates
// a temporary producer as necessary.
private void publishPCFMessage(final PCFMessage pcfMessage, MessageProducer producer, Destination topic, boolean retain) throws JMSException, IOException {
MessageProducer p = null;
BytesMessage message = session.createBytesMessage();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutput dataOutput = new DataOutputStream(baos);
// Build the real message body contents from the PCF message
pcfMessage.write(dataOutput);
baos.flush();
byte[] ba = baos.toByteArray();
message.writeBytes(ba);
try {
if (producer == null)
p = session.createProducer(topic);
MQTopic t = (MQTopic) topic;
t.setIntProperty(MQConstants.WMQ_MESSAGE_BODY, MQConstants.WMQ_MESSAGE_BODY_MQ);
t.setMQMDWriteEnabled(true);
t.setBooleanProperty(MQConstants.WMQ_MQMD_WRITE_ENABLED, true);
message.setStringProperty(JmsConstants.JMS_IBM_MQMD_FORMAT, "MQPCF");
message.setStringProperty(JmsConstants.JMS_IBM_CHARACTER_SET, Charset.defaultCharset().name());
// Metadata is sent as a retained publication, ready for any consumer to discover. Other
// publications of real metrics can be sent as non-persistent as it doesn't really matter for them
// to survive a restart.
if (retain) {
message.setIntProperty(JmsConstants.JMS_IBM_RETAIN, JmsConstants.RETAIN_PUBLICATION);
message.setJMSDeliveryMode(DeliveryMode.PERSISTENT);
} else {
message.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
}
if (producer != null)
producer.send(message);
else
p.send(message);
} finally {
if (p != null)
p.close();
}
baos.close();
System.out.println(" Publishing on topic " + ((MQTopic)topic).getBaseTopicName());
}
}
// A simple class that we use in this demonstration. It holds just
// its own name, and the Producer object for its metrics. In real life
// of course, this kind of object would hold its own statistics too,
// but we're only sending random numbers for now.
class DemoObject {
String name;
MessageProducer producer;
DemoObject(String s) {
name = s;
}
}
// For the various CLASS/TYPE etc resources, we need to keep track
// of the parent/child relationships. This class manages that.
class MQMonitorResource {
String name;
String desc;
int CCSID;
int flags;
HashMap<Integer, MQMonitorResource> children = new HashMap<Integer, MQMonitorResource>();
// Only use this constructor if you need to override
// the default flags.
MQMonitorResource(String name, String desc, int flags) {
this.name = name;
this.desc = desc;
this.CCSID = MQConstants.MQCCSI_DEFAULT;
this.flags = flags;
}
MQMonitorResource(String name, String desc) {
this(name, desc, MQConstants.MQIAMO_MONITOR_FLAGS_NONE);
}
}
@ayzalzaleh
Copy link

Hi Mark,

This code must be used with amqsrua tool or it can be used alone?
Can you please provide a sample run and output?

I am looking for a way to get the list of MQ queues that may suffer from lock contention so that I can take action to create multiple queues for the same service.

Thanks

@ibmmqmet
Copy link
Author

ibmmqmet commented Mar 1, 2023

This gist is about adding the generation of metrics in your application so that tools like amqsrua could use it. So it has nothing to do with your question.

You can find the amqsrua examples in the MQ documentation. Lock contention is one of the metrics that is published, so you can get that info. A command something like amqsrua -m <QM> -c STATQ -t PUT -o <qname> will show that, as would any other tool that can process the same publications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment