/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.remoting.invoker.process;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
import com.dianping.pigeon.config.ConfigChangeListener;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.monitor.Monitor;
import com.dianping.pigeon.monitor.MonitorLoader;
import com.dianping.pigeon.monitor.MonitorTransaction;
import com.dianping.pigeon.registry.RegistryManager;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.exception.RpcException;
import com.dianping.pigeon.remoting.common.util.Constants;
import com.dianping.pigeon.remoting.invoker.exception.RequestTimeoutException;
import com.dianping.pigeon.remoting.invoker.util.InvokerHelper;
import com.dianping.pigeon.remoting.invoker.util.InvokerUtils;
/**
* @author xiangwu
*
*/
public enum ExceptionManager {
INSTANCE;
private static final Logger logger = LoggerLoader.getLogger(ExceptionManager.class);
private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager();
private static final Monitor monitor = MonitorLoader.getMonitor();
private static volatile Map<String, Integer> appLogTimeoutPeriodMap = new ConcurrentHashMap<String, Integer>();
private static final int logTimeoutPeriodLimit = ConfigManagerLoader.getConfigManager()
.getIntValue("pigeon.invoker.log.timeout.period.limit", 9999);
private static volatile Map<String, AtomicInteger> appTimeouts = new ConcurrentHashMap<String, AtomicInteger>();
private static final String KEY_LOG_TIMEOUT_PERIOD = "pigeon.invoker.log.timeout.period.apps";
private static final String KEY_LOG_EXCEPTION_IGNORED = "pigeon.invoker.log.exception.ignored";
private static volatile Set<String> ignoredLogExceptions = Collections
.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static final String KEY_LOG_SERVICE_IGNORED = "pigeon.invoker.log.service.ignored";
private static volatile Set<String> ignoredLogServices = Collections
.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static final String KEY_LOG_SERVICE_EXCEPTION = "pigeon.invoker.log.bizexception.enable";
private ExceptionManager() {
}
static {
String appLogTimeoutPeriodConfig = ConfigManagerLoader.getConfigManager().getStringValue(KEY_LOG_TIMEOUT_PERIOD,
"");
parseAppLogTimeoutPeriod(appLogTimeoutPeriodConfig);
String ignoredLogExceptionsConfig = ConfigManagerLoader.getConfigManager()
.getStringValue(KEY_LOG_EXCEPTION_IGNORED, "");
parseIgnoredLogExceptions(ignoredLogExceptionsConfig);
String ignoredLogServicesConfig = ConfigManagerLoader.getConfigManager().getStringValue(KEY_LOG_SERVICE_IGNORED,
"");
parseIgnoredLogServices(ignoredLogServicesConfig);
ConfigManagerLoader.getConfigManager().registerConfigChangeListener(new InnerConfigChangeListener());
ConfigManagerLoader.getConfigManager().getBooleanValue(KEY_LOG_SERVICE_EXCEPTION, false);
}
private static class InnerConfigChangeListener implements ConfigChangeListener {
@Override
public void onKeyUpdated(String key, String value) {
if (key.endsWith(KEY_LOG_TIMEOUT_PERIOD)) {
try {
parseAppLogTimeoutPeriod(value);
} catch (RuntimeException e) {
}
} else if (key.endsWith(KEY_LOG_EXCEPTION_IGNORED)) {
try {
parseIgnoredLogExceptions(value);
} catch (RuntimeException e) {
}
} else if (key.endsWith(KEY_LOG_SERVICE_IGNORED)) {
try {
parseIgnoredLogServices(value);
} catch (RuntimeException e) {
}
}
}
@Override
public void onKeyAdded(String key, String value) {
}
@Override
public void onKeyRemoved(String key) {
}
}
private static void parseIgnoredLogExceptions(String config) {
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
String[] exceptions = config.split(",");
for (String ex : exceptions) {
if (StringUtils.isNotBlank(ex)) {
set.add(ex);
}
}
ignoredLogExceptions = set;
}
private static void parseIgnoredLogServices(String config) {
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
String[] services = config.split(",");
for (String s : services) {
if (StringUtils.isNotBlank(s)) {
set.add(s);
}
}
ignoredLogServices = set;
}
private static void parseAppLogTimeoutPeriod(String appLogTimeoutPeriod) {
Map<String, Integer> configMap = new ConcurrentHashMap<String, Integer>();
Map<String, AtomicInteger> timeoutsMap = new ConcurrentHashMap<String, AtomicInteger>();
if (StringUtils.isNotBlank(appLogTimeoutPeriod)) {
String[] appArray = appLogTimeoutPeriod.split(",");
for (String appConfig : appArray) {
if (StringUtils.isNotBlank(appConfig) && appConfig.indexOf(":") != -1) {
String[] appPeriodArray = appConfig.split(":");
String app = appPeriodArray[0];
int period = Integer.parseInt(appPeriodArray[1]);
configMap.put(app, period);
timeoutsMap.put(app, new AtomicInteger(0));
}
}
}
if (!CollectionUtils.isEmpty(configMap)) {
appLogTimeoutPeriodMap.clear();
appLogTimeoutPeriodMap = configMap;
appTimeouts.clear();
appTimeouts = timeoutsMap;
}
}
public RpcException logRemoteCallException(String remoteAddress, String serviceName, String method, String msg,
InvocationRequest request, InvocationResponse response, MonitorTransaction transaction) {
if (response.getMessageType() == Constants.MESSAGE_TYPE_EXCEPTION) {
RpcException ex = InvokerUtils.toRpcException(response);
logRpcException(remoteAddress, serviceName, method, msg, ex, request, response, transaction);
return ex;
}
return null;
}
public Exception logRemoteServiceException(String msg, InvocationRequest request, InvocationResponse response) {
if (response.getMessageType() == Constants.MESSAGE_TYPE_SERVICE_EXCEPTION) {
Exception cause = InvokerUtils.toApplicationException(response);
if (ConfigManagerLoader.getConfigManager().getBooleanValue(KEY_LOG_SERVICE_EXCEPTION, false)) {
msg = String.format("%s# request:\r\n%s,\r\n response:\r\n%s\r\n", msg, request, response);
logger.error(msg, cause);
if (monitor != null) {
monitor.logError(msg, cause);
}
}
return cause;
}
return null;
}
public void logRpcException(String remoteAddress, String serviceName, String method, String msg, Throwable e,
InvocationRequest request, InvocationResponse response, MonitorTransaction transaction) {
boolean isLog = true;
if (!InvokerHelper.getLogCallException()) {
isLog = false;
} else if (ignoredLogExceptions.contains(e.getClass().getName())) {
isLog = false;
} else if (!(e instanceof RpcException)
&& !ConfigManagerLoader.getConfigManager().getBooleanValue(KEY_LOG_SERVICE_EXCEPTION, false)) {
isLog = false;
} else {
if (e instanceof RequestTimeoutException) {
isLog = false;
int logTimeoutPeriod = 0;
String targetApp = null;
if (remoteAddress != null) {
targetApp = RegistryManager.getInstance().getReferencedAppFromCache(remoteAddress);
}
if (targetApp != null && appLogTimeoutPeriodMap.containsKey(targetApp)) {
logTimeoutPeriod = appLogTimeoutPeriodMap.get(targetApp);
}
if (targetApp != null && logTimeoutPeriod > 0) {
if (logTimeoutPeriod <= logTimeoutPeriodLimit) {
AtomicInteger timeouts = appTimeouts.get(targetApp);
if (timeouts != null && timeouts.incrementAndGet() > logTimeoutPeriod) {
isLog = true;
timeouts.set(0);
}
}
} else {
isLog = true;
}
}
if (ignoredLogServices.contains(serviceName + "#" + method)) {
isLog = false;
}
}
if (isLog) {
StringBuilder error = new StringBuilder();
if (StringUtils.isNotBlank(msg)) {
error.append(msg).append(", ");
}
if (serviceName != null && request == null) {
error.append("call:").append(serviceName).append("#").append(method).append(", ");
}
if (remoteAddress != null) {
error.append("address:").append(remoteAddress).append(", ");
}
if (request != null) {
error.append("\r\nrequest:\r\n").append(request);
}
if (response != null) {
error.append("\r\nresponse:\r\n").append(response);
}
String s = error.toString();
logger.error(s, e);
if (monitor != null) {
monitor.logError(s, e);
}
if (transaction != null) {
transaction.setStatusError(e);
}
}
}
}