Как найти вызывающего метода, используя трассировку стека или отражение?

У меня была аналогичная проблема, и я писал о ней здесь . Я начал с регулярного выражения для альфа-чисел, а затем добавил в несколько основных символов пунктуации, которые мне понравились:

select dump(a,1016), a, b
from
 (select regexp_replace(COLUMN,'[[:alnum:]/''%()> -.:=;[]','') a,
         COLUMN b
  from TABLE)
where a is not null
order by a;

Я использовал дамп с вариантом 1016, чтобы выдать шестнадцатеричные символы, которые я хотел заменить, может затем пользователь в utl_raw.cast_to_varchar2.

371
задан Jeroen Vannevel 8 February 2014 в 17:55
поделиться

12 ответов

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()

Согласно Javadocs:

Последний элемент массива представляет нижнюю часть стека, что является наименьшим последним вызовом метода в последовательности.

A StackTraceElement имеет getClassName(), getFileName(), getLineNumber() и getMethodName().

Вам придется поэкспериментировать, чтобы определить, какой индекс вы хотите (вероятно, stackTraceElements[1] или [2]).

396
ответ дан towi 8 February 2014 в 17:55
поделиться
  • 1
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27
  • 2
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27
  • 3
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27
  • 4
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27
  • 5
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27
  • 6
    Правда. Я использовал CATransaction.begin в DispatchQueue.main.async. момент, когда я удалил CoreAnimation из DispatchQueue.main.async. Предупреждение сработало. – Yash Bedi 5 June 2019 в 20:27

Oneliner :

Thread.currentThread().getStackTrace()[2].getMethodName()

Обратите внимание, что вам может понадобиться заменить 2 на 1.

11
ответ дан Andro Secy 8 February 2014 в 17:55
поделиться

Похоже, вы пытаетесь избежать передачи ссылки на this в метод. Передача this намного лучше, чем поиск вызывающего по текущей трассировке стека. Рефакторинг для более ОО дизайна еще лучше. Вам не нужно знать звонящего. При необходимости передайте объект обратного вызова.

34
ответ дан Craig P. Motlin 8 February 2014 в 17:55
поделиться

Этот метод делает то же самое, но немного проще и, возможно, немного более производительно, и, если вы используете отражение, он автоматически пропускает эти кадры. Единственная проблема заключается в том, что он может отсутствовать в JVM не от Sun, хотя он включен в классы выполнения JRockit 1.4 -> 1.6. (Дело в том, что это не публичный класс).

sun.reflect.Reflection

    /** Returns the class of the method <code>realFramesToSkip</code>
        frames up the stack (zero-based), ignoring frames associated
        with java.lang.reflect.Method.invoke() and its implementation.
        The first frame is that associated with this method, so
        <code>getCallerClass(0)</code> returns the Class object for
        sun.reflect.Reflection. Frames associated with
        java.lang.reflect.Method.invoke() and its implementation are
        completely ignored and do not count toward the number of "real"
        frames skipped. */
    public static native Class getCallerClass(int realFramesToSkip);

Что касается значения realFramesToSkip, версии VM для Sun 1.5 и 1.6 java.lang.System, то существует защищенный пакетный метод getCallerClass (), который вызывает sun.reflect.Reflection.getCallerClass(3), но в моем помощнике служебный класс Я использовал 4, так как добавлен фрейм вызова вспомогательного класса.

10
ответ дан Nicholas 8 February 2014 в 17:55
поделиться

Я делал это раньше. Вы можете просто создать новое исключение и получить трассировку стека, не выбрасывая его, а затем изучить трассировку стека. Как говорится в другом ответе, это чрезвычайно дорого - не делайте это в узком кругу.

Я делал это раньше для утилиты ведения журналов в приложении, где производительность не имела большого значения (на самом деле производительность вообще не имеет большого значения), если вы отображаете результат для действия, такого как быстрое нажатие кнопки. ).

Это было до того, как вы смогли получить трассировку стека, исключения имели только .printStackTrace (), поэтому мне пришлось перенаправить System.out в поток моего собственного создания, затем (new Exception ()). PrintStackTrace () ; Перенаправьте System.out обратно и проанализируйте поток. Прикольные вещи.

