Permalink
Browse files

init

  • Loading branch information...
0 parents commit 5d447079f47054ad425b66d1ebc5a396066aa435 @tcurdt committed Mar 25, 2011
@@ -0,0 +1,8 @@
+.DS_Store
+.classpath
+.project
+.fatjar
+target
+eclipse
+old
+bin
@@ -0,0 +1,66 @@
+# jmx2munin
+
+The [jmx2munin](http://github.com/tcurdt/jmx2munin) project exposes JMX MBean attributes to [Munin](http://munin-monitoring.org/).
+Some of it's features:
+
+ * strictly complies to the plugin format
+ * exposes composite types like Lists, Maps, Set as useful as possible
+ * String values can be mapped to numbers
+
+# How to use
+
+This is what Munin will call. So you should test this first. Of course with your parameters. This example expose all Cassandra information to Munin.
+
+ java -jar jmx2munin.jar \
+ -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \
+ -query "org.apache.cassandra.*:*" \
+ -config /path/to/plugin.cfg \
+
+The "url" parameters specifies the JMX URL, the query selects the MBean to expose and the config points to the Munin plugin configuration. Something along the lines of:
+
+ graph_title Load average
+ graph_args --base 1000 -l 0
+ graph_vlabel load
+ graph_scale no
+ graph_category system
+ load.label load
+ load.warning 10
+ load.critical 120
+ graph_info The load average.
+ load.info Average load for the five minutes.
+
+# More advanced
+
+Sometimes it can be useful to track String values by mapping them into an enum as they really describe states. To find this possible candidates you can call:
+
+ java -jar jmx2munin.jar \
+ -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \
+ -query "org.apache.cassandra.*:*" \
+ list
+
+It should output a list of possible candidates. This can now be turned into a enum configuration file:
+
+ [org.apache.cassandra.db.StorageService:OperationMode]
+ 0 = ^Normal
+ 1 = ^Client
+ 2 = ^Joining
+ 3 = ^Bootstrapping
+ 4 = ^Leaving
+ 5 = ^Decommissioned
+ 6 = ^Starting drain
+ 7 = ^Node is drained
+
+Which we then provide:
+
+ java -jar jmx2munin.jar \
+ -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \
+ -query "org.apache.cassandra.*:*" \
+ -config /path/to/plugin.cfg \
+ -enums /path/to/enums.cfg
+
+Now matching values get replaced by their numerical representation. On the left needs to be a unique number on the right side is a regular expression. If a string cannot be matched according to the spec "U" for "undefined" will be returned.
+
+# License
+
+Licensed under the Apache License, Version 2.0 (the "License")
+You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
120 pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.vafer</groupId>
+ <artifactId>jmx2munin</artifactId>
+ <name>jmx2munin</name>
+ <version>1.0-SNAPSHOT</version>
+ <description>
+ </description>
+ <url>http://github.com/tcurdt/jmx2munin</url>
+
+ <developers>
+ <developer>
+ <id>tcurdt</id>
+ <name>Torsten Curdt</name>
+ <email>tcurdt at vafer.org</email>
+ <timezone>+1</timezone>
+ </developer>
+ </developers>
+
+ <licenses>
+ <license>
+ <name>Apache License 2</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+
+ <scm>
+ <connection>scm:git:git://github.com:tcurdt/jmx2munin.git</connection>
+ <developerConnection>scm:git:git://github.com:tcurdt/jmx2munin.git</developerConnection>
+ <url>http://github.com/tcurdt/jmx2munin/tree/master</url>
+ </scm>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.beust</groupId>
+ <artifactId>jcommander</artifactId>
+ <version>1.17</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.5</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkMode>never</forkMode>
+ <includes>
+ <include>**/*TestCase.java</include>
+ </includes>
+ <excludes>
+ <exclude>**/Abstract*</exclude>
+ </excludes>
+ <testFailureIgnore>true</testFailureIgnore>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1</version>
+ <configuration>
+ <attach>true</attach>
+ </configuration>
+ <executions>
+ <execution>
+ <id>create-source-jar</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <minimizeJar>false</minimizeJar>
+ <artifactSet>
+ <includes>
+ <include>com.beust:jcommander</include>
+ </includes>
+ </artifactSet>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>org.vafer.jmx.munin.Munin</mainClass>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
@@ -0,0 +1,77 @@
+package org.vafer.jmx;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import javax.management.ObjectName;
+
+public final class Enums {
+
+ private TreeMap<String, LinkedHashMap<Integer, Pattern>> sections = new TreeMap<String, LinkedHashMap<Integer, Pattern>>();
+
+ public boolean load(String filePath) throws IOException {
+ BufferedReader input = null;
+ LinkedHashMap<Integer, Pattern> section = new LinkedHashMap<Integer, Pattern>();
+ try {
+ input = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
+ String line;
+ int linenr = 0;
+ while((line = input.readLine()) != null) {
+ linenr += 1;
+ line = line.trim();
+ if (line.startsWith("#")) {
+ continue;
+ }
+ if (line.startsWith("[") && line.endsWith("]")) {
+ // new section
+ String id = line.substring(1, line.length() - 1);
+ section = new LinkedHashMap<Integer, Pattern>();
+ sections.put(id, section);
+ } else {
+ String[] pair = line.split("=");
+ if (pair.length == 2) {
+ Integer number = Integer.parseInt(pair[0].trim());
+ Pattern pattern = Pattern.compile(pair[1].trim());
+ if (section.put(number, pattern) != null) {
+ System.err.println("Line " + linenr + ": previous definitions of " + number);
+ }
+ }
+ }
+ }
+ } finally {
+ if (input != null) {
+ input.close();
+ }
+ }
+ return false;
+ }
+
+ public static String id(ObjectName beanName, String attributeName) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(beanName.getDomain());
+ sb.append('.');
+ sb.append(beanName.getKeyProperty("type"));
+ sb.append(':');
+ sb.append(attributeName);
+ return sb.toString();
+ }
+
+ public Number resolve(String id, String value) {
+ LinkedHashMap<Integer, Pattern> section = sections.get(id);
+ if (section == null) {
+ return null;
+ }
+ for(Map.Entry<Integer, Pattern> entry : section.entrySet()) {
+ if (entry.getValue().matcher(value).matches()) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+}
@@ -0,0 +1,7 @@
+package org.vafer.jmx;
+
+public interface Filter {
+
+ public boolean include(String bean, String attribute);
+
+}
@@ -0,0 +1,26 @@
+package org.vafer.jmx;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.ObjectName;
+
+public final class ListOutput implements Output {
+
+ private final Set<String> seen = new HashSet<String>();
+
+ public void output(ObjectName beanName, String attributeName, Object value) {
+ Value.flatten(beanName, attributeName, value, new Value.Listener() {
+ public void value(ObjectName beanName, String attributeName, String value) {
+ final String id = Enums.id(beanName, attributeName);
+ if (!seen.contains(id)) {
+ System.out.println("[" + id + "]");
+ seen.add(id);
+ }
+ }
+ public void value(ObjectName beanName, String attributeName, Number value) {
+ }
+ });
+ }
+
+}
@@ -0,0 +1,8 @@
+package org.vafer.jmx;
+
+public final class NoFilter implements Filter {
+
+ public boolean include(String bean, String attribute) {
+ return true;
+ }
+}
@@ -0,0 +1,9 @@
+package org.vafer.jmx;
+
+import javax.management.ObjectName;
+
+public interface Output {
+
+ public void output(ObjectName beanName, String attributeName, Object value);
+
+}
@@ -0,0 +1,52 @@
+package org.vafer.jmx;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
+public final class Query {
+
+ public void run(String url, String expression, Filter filter, Output output) throws IOException, MalformedObjectNameException, InstanceNotFoundException, ReflectionException, IntrospectionException, AttributeNotFoundException, MBeanException {
+ JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(url));
+ MBeanServerConnection connection = connector.getMBeanServerConnection();
+ final Collection<ObjectInstance> mbeans = connection.queryMBeans(new ObjectName(expression), null);
+
+ for(ObjectInstance mbean : mbeans) {
+ final ObjectName mbeanName = mbean.getObjectName();
+ final MBeanInfo mbeanInfo = connection.getMBeanInfo(mbeanName);
+ final MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes();
+ for (final MBeanAttributeInfo attribute : attributes) {
+ if (attribute.isReadable()) {
+ if (filter.include(mbeanName.getCanonicalName(), attribute.getName())) {
+ final String attributeName = attribute.getName();
+ try {
+ output.output(
+ mbean.getObjectName(),
+ attributeName,
+ connection.getAttribute(mbeanName, attributeName)
+ );
+ } catch(Exception e) {
+ // System.err.println("Failed to read " + mbeanName + "." + attributeName);
+ }
+ }
+ }
+ }
+
+ }
+ connector.close();
+ }
+}
Oops, something went wrong.

0 comments on commit 5d44707

Please sign in to comment.