reales - Registro de Java: muestra el número de línea de origen de la persona que llama(no el método de ayuda de registro)
libro de android studio en español pdf (8)
Los numerosos (suspiro ...) frameworks de registro para Java hacen un buen trabajo al mostrar el número de línea del nombre del archivo fuente para el método que creó el mensaje de registro:
log.info("hey");
[INFO] [Foo:413] hey
Pero si tiene un método de ayuda en el medio, la persona que llama será el método de ayuda, y eso no es demasiado informativo.
log_info("hey");
[INFO] [LoggingSupport:123] hey
¿Hay alguna manera de decirle al sistema de registro que elimine un cuadro de la pila de llamadas al calcular la ubicación de origen para imprimir?
Supongo que esto es específico de la implementación; lo que necesito es Log4J a través de Commons Logging, pero estoy interesado en conocer otras opciones.
Agregando detalles a la respuesta KLE. (Lo siento, usuario novato, no sé mejor manera que crear una respuesta separada)
En lugar de pegar el número de línea al mensaje, puede ponerlo en el contexto de MDC. Ver org.apache.log4j.MDC
Por ejemplo:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
int l = stackTraceElement.getLineNumber();
MDC.put("myLineNumber", l);
Eso les permite a los usuarios usar mylineNumber en su archivo de configuración log4j
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="Line(%X{myLineNumber})- %m%n"/>
</layout>
Nota: eso le permite al usuario controlar dónde y cómo aparece el número de línea en el mensaje. Sin embargo, dado que obtener stacktrace es muy costoso, aún necesita encontrar la forma de desactivar la característica.
Aparece que hay una solución muy simple, simplemente agregue FQCN (nombre de clase completamente calificado de la clase contenedora) a su ayudante de registro:
public class MyLogger extends Logger {
private static final String FQCN = MyLogger.class.getName() + ".";
protected MyLogger(String name) {
super(name);
}
public void info(final Object msg) {
super.log(FQCN, Level.INFO, msg, null);
}
//etc...
En tu clase trabajadora solo haces:
public class MyClass {
private static final Logger LOG = MyLogger.getLogger();
private void test()
{
LOG.info("test");
}
}
Esto no es posible de la caja. Lo mejor que puede hacer en este caso es crear el registrador en la persona que llama y pasarlo al método util. De esta forma, al menos puede hacerse una idea de dónde proviene la llamada.
Para Log4j2, la respuesta se proporciona completamente mediante el uso de envoltorios de registrador como se describe en el manual de Log4j2 en Uso de ejemplo de un Contenedor de registrador generado . Uno puede simplemente generar (utilizando las herramientas org.apache.logging.log4j.core.tools.Generate $ ExtendedLogger ilustradas allí) un contenedor logger con un solo nivel STUB, y luego adaptarlo para crear métodos de registro personalizados que imiten el uso del logIfEnabled (FQCN, LEVEL, Marker, message, Throwable) - posiblemente ignorando el nivel de STUB y usando los regulares - luego, si lo desea, eliminando o comentando el nivel de STUB y sus métodos). Para este propósito, FormattedMessage puede ser útil.
La línea fuente, aunque costosa, puede mostrarse fácilmente como parte de la información de ubicación completa mediante el uso del elemento de patrón de conversión% l ubicación en el PatternLayout proporcionado en la configuración, o más específicamente utilizando el número de línea% L y / o el% M conversión de método.
Ahora con un ejemplo completo en: Registro de Java: Log4j Version2.x: muestra el método de un llamante de cliente final (no un método de ayuda de registro intermedio)
Respuesta alternativa
Es posible pedir a log4j que excluya la clase de ayuda utilizando el método
Category.log (String callerFQCN, Nivel de prioridad, Mensaje de objeto, Throwable t)
y especificando la clase auxiliar como ''callerFQCN''.
Por ejemplo, aquí hay una clase que usa un ayudante:
public class TheClass {
public static void main(String...strings) {
LoggingHelper.log("Message using full log method in logging helper.");
LoggingHelper.logNotWorking("Message using class info method");
}}
y el código del ayudante:
public class LoggingHelper {
private static Logger LOG = Logger.getLogger(LoggingHelper.class);
public static void log(String message) {
LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);
}
public static void logNotWorking(String message) {
LOG.info(message);
} }
El primer método generará el resultado esperado.
Line(TheClass.main(TheClass.java:4)) Message using full log method in logging helper. Line(LoggingHelper.logNotWorking(LoggingHelper.java:12)) Message using class info method
Al usar este método, Log4j funcionará como siempre, evitando calcular el seguimiento de la pila si no se requiere.
Si tiene sus propios métodos de utilidad de registro, puede agregar linenumber y filename a la lista de argumentos de registro y tomar la ruta cpp. es decir, preprocesa el origen para reemplazar etiquetas como _ LÍNEA _ y _ ARCHIVO _ antes de hacer la compilación. Como una ventaja adicional, esto no llevaría tantos recursos como resolver en tiempo de ejecución.
Tal vez pueda implementar la función log helper utilizando el elemento traza de pila, obtener los números de línea y omitir los marcos con el método con algunas anotaciones específicas, como
public @interface SkipFrame {}
// helper function
@SkipFrame // not necessary on the concrete log function
void log(String... message) {
// getStackTrace()...
int callerDepth = 2; // a constant number depends on implementation
StackTraceElement callerElement = null;
for (StackTraceElement e: stackTrace) {
String className, methodName = e.getClassName, getMethodName()...
Class callClass = Class.forName(className);
// since there maybe several methods with the same name
// here skip those overloaded methods
Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName);
SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class);
if (skipFrame != null)
continue; // skip this stack trace element
if (callerDepth-- == 0) {
callerElement = e;
break;
}
}
assert callerDepth == 0;
assert callerElement != null;
Log4j.info(callerElement.getLineNumber()... + "message... ");
}
@SkipFrame
void logSendMail(Mail mailObject) {
log("Send mail " + mailObject.getSubject());
}
Por lo tanto, si la función auxiliar está anidada, o si hay más funciones auxiliares utilizadas, simplemente marque la anotación SkipFrame en todas ellas y obtendrá el número de línea fuente correcto que realmente desea.
Tenga en cuenta que dar el número de línea es algo muy costoso , ya sea por lo que obtienes naturalmente de Log4j o por lo siguiente. Tienes que aceptar ese costo ...
Puede usar las siguientes API:
StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
stackTraceElement.getLineNumber();
Actualizado:
Tendría que calcularlo usted mismo. Asi que:
- pida a log4j que no lo muestre (en su formato de registro),
- e inserte el número de línea explicitement al comienzo de su mensaje (la cadena que envía a log4j).
Dependiendo de cómo prefiera sus registradores, su método de ayuda puede:
- usar un registrador explícito (pasado como parámetro, supongo), cuando corresponda (a veces definimos registradores específicos para un contexto específico, por ejemplo, tenemos un registrador para enviar nuestras solicitudes de base de datos, sin importar qué clase lo haga; esto nos permite reducir en un lugar, los cambios realizados en nuestro archivo de configuración, cuando queremos (des) activarlos ...)
- utilice un registrador para la clase de llamada: en este caso, en lugar de pasar el parámetro, también puede deducir el nombre de la clase de llamada ...