6
ответ дан Bill K 8 February 2014 в 17:55
поделиться
private void parseExceptionContents(
      final Exception exception,
      final OutputStream out)
   {
      final StackTraceElement[] stackTrace = exception.getStackTrace();
      int index = 0;
      for (StackTraceElement element : stackTrace)
      {
         final String exceptionMsg =
              "Exception thrown from " + element.getMethodName()
            + " in class " + element.getClassName() + " [on line number "
            + element.getLineNumber() + " of file " + element.getFileName() + "]";
         try
         {
            out.write((headerLine + newLine).getBytes());
            out.write((headerTitlePortion + index++ + newLine).getBytes() );
            out.write((headerLine + newLine).getBytes());
            out.write((exceptionMsg + newLine + newLine).getBytes());
            out.write(
               ("Exception.toString: " + element.toString() + newLine).getBytes());
         }
         catch (IOException ioEx)
         {
            System.err.println(
                 "IOException encountered while trying to write "
               + "StackTraceElement data to provided OutputStream.\n"
               + ioEx.getMessage() );
         }
      }
   }
1
ответ дан AZ_ 8 February 2014 в 17:55
поделиться

Вот часть кода, который я сделал на основе подсказок, показанных в этой теме. Надеюсь, это поможет.

(не стесняйтесь вносить предложения по улучшению этого кода, пожалуйста, сообщите мне)

Счетчик:

public class InstanceCount{
    private static Map<Integer, CounterInstanceLog> instanceMap = new HashMap<Integer, CounterInstanceLog>();
private CounterInstanceLog counterInstanceLog;


    public void count() {
        counterInstanceLog= new counterInstanceLog();
    if(counterInstanceLog.getIdHashCode() != 0){
    try {
        if (instanceMap .containsKey(counterInstanceLog.getIdHashCode())) {
         counterInstanceLog= instanceMap .get(counterInstanceLog.getIdHashCode());
    }

    counterInstanceLog.incrementCounter();

            instanceMap .put(counterInstanceLog.getIdHashCode(), counterInstanceLog);
    }

    (...)
}

И объект:

public class CounterInstanceLog{
    private int idHashCode;
    private StackTraceElement[] arrayStackTraceElements;
    private int instanceCount;
    private String callerClassName;

    private StackTraceElement getProjectClasses(int depth) {
      if(depth< 10){
        getCallerClassName(sun.reflect.Reflection.getCallerClass(depth).getName());
        if(getCallerClassName().startsWith("com.yourproject.model")){
            setStackTraceElements(Thread.currentThread().getStackTrace());
            setIdHashCode();
        return arrayStackTraceElements[depth];
        }
        //+2 because one new item are added to the stackflow
        return getProjectClasses(profundidade+2);           
      }else{
        return null;
      }
    }

    private void setIdHashCode() {
        if(getNomeClasse() != null){
            this.idHashCode = (getCallerClassName()).hashCode();
        }
    }

    public void incrementaContador() {
    this.instanceCount++;
}

    //getters and setters

    (...)



}
0
ответ дан Pmt 8 February 2014 в 17:55
поделиться

используйте этот метод: -

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
 stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
 System.out.println(e.getMethodName());

Пример вызова метода здесь: -

public class TestString {

    public static void main(String[] args) {
        TestString testString = new TestString();
        testString.doit1();
        testString.doit2();
        testString.doit3();
        testString.doit4();
    }

    public void doit() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
        System.out.println(e.getMethodName());
    }

    public void doit1() {
        doit();
    }

    public void doit2() {
        doit();
    }

    public void doit3() {
        doit();
    }

    public void doit4() {
        doit();
    }
}
0
ответ дан Reegan Miranda 8 February 2014 в 17:55
поделиться
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

class DBConnection {
    String createdBy = null;

