/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package scouter.agent.counter.task;
import scouter.agent.Configure;
import scouter.agent.Logger;
import scouter.agent.ObjTypeDetector;
import scouter.agent.counter.CounterBasket;
import scouter.agent.counter.anotation.Counter;
import scouter.agent.counter.meter.MeterResource;
import scouter.lang.TimeTypeEnum;
import scouter.lang.conf.ConfObserver;
import scouter.lang.counters.CounterConstants;
import scouter.lang.value.DecimalValue;
import scouter.lang.value.FloatValue;
import scouter.lang.value.ValueEnum;
import scouter.util.CastUtil;
import scouter.util.HashUtil;
import scouter.util.StringUtil;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.*;
public class TomcatJMXPerf {
HashMap<MeterKey, MeterResource> meters = new HashMap<MeterKey, MeterResource>();
HashMap<MeterKey, Long> lastValues = new HashMap<MeterKey, Long>();
private static HashSet<String> deltas = new HashSet<String>();
static {
deltas.add(CounterConstants.REQUESTPROCESS_BYTES_RECEIVED);
deltas.add(CounterConstants.REQUESTPROCESS_BYTES_SENT);
deltas.add(CounterConstants.REQUESTPROCESS_ERROR_COUNT);
deltas.add(CounterConstants.REQUESTPROCESS_PROCESSING_TIME);
deltas.add(CounterConstants.REQUESTPROCESS_REQUEST_COUNT);
ConfObserver.add("TomcatJMXPerf", new Runnable() {
public void run() {
ObjTypeDetector.dirtyConfig = true;
}
});
}
private long getDelta(MeterKey key, Long newValue) {
Long oldValue = lastValues.put(key, newValue);
return oldValue == null ? 0 : newValue.longValue() - oldValue.longValue();
}
private MeterResource getMeter(MeterKey key) {
MeterResource meter = meters.get(key);
if (meter == null) {
meter = new MeterResource();
meters.put(key, meter);
}
return meter;
}
static class MeterKey {
int mbeanHash;
String counter;
public MeterKey(int mbeanHash, String counter) {
this.mbeanHash = mbeanHash;
this.counter = counter;
}
public int hashCode() {
return mbeanHash ^ counter.hashCode();
}
public boolean equals(Object obj) {
if (obj instanceof MeterKey) {
MeterKey key = (MeterKey) obj;
return (this.mbeanHash == key.mbeanHash) && (this.counter.equals(key.counter));
}
return false;
}
}
private MBeanServer server;
List<MBeanObj> beanList = new ArrayList<MBeanObj>();
public long collectCnt = 0;
@Counter
public void process(CounterBasket pw) {
if (CounterConstants.TOMCAT.equals(ObjTypeDetector.objType) == false || conf.jmx_counter_enabled == false) {
return;
}
getMBeanServer();
if ((collectCnt <= 40 && collectCnt % 5 == 0) || ObjTypeDetector.dirtyConfig) {
if (ObjTypeDetector.dirtyConfig) {
AgentHeartBeat.clearSubObjects();
ObjTypeDetector.dirtyConfig = false;
}
getMBeanList();
}
collectCnt++;
for (MBeanObj beanObj : beanList) {
if (errors.contains(beanObj.attrName)) continue;
if (beanObj.valueType == ValueEnum.DECIMAL) {
try {
MeterKey key = new MeterKey(beanObj.mbeanHash, beanObj.counter);
long v = CastUtil.clong(server.getAttribute(beanObj.mbean, beanObj.attrName));
if (deltas.contains(beanObj.counter)) {
v = getDelta(key, v);
MeterResource meter = getMeter(key);
meter.add(v);
v = (long) meter.getSum(60);
long sum = (long) meter.getSum(300) / 5;
pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v));
pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, new DecimalValue(sum));
} else {
MeterResource meter = getMeter(key);
meter.add(v);
double avg = meter.getAvg(300);
FloatValue avgValue = new FloatValue((float) avg);
pw.getPack(beanObj.objName, TimeTypeEnum.REALTIME).add(beanObj.counter, new DecimalValue(v));
pw.getPack(beanObj.objName, TimeTypeEnum.FIVE_MIN).add(beanObj.counter, avgValue);
}
} catch (Exception e) {
errors.add(beanObj.attrName);
collectCnt = 0;
Logger.println("A902", e);
}
}
}
// long cpu2 = SysJMX.getCurrentThreadCPU();
}
private HashSet<String> errors = new HashSet<String>();
private void getMBeanServer() {
if (server == null) {
server = ManagementFactory.getPlatformMBeanServer();
}
}
Configure conf = Configure.getInstance();
private void getMBeanList() {
beanList.clear();
Set<ObjectName> mbeans = server.queryNames(null, null);
for (final ObjectName mbean : mbeans) {
String type = mbean.getKeyProperty("type");
String connectionpool = mbean.getKeyProperty("connectionpool");
if (type == null) {
continue;
}
if ("GlobalRequestProcessor".equals(type)) {
String port = mbean.getKeyProperty("name");
try {
String objName = conf.getObjName() + "/" + checkObjName(port);
String objType = getReqProcType();
AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName);
add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesReceived",
CounterConstants.REQUESTPROCESS_BYTES_RECEIVED);
add(objName, mbean, objType, ValueEnum.DECIMAL, "bytesSent",
CounterConstants.REQUESTPROCESS_BYTES_SENT);
add(objName, mbean, objType, ValueEnum.DECIMAL, "errorCount",
CounterConstants.REQUESTPROCESS_ERROR_COUNT);
add(objName, mbean, objType, ValueEnum.DECIMAL, "processingTime",
CounterConstants.REQUESTPROCESS_PROCESSING_TIME);
add(objName, mbean, objType, ValueEnum.DECIMAL, "requestCount",
CounterConstants.REQUESTPROCESS_REQUEST_COUNT);
} catch (Exception e) {
}
} else if ("DataSource".equals(type) && connectionpool == null) { // datasource
String name = mbean.getKeyProperty("name");
if (StringUtil.isNotEmpty(name)) {
try {
String context = mbean.getKeyProperty("context");
if (context != null && context.length() > 0) {
context = context.substring(1);
}
if (StringUtil.isNotEmpty(context)) {
name = context + "_" + name;
}
String objName = conf.getObjName() + "/" + checkObjName(name);
String objType = getDataSourceType();
AgentHeartBeat.addObject(objType, HashUtil.hash(objName), objName);
add(objName, mbean, objType, ValueEnum.DECIMAL, "numActive",
CounterConstants.DATASOURCE_CONN_ACTIVE);
add(objName, mbean, objType, ValueEnum.DECIMAL, "numIdle",
CounterConstants.DATASOURCE_CONN_IDLE);
add(objName, mbean, objType, ValueEnum.DECIMAL, "maxActive",
CounterConstants.DATASOURCE_CONN_MAX);
// for tomcat 5.5 +
// attribute name is changed from maxActive to maxTotal. (reported from zeroty : https://github.com/zeroty)
add(objName, mbean, objType, ValueEnum.DECIMAL, "maxTotal",
CounterConstants.DATASOURCE_CONN_MAX);
} catch (Exception e) {
}
}
}
}
}
private String getReqProcType() {
if (Configure.getInstance().obj_type_inherit_to_child_enabled) {
return Configure.getInstance().obj_type + "_req";
}
return CounterConstants.REQUESTPROCESS;
}
private String getDataSourceType() {
if (Configure.getInstance().obj_type_inherit_to_child_enabled) {
return Configure.getInstance().obj_type + "_ds";
}
return CounterConstants.DATASOURCE;
}
private void add(String objName, ObjectName mbean, String type, byte decimal, String attrName, String counterName) {
if (errors.contains(attrName))
return;
MBeanObj cObj = new MBeanObj(objName, mbean, type, ValueEnum.DECIMAL, attrName, counterName);
beanList.add(cObj);
}
private static String checkObjName(String name) {
StringBuffer sb = new StringBuffer();
char[] charArray = name.toCharArray();
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '-':
case '_':
sb.append(charArray[i]);
break;
case '/':
sb.append('_');
break;
default:
if (Character.isLetterOrDigit(charArray[i])) {
sb.append(charArray[i]);
}
}
}
return sb.toString();
}
class MBeanObj {
public int mbeanHash;
public String objName;
public ObjectName mbean;
public String objType;
public byte valueType;
public String attrName;
public String counter;
public MBeanObj(String objName, ObjectName mbean, String objType, byte valueType, String attrName,
String counter) {
this.objName = objName;
this.mbean = mbean;
this.mbeanHash = HashUtil.hash(mbean.toString());
this.objType = objType;
this.valueType = valueType;
this.attrName = attrName;
this.counter = counter;
}
@Override
public String toString() {
return "MBeanObj [objName=" + objName + ", mbean=" + mbean + ", objType=" + objType + ", valueType="
+ valueType + ", attrName=" + attrName + ", counter=" + counter + "]";
}
}
}