- Change log file name; - Add configurability to log file name and metric file name.master
@@ -25,7 +25,7 @@ import java.util.logging.Logger; | |||
public class CommandCenterLog extends LogBase { | |||
private static final Logger heliumRecordLog = Logger.getLogger("cspCommandCenterLog"); | |||
private static final String FILE_NAME = "commandCenter.log"; | |||
private static final String FILE_NAME = "command-center.log"; | |||
private static Handler logHandler = null; | |||
static { | |||
@@ -24,7 +24,10 @@ import java.util.logging.Logger; | |||
import com.alibaba.csp.sentinel.util.PidUtil; | |||
/** | |||
* Default log base dir is ${user.home}, we can use {@link #LOG_DIR} System property to override it. | |||
* Default log base dir is ${user.home}/logs/csp/, we can use {@link #LOG_DIR} System property to override it. | |||
* Default log file name dose not contain pid, but if multi instances of the same app are running in the same | |||
* machine, we may want to distinguish the log file by pid number, in this case, {@link #LOG_NAME_USE_PID} | |||
* System property could be configured as "true" to turn on this switch. | |||
* | |||
* @author leyou | |||
*/ | |||
@@ -33,9 +36,21 @@ public class LogBase { | |||
private static final String DIR_NAME = "logs" + File.separator + "csp"; | |||
private static final String USER_HOME = "user.home"; | |||
public static final String LOG_DIR = "csp.sentinel.log.dir"; | |||
public static final String LOG_NAME_USE_PID = "csp.sentinel.log.use.pid"; | |||
private static boolean logNameUsePid = false; | |||
private static String logBaseDir; | |||
static { | |||
try { | |||
init(); | |||
} catch (Throwable t) { | |||
t.printStackTrace(); | |||
System.exit(-1); | |||
} | |||
} | |||
private static void init() { | |||
// first use -D, then use user home. | |||
String logDir = System.getProperty(LOG_DIR); | |||
@@ -53,6 +68,19 @@ public class LogBase { | |||
// logBaseDir must end with File.separator | |||
logBaseDir = logDir; | |||
System.out.println("INFO: log base dir is: " + logBaseDir); | |||
String usePid = System.getProperty(LOG_NAME_USE_PID, ""); | |||
logNameUsePid = "true".equalsIgnoreCase(usePid); | |||
System.out.println("INFO: log name use pid is: " + logNameUsePid); | |||
} | |||
/** | |||
* Whether log file name should contain pid. This switch is configured by {@link #LOG_NAME_USE_PID} System property. | |||
* | |||
* @return if log file name should contain pid, return true, otherwise return false. | |||
*/ | |||
public static boolean isLogNameUsePid() { | |||
return logNameUsePid; | |||
} | |||
private static String addSeparator(String logDir) { | |||
@@ -61,7 +89,7 @@ public class LogBase { | |||
} | |||
return logDir; | |||
} | |||
protected static void log(Logger logger, Handler handler, Level level, String detail, Object... params) { | |||
if (detail == null) { | |||
return; | |||
@@ -73,7 +101,7 @@ public class LogBase { | |||
logger.log(level, detail, params); | |||
} | |||
} | |||
protected static void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) { | |||
if (detail == null) { | |||
return; | |||
@@ -93,7 +121,10 @@ public class LogBase { | |||
protected static Handler makeLogger(String logName, Logger heliumRecordLog) { | |||
CspFormatter formatter = new CspFormatter(); | |||
String fileName = LogBase.getLogBaseDir() + logName + ".pid" + PidUtil.getPid(); | |||
String fileName = LogBase.getLogBaseDir() + logName; | |||
if (isLogNameUsePid()) { | |||
fileName += ".pid" + PidUtil.getPid(); | |||
} | |||
Handler handler = null; | |||
try { | |||
handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true); | |||
@@ -25,14 +25,14 @@ import java.util.logging.Logger; | |||
* @author youji.zj | |||
*/ | |||
public class RecordLog extends LogBase { | |||
private static final Logger heliumRecordLog = Logger.getLogger("cspRecordLog"); | |||
private static final String FILE_NAME = "record.log"; | |||
private static final Logger heliumRecordLog = Logger.getLogger("cspSentinelRecordLog"); | |||
private static final String FILE_NAME = "sentinel-record.log"; | |||
private static Handler logHandler = null; | |||
static { | |||
logHandler = makeLogger(FILE_NAME, heliumRecordLog); | |||
} | |||
public static void info(String detail, Object... params) { | |||
log(heliumRecordLog, logHandler, Level.INFO, detail, params); | |||
} | |||
@@ -27,6 +27,7 @@ import java.util.Comparator; | |||
import java.util.Date; | |||
import java.util.List; | |||
import com.alibaba.csp.sentinel.log.LogBase; | |||
import com.alibaba.csp.sentinel.util.PidUtil; | |||
import com.alibaba.csp.sentinel.config.SentinelConfig; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
@@ -36,7 +37,7 @@ import com.alibaba.csp.sentinel.log.RecordLog; | |||
* <ol> | |||
* <li>metric with the same second should write to the same file;</li> | |||
* <li>single file size must be controlled;</li> | |||
* <li>file name is like: {@code ${AppName}_pid-metrics.log.yyyy-MM-dd.[number]}</li> | |||
* <li>file name is like: {@code ${appName}-metrics.log.pid${pid}.yyyy-MM-dd.[number]}</li> | |||
* <li>metric of different day should in different file;</li> | |||
* <li>every metric file is accompanied with an index file, which file name is {@code ${metricFileName}.idx}</li> | |||
* </ol> | |||
@@ -47,16 +48,21 @@ public class MetricWriter { | |||
private static final String CHARSET = SentinelConfig.charset(); | |||
public static final String METRIC_BASE_DIR = RecordLog.getLogBaseDir(); | |||
public static final String METRIC_FILE_SUFFIX = "-metrics.log"; | |||
/** | |||
* Note: {@link MetricFileNameComparator}'s implementation relays on the metric file name, | |||
* we should be careful when changing the metric file name. | |||
* | |||
* @see #formMetricFileName(String, int) | |||
*/ | |||
public static final String METRIC_FILE = "metrics.log"; | |||
public static final String METRIC_FILE_INDEX_SUFFIX = ".idx"; | |||
public static final Comparator<String> METRIC_FILENAME_CMP = new MetricFileNameComparator(); | |||
public static final Comparator<String> METRIC_FILE_NAME_CMP = new MetricFileNameComparator(); | |||
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||
/** | |||
* 排除时差干扰 | |||
*/ | |||
private long timeSecondBase; | |||
private final StringBuilder sb = new StringBuilder(32); | |||
private String baseDir; | |||
private String baseFileName; | |||
/** | |||
@@ -86,7 +92,9 @@ public class MetricWriter { | |||
if (singleFileSize <= 0 || totalFileCount <= 0) { | |||
throw new IllegalArgumentException(); | |||
} | |||
RecordLog.info("[MetricWriter] Creating new MetricWriter, singleFileSize=" + singleFileSize + ", totalFileCount=" + totalFileCount); | |||
RecordLog.info( | |||
"[MetricWriter] Creating new MetricWriter, singleFileSize=" + singleFileSize + ", totalFileCount=" | |||
+ totalFileCount); | |||
this.baseDir = METRIC_BASE_DIR; | |||
File dir = new File(baseDir); | |||
if (!dir.exists()) { | |||
@@ -193,7 +201,7 @@ public class MetricWriter { | |||
list.add(file.getAbsolutePath()); | |||
} | |||
} | |||
Collections.sort(list, METRIC_FILENAME_CMP); | |||
Collections.sort(list, METRIC_FILE_NAME_CMP); | |||
if (list.isEmpty()) { | |||
return baseDir + baseFileName + "." + dateStr; | |||
} | |||
@@ -209,30 +217,38 @@ public class MetricWriter { | |||
/** | |||
* A comparator for metric file name. Metric file name is like: <br/> | |||
* <pre> | |||
* aliswitch-8728-metrics.log.2018-03-06 | |||
* aliswitch-8728-metrics.log.2018-03-07 | |||
* aliswitch-8728-metrics.log.2018-03-07.10 | |||
* aliswitch-8728-metrics.log.2018-03-06.100 | |||
* metrics.log.2018-03-06 | |||
* metrics.log.2018-03-07 | |||
* metrics.log.2018-03-07.10 | |||
* metrics.log.2018-03-06.100 | |||
* </pre> | |||
* <p> | |||
* File name with the early date is smaller, if date is same, the one with the small file number is smaller. | |||
* Note that if the name is an absolute path, only the fileName({@link File#getName()}) part will be considered. | |||
* So the above file names should be sorted as: <br/> | |||
* <pre> | |||
* aliswitch-8728-metrics.log.2018-03-06 | |||
* aliswitch-8728-metrics.log.2018-03-06.100 | |||
* aliswitch-8728-metrics.log.2018-03-07 | |||
* aliswitch-8728-metrics.log.2018-03-07.10 | |||
* metrics.log.2018-03-06 | |||
* metrics.log.2018-03-06.100 | |||
* metrics.log.2018-03-07 | |||
* metrics.log.2018-03-07.10 | |||
* | |||
* </pre> | |||
* </p> | |||
*/ | |||
private static final class MetricFileNameComparator implements Comparator<String> { | |||
private final String pid = "pid"; | |||
@Override | |||
public int compare(String o1, String o2) { | |||
String name1 = new File(o1).getName(); | |||
String name2 = new File(o2).getName(); | |||
String dateStr1 = name1.split("\\.")[2]; | |||
String dateStr2 = name2.split("\\.")[2]; | |||
// in case of file name contains pid, skip it | |||
if (dateStr1.startsWith(pid)) { | |||
dateStr1 = name1.split("\\.")[3]; | |||
dateStr2 = name2.split("\\.")[3]; | |||
} | |||
// compare date first | |||
int t = dateStr1.compareTo(dateStr2); | |||
@@ -273,7 +289,7 @@ public class MetricWriter { | |||
list.add(file.getAbsolutePath()); | |||
} | |||
} | |||
Collections.sort(list, MetricWriter.METRIC_FILENAME_CMP); | |||
Collections.sort(list, MetricWriter.METRIC_FILE_NAME_CMP); | |||
return list; | |||
} | |||
@@ -323,9 +339,12 @@ public class MetricWriter { | |||
} | |||
/** | |||
* Form metric file name use the specific appName and pid. Not that only | |||
* Form metric file name use the specific appName and pid. Note that only | |||
* form the file name, not include path. | |||
* | |||
* Note: {@link MetricFileNameComparator}'s implementation relays on the metric file name, | |||
* we should be careful when changing the metric file name. | |||
* | |||
* @param appName | |||
* @param pid | |||
* @return metric file name. | |||
@@ -334,7 +353,17 @@ public class MetricWriter { | |||
if (appName == null) { | |||
appName = ""; | |||
} | |||
return appName + "-" + pid + METRIC_FILE_SUFFIX; | |||
// dot is special char that should be replaced. | |||
final String dot = "."; | |||
final String separator = "-"; | |||
if (appName.contains(dot)) { | |||
appName = appName.replace(dot, separator); | |||
} | |||
String name = appName + separator + METRIC_FILE; | |||
if (LogBase.isLogNameUsePid()) { | |||
name += ".pid" + pid; | |||
} | |||
return name; | |||
} | |||
/** | |||
@@ -19,6 +19,7 @@ import java.io.File; | |||
import com.alibaba.csp.sentinel.log.LogBase; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.alibaba.csp.sentinel.util.PidUtil; | |||
import org.junit.Test; | |||
@@ -61,4 +62,30 @@ public class RecordLogTest { | |||
assertTrue(RecordLog.getLogBaseDir().startsWith(System.getProperty("user.home"))); | |||
} | |||
public void testLogNameNotUsePid() { | |||
String userHome = System.getProperty("user.home"); | |||
String newLogBase = userHome + File.separator + "tmpLogDir" + System.currentTimeMillis(); | |||
System.setProperty(LogBase.LOG_DIR, newLogBase); | |||
RecordLog.info("testLogNameNotUsePid"); | |||
File[] files = new File(newLogBase).listFiles(); | |||
assertTrue(files != null && files.length > 0); | |||
for (File f : files) { | |||
assertTrue(!f.getName().contains("pid" + PidUtil.getPid())); | |||
} | |||
} | |||
public void testLogNameUsePid() { | |||
String userHome = System.getProperty("user.home"); | |||
String newLogBase = userHome + File.separator + "tmpLogDir" + System.currentTimeMillis(); | |||
System.setProperty(LogBase.LOG_DIR, newLogBase); | |||
System.setProperty(LogBase.LOG_NAME_USE_PID, "true"); | |||
RecordLog.info("testLogNameUsePid"); | |||
File[] files = new File(newLogBase).listFiles(); | |||
assertTrue(files != null && files.length > 0); | |||
for (File f : files) { | |||
assertTrue(f.getName().contains("pid" + PidUtil.getPid())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
package com.alibaba.csp.sentinel.node.metric; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import org.junit.Test; | |||
import static org.junit.Assert.*; | |||
public class MetricWriterTest { | |||
@Test | |||
public void testFileNameCmp() { | |||
String[] arr = new String[] { | |||
"metrics.log.2018-03-06", | |||
"metrics.log.2018-03-07", | |||
"metrics.log.2018-03-07.51", | |||
"metrics.log.2018-03-07.10", | |||
"metrics.log.2018-03-06.100" | |||
}; | |||
String[] key = new String[] { | |||
"metrics.log.2018-03-06", | |||
"metrics.log.2018-03-06.100", | |||
"metrics.log.2018-03-07", | |||
"metrics.log.2018-03-07.10", | |||
"metrics.log.2018-03-07.51" | |||
}; | |||
ArrayList<String> list = new ArrayList<String>(Arrays.asList(arr)); | |||
Collections.sort(list, MetricWriter.METRIC_FILE_NAME_CMP); | |||
assertEquals(Arrays.asList(key), list); | |||
} | |||
@Test | |||
public void testFileNamePidCmp() { | |||
String[] arr = new String[] { | |||
"metrics.log.pid1234.2018-03-06", | |||
"metrics.log.pid1234.2018-03-07", | |||
"metrics.log.pid1234.2018-03-07.51", | |||
"metrics.log.pid1234.2018-03-07.10", | |||
"metrics.log.pid1234.2018-03-06.100" | |||
}; | |||
String[] key = new String[] { | |||
"metrics.log.pid1234.2018-03-06", | |||
"metrics.log.pid1234.2018-03-06.100", | |||
"metrics.log.pid1234.2018-03-07", | |||
"metrics.log.pid1234.2018-03-07.10", | |||
"metrics.log.pid1234.2018-03-07.51" | |||
}; | |||
ArrayList<String> list = new ArrayList<String>(Arrays.asList(arr)); | |||
Collections.sort(list, MetricWriter.METRIC_FILE_NAME_CMP); | |||
assertEquals(Arrays.asList(key), list); | |||
} | |||
} |