自定义 SLF4J 记录器
Customize SLF4J Logger
我正在尝试找到一种很好的方法来向我的日志添加前缀,而无需在每次调用时都传递它,而无需再次实例化 Logger。
目的是单独跟踪 Rest 调用。
(前缀将在每次调用时使用 UUID 重新生成)
这就像
@RestController
class MyClass {
//Here the prefix is initialise once
//default value is X
Logger LOG = LoggerFactory.getLogger(MyClass.class);
@RequestMapping("/a")
void methodA() {
LOG.debug("foo");
}
@RequestMapping("/b")
void methodB() {
LOG.setPrefix("B");
LOG.debug("bar");
}
使用此输出
[...] [prefix X] foo
[...] [prefix B] bar
正如您所说的,您正在使用 Logback,这里有几个选项可以完成您想要做的事情:
标记
每个日志条目都可以为其建立一个 "marker"。 (我见过的最好的文档在 SLF4J FAQ 中。)像这样的东西:
class MyClass {
Marker methodBMarker = MarkerFactory.getMarker("B");
Logger logger = LoggerFactory.getLogger(MyClass.class);
…
void methodB() {
logger.debug(methodBMarker, "bar");
}
}
您需要更新每个方法中的所有日志条目以使用适当的标记。然后,您可以将 %marker
放入您的布局中,以将日志条目的标记放入日志中。
MDC
另一种选择是使用“Mapped Diagnostic Context”功能为每个日志条目指定当前 "context"。
class MyClass {
Logger logger = LoggerFactory.getLogger(MyClass.class);
…
void methodB() {
MDC.put("method", "b");
try {
…
logger.debug("bar");
…
} finally {
MDC.clear();
}
}
}
然后您将在布局中使用 %mdc{method}
来输出特定的 MDC 值。请注意,MDC 实际上旨在用于每个线程的值,例如特定于 Web 连接的值,因此当您离开要记录值的上下文时,确保清除不需要的内容非常重要英寸
有关如何使用 MDC 的示例,请参阅 http://www.slf4j.org/extensions.html#event_logger。您不必使用 EventLogger。一旦您在 MDC 中设置了内容,它们就会出现在每条日志记录中。
一个标记不符合您的标准,因为它必须在每次调用时指定。
下面是我的 MDC 实现说明,以分享我对 MDC 的实验。
//In this abstract class i'm defining initLogData methods to set MDC context
//It would be inherited by Controller and other classes who needs logging with traced transactions
public abstract class AbstractService {
protected LogData initLogData() {
return LogData.init();
}
protected LogData initLogData(String tName) {
return LogData.init(tName);
}
}
//LogData holds the MDC logic
public class LogData {
private final static int nRandom = 8;
//this keys are defined in logback pattern (see below)
private final static String tIdKey = "TID";
private final static String tNameKey = "TNAME";
//Transaction id
private String tId;
//Transaction name
private String tName;
public String getTId() {
return tId;
}
public void setTId(String tId) {
this.tId = tId;
}
public String gettName() {
return tName;
}
public void settName(String tName) {
this.tName = tName;
}
//random transaction id
//I'm not using uuid since its too longs and perfect unicity is not critical here
public String createTId(){
Random r = new Random();
StringBuilder sb = new StringBuilder();
while(sb.length() < nRandom){
sb.append(Integer.toHexString(r.nextInt()));
}
return sb.toString().substring(0, nRandom);
}
//private constructors (use init() methods to set LogData)
private LogData(String tId, String tName) {
this.tId = tId;
this.tName = tName;
}
private LogData(String tName) {
this.tId = createTId();
this.tName = tName;
}
private LogData() {
this.tId = createTId();
}
//init MDC with cascading calls processing (using same id/name within same context
//even if init() is called again)
public static LogData init(String tName) {
String previousTId = MDC.get(tIdKey);
String previousTName = MDC.get(tNameKey);
MDC.clear();
LogData logData = null;
if(previousTId != null) {
logData = new LogData(previousTId, previousTName);
} else {
logData = new LogData(tName);
}
MDC.put(tIdKey, logData.getTId());
MDC.put(tNameKey, logData.gettName());
return logData;
}
//init MDC without cascading calls management (new keys are generated for each init() call)
public static LogData init() {
MDC.clear();
LogData logData = new LogData();
MDC.put(tIdKey, logData.getTId());
return logData;
}
}
//logback.xml : values to include in log pattern
[%X{TID}] [%X{TNAME}]
@RestController
@RequestMapping("/test")
public class RestControllerTest extends AbstractRestService {
private final Logger LOG = LoggerFactory.getLogger(ServiceRestEntrypointStatus.class);
@RequestMapping(value="/testA")
public void testA() {
initLogData("testA");
LOG.debug("This is A");
}
@RequestMapping(value="/testB")
public void testB() {
initLogData("testA");
LOG.debug("This is B");
}
@RequestMapping(value="/testC")
public void testC() {
initLogData("testC");
LOG.debug("This is C");
testA();
testB();
}
}
调用映射的 RestControllerTest /test/testA 产生:
[fdb5d310] [testA] This is A
调用/test/testC产生(即使在子方法中调用了initLogData也保留了id和name):
[c7b0af53] [testC] This is C
[c7b0af53] [testC] This is A
[c7b0af53] [testC] This is B
我正在尝试找到一种很好的方法来向我的日志添加前缀,而无需在每次调用时都传递它,而无需再次实例化 Logger。
目的是单独跟踪 Rest 调用。 (前缀将在每次调用时使用 UUID 重新生成)
这就像
@RestController
class MyClass {
//Here the prefix is initialise once
//default value is X
Logger LOG = LoggerFactory.getLogger(MyClass.class);
@RequestMapping("/a")
void methodA() {
LOG.debug("foo");
}
@RequestMapping("/b")
void methodB() {
LOG.setPrefix("B");
LOG.debug("bar");
}
使用此输出
[...] [prefix X] foo
[...] [prefix B] bar
正如您所说的,您正在使用 Logback,这里有几个选项可以完成您想要做的事情:
标记
每个日志条目都可以为其建立一个 "marker"。 (我见过的最好的文档在 SLF4J FAQ 中。)像这样的东西:
class MyClass {
Marker methodBMarker = MarkerFactory.getMarker("B");
Logger logger = LoggerFactory.getLogger(MyClass.class);
…
void methodB() {
logger.debug(methodBMarker, "bar");
}
}
您需要更新每个方法中的所有日志条目以使用适当的标记。然后,您可以将 %marker
放入您的布局中,以将日志条目的标记放入日志中。
MDC
另一种选择是使用“Mapped Diagnostic Context”功能为每个日志条目指定当前 "context"。
class MyClass {
Logger logger = LoggerFactory.getLogger(MyClass.class);
…
void methodB() {
MDC.put("method", "b");
try {
…
logger.debug("bar");
…
} finally {
MDC.clear();
}
}
}
然后您将在布局中使用 %mdc{method}
来输出特定的 MDC 值。请注意,MDC 实际上旨在用于每个线程的值,例如特定于 Web 连接的值,因此当您离开要记录值的上下文时,确保清除不需要的内容非常重要英寸
有关如何使用 MDC 的示例,请参阅 http://www.slf4j.org/extensions.html#event_logger。您不必使用 EventLogger。一旦您在 MDC 中设置了内容,它们就会出现在每条日志记录中。
一个标记不符合您的标准,因为它必须在每次调用时指定。
下面是我的 MDC 实现说明,以分享我对 MDC 的实验。
//In this abstract class i'm defining initLogData methods to set MDC context
//It would be inherited by Controller and other classes who needs logging with traced transactions
public abstract class AbstractService {
protected LogData initLogData() {
return LogData.init();
}
protected LogData initLogData(String tName) {
return LogData.init(tName);
}
}
//LogData holds the MDC logic
public class LogData {
private final static int nRandom = 8;
//this keys are defined in logback pattern (see below)
private final static String tIdKey = "TID";
private final static String tNameKey = "TNAME";
//Transaction id
private String tId;
//Transaction name
private String tName;
public String getTId() {
return tId;
}
public void setTId(String tId) {
this.tId = tId;
}
public String gettName() {
return tName;
}
public void settName(String tName) {
this.tName = tName;
}
//random transaction id
//I'm not using uuid since its too longs and perfect unicity is not critical here
public String createTId(){
Random r = new Random();
StringBuilder sb = new StringBuilder();
while(sb.length() < nRandom){
sb.append(Integer.toHexString(r.nextInt()));
}
return sb.toString().substring(0, nRandom);
}
//private constructors (use init() methods to set LogData)
private LogData(String tId, String tName) {
this.tId = tId;
this.tName = tName;
}
private LogData(String tName) {
this.tId = createTId();
this.tName = tName;
}
private LogData() {
this.tId = createTId();
}
//init MDC with cascading calls processing (using same id/name within same context
//even if init() is called again)
public static LogData init(String tName) {
String previousTId = MDC.get(tIdKey);
String previousTName = MDC.get(tNameKey);
MDC.clear();
LogData logData = null;
if(previousTId != null) {
logData = new LogData(previousTId, previousTName);
} else {
logData = new LogData(tName);
}
MDC.put(tIdKey, logData.getTId());
MDC.put(tNameKey, logData.gettName());
return logData;
}
//init MDC without cascading calls management (new keys are generated for each init() call)
public static LogData init() {
MDC.clear();
LogData logData = new LogData();
MDC.put(tIdKey, logData.getTId());
return logData;
}
}
//logback.xml : values to include in log pattern
[%X{TID}] [%X{TNAME}]
@RestController
@RequestMapping("/test")
public class RestControllerTest extends AbstractRestService {
private final Logger LOG = LoggerFactory.getLogger(ServiceRestEntrypointStatus.class);
@RequestMapping(value="/testA")
public void testA() {
initLogData("testA");
LOG.debug("This is A");
}
@RequestMapping(value="/testB")
public void testB() {
initLogData("testA");
LOG.debug("This is B");
}
@RequestMapping(value="/testC")
public void testC() {
initLogData("testC");
LOG.debug("This is C");
testA();
testB();
}
}
调用映射的 RestControllerTest /test/testA 产生:
[fdb5d310] [testA] This is A
调用/test/testC产生(即使在子方法中调用了initLogData也保留了id和name):
[c7b0af53] [testC] This is C
[c7b0af53] [testC] This is A
[c7b0af53] [testC] This is B