diff --git a/README.md b/README.md
index 7482bef..a1676cf 100644
--- a/README.md
+++ b/README.md
@@ -55,13 +55,60 @@ Support [IBM MQ](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com
- [IBM MQ](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.helphome.v90.doc/WelcomePagev9r0.htm)
#### Configuration
-All settings have to be set in mq-java-exporter\src\main\resources\exporter_config.yaml.
-- MQ connection information. Describes MQ connection information.
-- Prometheus connection information. Describes Prometheus connection information.
-- Monitoring objects. Sets names of objects, that have to be monitored: queues, channels.
+All connection and monitoring settings have to be set in exporter_config.yaml file.
+Below is an example of a filled configuration file with all possible fields:
+```yaml
+# MQ connection information -------------------------------
+qmgrConnectionParams:
+# Queue manager name.
+ qmgrName: QM
+# Queue manager host.
+ qmgrHost: hostname
+# Queue manager connection port.
+ qmgrPort: 1414
+# Queue manager connection channel.
+ qmgrChannel: SYSTEM.DEF.SVRCONN
+# Username, which will be used for connection (optional).
+ user: mqm
+# Password, which will be used for connection (optional).
+ password: mqm
+# Use MQCSP for connection?
+ mqscp: false
-1. Fill exporter_config.yaml with your enviroments configuration.
+# Prometheus connection information -------------------------------
+prometheusEndpointParams:
+# URL and port which will be used to expose metrics for Prometheus.
+ url: /metrics
+ port: 8080
+
+# Monitoring objects ----------------------------------
+# This block refers to collecting of additional metrics.
+# If there are any queues, channels or listeners in the config file below,
+# these metrics may be useful for you. (More info about additional metrics is located
+# under "MQ PCF API specific statistics" section.
+PCFParameters:
+# Collect additional metrics? If false, all settings in this section below are ignored.
+ sendPCFCommands: true
+# Use wildcards? If yes, only one PCF command will be send, matching all objects on queue manager. Otherwise, each
+# object will be monitored by separate PCF command.
+ usePCFWildcards: true
+# Interval in seconds between sending PCF commands.
+ scrapeInterval: 10
+
+# Monitored queues.
+queues:
+ - QUEUE1
+ - QUEUE2
+
+# Monitored listeners.
+listeners:
+ - LISTENER01
+
+# Monitored channels.
+channels:
+ - MANAGEMENT.CHANNEL
+```
#### Build
1. Download current repository.
@@ -79,6 +126,7 @@ mvn package
```shell
java -jar mq_exporter.jar /opt/mq_exporter/exporter_config.yaml
```
+The only input parameter is the path to your configuration file.
## Metrics
#### Platform central processing units
diff --git a/pom.xml b/pom.xml
index 88846a8..fdc57b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,7 @@
9.0.0.1
1.23
0.6.0
+ 2.11.2
/lib/com.ibm.mq.allclient.jar
UTF-8
UTF-8
@@ -41,6 +42,16 @@
simpleclient_common
${prometheus.version}
+
+ org.apache.logging.log4j
+ log4j-api
+ ${log4j.version}
+
+
+ org.apache.logging.log4j
+ log4j-core
+ ${log4j.version}
+
webspheremq_exporter
@@ -61,6 +72,10 @@
maven-jar-plugin
3.1.1
+
+ **/exporter_config.yaml
+ **/log4j.properties
+
diff --git a/src/main/java/ru/cinimex/exporter/Config.java b/src/main/java/ru/cinimex/exporter/Config.java
index 31b8207..d01764a 100644
--- a/src/main/java/ru/cinimex/exporter/Config.java
+++ b/src/main/java/ru/cinimex/exporter/Config.java
@@ -1,5 +1,7 @@
package ru.cinimex.exporter;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.yaml.snakeyaml.Yaml;
import java.io.BufferedReader;
@@ -14,6 +16,7 @@
* Class is used for parsing config file.
*/
public class Config {
+ private static final Logger logger = LogManager.getLogger(Config.class);
private String qmgrName;
private String qmgrHost;
private int qmgrPort;
@@ -37,7 +40,7 @@ public Config(String path) {
try {
br = new BufferedReader(new FileReader(rawFile));
} catch (FileNotFoundException e) {
- System.err.println(String.format("Unable to locate config file. Make sure %s is valid path.", path));
+ logger.error("Unable to locate config file. Make sure \"{}\" is a valid path.", path);
System.exit(1);
}
LinkedHashMap config = file.load(br);
@@ -59,6 +62,7 @@ public Config(String path) {
this.sendPCFCommands = (boolean) pcfParameters.get("sendPCFCommands");
this.usePCFWildcards = (boolean) pcfParameters.get("usePCFWildcards");
this.scrapeInterval = (Integer) pcfParameters.get("scrapeInterval");
+ logger.info("Successfully parsed configuration file!");
}
public boolean useMqscp() {
diff --git a/src/main/java/ru/cinimex/exporter/ExporterLauncher.java b/src/main/java/ru/cinimex/exporter/ExporterLauncher.java
index fdee878..9575c36 100644
--- a/src/main/java/ru/cinimex/exporter/ExporterLauncher.java
+++ b/src/main/java/ru/cinimex/exporter/ExporterLauncher.java
@@ -6,6 +6,8 @@
import com.ibm.mq.MQTopic;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.pcf.PCFMessage;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import ru.cinimex.exporter.mq.MQConnection;
import ru.cinimex.exporter.mq.MQObject;
import ru.cinimex.exporter.mq.MQSubscriberManager;
@@ -25,46 +27,51 @@
* Main class of mq exporter tool. Parses config, scans topics, starts subscribers.
*/
public class ExporterLauncher {
+ private static final Logger logger = LogManager.getLogger(ExporterLauncher.class);
private static final String topicString = "$SYS/MQ/INFO/QMGR/%s/Monitor/METADATA/CLASSES";
private static final int getMsgOpt = MQConstants.MQGMO_WAIT | MQConstants.MQGMO_COMPLETE_MSG | MQConstants.MQGMO_SYNCPOINT;
- public static void main(String[] args) throws MQException, IOException {
+ public static void main(String[] args) {
if (args.length == 0) {
- System.err.println("It seems that you forgot to specify the path to the config file.");
+ logger.error("It seems like you forgot to specify path to the config file.");
System.exit(1);
}
Config config = new Config(args[0]);
- try {
- ArrayList elements = getAllPublishedMetrics(config);
- ArrayList monitoringTypes = new ArrayList<>();
- ArrayList objects = new ArrayList<>();
- if (config.sendPCFCommands()) {
- if (config.getQueues() != null && config.getQueues().size() > 0) {
- monitoringTypes.add(MQObject.MQType.QUEUE);
- for (String queueName : config.getQueues()) {
- objects.add(new MQObject(queueName, MQObject.MQType.QUEUE));
- }
+ ArrayList elements = getAllPublishedMetrics(config);
+ ArrayList monitoringTypes = new ArrayList<>();
+ ArrayList objects = new ArrayList<>();
+
+ if (config.sendPCFCommands()) {
+ if (config.getQueues() != null && config.getQueues().size() > 0) {
+ monitoringTypes.add(MQObject.MQType.QUEUE);
+ for (String queueName : config.getQueues()) {
+ objects.add(new MQObject(queueName, MQObject.MQType.QUEUE));
+ logger.debug("Queue {} was added for additional monitoring.", queueName);
}
- if (config.getChannels() != null && config.getChannels().size() > 0) {
- monitoringTypes.add(MQObject.MQType.CHANNEL);
- for (String channelName : config.getChannels()) {
- objects.add(new MQObject(channelName, MQObject.MQType.CHANNEL));
- }
+ }
+ if (config.getChannels() != null && config.getChannels().size() > 0) {
+ monitoringTypes.add(MQObject.MQType.CHANNEL);
+ for (String channelName : config.getChannels()) {
+ objects.add(new MQObject(channelName, MQObject.MQType.CHANNEL));
+ logger.debug("Channel {} was added for additional monitoring.", channelName);
}
- if (config.getListeners() != null && config.getListeners().size() > 0) {
- monitoringTypes.add(MQObject.MQType.LISTENER);
- for (String listenerName : config.getListeners()) {
- objects.add(new MQObject(listenerName, MQObject.MQType.LISTENER));
- }
+ }
+ if (config.getListeners() != null && config.getListeners().size() > 0) {
+ monitoringTypes.add(MQObject.MQType.LISTENER);
+ for (String listenerName : config.getListeners()) {
+ objects.add(new MQObject(listenerName, MQObject.MQType.LISTENER));
+ logger.debug("Listener {} was added for additional monitoring.", listenerName);
}
}
- MetricsManager.initMetrics(elements, monitoringTypes);
- MQSubscriberManager manager = new MQSubscriberManager(config.getQmgrHost(), config.getQmgrPort(), config.getQmgrChannel(), config.getQmgrName(), config.getUser(), config.getPassword(), config.useMqscp());
- manager.runSubscribers(elements, objects, config.sendPCFCommands(), config.usePCFWildcards(), config.getScrapeInterval());
+ }
+ MetricsManager.initMetrics(elements, monitoringTypes);
+ MQSubscriberManager manager = new MQSubscriberManager(config.getQmgrHost(), config.getQmgrPort(), config.getQmgrChannel(), config.getQmgrName(), config.getUser(), config.getPassword(), config.useMqscp());
+ manager.runSubscribers(elements, objects, config.sendPCFCommands(), config.usePCFWildcards(), config.getScrapeInterval());
+ try {
new HTTPServer(new InetSocketAddress("0.0.0.0", config.getEndpPort()), config.getEndpURL(), Registry.getRegistry(), false);
- } catch (Exception e) {
- System.err.println(e.getMessage());
+ } catch (IOException e) {
+ logger.error("Error occurred during expanding endpoint for Prometheus: ", e);
}
}
@@ -81,9 +88,8 @@ private static ArrayList getAllPublishedMetrics(Config config) {
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = getMsgOpt;
gmo.waitInterval = 30000;
- connection.establish(config.getQmgrHost(), config.getQmgrPort(), config.getQmgrChannel(), config.getQmgrName(), config.getUser(), config.getPassword(), config.useMqscp());
-
try {
+ connection.establish(config.getQmgrHost(), config.getQmgrPort(), config.getQmgrChannel(), config.getQmgrName(), config.getUser(), config.getPassword(), config.useMqscp());
topic = connection.createTopic(String.format(topicString, config.getQmgrName()));
MQMessage msg = getEmptyMessage();
topic.get(msg, gmo);
@@ -103,8 +109,8 @@ private static ArrayList getAllPublishedMetrics(Config config) {
elements.addAll(PCFDataParser.getPCFElements(pcfResponse));
}
}
- } catch (Exception e) {
- System.err.println(e.getMessage());
+ } catch (MQException | IOException e) {
+ logger.error("Failed!", e);
} finally {
try {
if (topic != null && topic.isOpen()) {
@@ -112,7 +118,7 @@ private static ArrayList getAllPublishedMetrics(Config config) {
}
connection.close();
} catch (MQException e) {
- System.err.println(String.format("Error occured during disconnecting from topic %s. Error: %s", topic.toString(), e.getStackTrace()));
+ logger.error("Error occurred during disconnecting from topic {}. Error: ", topic.toString(), e);
}
}
return elements;
diff --git a/src/main/java/ru/cinimex/exporter/mq/MQConnection.java b/src/main/java/ru/cinimex/exporter/mq/MQConnection.java
index 4d3375c..63c80a3 100644
--- a/src/main/java/ru/cinimex/exporter/mq/MQConnection.java
+++ b/src/main/java/ru/cinimex/exporter/mq/MQConnection.java
@@ -5,6 +5,8 @@
import com.ibm.mq.MQTopic;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.MQConstants;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import java.util.Hashtable;
@@ -12,6 +14,7 @@
* Class represents MQ connection.
*/
public class MQConnection {
+ private static final Logger logger = LogManager.getLogger(MQConnection.class);
private Hashtable connectionProperties;
private MQQueueManager queueManager;
@@ -58,13 +61,9 @@ protected static Hashtable createMQConnectionParams(String host,
* @param password - password, which is required to establish connection with queue manager (optional).
* @param useMQCSP - flag, which indicates, if MQCSP auth should be used.
*/
- public void establish(String host, int port, String channel, String qmName, String user, String password, boolean useMQCSP) {
+ public void establish(String host, int port, String channel, String qmName, String user, String password, boolean useMQCSP) throws MQException {
connectionProperties = createMQConnectionParams(host, port, channel, user, password, useMQCSP);
- try {
- queueManager = new MQQueueManager(qmName, connectionProperties);
- } catch (MQException e) {
- System.err.println(e.getMessage());
- }
+ queueManager = new MQQueueManager(qmName, connectionProperties);
}
@@ -74,12 +73,8 @@ public void establish(String host, int port, String channel, String qmName, Stri
* @param qmNqme - queue manager's name.
* @param connectionProperties - prepared structure with all parameters transformed into queue manager's format. See {@link #createMQConnectionParams(String, int, String, String, String, boolean)} for more info.
*/
- public void establish(String qmNqme, Hashtable connectionProperties) {
- try {
- queueManager = new MQQueueManager(qmNqme, connectionProperties);
- } catch (MQException e) {
- System.err.println(e.getMessage());
- }
+ public void establish(String qmNqme, Hashtable connectionProperties) throws MQException {
+ queueManager = new MQQueueManager(qmNqme, connectionProperties);
}
/**
@@ -90,7 +85,7 @@ public void close() {
try {
queueManager.disconnect();
} catch (MQException e) {
- System.err.println(e.getMessage());
+ logger.error("Failed!", e);
}
}
}
@@ -108,6 +103,7 @@ public MQTopic createTopic(String topic) throws MQException {
/**
* Returns MQQueueManager object.
+ *
* @return - MQQueueManager object.
*/
public MQQueueManager getQueueManager() {
diff --git a/src/main/java/ru/cinimex/exporter/mq/MQObject.java b/src/main/java/ru/cinimex/exporter/mq/MQObject.java
index 0cf7497..a9f2c8c 100644
--- a/src/main/java/ru/cinimex/exporter/mq/MQObject.java
+++ b/src/main/java/ru/cinimex/exporter/mq/MQObject.java
@@ -2,11 +2,14 @@
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.pcf.PCFMessage;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
/**
* Class represents MQObject (Queue, channel or listener). It stores object type and all PCFParameters, required for correct request.
*/
public class MQObject {
+ private static final Logger logger = LogManager.getLogger(MQObject.class);
private String name;
private MQType type;
private PCFMessage PCFCmd;
@@ -41,8 +44,11 @@ public MQObject(String name, MQType type) {
PCFCmd.addParameter(MQConstants.MQCACH_CHANNEL_NAME, name); //PCF command would try to retrieve statistics about channel with specific name
PCFHeader = MQConstants.MQIACH_CHANNEL_STATUS;//the only statistics we want to know about channel is it's status.
break;
- default:
- //TODO:Exception
+ default: {
+ logger.error("Unknown type for MQObject: {}", type.name());
+ throw new RuntimeException("Unable to create new MQObject. Received unexpected MQObject type: " + type.name());
+ }
+
}
}
@@ -50,7 +56,7 @@ public MQObject(String name, MQType type) {
* This method returns MQConstant int code, which represents name for input object.
*
* @param type - object type.
- * @return - integer code.
+ * @return - integer code. Returns -1 if code wasn't found.
*/
public static int objectNameCode(MQObject.MQType type) {
int code = -1;
@@ -108,7 +114,5 @@ public PCFMessage getPCFCmd() {
/**
* This enum represents all supported MQObject types.
*/
- public enum MQType {
- QUEUE, CHANNEL, LISTENER
- }
+ public enum MQType {QUEUE, CHANNEL, LISTENER}
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/MQPCFSubscriber.java b/src/main/java/ru/cinimex/exporter/mq/MQPCFSubscriber.java
index 19f65e8..96555fe 100644
--- a/src/main/java/ru/cinimex/exporter/mq/MQPCFSubscriber.java
+++ b/src/main/java/ru/cinimex/exporter/mq/MQPCFSubscriber.java
@@ -5,6 +5,8 @@
import com.ibm.mq.pcf.PCFException;
import com.ibm.mq.pcf.PCFMessage;
import com.ibm.mq.pcf.PCFMessageAgent;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import ru.cinimex.exporter.prometheus.metrics.MetricsManager;
import ru.cinimex.exporter.prometheus.metrics.MetricsReference;
@@ -17,6 +19,7 @@
* retrieve specific statistics, which couldn't be retrieved by MQTopicSubscriber.
*/
public class MQPCFSubscriber implements Runnable {
+ private static final Logger logger = LogManager.getLogger(MQPCFSubscriber.class);
private MQConnection connection;
private String queueManagerName;
private MQObject object;
@@ -55,16 +58,15 @@ public MQPCFSubscriber(String queueManagerName, Hashtable connec
* @param connectionProperties - map with all required connection params.
*/
private void establishMQConnection(String queueManagerName, Hashtable connectionProperties) {
- if (connection == null) {
- connection = new MQConnection();
- connection.establish(queueManagerName, connectionProperties);
- }
- this.queueManagerName = queueManagerName;
- //TODO: error handling
try {
+ if (connection == null) {
+ connection = new MQConnection();
+ connection.establish(queueManagerName, connectionProperties);
+ }
+ this.queueManagerName = queueManagerName;
this.agent = new PCFMessageAgent(connection.getQueueManager());
} catch (MQException e) {
- e.printStackTrace();
+ logger.error("Error occurred during establishing connection with queue manager: ", e);
}
}
@@ -114,14 +116,12 @@ private void updateMetricsWithWildcards(PCFMessage[] pcfResponse) {
updateMetricWithoutWildcards(directPCFResponse[0], objectName);
} catch (PCFException e) {
//This error means, that channel has status "inactive".
+ logger.warn("Channel {} is possibly inactive.", objectName);
if (e.reasonCode == MQConstants.MQRCCF_CHL_STATUS_NOT_FOUND) {
MetricsManager.updateMetric(MetricsReference.getMetricName(object.getType()), MetricsReference.getMetricValue(object.getType(), MQConstants.MQCHS_INACTIVE), queueManagerName, objectName);
}
- } catch (MQException e) {
- //TODO: error handling
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ } catch (IOException | MQException e) {
+ logger.error("Error occurred during sending PCF command: ", e);
}
}
}
@@ -130,6 +130,7 @@ private void updateMetricsWithWildcards(PCFMessage[] pcfResponse) {
@Override
public void run() {
try {
+ logger.debug("Sending PCF command for object type {} with name {}...", object.getType(), object.getName());
PCFMessage[] pcfResponse = agent.send(object.getPCFCmd());
if (!objects.isEmpty()) {
updateMetricsWithWildcards(pcfResponse);
@@ -138,15 +139,14 @@ public void run() {
updateMetricWithoutWildcards(response, object.getName());
}
}
+ logger.debug("PCF response for object type {} with name {} was processed successfully.", object.getType(), object.getName());
} catch (PCFException e) {
if (e.reasonCode == MQConstants.MQRCCF_CHL_STATUS_NOT_FOUND) {
+ logger.warn("Channel {} is possibly inactive.", object.getName());
MetricsManager.updateMetric(MetricsReference.getMetricName(object.getType()), MetricsReference.getMetricValue(object.getType(), MQConstants.MQCHS_INACTIVE), queueManagerName, object.getName());
}
- } catch (MQException e) {
- //TODO: error handling
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ } catch (MQException | IOException e) {
+ logger.error("Error occurred during sending PCF command: ", e);
}
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/MQSubscriberManager.java b/src/main/java/ru/cinimex/exporter/mq/MQSubscriberManager.java
index 300a329..b41d238 100644
--- a/src/main/java/ru/cinimex/exporter/mq/MQSubscriberManager.java
+++ b/src/main/java/ru/cinimex/exporter/mq/MQSubscriberManager.java
@@ -1,5 +1,8 @@
package ru.cinimex.exporter.mq;
+import com.ibm.mq.MQException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import ru.cinimex.exporter.mq.pcf.PCFElement;
import java.util.ArrayList;
@@ -12,7 +15,7 @@
* Class is used to manage work of all subscribers.
*/
public class MQSubscriberManager {
-
+ private static final Logger logger = LogManager.getLogger(MQSubscriberManager.class);
private Hashtable connectionProperties;
private String queueManagerName;
private ArrayList subscribers;
@@ -36,28 +39,41 @@ public MQSubscriberManager(String host, int port, String channel, String qmName,
/**
* Creates pool with subscribers and starts them.
*
- * @param elements - elements, received via MQ monitoring topics.
- * @param objects - objects, retrieved from configuration file.
+ * @param elements - elements, received via MQ monitoring topics.
+ * @param objects - objects, retrieved from configuration file.
* @param sendPCFCommands - this flag indicates, if we should send additional PCF commands (To get queues max depth, channels and listeners statuses).
* @param usePCFWildcards - this flag indicates, if we should use wildcards (uses only 1 connection per MQObject type, but longer response processing).
- * @param interval - interval in seconds, at which additional PCF commands are sent.
+ * @param interval - interval in seconds, at which additional PCF commands are sent.
*/
public void runSubscribers(ArrayList elements, ArrayList objects, boolean sendPCFCommands, boolean usePCFWildcards, int interval) {
+ logger.info("Launching subscribers...");
subscribers = new ArrayList<>();
int i = 0;
for (PCFElement element : elements) {
if (!element.requiresMQObject()) {
- subscribers.add(i, new Thread(new MQTopicSubscriber(element, queueManagerName, connectionProperties, queueManagerName)));
- subscribers.get(i).start();
- i++;
+ try {
+ subscribers.add(i, new Thread(new MQTopicSubscriber(element, queueManagerName, connectionProperties, queueManagerName)));
+ logger.debug("Starting subscriber for {}...", element.getTopicString());
+ subscribers.get(i).start();
+ logger.debug("Subscriber for {} was started.", element.getTopicString());
+ i++;
+ } catch (MQException e) {
+ logger.error("Error during creating topic subscriber: ", e);
+ }
} else {
for (MQObject object : objects) {
if (object.getType() == MQObject.MQType.QUEUE) {
PCFElement objElement = new PCFElement(element.getTopicString(), element.getRows());
objElement.formatTopicString(object.getName());
- subscribers.add(i, new Thread(new MQTopicSubscriber(objElement, queueManagerName, connectionProperties, queueManagerName, object.getName())));
- subscribers.get(i).start();
- i++;
+ try {
+ subscribers.add(i, new Thread(new MQTopicSubscriber(objElement, queueManagerName, connectionProperties, queueManagerName, object.getName())));
+ logger.debug("Starting subscriber for {}...", objElement.getTopicString());
+ subscribers.get(i).start();
+ logger.debug("Subscriber for {} was started.", objElement.getTopicString());
+ i++;
+ } catch (MQException e) {
+ logger.error("Error during creating topic subscriber: ", e);
+ }
}
}
}
@@ -81,28 +97,49 @@ public void runSubscribers(ArrayList elements, ArrayList o
case LISTENER:
listeners.add(object);
break;
+ default:
+ logger.error("Error during parsing objects list: Unknown object type! Make sure it is one of: {}", MQObject.MQType.values());
}
}
- MQPCFSubscriber subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, queues);
- subscribers.add(i++, new Thread(subscriber));
- executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
-
- subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, channels);
- subscribers.add(i++, new Thread(subscriber));
- executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
-
- subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, listeners);
- subscribers.add(i++, new Thread(subscriber));
- executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
+ if (queues.size() > 0) {
+ MQPCFSubscriber subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, queues);
+ subscribers.add(i++, new Thread(subscriber));
+ logger.debug("Starting subscriber for sending direct PCF commands about queues max depth...");
+ executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
+ logger.debug("Subscriber for sending direct PCF commands about queues max depth successfully " + "started.");
+ }
+ if (channels.size() > 0) {
+ MQPCFSubscriber subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, channels);
+ subscribers.add(i++, new Thread(subscriber));
+ logger.debug("Starting subscriber for sending direct PCF commands about channels statuses...");
+ executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
+ logger.debug("Subscriber for sending direct PCF commands about channels statuses successfully " + "started.");
+ }
+ if (listeners.size() > 0) {
+ MQPCFSubscriber subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, listeners);
+ subscribers.add(i++, new Thread(subscriber));
+ logger.debug("Starting subscriber for sending direct PCF commands about listeners statuses...");
+ executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
+ logger.debug("Subscriber for sending direct PCF commands about listeners statuses successfully " + "started.");
+ }
} else {
for (MQObject object : objects) {
MQPCFSubscriber subscriber = new MQPCFSubscriber(queueManagerName, connectionProperties, object);
subscribers.add(i++, new Thread(subscriber));
+ logger.debug("Starting subscriber for sending direct PCF commands to retrieve statistics about " + "object with type {} and name {}.", object.getType().name(), object.getName());
executor.scheduleAtFixedRate(subscriber, 0, interval, TimeUnit.SECONDS);
+ logger.debug("Subscriber for sending direct PCF commands to retrieve statistics about " + "object with type {} and name {} successfully started.", object.getType().name(), object.getName());
}
}
}
+ if (subscribers.size() > 0) {
+ logger.info("Successfully launched {} subscribers!", subscribers.size());
+ } else {
+ logger.warn("Didn't launch any subscriber. Exporter finishes it's work!", subscribers.size());
+ System.exit(1);
+ }
}
+
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/MQTopicSubscriber.java b/src/main/java/ru/cinimex/exporter/mq/MQTopicSubscriber.java
index c6c42a2..341e7f7 100644
--- a/src/main/java/ru/cinimex/exporter/mq/MQTopicSubscriber.java
+++ b/src/main/java/ru/cinimex/exporter/mq/MQTopicSubscriber.java
@@ -5,6 +5,8 @@
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQTopic;
import com.ibm.mq.constants.MQConstants;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import ru.cinimex.exporter.mq.pcf.PCFDataParser;
import ru.cinimex.exporter.mq.pcf.PCFElement;
import ru.cinimex.exporter.prometheus.metrics.MetricsManager;
@@ -19,65 +21,67 @@
* MQTopicSubscriber is used to subscribe to specific topic.
*/
public class MQTopicSubscriber implements Runnable {
+ private static final Logger logger = LogManager.getLogger(MQTopicSubscriber.class);
+ private MQTopic topic;
+ private PCFElement element;
+ private MQConnection connection;
+ private String[] labels;
- private MQTopic topic;
- private PCFElement element;
- private MQConnection connection;
- private String[] labels;
-
- /**
- * Subscriber constructor
- *
- * @param element - PCF message data, which is required for parsing statistics.
- * @param labels - labels array, which should be used for metrics.
- */
- public MQTopicSubscriber(PCFElement element, String queueManagerName,
- Hashtable connectionProperties, String... labels) {
- this.element = element;
- if (connection == null) {
- connection = new MQConnection();
- connection.establish(queueManagerName, connectionProperties);
- }
- this.labels = labels;
+ /**
+ * Subscriber constructor
+ *
+ * @param element - PCF message data, which is required for parsing statistics.
+ * @param labels - labels array, which should be used for metrics.
+ */
+ public MQTopicSubscriber(PCFElement element, String queueManagerName, Hashtable connectionProperties, String... labels) throws MQException {
+ this.element = element;
+ if (connection == null) {
+ connection = new MQConnection();
+ connection.establish(queueManagerName, connectionProperties);
}
+ this.labels = labels;
+ }
- /**
- * Starts subscriber.
- */
- public void run() {
- try {
- topic = connection.createTopic(element.getTopicString());
- MQGetMessageOptions gmo = new MQGetMessageOptions();
- gmo.options = MQConstants.MQGMO_WAIT | MQConstants.MQGMO_COMPLETE_MSG;
- gmo.waitInterval = 12000;
- while (true) {
- try {
- MQMessage msg = new MQMessage();
- topic.get(msg, gmo);
- HashMap receivedMetrics = PCFDataParser.getParsedData(PCFDataParser.convertToPCF(msg));
- Iterator> it = receivedMetrics.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry pair = it.next();
- MetricsManager.updateMetric(MetricsReference.getMetricName(element.getMetricDescription(pair.getKey()), element.requiresMQObject(), element.getRowDatatype(pair.getKey().intValue())), pair.getValue(), labels);
- it.remove();
- }
- } catch (MQException e) {
- if (e.getReason() == MQConstants.MQRC_NO_MSG_AVAILABLE)
- System.out.println("No messages in " + element.getTopicString());
+ /**
+ * Starts subscriber.
+ */
+ public void run() {
+ try {
+ topic = connection.createTopic(element.getTopicString());
+ MQGetMessageOptions gmo = new MQGetMessageOptions();
+ gmo.options = MQConstants.MQGMO_WAIT | MQConstants.MQGMO_COMPLETE_MSG;
+ gmo.waitInterval = 12000;
+ while (true) {
+ try {
+ MQMessage msg = new MQMessage();
+ logger.debug("Waiting for message on {} ...", element.getTopicString());
+ topic.get(msg, gmo);
+ logger.debug("Message received on {}", element.getTopicString());
+ HashMap receivedMetrics = PCFDataParser.getParsedData(PCFDataParser.convertToPCF(msg));
+ Iterator> it = receivedMetrics.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = it.next();
+ MetricsManager.updateMetric(MetricsReference.getMetricName(element.getMetricDescription(pair.getKey()), element.requiresMQObject(), element.getRowDatatype(pair.getKey().intValue())), pair.getValue(), labels);
+ it.remove();
}
+ } catch (MQException e) {
+ if (e.getReason() == MQConstants.MQRC_NO_MSG_AVAILABLE)
+ logger.warn("No messages found in {}", element.getTopicString());
+ else
+ logger.error("Error occurred during retrieving message from {}: ", element.getTopicString(), e);
}
- } catch (MQException e) {
- System.out.println("An error occured while trying to get queue object " + element.getTopicString());
- System.err.println(e.getMessage());
- } finally {
- if (topic != null && topic.isOpen()) {
- try {
- topic.close();
- connection.close();
- } catch (MQException e) {
- System.err.println(e.getMessage());
- }
+ }
+ } catch (MQException e) {
+ logger.error("Error occurred during establishing connection with topic {}", element.getTopicString(), e);
+ } finally {
+ if (topic != null && topic.isOpen()) {
+ try {
+ topic.close();
+ connection.close();
+ } catch (MQException e) {
+ logger.error("Error occurred during disconnecting from topic {}. Error: ", topic.toString(), e);
}
}
}
}
+}
diff --git a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFClass.java b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFClass.java
index d2c4fd0..43707a3 100644
--- a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFClass.java
+++ b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFClass.java
@@ -1,10 +1,14 @@
package ru.cinimex.exporter.mq.pcf;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
/**
* This class represents PCFMessage parameter, which could be received from
* $SYS/MQ/INFO/QMGR/{QMGR_NAME}/Monitor/METADATA/CLASSES MQ topic.
*/
public class PCFClass {
+ private static final Logger logger = LogManager.getLogger(PCFClass.class);
private int monitorId;
private int monitorFlag;
private String monitorName;
@@ -21,11 +25,13 @@ public class PCFClass {
* @param topicString - MQ topic, where detailed information is published (each class has it's own topic)
*/
protected PCFClass(int monitorId, int monitorFlag, String monitorName, String monitorDesc, String topicString) {
- //TODO: add null-checks and -1 checks (warnings, i guess)
this.monitorId = monitorId;
this.monitorFlag = monitorFlag;
this.monitorName = monitorName;
this.monitorDesc = monitorDesc;
+ if (topicString == null) {
+ logger.warn("Topic string is empty: ", this.toString());
+ }
this.topicString = topicString;
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFDataParser.java b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFDataParser.java
index 1f616a3..0599274 100644
--- a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFDataParser.java
+++ b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFDataParser.java
@@ -4,6 +4,8 @@
import com.ibm.mq.MQMessage;
import com.ibm.mq.constants.MQConstants;
import com.ibm.mq.pcf.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.ArrayList;
@@ -14,6 +16,7 @@
* Class PCFDataParser contains only static methods and was created to simplify work with PCF messages.
*/
public class PCFDataParser {
+ private static final Logger logger = LogManager.getLogger(PCFDataParser.class);
/**
* Method parses PCFMessage, which contains info about all monitoring classes
@@ -53,7 +56,7 @@ public static ArrayList getPCFClasses(PCFMessage pcfMessage) {
topicString = groupParam.getStringValue();
break;
default:
- //TODO:add warning
+ logger.warn("Unknown parameter type was found while parsing PCFClass! Will be ignored. {} = {}", groupParam.getParameterName(), groupParam.getStringValue());
break;
}
@@ -96,7 +99,7 @@ public static ArrayList getPCFTypes(PCFMessage pcfMessage) {
topicString = groupParam.getStringValue();
break;
default:
- //TODO:add warning
+ logger.warn("Unknown parameter type was found while parsing PCFType! Will be ignored." + " {} = {}", groupParam.getParameterName(), groupParam.getStringValue());
break;
}
@@ -140,7 +143,7 @@ public static ArrayList getPCFElements(PCFMessage pcfMessage) {
rowDesc = groupParam.getStringValue();
break;
default:
- //TODO:add warning
+ logger.warn("Unknown parameter type was found while parsing PCFElement! Will be " + "ignored." + " {} = {}", groupParam.getParameterName(), groupParam.getStringValue());
break;
}
@@ -171,7 +174,6 @@ public static HashMap getParsedData(PCFMessage pcfMessage) {
HashMap data = new HashMap();
while (params.hasMoreElements()) {
PCFParameter param = params.nextElement();
- //TODO: Add other types for parsing (look for example here: https://www.ibm.com/developerworks/community/blogs/messaging/entry/A_first_look_at_MQ_Resource_USeage_handling_using_Java?lang=en).
switch (param.getParameter()) {
case MQConstants.MQCA_Q_MGR_NAME:
break;
@@ -199,6 +201,10 @@ public static HashMap getParsedData(PCFMessage pcfMessage) {
data.put(statistic.getParameter(), new Double(statistic.getLongValue()));
break;
}
+ default: {
+ logger.warn("Unknown parameter type was found while parsing PCF monitoring data! Will be " + "ignored." + " {} = {}", param.getParameterName(), param.getStringValue());
+ break;
+ }
}
}
@@ -213,13 +219,10 @@ public static HashMap getParsedData(PCFMessage pcfMessage) {
* @return - converted PCFMessage
*/
public static PCFMessage convertToPCF(MQMessage message) {
- //TODO: add error processing
try {
return new PCFMessage(message);
- } catch (MQException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ } catch (MQException | IOException e) {
+ logger.error("Unable to convert MQMessage to PCFMessage: ", e);
}
return null;
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFElement.java b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFElement.java
index e84efb9..6332f3d 100644
--- a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFElement.java
+++ b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFElement.java
@@ -1,5 +1,8 @@
package ru.cinimex.exporter.mq.pcf;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
import java.util.ArrayList;
/**
@@ -7,6 +10,7 @@
* $SYS/MQ/INFO/QMGR/{QMGR_NAME}/Monitor/{CLASS}/{TYPE} MQ topic.
*/
public class PCFElement {
+ private static final Logger logger = LogManager.getLogger(PCFElement.class);
private final ArrayList rows;
private String sourceTopicString;
private String topicString;
@@ -25,6 +29,9 @@ public PCFElement(String topicString, ArrayList rows) {
this.topicString = topicString;
this.rows = rows;
this.mqObjectRequired = topicString.contains("%s");
+ if (topicString == null) {
+ logger.warn("Topic string is empty: ", this.toString());
+ }
}
diff --git a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFType.java b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFType.java
index 3c895c4..adee0db 100644
--- a/src/main/java/ru/cinimex/exporter/mq/pcf/PCFType.java
+++ b/src/main/java/ru/cinimex/exporter/mq/pcf/PCFType.java
@@ -1,10 +1,14 @@
package ru.cinimex.exporter.mq.pcf;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
/**
* This class represents PCFMessage parameter, which could be received from
* $SYS/MQ/INFO/QMGR/{QMGR_NAME}/Monitor/METADATA/{CLASS}/TYPES MQ topic.
*/
public class PCFType {
+ private static final Logger logger = LogManager.getLogger(PCFType.class);
private String monitorName;
private String monitorDesc;
private String topicString;
@@ -17,10 +21,12 @@ public class PCFType {
* @param topicString - MQ topic, where detailed information is published (each type has it's own topic)
*/
protected PCFType(String monitorName, String monitorDesc, String topicString) {
- //TODO: add null-checks and -1 checks (warnings, i guess)
this.monitorName = monitorName;
this.monitorDesc = monitorDesc;
this.topicString = topicString;
+ if(topicString == null){
+ logger.warn("Topic string is empty: ", this.toString());
+ }
}
/**
diff --git a/src/main/java/ru/cinimex/exporter/prometheus/HTTPServer.java b/src/main/java/ru/cinimex/exporter/prometheus/HTTPServer.java
index 8fdbc1a..7f14912 100644
--- a/src/main/java/ru/cinimex/exporter/prometheus/HTTPServer.java
+++ b/src/main/java/ru/cinimex/exporter/prometheus/HTTPServer.java
@@ -5,6 +5,9 @@
import com.sun.net.httpserver.HttpServer;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import ru.cinimex.exporter.ExporterLauncher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -20,6 +23,7 @@
import java.util.zip.GZIPOutputStream;
public class HTTPServer {
+ private static final Logger logger = LogManager.getLogger(HTTPServer.class);
protected final HttpServer server;
protected final ExecutorService executorService;
@@ -35,6 +39,7 @@ public HTTPServer(InetSocketAddress addr, String url, CollectorRegistry registry
executorService = Executors.newFixedThreadPool(5, NamedDaemonThreadFactory.defaultThreadFactory(daemon));
server.setExecutor(executorService);
start(daemon);
+ logger.info("Endpoint {} on port {} successfully expanded!", url, addr.getPort());
}
@@ -118,6 +123,7 @@ static class HTTPMetricHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
+ logger.debug("Received request from Prometheus.");
String query = t.getRequestURI().getRawQuery();
ByteArrayOutputStream response = this.response.get();
@@ -142,6 +148,7 @@ public void handle(HttpExchange t) throws IOException {
response.writeTo(t.getResponseBody());
}
t.close();
+ logger.debug("Data was sent to Prometheus.");
}
}
diff --git a/src/main/java/ru/cinimex/exporter/prometheus/metrics/MetricsManager.java b/src/main/java/ru/cinimex/exporter/prometheus/metrics/MetricsManager.java
index 8777297..985ed5b 100644
--- a/src/main/java/ru/cinimex/exporter/prometheus/metrics/MetricsManager.java
+++ b/src/main/java/ru/cinimex/exporter/prometheus/metrics/MetricsManager.java
@@ -1,5 +1,7 @@
package ru.cinimex.exporter.prometheus.metrics;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import ru.cinimex.exporter.mq.MQObject;
import ru.cinimex.exporter.mq.pcf.PCFElement;
import ru.cinimex.exporter.mq.pcf.PCFElementRow;
@@ -11,6 +13,7 @@
* Class is used to manage work of all metrics.
*/
public class MetricsManager {
+ private static final Logger logger = LogManager.getLogger(MetricsManager.class);
private static HashMap metrics;
/**
@@ -20,6 +23,7 @@ public class MetricsManager {
* @param types - Array, which contains all MQObject types, which should be monitored via direct PCFCommands.
*/
public static void initMetrics(ArrayList elements, ArrayList types) {
+ logger.debug("Preparing to initialize metrics. {} metrics will be received from MQ topics and {} metrics will be received via direct PCF commands.", elements.size(), types.size());
metrics = new HashMap();
for (PCFElement element : elements) {
for (PCFElementRow row : element.getRows()) {
@@ -35,21 +39,24 @@ public static void initMetrics(ArrayList elements, ArrayList QUEUE_MANAGER_METRICS_REFERENCE = new HashMap() {
{
@@ -233,13 +236,14 @@ private static String generateMetricName(String description, boolean requiresObj
metricName = metricName.concat("_delta");
break;
}
+ default: {
+ logger.warn("Unknown metric type: {}", MQConstants.lookup(dataType, "MQIAMO_"));
+ }
}
- //TODO:Add warning!
- System.out.println("Unknown metric name! Generated new name " + metricName + " from description " + description);
+ logger.warn("Unknown metric name! Generated new name '{}' from description '{}'", metricName, description);
return metricName;
}
- //TODO: add error processing, think about returnValue possible values.
public static double getMetricValue(MQObject.MQType type, int value) {
double returnValue = -1;
switch (type) {
@@ -253,6 +257,7 @@ public static double getMetricValue(MQObject.MQType type, int value) {
returnValue = listenerStatus.get(value);
break;
default:
+ logger.warn("Unknown metric type {}.", type.name());
break;
}
return returnValue;
diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties
new file mode 100644
index 0000000..0fd2365
--- /dev/null
+++ b/src/main/resources/log4j2.properties
@@ -0,0 +1,42 @@
+status = warn
+name= exporter_log_configuration
+
+# Give directory path where log files should get stored
+property.basePath = ./log/
+
+# ConsoleAppender will print logs on console
+appender.console.type = Console
+appender.console.name = consoleLogger
+appender.console.target = SYSTEM_OUT
+appender.console.layout.type = PatternLayout
+
+# Specify the pattern of the logs
+appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n
+
+
+# RollingFileAppender will print logs in file which can be rotated based on time or size
+appender.rolling.type = RollingFile
+appender.rolling.name = fileLogger
+appender.rolling.fileName= ${basePath}app.log
+appender.rolling.filePattern= ${basePath}app_%d{yyyyMMdd}.log
+appender.rolling.layout.type = PatternLayout
+appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n
+appender.rolling.policies.type = Policies
+
+# Rotate log file each day and keep 30 days worth
+appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
+appender.rolling.policies.time.interval = 1
+appender.rolling.policies.time.modulate = true
+appender.rolling.strategy.type = DefaultRolloverStrategy
+appender.rolling.strategy.delete.type = Delete
+appender.rolling.strategy.delete.basePath = ${basePath}
+appender.rolling.strategy.delete.maxDepth = 1
+appender.rolling.strategy.delete.ifLastModified.type = IfLastModified
+# Delete files older than 30 days
+appender.rolling.strategy.delete.ifLastModified.age = 30d
+
+# Configure root logger for logging error logs in classes which are in package other than above specified package
+rootLogger.level = info
+rootLogger.additivity = false
+rootLogger.appenderRef.rolling.ref = fileLogger
+rootLogger.appenderRef.console.ref = consoleLogger
\ No newline at end of file