- 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 { | public class CommandCenterLog extends LogBase { | ||||
private static final Logger heliumRecordLog = Logger.getLogger("cspCommandCenterLog"); | 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; | private static Handler logHandler = null; | ||||
static { | static { | ||||
@@ -24,7 +24,10 @@ import java.util.logging.Logger; | |||||
import com.alibaba.csp.sentinel.util.PidUtil; | 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 | * @author leyou | ||||
*/ | */ | ||||
@@ -33,9 +36,21 @@ public class LogBase { | |||||
private static final String DIR_NAME = "logs" + File.separator + "csp"; | private static final String DIR_NAME = "logs" + File.separator + "csp"; | ||||
private static final String USER_HOME = "user.home"; | private static final String USER_HOME = "user.home"; | ||||
public static final String LOG_DIR = "csp.sentinel.log.dir"; | 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; | private static String logBaseDir; | ||||
static { | static { | ||||
try { | |||||
init(); | |||||
} catch (Throwable t) { | |||||
t.printStackTrace(); | |||||
System.exit(-1); | |||||
} | |||||
} | |||||
private static void init() { | |||||
// first use -D, then use user home. | // first use -D, then use user home. | ||||
String logDir = System.getProperty(LOG_DIR); | String logDir = System.getProperty(LOG_DIR); | ||||
@@ -53,6 +68,19 @@ public class LogBase { | |||||
// logBaseDir must end with File.separator | // logBaseDir must end with File.separator | ||||
logBaseDir = logDir; | logBaseDir = logDir; | ||||
System.out.println("INFO: log base dir is: " + logBaseDir); | 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) { | private static String addSeparator(String logDir) { | ||||
@@ -61,7 +89,7 @@ public class LogBase { | |||||
} | } | ||||
return logDir; | return logDir; | ||||
} | } | ||||
protected static void log(Logger logger, Handler handler, Level level, String detail, Object... params) { | protected static void log(Logger logger, Handler handler, Level level, String detail, Object... params) { | ||||
if (detail == null) { | if (detail == null) { | ||||
return; | return; | ||||
@@ -73,7 +101,7 @@ public class LogBase { | |||||
logger.log(level, detail, params); | logger.log(level, detail, params); | ||||
} | } | ||||
} | } | ||||
protected static void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) { | protected static void log(Logger logger, Handler handler, Level level, String detail, Throwable throwable) { | ||||
if (detail == null) { | if (detail == null) { | ||||
return; | return; | ||||
@@ -93,7 +121,10 @@ public class LogBase { | |||||
protected static Handler makeLogger(String logName, Logger heliumRecordLog) { | protected static Handler makeLogger(String logName, Logger heliumRecordLog) { | ||||
CspFormatter formatter = new CspFormatter(); | 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; | Handler handler = null; | ||||
try { | try { | ||||
handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true); | handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true); | ||||
@@ -25,14 +25,14 @@ import java.util.logging.Logger; | |||||
* @author youji.zj | * @author youji.zj | ||||
*/ | */ | ||||
public class RecordLog extends LogBase { | 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; | private static Handler logHandler = null; | ||||
static { | static { | ||||
logHandler = makeLogger(FILE_NAME, heliumRecordLog); | logHandler = makeLogger(FILE_NAME, heliumRecordLog); | ||||
} | } | ||||
public static void info(String detail, Object... params) { | public static void info(String detail, Object... params) { | ||||
log(heliumRecordLog, logHandler, Level.INFO, detail, params); | log(heliumRecordLog, logHandler, Level.INFO, detail, params); | ||||
} | } | ||||
@@ -27,6 +27,7 @@ import java.util.Comparator; | |||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.List; | import java.util.List; | ||||
import com.alibaba.csp.sentinel.log.LogBase; | |||||
import com.alibaba.csp.sentinel.util.PidUtil; | import com.alibaba.csp.sentinel.util.PidUtil; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
@@ -36,7 +37,7 @@ import com.alibaba.csp.sentinel.log.RecordLog; | |||||
* <ol> | * <ol> | ||||
* <li>metric with the same second should write to the same file;</li> | * <li>metric with the same second should write to the same file;</li> | ||||
* <li>single file size must be controlled;</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>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> | * <li>every metric file is accompanied with an index file, which file name is {@code ${metricFileName}.idx}</li> | ||||
* </ol> | * </ol> | ||||
@@ -47,16 +48,21 @@ public class MetricWriter { | |||||
private static final String CHARSET = SentinelConfig.charset(); | private static final String CHARSET = SentinelConfig.charset(); | ||||
public static final String METRIC_BASE_DIR = RecordLog.getLogBaseDir(); | 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 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 final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | ||||
/** | /** | ||||
* 排除时差干扰 | * 排除时差干扰 | ||||
*/ | */ | ||||
private long timeSecondBase; | private long timeSecondBase; | ||||
private final StringBuilder sb = new StringBuilder(32); | |||||
private String baseDir; | private String baseDir; | ||||
private String baseFileName; | private String baseFileName; | ||||
/** | /** | ||||
@@ -86,7 +92,9 @@ public class MetricWriter { | |||||
if (singleFileSize <= 0 || totalFileCount <= 0) { | if (singleFileSize <= 0 || totalFileCount <= 0) { | ||||
throw new IllegalArgumentException(); | 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; | this.baseDir = METRIC_BASE_DIR; | ||||
File dir = new File(baseDir); | File dir = new File(baseDir); | ||||
if (!dir.exists()) { | if (!dir.exists()) { | ||||
@@ -193,7 +201,7 @@ public class MetricWriter { | |||||
list.add(file.getAbsolutePath()); | list.add(file.getAbsolutePath()); | ||||
} | } | ||||
} | } | ||||
Collections.sort(list, METRIC_FILENAME_CMP); | |||||
Collections.sort(list, METRIC_FILE_NAME_CMP); | |||||
if (list.isEmpty()) { | if (list.isEmpty()) { | ||||
return baseDir + baseFileName + "." + dateStr; | return baseDir + baseFileName + "." + dateStr; | ||||
} | } | ||||
@@ -209,30 +217,38 @@ public class MetricWriter { | |||||
/** | /** | ||||
* A comparator for metric file name. Metric file name is like: <br/> | * A comparator for metric file name. Metric file name is like: <br/> | ||||
* <pre> | * <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> | * </pre> | ||||
* <p> | * <p> | ||||
* File name with the early date is smaller, if date is same, the one with the small file number is smaller. | * 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. | * 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/> | * So the above file names should be sorted as: <br/> | ||||
* <pre> | * <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> | * </pre> | ||||
* </p> | * </p> | ||||
*/ | */ | ||||
private static final class MetricFileNameComparator implements Comparator<String> { | private static final class MetricFileNameComparator implements Comparator<String> { | ||||
private final String pid = "pid"; | |||||
@Override | @Override | ||||
public int compare(String o1, String o2) { | public int compare(String o1, String o2) { | ||||
String name1 = new File(o1).getName(); | String name1 = new File(o1).getName(); | ||||
String name2 = new File(o2).getName(); | String name2 = new File(o2).getName(); | ||||
String dateStr1 = name1.split("\\.")[2]; | String dateStr1 = name1.split("\\.")[2]; | ||||
String dateStr2 = name2.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 | // compare date first | ||||
int t = dateStr1.compareTo(dateStr2); | int t = dateStr1.compareTo(dateStr2); | ||||
@@ -273,7 +289,7 @@ public class MetricWriter { | |||||
list.add(file.getAbsolutePath()); | list.add(file.getAbsolutePath()); | ||||
} | } | ||||
} | } | ||||
Collections.sort(list, MetricWriter.METRIC_FILENAME_CMP); | |||||
Collections.sort(list, MetricWriter.METRIC_FILE_NAME_CMP); | |||||
return list; | 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. | * 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 appName | ||||
* @param pid | * @param pid | ||||
* @return metric file name. | * @return metric file name. | ||||
@@ -334,7 +353,17 @@ public class MetricWriter { | |||||
if (appName == null) { | if (appName == null) { | ||||
appName = ""; | 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.LogBase; | ||||
import com.alibaba.csp.sentinel.log.RecordLog; | import com.alibaba.csp.sentinel.log.RecordLog; | ||||
import com.alibaba.csp.sentinel.util.PidUtil; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
@@ -61,4 +62,30 @@ public class RecordLogTest { | |||||
assertTrue(RecordLog.getLogBaseDir().startsWith(System.getProperty("user.home"))); | 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); | |||||
} | |||||
} |