package com.ctrip.platform.dal.dao.client;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import com.ctrip.platform.dal.dao.helper.LoggerHelper;
/**
*
* Implements the common logic.
*
* @author gzxia
*
*/
public abstract class LoggerAdapter implements DalLogger {
private static final String SAMPLING = "sampling";
private static final String ENCRYPT = "encrypt";
private static final String SECRETKEY = "secretKey";
private static final String SIMPLIFIED = "simplified";
private static final String ASYNCLOGGING = "asyncLogging";
private static final String CAPACITY = "capacity";
private static final String SAMPLINGLOW = "samplingLow";
private static final String SAMPLINGHIGH = "samplingHigh";
private static final String SAMPLEMAXNUM = "sampleMaxNum";
private static final String SAMPLECLEARINTERVAL = "sampleClearInterval";
protected static boolean simplifyLogging = false;
protected static boolean encryptLogging = true;
public static String secretKey = "dalctripcn";
protected static boolean samplingLogging = false;
protected static long samplingLow = 60 * 60 * 1000;//milliseconds
protected static long samplingHigh = 5 * 60 * 1000;//milliseconds
//key is the sql hash code
private static final ConcurrentHashMap<Integer, Long> logEntryCache = new ConcurrentHashMap<Integer, Long>();
protected static int sampleMaxNum = 5000;
protected static int sampleClearInterval = 30;
private static ScheduledExecutorService scheduler = null;
private static final AtomicBoolean isClearingCache = new AtomicBoolean(false);
protected static boolean asyncLogging = false;
protected static ExecutorService executor = null;
@Override
public void initialize(Map<String, String> settings) {
if(settings == null)
return;
initSampling(settings);
if(settings.containsKey(SIMPLIFIED))
simplifyLogging = Boolean.parseBoolean(settings.get(SIMPLIFIED));
if(settings.containsKey(ENCRYPT))
encryptLogging = Boolean.parseBoolean(settings.get(ENCRYPT));
if(settings.containsKey(SECRETKEY))
secretKey = settings.get(SECRETKEY);
initAsyncLogging(settings);
}
private void initSampling(Map<String, String> settings) {
if(settings.containsKey(SAMPLING))
samplingLogging = Boolean.parseBoolean(settings.get(SAMPLING));
if(settings.containsKey(SAMPLEMAXNUM))
sampleMaxNum = Integer.parseInt(settings.get(SAMPLEMAXNUM));
if(settings.containsKey(SAMPLECLEARINTERVAL))
sampleClearInterval = Integer.parseInt(settings.get(SAMPLECLEARINTERVAL));
if(settings.containsKey(SAMPLINGLOW))
samplingLow = Integer.parseInt(settings.get(SAMPLINGLOW)) * 60 * 1000;
if(settings.containsKey(SAMPLINGHIGH))
samplingHigh = Integer.parseInt(settings.get(SAMPLINGHIGH)) * 60 * 1000;
if (samplingLogging) {
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
clearCache();
}
}, 5, sampleClearInterval, TimeUnit.SECONDS);
}
}
private void clearCache() {
int currentCount = logEntryCache.size();
if (sampleMaxNum > currentCount)
return;
isClearingCache.set(true);
for(Map.Entry<Integer, Long> entry : logEntryCache.entrySet()) {
Integer key = entry.getKey();
long old = entry.getValue();
long now = System.currentTimeMillis();
if ( (now - old) > samplingLow ) {
logEntryCache.remove(key);
}
}
isClearingCache.set(false);
}
private void initAsyncLogging(Map<String, String> settings) {
if(settings.containsKey(ASYNCLOGGING))
asyncLogging = Boolean.parseBoolean(settings.get(ASYNCLOGGING));
if (settings.containsKey(CAPACITY)) {
executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(Integer.parseInt(settings.get(CAPACITY)), true),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//do nothing
}
});
} else {
executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
}
@Override
public void shutdown() {
if (executor != null)
executor.shutdown();
if (scheduler != null)
scheduler.shutdown();
}
/**
* 1. If log level is Warning/Error/Fatal, then immediately write with out validate.
* 2. If log level is Info or less-than Info, validate according the below
* a. If the SQL have parameters, the log will send only once in the low interval minutes default is sixty minutes.
* b. If the SQL do not have any parameters, the log will send only once in the high interval minutes default is five minutes.
* @param entry
* @return The log can be sent only when returning value is true
*/
protected boolean validate(LogEntry entry) {
if ( isClearingCache.get() )
return false;
String sqlTpl = LoggerHelper.getSqlTpl(entry);
if (LoggerHelper.SQLHIDDENString.equals(sqlTpl) || "".equals(sqlTpl) )
return true;
int hashCode = LoggerHelper.getHashCode(sqlTpl);
long now = System.currentTimeMillis();
Long old = logEntryCache.putIfAbsent(hashCode, now);
if (old == null) {
if (logEntryCache.size() > sampleMaxNum)
logEntryCache.remove(hashCode);
return true;
}
boolean userLow = useLow(entry);
if ( (now - old) < (userLow ? samplingLow : samplingHigh) ) {
return false;
} else {
//update the old timestamp associated the sqlTpl to now
logEntryCache.put(hashCode, System.currentTimeMillis());
return true;
}
}
private boolean useLow(LogEntry entry) {
String[] pramemters = entry.getPramemters();
//use low when have parameters, otherwise use high.
if (pramemters == null || pramemters.length <= 0)
return false;
return true;
}
}