├── .gitignore
├── JMX-Config.png
├── JVM-Dashboard.png
├── LICENSE
├── README.md
├── graylog-plugin-input-jmx.iml
├── pom.xml
└── src
└── main
├── java
├── com
│ └── googlecode
│ │ └── jmxtrans
│ │ ├── jmx
│ │ ├── JmxQueryProcessor.java
│ │ └── JmxResultProcessor.java
│ │ └── model
│ │ ├── PropertyResolver.java
│ │ ├── Query.java
│ │ ├── Result.java
│ │ └── Server.java
└── org
│ └── graylog
│ └── inputs
│ └── jmx
│ ├── JMXInput.java
│ ├── JMXInputMetaData.java
│ ├── JMXInputPlugin.java
│ ├── JMXInputPluginModule.java
│ ├── JMXTransport.java
│ └── model
│ ├── GLAttribute.java
│ ├── GLQuery.java
│ └── GLQueryConfig.java
└── resources
├── META-INF
└── services
│ └── org.graylog2.plugin.Plugin
├── jvm.json
├── kafka.json
└── tomcat.json
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | pom.xml.tag
3 | pom.xml.releaseBackup
4 | pom.xml.versionsBackup
5 | pom.xml.next
6 | release.properties
7 | dependency-reduced-pom.xml
8 | buildNumber.properties
9 | .mvn/timing.properties
10 | .idea/
--------------------------------------------------------------------------------
/JMX-Config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivasamyk/graylog-plugin-input-jmx/b8ccb5aa034bb5c40074c74b00315a09aebd378e/JMX-Config.png
--------------------------------------------------------------------------------
/JVM-Dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sivasamyk/graylog-plugin-input-jmx/b8ccb5aa034bb5c40074c74b00315a09aebd378e/JVM-Dashboard.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Sivasamy Kaliappan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Graylog JMX Input Plugin
2 |
3 | Graylog input plugin to monitor JMX end points with built-in support for JVM and Tomcat endpoints
4 |
5 | Features
6 | --------
7 |
8 | * No agent required in the machines to be monitored
9 | * Support for Authentication
10 | * Support for SSL
11 | * Built-in support for monitoring JVM and Tomcat servers (more to come)
12 | * Monitor multiple servers from single input plugin instance
13 | * Support for monitoring custom JMX endpoints
14 | * Tested with Graylog 2.0.0
15 |
16 | Setup
17 | -----
18 |
19 | Download the plugin [jar](https://github.com/sivasamyk/graylog-plugin-input-jmx/releases/download/v1.0.2/graylog-plugin-input-jmx-1.0.2-SNAPSHOT.jar) and copy to graylog plugin directory (restart the graylog server for the changes to take effect).
20 | From Graylog UI, launch System->Input and select "JMX" input type
21 |
22 | Following parameters can be configured
23 |
24 | * Servers to monitor - Comma separated value of list of server IP Address or names to be monitored e.g. (10.220.5.123,webserver )
25 | * Port - Port on which the JMX endpoint is listening ( firewall should be configured for bidirectional access to this port)
26 | * JMX Object type - List of built-in JMX Object Types available. Select 'Custom' for monitoring custom endpoints.
27 | In this case the json config file path has to be specified in 'Config File Path' parameter
28 | * Username - Username configured in JMX access file (applicable when JMX authentication is enabled)
29 | * Password - Password configured in JMX password file (applicable when JMX authentication is enabled)
30 | * Polling Interval - Interval to poll JMX endpoints (recommend to set the interval > 30 secs)
31 | * Polling Interval time unit - Polling interval time unit
32 |
33 |
34 | To enable JMX monitoring in your Java application you need to pass certain command options to the application.
35 | e.g. To enable bare minimum JMX monitoring without security:
36 |
37 | ```
38 | java \
39 | -Dcom.sun.management.jmxremote \
40 | -Dcom.sun.management.jmxremote.port=12345 \
41 | -Dcom.sun.management.jmxremote.authenticate=false \
42 | -Dcom.sun.management.jmxremote.ssl=false \
43 | -jar /usr/share/doc/openjdk-6-jre-headless/demo/jfc/Notepad/Notepad.jar
44 | ```
45 |
46 | For more info on authentication and options refer
47 | [http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html](http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html)
48 |
49 | Custom Configuration
50 | --------------------
51 |
52 | To monitor custom JMX object types ( and to extend existing JMX type), a custom config file can be writtern and
53 | specified while launching the plugin. Example config file
54 |
55 | ```
56 | {
57 | "type": "jvm", /* Type of the endpoint */
58 | "queries": [
59 | {
60 | "object": "java.lang:type=Memory", /* JMX ObjectName */
61 | "attributes": [
62 | {
63 | "name": "HeapMemoryUsage", /* JMX MBean Attribute Name */
64 | "key": "used", /* JMX Attribute Key if applicable */
65 | "label": "jvm.mem.heap.used" /* Maps to a field in the graylog message. Allowed characters are A-Z,a-z,0-9,.,_ */
66 | }
67 | ]
68 | },
69 | {
70 | "object": "java.lang:type=GarbageCollector,name=*",
71 | "attributes": [
72 | {
73 | "name": "CollectionCount",
74 | "label": "jvm.gc.{name}.count" /* Support for dynamic field names based on object name property values */
75 | }
76 | ]
77 | }
78 | ]
79 | }
80 | ```
81 |
82 | This plugin uses the JMX Query code from [JMXTrans](https://github.com/jmxtrans/jmxtrans) project
83 |
84 | Enabling SSL
85 | ------------
86 |
87 | * To enable SSL generate keystore and truststore using keytool. Refer to following like for detailed steps.
88 | [https://pubs.vmware.com/continuent/tungsten-replicator-3.0/deployment-ssl-stores.html](https://pubs.vmware.com/continuent/tungsten-replicator-3.0/deployment-ssl-stores.html)
89 |
90 | * Start the application to be monitored using following command line arguments:
91 | ```
92 | java -Dcom.sun.management.jmxremote.port=1234
93 | -Djavax.net.ssl.keyStore=/home/user/Documents/keystore.jks
94 | -Djavax.net.ssl.keyStorePassword=keystorepass
95 | -Djavax.net.ssl.trustStore=/home/user/Documents/truststore.ts
96 | -Djavax.net.ssl.trustStorePassword=truststorepass
97 | -Dcom.sun.management.jmxremote.registry.ssl=true
98 | -Dcom.sun.management.jmxremote.authenticate=false
99 | MainClass
100 | ```
101 | * Configure Truststore path and password in Graylog plugin configuration window.
102 | * If you are using self-signed or untrusted certificates, remember to add them to trusted certificates in JRE lib/security
103 | For more info refer to [https://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/](https://www.mkyong.com/webservices/jax-ws/suncertpathbuilderexception-unable-to-find-valid-certification-path-to-requested-target/)
104 |
105 | Screenshots
106 | -----------
107 |
108 | Configuration Window
109 |
110 | 
111 |
112 | JVM Dashboard
113 |
114 | 
115 |
--------------------------------------------------------------------------------
/graylog-plugin-input-jmx.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.graylog
8 | graylog-plugin-input-jmx
9 | 1.0.2-SNAPSHOT
10 | jar
11 |
12 | ${project.artifactId}
13 | Graylog ${project.artifactId} plugin.
14 | https://www.graylog.org
15 |
16 |
17 | UTF-8
18 | 1.7
19 | 1.7
20 | 1.0.0
21 | /usr/share/graylog-server/plugin
22 |
23 |
24 |
25 |
26 | org.graylog2
27 | graylog2-plugin
28 | ${graylog2.version}
29 | provided
30 |
31 |
32 | org.graylog2
33 | graylog2-inputs
34 | ${graylog2.version}
35 | provided
36 |
37 |
38 | log4j
39 | log4j
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | org.apache.maven.plugins
49 | maven-shade-plugin
50 | 2.3
51 |
52 | false
53 |
54 |
55 |
56 | package
57 |
58 | shade
59 |
60 |
61 |
62 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | jdeb
72 | org.vafer
73 | 1.3
74 |
75 | ${project.build.directory}/${project.artifactId}-${project.version}.deb
76 |
77 |
78 | ${project.build.directory}/${project.build.finalName}.jar
79 | file
80 |
81 | perm
82 | ${graylog2.plugin-dir}
83 | 644
84 | root
85 | root
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | org.codehaus.mojo
94 | rpm-maven-plugin
95 | 2.1.2
96 |
97 | Application/Internet
98 |
99 | /usr
100 |
101 |
102 | _unpackaged_files_terminate_build 0
103 | _binaries_in_noarch_packages_terminate_build 0
104 |
105 | 644
106 | 755
107 | root
108 | root
109 |
110 |
111 | ${graylog2.plugin-dir}
112 |
113 |
114 | ${project.build.directory}/
115 |
116 | ${project.build.finalName}.jar
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jmxtrans/jmx/JmxQueryProcessor.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jmxtrans.jmx;
2 |
3 | import com.google.common.collect.HashMultimap;
4 | import com.google.common.collect.ImmutableList;
5 | import com.googlecode.jmxtrans.model.Query;
6 | import com.googlecode.jmxtrans.model.Result;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import javax.management.*;
11 | import java.io.IOException;
12 | import java.rmi.UnmarshalException;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class JmxQueryProcessor {
17 | private final Logger log = LoggerFactory.getLogger(getClass());
18 |
19 | /**
20 | * Responsible for processing individual Queries.
21 | */
22 | public HashMultimap processQuery(MBeanServerConnection mbeanServer, Query query) throws Exception {
23 | HashMultimap objectResults = HashMultimap.create();
24 | ObjectName oName = new ObjectName(query.getObj());
25 | for (ObjectName queryName : mbeanServer.queryNames(oName, null)) {
26 | ImmutableList results = fetchResults(mbeanServer, query, queryName);
27 | for (Result result : results) {
28 | objectResults.put(queryName, result);
29 | }
30 | }
31 | return objectResults;
32 | }
33 |
34 | private ImmutableList fetchResults(MBeanServerConnection mbeanServer, Query query, ObjectName queryName) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
35 | MBeanInfo info = mbeanServer.getMBeanInfo(queryName);
36 | ObjectInstance oi = mbeanServer.getObjectInstance(queryName);
37 |
38 | List attributes;
39 | if (query.getAttr().isEmpty()) {
40 | attributes = new ArrayList<>();
41 | for (MBeanAttributeInfo attrInfo : info.getAttributes()) {
42 | attributes.add(attrInfo.getName());
43 | }
44 | } else {
45 | attributes = query.getAttr();
46 | }
47 |
48 | ImmutableList results = ImmutableList.of();
49 | try {
50 | if (attributes.size() > 0) {
51 | log.debug("Executing queryName [{}] from query [{}]", queryName.getCanonicalName(), query);
52 |
53 | AttributeList al = mbeanServer.getAttributes(queryName, attributes.toArray(new String[attributes.size()]));
54 |
55 | results = new JmxResultProcessor(query, oi, al.asList(), info.getClassName(), queryName.getDomain()).getResults();
56 | }
57 | } catch (UnmarshalException ue) {
58 | if ((ue.getCause() != null) && (ue.getCause() instanceof ClassNotFoundException)) {
59 | log.debug("Bad unmarshall, continuing. This is probably ok and due to something like this: "
60 | + "http://ehcache.org/xref/net/sf/ehcache/distribution/RMICacheManagerPeerListener.html#52", ue.getMessage());
61 | }
62 | }
63 | return results;
64 | }
65 |
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/googlecode/jmxtrans/jmx/JmxResultProcessor.java:
--------------------------------------------------------------------------------
1 | package com.googlecode.jmxtrans.jmx;
2 |
3 | import com.google.common.collect.ImmutableList;
4 | import com.google.common.collect.ImmutableMap;
5 | import com.googlecode.jmxtrans.model.Query;
6 | import com.googlecode.jmxtrans.model.Result;
7 |
8 | import javax.management.Attribute;
9 | import javax.management.ObjectInstance;
10 | import javax.management.ObjectName;
11 | import javax.management.openmbean.CompositeData;
12 | import javax.management.openmbean.CompositeDataSupport;
13 | import javax.management.openmbean.CompositeType;
14 | import javax.management.openmbean.TabularDataSupport;
15 | import java.lang.reflect.Array;
16 | import java.util.Collections;
17 | import java.util.List;
18 | import java.util.Map;
19 | import java.util.Set;
20 |
21 | import static com.google.common.collect.ImmutableList.Builder;
22 | import static com.google.common.collect.Maps.newHashMap;
23 |
24 | public class JmxResultProcessor {
25 |
26 | private final Query query;
27 | private final ObjectInstance objectInstance;
28 | private final String className;
29 | private final String objDomain;
30 | private final List attributes;
31 | private static final String SEPERATOR = "_";
32 |
33 | public JmxResultProcessor(Query query, ObjectInstance objectInstance, List attributes, String className, String objDomain) {
34 | this.query = query;
35 | this.objectInstance = objectInstance;
36 | this.className = className;
37 | this.objDomain = objDomain;
38 | this.attributes = attributes;
39 | }
40 |
41 | public ImmutableList getResults() {
42 | Builder accumulator = ImmutableList.builder();
43 | for (Attribute attribute : attributes) {
44 | getResult(accumulator, attribute);
45 | }
46 | return accumulator.build();
47 | }
48 |
49 | /**
50 | * Used when the object is effectively a java type
51 | */
52 | private void getResult(Builder accumulator, Attribute attribute) {
53 | Object value = attribute.getValue();
54 | if (value == null) {
55 | return;
56 | }
57 |
58 | if (value instanceof CompositeData) {
59 | getResult(accumulator, attribute.getName(), (CompositeData) value);
60 | } else if (value instanceof CompositeData[]) {
61 | for (CompositeData cd : (CompositeData[]) value) {
62 | getResult(accumulator, attribute.getName(), cd);
63 | }
64 | } else if (value instanceof ObjectName[]) {
65 | Map values = newHashMap();
66 | for (ObjectName obj : (ObjectName[]) value) {
67 | values.put(obj.getCanonicalName(), obj.getKeyPropertyListString());
68 | }
69 | Result r = getNewResultObject(attribute.getName(), values);
70 | accumulator.add(r);
71 | } else if (value.getClass().isArray()) {
72 | // OMFG: this is nutty. some of the items in the array can be
73 | // primitive! great interview question!
74 | Map values = newHashMap();
75 | for (int i = 0; i < Array.getLength(value); i++) {
76 | Object val = Array.get(value, i);
77 | values.put(attribute.getName() + SEPERATOR + i, val);
78 | }
79 | accumulator.add(getNewResultObject(attribute.getName(), values));
80 | } else if (value instanceof TabularDataSupport) {
81 | TabularDataSupport tds = (TabularDataSupport) value;
82 | Map values = Collections.emptyMap();
83 | Result r = getNewResultObject(attribute.getName(), values);
84 | processTabularDataSupport(accumulator, attribute.getName(), tds);
85 | accumulator.add(r);
86 | } else if (value instanceof Map) {
87 | Result r = getNewResultObject(attribute.getName(), convertKeysToString((Map