実際に自分でやりました。適切な 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");
}
}
}
何かが明確でない場合、またはそれを改善する方法を見つけた場合はお知らせください。