java - example - Obteniendo diferentes resultados para getStackTrace()[2].getMethodName()
testng tutorial (7)
Creo que es la profundidad específica que está causando el problema que es 2 en su escenario.
Así que, en lugar de escribir
String method = Thread.currentThread().getStackTrace()[2].getMethodName();
Si tú escribes
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
String method = null;
boolean doNext = false;
for (StackTraceElement s : ste) {
if (doNext) {
method = s.getMethodName();
return;
}
doNext = s.getMethodName().equals("getStackTrace");
}
Solo funcionará para JDK 1.5+
La otra opción es la siguiente:
String method = new Object(){}.getClass().getEnclosingMethod().getName();
O una opción más lenta será:
String method = new Exception().getStackTrace()[0].getMethodName();
Como esto creará una instancia de excepción cada vez.
Espero que te ayude.
Para fines de registro, creé un método logTitle () que imprime el nombre del método de llamada para nuestras pruebas de TestNG. El código de muestra está abajo.
@Test
public void test1() throws Exception {
method1();
}
public static void method1() throws Exception {
Utils.logTitle(2);
}
...
public static void logTitle(Integer level) throws Exception {
// Gets calling method name
String method = Thread.currentThread().getStackTrace()[2].getMethodName();
// This would get current method name
switch (level) {
case 1:
logger.info("=======================================================");
logger.info(method);
logger.info("=======================================================");
break;
case 2:
logger.info("------------------------------------");
logger.info(method);
logger.info("------------------------------------");
break;
case 3:
logger.info("---------------------");
logger.info(method);
logger.info("---------------------");
break;
case 4:
logger.info("--------- " + method + " ------------");
break;
default:
logger.info(method);
}
}
El problema es que estoy obteniendo resultados diferentes para logTitle () en dos máquinas diferentes.
El portátil de todos devuelve correctamente:
2016-06-20 14:22:06 INFO - ------------------------------------
2016-06-20 14:22:06 INFO - method1
2016-06-20 14:22:06 INFO - ------------------------------------
Nuestra caja dev unix regresa de manera diferente:
2016-06-20 14:42:26 INFO - ------------------------------------
2016-06-20 14:42:26 INFO - logTitle
2016-06-20 14:42:26 INFO - ------------------------------------
Esto funciona correctamente en las computadoras portátiles de todos los demás, pero no en la caja dev unix. Creo que la caja dev unix está usando la versión de IBM de Java, mientras que todos los demás están usando la versión de Oracle de Java, pero no estoy seguro de si ese es el culpable o no.
¿Algunas ideas?
Desde Javadoc :
Algunas máquinas virtuales pueden, en algunas circunstancias, omitir uno o más marcos de pila de la traza de pila. En el caso extremo, se permite que una máquina virtual que no tenga información de rastreo de pila con respecto a este lanzable devuelva una matriz de longitud cero desde este método.
Por lo tanto, la única forma garantizada de hacer esto es usar aspectos o recopilar el seguimiento de la pila con alguna otra forma personalizada. Pero puede combinar este enfoque con el respaldo para obtener el nombre del método actual (en el caso de que su método logTitle
esté en línea). Se puede encontrar here , por ejemplo. Una vez más, no hay garantía, pero mejor oportunidad.
La forma más sencilla de tener un nombre de método de prueba es mediante un @BeforeMethod
e inyectando el Method
. Vea la documentación de TestNG, here .
Simplemente almacene el nombre en algún lugar y utilícelo en su registro (¿por qué no en un @AfterMethod
?)
Log4j 2 usa el nombre de clase completamente calificado del registrador para localizar la clase y el método desde el cual se llamó al registrador. El código para encontrar la ubicación sigue a continuación. Sientase libre de usarlo.
Tenga en cuenta que el bucle comienza desde la parte inferior del seguimiento de pila; esto es necesario para detectar casos excepcionales en los que el registrador se llama de forma recursiva (quizás del método toString()
de un objeto que se registró). En tales casos, queremos informar la primera clase / método que llamó al registrador, no la última, por lo que no tenemos más remedio que recorrer el seguimiento de la pila desde la parte inferior hacia arriba.
public static StackTraceElement calcLocation(final String fqcnOfLogger) {
if (fqcnOfLogger == null) {
return null;
}
// LOG4J2-1029 new Throwable().getStackTrace is faster
// than Thread.currentThread().getStackTrace().
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement last = null;
for (int i = stackTrace.length - 1; i > 0; i--) {
final String className = stackTrace[i].getClassName();
if (fqcnOfLogger.equals(className)) {
return last;
}
last = stackTrace[i];
}
return null;
}
Mi conjetura, y como lo menciona MeBigFatGuy . Esto puede suceder debido a las diferentes implementaciones / valores predeterminados del compilador JIT de IBM / Oracle JVM cuando se realiza la optimización de alineación de métodos.
Sugiero ejecutar el código en el cuadro dev unix con
-Xjit:disableInlining
y ver si el problema desaparece.
Si esto funciona para usted, puede estar bien para la prueba, pero como se mencionó en la respuesta de no podemos confiar en que Java consista en los cuadros de pila.
Ver también:
Supongo que el comportamiento es JVM específico. En el pasado se me ocurrió esta solución:
// find first stack trace entry that is not in this class
Optional<StackTraceElement> ste = Iterables.tryFind(
Arrays.asList(new RuntimeException().getStackTrace()),
new Predicate<StackTraceElement>() {
@Override
public boolean apply(StackTraceElement input) {
return !input.getClassName().equals(PutYourClassHere.class.getName());
}
});
if (ste.isPresent()) {
LOG.trace("Method called by: {}.{}", ste.get().getClassName(), ste.get().getMethodName());
}
El fragmento está usando Google Guava porque es para Java 7. Si tienes Java 8, puedes usar Streams API y lambdas. Hice la comprobación ste.isPresent()
porque encontré una traza de pila vacía una vez. Por lo que recuerdo, la JVM de Oracle está omitiendo los rastros de pila cuando se lanza la misma excepción una y otra vez.
EDITAR: Java 8 vías
Optional<StackTraceElement> ste = Arrays.stream(new RuntimeException().getStackTrace())
.filter(x -> !x.getClassName().equals(Utils.class.getName()))
.findFirst();
Log4J encuentra el nombre del método buscando en el seguimiento de la pila hasta que encuentra el nombre de la clase de destino que se debe pasar, luego lee el nombre del método.
En su código, podría usar una técnica similar; en lugar de un método estático Utils, podría crear una instancia en su prueba, pasando la clase de la prueba:
Utils utils = new Utils(MyTest.class);
Luego use la técnica de búsqueda mencionada anteriormente en el método Utils.logTitle()
.
Utils.logTitle()
buscará hacia adelante a través de elementos de rastreo de pila de un Throwable recién creado hasta que encuentre el primer elemento con la clase objetivo deseada.