import agent_util
import os
try:
from jpype import java, javax
import jpype
except:
jpype = None
import traceback
from agent_util import float
metrics = {
#Work Manager Metrics
"work_manager": {
"domain": "com.bea",
"type": "WorkManagerRuntime",
"option_key": "Name",
"metrics": {
"completed_requests": {
"label": "Requests completed",
"property": "CompletedRequests",
"unit": "requests"
},
"pending_requests": {
"label": "Requests pending",
"property": "PendingRequests",
"unit": "requests"
},
"stuck_thread_count": {
"label": "Threads stuck",
"property": "StuckThreadCount",
"unit": "threads"
}
}
},
#JDBC Data Source Metrics
"jdbc_datasource": {
"domain": "com.bea",
"type": "JDBCDataSourceRuntime",
"metrics": {
"active_connections_current_count": {
"label": "JDBC active connections",
"property": "ActiveConnectionsCurrentCount",
"unit": "connections"
}
}
},
#EJB Pool Metrics
"ejb_pool": {
"domain": "com.bea",
"type": "EJBPoolRuntime",
"metrics": {
"access_total_count": {
"label": "EJB pool access count",
"property": "AccessTotalCount",
"unit": "accesses"
},
"beans_in_use_current_count": {
"label": "EJB pool beans in use",
"property": "BeansInUseCurrentCount",
"unit": "beans"
},
"destroyed_total_count": {
"label": "EJB pool beans destroyed",
"property": "DestroyedTotalCount",
"unit": "beans"
},
"idle_beans_count": {
"label": "EJB pool idle beansl",
"property": "IdleBeansCount",
"unit": "beans"
},
"miss_total_count": {
"label": "EJB pool miss count",
"property": "MissTotalCount",
"unit": "attempts"
},
"pooled_beans_current_count": {
"label": "EJB pool available bean instances",
"property": "PooledBeansCurrentCount",
"unit": "beans"
},
"timeout_total_count": {
"label": "EJB pool thread timeouts",
"property": "TimeoutTotalCount",
"unit": "threads",
},
"waiter_current_count": {
"label": "EJB pool threads waiting",
"property": "WaiterCurrentCount",
"unit": "threads"
},
}
},
#EJB Transaction Metrics
"ejb_transaction": {
"domain": "com.bea",
"type": "EJBTransactionRuntime",
"metrics": {
"transactions_committed_total_count": {
"label": "EJB transactions committed",
"property": "TransactionsCommittedTotalCount",
"unit": "transactions",
},
"transactions_rolled_back_total_count": {
"label": "EJB transactions rolled back",
"property": "TransactionsRolledBackTotalCount",
"unit": "transactions",
},
"transactions_timeout_total_count": {
"label": "EJB transactions timed out",
"property": "TransactionsTimedOutTotalCount",
"unit": "transactions",
}
}
},
#Executive Queue Metrics
"executive_queue": {
"domain": "com.bea",
"type": "ExecuteQueueRuntime",
"metrics": {
"execute_thread_current_idle_count": {
"label": "Execute queue idle threads",
"property": "ExecuteThreadCurrentIdleCount",
"unit": "threads",
},
"execute_thread_total_count": {
"label": "Execute queue total threads",
"property": "ExecuteThreadTotalCount",
"unit": "threads",
},
"pending_request_current_count": {
"label": "Execute queue waiting requests",
"property": "PendingRequestCurrentCount",
"unit": "requests",
},
"pending_request_oldest_time": {
"label": "Execute queue oldest waiting request age",
"property": "PendingRequestOldestTime",
"unit": "milliseconds"
},
"serviced_request_total_count": {
"label": "Execute queue requests processed",
"property": "ServicedRequestTotalCount",
"unit": "requests",
}
}
},
#Servlet Metrics
"servlet": {
"domain": "com.bea",
"type": "ServletRuntime",
"option_key": "Name",
"metrics": {
"execution_time_average": {
"label": "Servlet average execution time",
"property": "ExecutionTimeAverage",
"unit": "milliseconds",
},
"execution_time_high": {
"label": "Servlet longest execution time",
"property": "ExecutionTimeHigh",
"unit": "milliseconds",
},
"execution_time_low": {
"label": "Servlet shortest execution time",
"property": "ExecutionTimeLow",
"unit": "milliseconds",
},
"execution_time_total": {
"label": "Servlet total execution time",
"property": "ExecutionTimeTotal",
"unit": "seconds",
"scaleby": 0.001
},
"invocation_total_count": {
"label": "Servlet total invocations",
"property": "InvocationTotalCount",
"unit": "invocations",
},
"pool_max_capacity": {
"label": "Servlet maximum capacity",
"property": "PoolMaxCapacity"
},
"reload_total_count": {
"label": "Servlet reload count",
"property": "ReloadTotalCount",
"unit": "reloads"
}
}
},
#Web App Component Metrics
"web_app_component": {
"domain": "com.bea",
"type": "WebAppComponentRuntime",
"option_key": "Name",
"metrics": {
"open_sessions_current_count": {
"label": "Webapp current sessions",
"property": "OpenSessionsCurrentCount",
"unit": "sessions"
},
"open_sessions_high_count": {
"label": "Webapp maximum sessions",
"property": "OpenSessionsHighCount",
"unit": "sessions"
},
"sessions_opened_total_count": {
"label": "Webapp total sessions",
"property": "SessionsOpenedTotalCount",
"unit": "sessions"
}
}
},
# JVM stats
"jvm": {
"domain": "com.bea",
"type": "JVMRuntime",
"metrics": {
"uptime": {
"label": "JVM uptime",
"property": "Uptime",
"unit": "seconds",
"scaleby": 0.001
},
"heap_percent_free": {
"label": "JVM heap percent free",
"property": "HeapFreePercent",
"unit": "percent",
},
"heap_free": {
"label": "JVM heap free",
"property": "HeapFreeCurrent",
"unit": "bytes",
}
}
},
# JVM threading
"jvm_threading": {
"domain": "java.lang",
"type": "Threading",
"metrics": {
"jvm_thread_count_peak": {
"label": "Thread count - peak",
"property": "PeakThreadCount",
"unit": "threads",
},
"jvm_thread_count_daemon": {
"label": "Thread count - daemon",
"property": "DaemonThreadCount",
"unit": "threads",
},
"jvm_thread_count_total_started": {
"label": "Thread count - total started",
"property": "TotalStartedThreadCount",
"unit": "threads",
},
"jvm_thread_count": {
"label": "Thread count",
"property": "ThreadCount",
"unit": "threads",
},
}
},
# # JVM Compilations
"jvm_compilation": {
"domain": "java.lang",
"type": "Compilation",
"metrics": {
"jvm_compilation_time": {
"label": "JVM compilation time",
"property": "TotalCompilationTime",
"unit": "seconds",
"scaleby": 0.001,
}
}
},
#JVM Garbage Collector
"jvm_gc": {
"domain": "java.lang",
"type": "GarbageCollector",
"option_key": "name",
"metrics": {
"copy_count": {
"label": "JVM GC count",
"property": "CollectionCount",
"unit": "collections",
},
"copy_time": {
"label": "JVM GC time",
"property": "CollectionTime",
"unit": "seconds",
"scaleby": 0.001,
},
}
}
}
class WeblogicPlugin(agent_util.Plugin):
textkey = "weblogic12c"
label = "Oracle WebLogic 12c"
@classmethod
def get_jmx_connection(self, config):
"""
Establish a connection to the JMX endpoint on the server
"""
# Bail of we don't have the Jpype library
if not jpype:
return None
classpath = config.get("classpath")
if not jpype.isJVMStarted():
if classpath:
jpype.startJVM(jpype.getDefaultJVMPath(), "-Djava.class.path=%s" % classpath)
else:
jpype.startJVM(jpype.getDefaultJVMPath())
# Set authentication if provided in the configuration file
jhash = java.util.HashMap()
if "username" in config and "password" in config:
jarray = jpype.JArray(java.lang.String)([config['username'], config['password']])
jhash.put(javax.management.remote.JMXConnector.CREDENTIALS, jarray)
# Build up the JMX URL from configuration pieces
protocol = config.get("protocol", "iiop")
hostname = config.get("hostname", "localhost")
port = config.get("port", "7001")
jndi_name = config.get("jndi_name", "weblogic.management.mbeanservers.runtime")
url = "service:jmx:rmi:///jndi/%s://%s:%s/%s" % (protocol,
hostname,
port,
jndi_name)
# Let customers override the full JMX URL if desired
url = config.get("jmx_url", url)
jmxurl = javax.management.remote.JMXServiceURL(url)
jmxsoc = javax.management.remote.JMXConnectorFactory.connect(jmxurl, jhash)
connection = jmxsoc.getMBeanServerConnection()
return connection
@classmethod
def get_mbean_name(self, connection, bean_domain, bean_type, option_key=None, option_value=None):
"""
Get a reference to a specific MBean, referenced by the type of bean. Requires doing a
search through all current beans to find the right one.
"""
self.log.debug("GETTING MBEAN NAME: %s %s %s %s" % (bean_domain, bean_type, option_key, option_value))
for obj in connection.queryNames(None, None):
name = obj.toString()
domain, keys = name.split(":", 1)
if domain != bean_domain:
continue
fields = keys.split(",")
properties = {}
for field in fields:
key, value = field.split("=")
properties[key.lower()] = value
# We now have a mapping of the properties of the bean name. Check to see if we have
# a match for what we're looking for
if option_key:
if properties.get("type") == bean_type and properties[option_key.lower()] == option_value:
return name
elif properties.get("type") == bean_type:
return name
elif properties.get("type") == bean_type:
return name
return None
@classmethod
def get_mbean_options(self, connection, bean_domain, bean_type, option_key):
"""
Get the list of options for a given MBean type.
"""
options = []
self.log.debug("LOOKING FOR OPTIONS FOR %s %s %s" % (bean_domain, bean_type, option_key))
for obj in connection.queryNames(None, None):
name = obj.toString()
domain, keys = name.split(":", 1)
if domain != bean_domain:
continue
fields = keys.split(",")
properties = {}
for field in fields:
key, value = field.split("=")
properties[key.lower()] = value
if properties["type"] == bean_type and option_key.lower() in properties:
options.append(properties[option_key.lower()])
return options
@classmethod
def get_metric(self, connection, mbean_name, attribute_name, scaleby=1.0):
"""
Get a attribute value coming from a specific JMX MBean
"""
self.log.debug("GETTING METRIC %s %s" % (mbean_name, attribute_name))
try:
value = connection.getAttribute(javax.management.ObjectName(mbean_name), attribute_name)
value = value.floatValue() * scaleby
return value
except:
return None
@classmethod
def get_metadata(self, config):
status = agent_util.SUPPORTED
msg = None
# Check if WebLogic 12c has been enabled in the agnet config file
if not config:
self.log.info("The [weblogic12c] configuration block was not found in the agent config file")
status = agent_util.UNSUPPORTED
return {}
# Check if the Jpype library is available
if not jpype:
self.log.info("The Jpype library is not installed - see http://jpype.readthedocs.io/en/latest/ for instructions")
status = agent_util.UNSUPPORTED
return {}
if status == agent_util.SUPPORTED and not "classpath" in config:
msg = "Weblogic configuration parameters missing from the [weblogic] block of the agent config file - classpath must be specified."
status = agent_util.MISCONFIGURED
if status == agent_util.SUPPORTED and ("username" in config and not "password" in config):
msg = "Both username and password must be specified for WebLogic access"
status = agent_util.MISCONFIGURED
if status == agent_util.SUPPORTED and ("password" in config and not "username" in config):
msg = "Both username and password must be specified for WebLogic access"
status = agent_util.MISCONFIGURED
if status == agent_util.SUPPORTED:
# Make an actual call to get a value to ensure that everything works
try:
connection = self.get_jmx_connection(config)
name = self.get_mbean_name(connection, "com.bea", "WorkManagerRuntime")
value = self.get_metric(connection, name, "CompletedRequests")
except:
self.log.exception("Error getting weblogic metric: %s" % traceback.format_exc())
status = agent_util.MISCONFIGURED
msg = "Unable to access Weblogic metrics. Please double check your WebLogic JMX access configuration in the agent config file."
data = {}
connection = self.get_jmx_connection(config)
for type, vals in metrics.items():
for property, meta in vals["metrics"].items():
textkey = "%s.%s" % (type, property)
data[textkey] = {
"label": meta["label"][:100],
"options": meta.get('options', None),
"status": status,
"error_message": msg,
}
# If the bean takes options, go off and find them based on the option key
if "option_key" in vals:
data[textkey]["options"] = self.get_mbean_options(connection,
metrics[type]["domain"],
metrics[type]["type"],
metrics[type]["option_key"])
# If the metric's unit is specified, copy that over
if "unit" in meta:
data[textkey]["unit"] = meta["unit"]
# Check to make sure we can actually access the metric
try:
if "option_key" in vals and data[textkey]["options"]:
name = self.get_mbean_name(connection, metrics[type]["domain"], metrics[type]["type"],
vals["option_key"], data[textkey]["options"][0])
else:
name = self.get_mbean_name(connection, metrics[type]["domain"], metrics[type]["type"])
self.log.debug("VERIFYING BEAN: %s" % name)
value = self.get_metric(connection, name, meta["property"])
except:
status = agent_util.MISCONFIGURED
return data
def check(self, textkey, option, config):
try:
type, property = textkey.split(".")
except:
# Invalid metric specification, can't collect anything
return None
if type not in metrics or property not in metrics[type]["metrics"]:
return None
domain = metrics[type]["domain"]
metric = metrics[type]["metrics"][property]["property"]
scaleby = metrics[type]["metrics"][property].get("scaleby", 1.0)
connection = self.get_jmx_connection(config)
if not connection:
return None
if option and "option_key" in metrics[type]:
bean_name = self.get_mbean_name(connection, domain, metrics[type]["type"], metrics[type]["option_key"], option)
else:
bean_name = self.get_mbean_name(connection, domain, metrics[type]["type"])
return self.get_metric(connection, bean_name, metric, scaleby)