    DBConnection(Throwable whoCreatedMe) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintWriter pw = new PrintWriter(os);
        whoCreatedMe.printStackTrace(pw);
        try {
            createdBy = os.toString();
            pw.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class ThrowableTest {

    public static void main(String[] args) {

        Throwable createdBy = new Throwable(
                "Connection created from DBConnectionManager");
        DBConnection conn = new DBConnection(createdBy);
        System.out.println(conn.createdBy);
    }
}

ИЛИ

public static interface ICallback<T> { T doOperation(); }


public class TestCallerOfMethod {

    public static <T> T callTwo(final ICallback<T> c){
        // Pass the object created at callee to the caller
        // From the passed object we can get; what is the callee name like below.
        System.out.println(c.getClass().getEnclosingMethod().getName());
        return c.doOperation();
    }

    public static boolean callOne(){
        ICallback callBackInstance = new ICallback(Boolean){
            @Override
            public Boolean doOperation() 
            {
                return true;
            }
        };
        return callTwo(callBackInstance);
    }

    public static void main(String[] args) {
         callOne();
    }
}
0
ответ дан Kanagavelu Sugumar 8 February 2014 в 17:55
поделиться

Java 9 - JEP 259: API Stack-Walking

JEP 259 предоставляет эффективный стандартный API для обхода стека, который обеспечивает легкую фильтрацию и медленный доступ к информации в стеке следы. До API Stack-Walking распространенными способами доступа к фреймам стека были:

Throwable::getStackTrace и Thread::getStackTrace возвращать массив объектов StackTraceElement, которые содержат имя класса и имя метода каждого стека элемент трассировки.

SecurityManager::getClassContext - это защищенный метод, который позволяет подклассу SecurityManager обращаться к контексту класса.

JDK-внутренний метод sun.reflect.Reflection::getCallerClass, который вы не должны использовать в любом случае

Использование этих API-интерфейсов обычно неэффективно:

Для этих API требуется виртуальная машина чтобы охотно захватить снимок всего стека , и они возвращают информацию, представляющую весь стек. Невозможно избежать затрат на проверку всех кадров, если вызывающий абонент интересуется только несколькими верхними кадрами в стеке.

Чтобы найти класс непосредственного абонента, сначала получите StackWalker:

StackWalker walker = StackWalker
                           .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

Затем либо вызовите getCallerClass():

Class<?> callerClass = walker.getCallerClass();

или walk StackFrame и получить первое предшествующее StackFrame:

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());
28
ответ дан Ali Dehghani 8 February 2014 в 17:55
поделиться
     /**
       * Get the method name for a depth in call stack. <br />
       * Utility function
       * @param depth depth in the call stack (0 means current method, 1 means call method, ...)
       * @return method name
       */
      public static String getMethodName(final int depth)
      {
        final StackTraceElement[] ste = new Throwable().getStackTrace();

        //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
        return ste[ste.length - depth].getMethodName();
      }

Например, если вы пытаетесь получить строку вызывающего метода для целей отладки, вам нужно пройти класс Utility, в котором вы кодируете эти статические методы:
(old java1) .4 код, просто чтобы проиллюстрировать потенциальное использование StackTraceElement)

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils". <br />
          * From the Stack Trace.
          * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
          */
        public static String getClassMethodLine()
        {
            return getClassMethodLine(null);
        }

        /**
          * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass. <br />
          * Allows to get past a certain class.
          * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
          * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
          */
        public static String getClassMethodLine(final Class aclass)
        {
            final StackTraceElement st = getCallingStackTraceElement(aclass);
            final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
            +")] <" + Thread.currentThread().getName() + ">: ";
            return amsg;
        }

     /**
       * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass. <br />
       * Stored in array of the callstack. <br />
       * Allows to get past a certain class.
       * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. 
       * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
       * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
       */
      public static StackTraceElement getCallingStackTraceElement(final Class aclass)
      {
        final Throwable           t         = new Throwable();
        final StackTraceElement[] ste       = t.getStackTrace();
        int index = 1;
        final int limit = ste.length;
        StackTraceElement   st        = ste[index];
        String              className = st.getClassName();
        boolean aclassfound = false;
        if(aclass == null)
        {
            aclassfound = true;
        }
        StackTraceElement   resst = null;
        while(index < limit)
        {
            if(shouldExamine(className, aclass) == true)
            {
                if(resst == null)
                {
                    resst = st;
                }
                if(aclassfound == true)
                {
                    final StackTraceElement ast = onClassfound(aclass, className, st);
                    if(ast != null)
                    {
                        resst = ast;
                        break;
                    }
                }
                else
                {
                    if(aclass != null && aclass.getName().equals(className) == true)
                    {
                        aclassfound = true;
                    }
                }
            }
            index = index + 1;
            st        = ste[index];
            className = st.getClassName();
        }
        if(resst == null) 
        {
            //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies 
            throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
        }
        return resst;
      }

