我实际上是自己做的。我想我应该为它做一个合适的 Grails 插件,但我仍然对 Grails 不够满意,无法确保代码始终有效。我使用 Grails 2.2.4 从 Controller 和 Service 记录日志来测试它,它似乎运行良好。
它通过检查堆栈跟踪来查找发生调用的实际文件和行,然后在MDC线程上下文中添加此信息。添加到的值MDC
可以由使用%X{fileAndLine}
令牌的(其他)附加程序使用。
这是代码和 javadoc(阅读它!):
package logFileLineInjectorGrailsPlugin
import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.Logger;
import java.lang.StackTraceElement;
import org.apache.log4j.MDC;
/**
* Allows the log appenders to have access to the FILE and LINE where the log call actually occurred.
*
* (1) Add this pseudo appender to your other appenders, in Config.groovy. Then you can use
* "%X{fileAndLine}" in the other appenders to output the file and line where the log call actually occurred.
*
* ------------
* log4j = {
* appenders {
* appender name:'fileAndLineInjector', new logFileLineInjectorGrailsPlugin.FileAndLineInjector()
* // example of a console appender using the "%X{fileAndLine}" token :
* console name:'stdout', layout:pattern(conversionPattern: '[%d{yyyy-MM-dd HH:mm:ss}] %-5p ~ %m ~ %c ~ %X{fileAndLine}%n')
* }
* (...)
* ------------
*
* (2) Then add it has the *first* appender reference in the declarations of the loggers in which you want to use the "%X{fileAndLine}" token.
*
* For example :
*
* ------------
* root {
* error 'fileAndLineInjector', 'stdout'
* }
* ------------
*
* With this setup in place, a call to log.error("test!") will result in something like :
*
* [2013-08-12 19:16:15] ERROR ~ test! ~ grails.app.services.testProject.TestService ~ (TestService.groovy:8)
*
* In Eclipse/STS/GGTS (I didn't try in other IDEs), when "%X{fileAndLine}" is outputed in the internal console, the text is clickable
* and leads to the actual file/line.
*
*
*/
class FileAndLineInjector extends AppenderSkeleton {
@Override
public void close() {
}
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(LoggingEvent event) {
StackTraceElement[] strackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement targetStackTraceElement = null;
for(int i = 0; i < strackTraceElements.length; i++) {
StackTraceElement strackTraceElement = strackTraceElements[i];
if(strackTraceElement != null &&
strackTraceElement.declaringClass != null &&
strackTraceElement.declaringClass.startsWith("org.apache.commons.logging.Log\$") &&
i < (strackTraceElements.length - 1)) {
targetStackTraceElement = strackTraceElements[++i];
while(targetStackTraceElement.declaringClass != null &&
targetStackTraceElement.declaringClass.startsWith("org.codehaus.groovy.runtime.callsite.") &&
i < (strackTraceElements.length - 1)) {
targetStackTraceElement = strackTraceElements[++i];
}
break;
}
}
if(targetStackTraceElement != null) {
MDC.put("fileAndLine", "(" + targetStackTraceElement.getFileName() + ":" + targetStackTraceElement.getLineNumber() + ")");
} else {
MDC.remove("fileAndLine");
}
}
}
让我知道是否有不清楚的地方或者您是否找到了改进的方法!