Skip to content

Commit

Permalink
Merge pull request #40 from Cinimex-Informatica/feature/issue15_logging
Browse files Browse the repository at this point in the history
issue15 Resolves: #15. Log4j logging implemented, readme updated with basic configuration file description.
  • Loading branch information
echerniak authored Mar 13, 2019
2 parents 82914f7 + 5c9931e commit adcebb2
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 164 deletions.
58 changes: 53 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<mq.allclient.version>9.0.0.1</mq.allclient.version>
<snakeyaml.version>1.23</snakeyaml.version>
<prometheus.version>0.6.0</prometheus.version>
<log4j.version>2.11.2</log4j.version>
<mq.allclient.jar.path>/lib/com.ibm.mq.allclient.jar</mq.allclient.jar.path>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
Expand Down Expand Up @@ -41,6 +42,16 @@
<artifactId>simpleclient_common</artifactId>
<version>${prometheus.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<finalName>webspheremq_exporter</finalName>
Expand All @@ -61,6 +72,10 @@
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<excludes>
<exclude>**/exporter_config.yaml</exclude>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<!-- Jar file entry point -->
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/ru/cinimex/exporter/Config.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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<String, Object> config = file.load(br);
Expand All @@ -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() {
Expand Down
70 changes: 38 additions & 32 deletions src/main/java/ru/cinimex/exporter/ExporterLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<PCFElement> elements = getAllPublishedMetrics(config);
ArrayList<MQObject.MQType> monitoringTypes = new ArrayList<>();
ArrayList<MQObject> 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<PCFElement> elements = getAllPublishedMetrics(config);
ArrayList<MQObject.MQType> monitoringTypes = new ArrayList<>();
ArrayList<MQObject> 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);
}
}

Expand All @@ -81,9 +88,8 @@ private static ArrayList<PCFElement> 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);
Expand All @@ -103,16 +109,16 @@ private static ArrayList<PCFElement> 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()) {
topic.close();
}
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;
Expand Down
22 changes: 9 additions & 13 deletions src/main/java/ru/cinimex/exporter/mq/MQConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
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;

/**
* Class represents MQ connection.
*/
public class MQConnection {
private static final Logger logger = LogManager.getLogger(MQConnection.class);
private Hashtable<String, Object> connectionProperties;
private MQQueueManager queueManager;

Expand Down Expand Up @@ -58,13 +61,9 @@ protected static Hashtable<String, Object> 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);
}


Expand All @@ -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<String, Object> connectionProperties) {
try {
queueManager = new MQQueueManager(qmNqme, connectionProperties);
} catch (MQException e) {
System.err.println(e.getMessage());
}
public void establish(String qmNqme, Hashtable<String, Object> connectionProperties) throws MQException {
queueManager = new MQQueueManager(qmNqme, connectionProperties);
}

/**
Expand All @@ -90,7 +85,7 @@ public void close() {
try {
queueManager.disconnect();
} catch (MQException e) {
System.err.println(e.getMessage());
logger.error("Failed!", e);
}
}
}
Expand All @@ -108,6 +103,7 @@ public MQTopic createTopic(String topic) throws MQException {

/**
* Returns MQQueueManager object.
*
* @return - MQQueueManager object.
*/
public MQQueueManager getQueueManager() {
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/ru/cinimex/exporter/mq/MQObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,16 +44,19 @@ 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());
}

}
}

/**
* 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;
Expand Down Expand Up @@ -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}
}
Loading

0 comments on commit adcebb2

Please sign in to comment.