      static private boolean shouldExamine(String className, Class aclass)
      {
          final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
            ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
          return res;
      }

      static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
      {
          StackTraceElement   resst = null;
          if(aclass != null && aclass.getName().equals(className) == false)
          {
              resst = st;
          }
          if(aclass == null)
          {
              resst = st;
          }
          return resst;
      }
6
ответ дан VonC 8 February 2014 в 17:55
поделиться

Альтернативное решение можно найти в комментарии к этого запроса на улучшение . Он использует метод getClassContext() пользовательского SecurityManager и, кажется, быстрее, чем метод трассировки стека.

Следующая программа проверяет скорость работы различных предлагаемых методов (наиболее интересный бит находится во внутреннем классе SecurityManagerMethod):

/**
 * Test the speed of various methods for getting the caller class name
 */
public class TestGetCallerClassName {

  /**
   * Abstract class for testing different methods of getting the caller class name
   */
  private static abstract class GetCallerClassNameMethod {
      public abstract String getCallerClassName(int callStackDepth);
      public abstract String getMethodName();
  }

  /**
   * Uses the internal Reflection class
   */
  private static class ReflectionMethod extends GetCallerClassNameMethod {
      public String getCallerClassName(int callStackDepth) {
          return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
      }

      public String getMethodName() {
          return "Reflection";
      }
  }

  /**
   * Get a stack trace from the current thread
   */
  private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Current Thread StackTrace";
      }
  }

  /**
   * Get a stack trace from a new Throwable
   */
  private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {

      public String getCallerClassName(int callStackDepth) {
          return new Throwable().getStackTrace()[callStackDepth].getClassName();
      }

      public String getMethodName() {
          return "Throwable StackTrace";
      }
  }

  /**
   * Use the SecurityManager.getClassContext()
   */
  private static class SecurityManagerMethod extends GetCallerClassNameMethod {
      public String  getCallerClassName(int callStackDepth) {
          return mySecurityManager.getCallerClassName(callStackDepth);
      }

      public String getMethodName() {
          return "SecurityManager";
      }

      /** 
       * A custom security manager that exposes the getClassContext() information
       */
      static class MySecurityManager extends SecurityManager {
          public String getCallerClassName(int callStackDepth) {
              return getClassContext()[callStackDepth].getName();
          }
      }

      private final static MySecurityManager mySecurityManager =
          new MySecurityManager();
  }

  /**
   * Test all four methods
   */
  public static void main(String[] args) {
      testMethod(new ReflectionMethod());
      testMethod(new ThreadStackTraceMethod());
      testMethod(new ThrowableStackTraceMethod());
      testMethod(new SecurityManagerMethod());
  }

  private static void testMethod(GetCallerClassNameMethod method) {
      long startTime = System.nanoTime();
      String className = null;
      for (int i = 0; i < 1000000; i++) {
          className = method.getCallerClassName(2);
      }
      printElapsedTime(method.getMethodName(), startTime);
  }

  private static void printElapsedTime(String title, long startTime) {
      System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
  }
}

Пример вывода с моего Intel Core 2 с частотой 2,4 ГГц Duo MacBook под управлением Java 1.6.0_17:

Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.

Метод внутреннего отражения намного быстрее, чем другие. Получение трассировки стека из вновь созданного Throwable быстрее, чем получение его из текущего Thread. И среди не внутренних способов определения класса вызывающего абонента обычай SecurityManager кажется самым быстрым.

Обновление

Как lyomi указывает в на этот комментарий , метод sun.reflect.Reflection.getCallerClass() был отключен по умолчанию в Java 7, обновление 40 и полностью удалено в Java 8 Подробнее об этом читайте в об этой проблеме в базе данных ошибок Java .

Обновление 2

Как обнаружил zammbi , Oracle был вынужден отказаться от изменения , которое удалило sun.reflect.Reflection.getCallerClass(). Это все еще доступно в Java 8 (но это устарело).

Обновление 3

3 года спустя: обновление по времени с текущей JVM.

> java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
> java TestGetCallerClassName
Reflection: 0.194s.
Current Thread StackTrace: 3.887s.
Throwable StackTrace: 3.173s.
SecurityManager: 0.565s.
209
ответ дан Community 8 February 2014 в 17:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: