Browse Source

Add support for logging into console for common logs (#836)

* Add a ConsoleHandler to support logging into stdout and stderr.
* Add a `csp.sentinel.log.output.type` property to configure for output type of record logs (only a temporary design)
* Add millisecond to the format of CspFormatter
master
cdfive Eric Zhao 5 years ago
parent
commit
8661d9abc1
4 changed files with 216 additions and 13 deletions
  1. +85
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/ConsoleHandler.java
  2. +2
    -1
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CspFormatter.java
  3. +48
    -12
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java
  4. +81
    -0
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/log/ConsoleHandlerTest.java

+ 85
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/ConsoleHandler.java View File

@@ -0,0 +1,85 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.log;

import java.io.UnsupportedEncodingException;
import java.util.logging.*;

/**
* This Handler publishes log records to console by using {@link java.util.logging.StreamHandler}.
*
* Print log of WARNING level or above to System.err,
* and print log of INFO level or below to System.out.
*
* To use this handler, add the following VM argument:
* <pre>
* -Dcsp.sentinel.log.output.type=console
* </pre>
*
* @author cdfive
*/
class ConsoleHandler extends Handler {

/**
* A Handler which publishes log records to System.out.
*/
private StreamHandler stdoutHandler;

/**
* A Handler which publishes log records to System.err.
*/
private StreamHandler stderrHandler;

public ConsoleHandler() {
this.stdoutHandler = new StreamHandler(System.out, new CspFormatter());
this.stderrHandler = new StreamHandler(System.err, new CspFormatter());
}

@Override
public synchronized void setFormatter(Formatter newFormatter) throws SecurityException {
this.stdoutHandler.setFormatter(newFormatter);
this.stderrHandler.setFormatter(newFormatter);
}

@Override
public synchronized void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException {
this.stdoutHandler.setEncoding(encoding);
this.stderrHandler.setEncoding(encoding);
}

@Override
public void publish(LogRecord record) {
if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
stderrHandler.publish(record);
stderrHandler.flush();
} else {
stdoutHandler.publish(record);
stdoutHandler.flush();
}
}

@Override
public void flush() {
stdoutHandler.flush();
stderrHandler.flush();
}

@Override
public void close() throws SecurityException {
stdoutHandler.close();
stderrHandler.close();
}
}

+ 2
- 1
sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/CspFormatter.java View File

@@ -28,7 +28,7 @@ class CspFormatter extends Formatter {
private final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
public SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
}
};

@@ -37,6 +37,7 @@ class CspFormatter extends Formatter {
final DateFormat df = dateFormatThreadLocal.get();
StringBuilder builder = new StringBuilder(1000);
builder.append(df.format(new Date(record.getMillis()))).append(" ");
builder.append(record.getLevel().getName()).append(" ");
builder.append(formatMessage(record));

String throwable = "";


+ 48
- 12
sentinel-core/src/main/java/com/alibaba/csp/sentinel/log/LogBase.java View File

@@ -22,6 +22,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;

import com.alibaba.csp.sentinel.util.PidUtil;
import com.alibaba.csp.sentinel.util.StringUtil;

/**
* Default log base dir is ${user.home}/logs/csp/, we can use {@link #LOG_DIR} System property to override it.
@@ -35,15 +36,22 @@ public class LogBase {

public static final String LOG_CHARSET = "utf-8";

// Output biz log(RecordLog,CommandCenterLog) to file
public static final String LOG_OUTPUT_TYPE_FILE = "file";
// Output biz log(RecordLog,CommandCenterLog) to console
public static final String LOG_OUTPUT_TYPE_CONSOLE = "console";

private static final String DIR_NAME = "logs" + File.separator + "csp";
private static final String USER_HOME = "user.home";

// Output type of biz log(RecordLog,CommandCenterLog)
public static final String LOG_OUTPUT_TYPE = "csp.sentinel.log.output.type";
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 logOutputType;
private static String logBaseDir;
private static boolean logNameUsePid = false;

static {
try {
@@ -55,6 +63,15 @@ public class LogBase {
}

private static void init() {
logOutputType = System.getProperty(LOG_OUTPUT_TYPE);

// By default, output biz log(RecordLog,CommandCenterLog) to file
if (StringUtil.isBlank(logOutputType)) {
logOutputType = LOG_OUTPUT_TYPE_FILE;
} else if (!LOG_OUTPUT_TYPE_FILE.equalsIgnoreCase(logOutputType) && !LOG_OUTPUT_TYPE_CONSOLE.equalsIgnoreCase(logOutputType)) {
logOutputType = LOG_OUTPUT_TYPE_FILE;
}

// first use -D, then use user home.
String logDir = System.getProperty(LOG_DIR);

@@ -125,18 +142,37 @@ public class LogBase {

protected static Handler makeLogger(String logName, Logger heliumRecordLog) {
CspFormatter formatter = new CspFormatter();
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);
handler.setFormatter(formatter);
handler.setEncoding(LOG_CHARSET);
} catch (IOException e) {
e.printStackTrace();

// Create handler according to logOutputType, set formatter to CspFormatter, set encoding to LOG_CHARSET
switch (logOutputType) {
case LOG_OUTPUT_TYPE_FILE:
String fileName = LogBase.getLogBaseDir() + logName;
if (isLogNameUsePid()) {
fileName += ".pid" + PidUtil.getPid();
}
try {
handler = new DateFileLogHandler(fileName + ".%d", 1024 * 1024 * 200, 4, true);
handler.setFormatter(formatter);
handler.setEncoding(LOG_CHARSET);
} catch (IOException e) {
e.printStackTrace();
}
break;
case LOG_OUTPUT_TYPE_CONSOLE:
try {
handler = new ConsoleHandler();
handler.setFormatter(formatter);
handler.setEncoding(LOG_CHARSET);
} catch (IOException e) {
e.printStackTrace();
}
break;
default:
break;
}

if (handler != null) {
LoggerUtils.disableOtherHandlers(heliumRecordLog, handler);
}


+ 81
- 0
sentinel-core/src/test/java/com/alibaba/csp/sentinel/log/ConsoleHandlerTest.java View File

@@ -0,0 +1,81 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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 com.alibaba.csp.sentinel.log;

import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import static org.junit.Assert.*;

/**
* Test cases for {@link ConsoleHandler}.
*
* @author cdfive
*/
public class ConsoleHandlerTest {

@Test
public void testPublish() {
ByteArrayOutputStream baosOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(baosOut));

ByteArrayOutputStream baosErr = new ByteArrayOutputStream();
System.setErr(new PrintStream(baosErr));

CspFormatter cspFormatter = new CspFormatter();
ConsoleHandler consoleHandler = new ConsoleHandler();

LogRecord logRecord;

// Test INFO level, should log to stdout
logRecord = new LogRecord(Level.INFO, "test info message");
consoleHandler.publish(logRecord);
assertEquals(cspFormatter.format(logRecord), baosOut.toString());
assertEquals("", baosErr.toString());
baosOut.reset();
baosErr.reset();

// Test INFO level, should log to stderr
logRecord = new LogRecord(Level.WARNING, "test warning message");
consoleHandler.publish(logRecord);
assertEquals(cspFormatter.format(logRecord), baosErr.toString());
assertEquals("", baosOut.toString());
baosOut.reset();
baosErr.reset();

// Test FINE level, no log by default
// Default log level is INFO, to log FINE message to stdout, add following config in $JAVA_HOME/jre/lib/logging.properties
// java.util.logging.StreamHandler.level=FINE
logRecord = new LogRecord(Level.FINE, "test fine message");
consoleHandler.publish(logRecord);
assertEquals("", baosOut.toString());
assertEquals("", baosErr.toString());
baosOut.reset();
baosErr.reset();

// Test SEVERE level, should log to stderr
logRecord = new LogRecord(Level.SEVERE, "test severe message");
consoleHandler.publish(logRecord);
assertEquals(cspFormatter.format(logRecord), baosErr.toString());
assertEquals("", baosOut.toString());
baosOut.reset();
baosErr.reset();
}
}

Loading…
Cancel
Save