ฉันจะค้นหาผู้เรียกของเมธอดโดยใช้ stacktrace หรือ reflection ได้อย่างไร?


392

ฉันต้องการค้นหาผู้เรียกใช้ของวิธีการ เป็นไปได้โดยใช้ stacktrace หรือ reflect?


5
แค่สงสัย แต่ทำไมคุณต้องทำเช่นนี้?
Juliet

2
ฉันมีคลาส Parent (รุ่น MVC) ที่มีเหตุการณ์ตัวแจ้งและเซ็ตย่อยของคลาสย่อยของฉันเท่านั้นที่เรียกเมธอดนี้ ฉันไม่ต้องการทิ้งรหัสด้วยอาร์กิวเมนต์ที่ซ้ำซ้อน ฉันอยากให้วิธีการในชั้นเรียนผู้ปกครองคิดออกที่เรียกว่า
Sathish

30
เสียง @Sathish เช่นคุณควรคิดใหม่ว่าการออกแบบ
krosenvold

7
@ จูเลียตในฐานะส่วนหนึ่งของการปรับเปลี่ยนโค้ดขนาดใหญ่อีกครั้งเมื่อเร็ว ๆ นี้ฉันได้เปลี่ยนวิธีการที่ใช้โดยหลายสิ่ง มีวิธีการบางอย่างในการตรวจสอบว่ารหัสกำลังใช้วิธีการใหม่อย่างถูกต้องหรือไม่ดังนั้นฉันจึงพิมพ์คลาสและหมายเลขบรรทัดที่เรียกใช้ในกรณีเหล่านั้น นอกเหนือจากการเข้าสู่ระบบฉันไม่เห็นวัตถุประสงค์ที่แท้จริงสำหรับบางสิ่งเช่นนี้ แม้ว่าฉันต้องการเขียน API ตอนนี้ที่โยนDontNameYourMethodFooExceptionถ้าวิธีการโทรชื่อ foo
Cruncher

5
ฉันพบว่าการเรียกเครื่องมือของฉันเป็นเครื่องมือดีบั๊กอันล้ำค่าซึ่งเป็นวิธีการค้นเว็บที่นำฉันมาที่นี่ หากวิธีการของฉันถูกเรียกจากหลายสถานที่จะถูกเรียกจากตำแหน่งที่ถูกต้องในเวลาที่เหมาะสมหรือไม่ นอกเหนือจากการแก้ไขข้อผิดพลาดหรือการบันทึกแล้วความได้เปรียบอาจมี จำกัด ที่สุดเท่าที่ @Cruncher กล่าวถึง
Ogre Psalm33

คำตอบ:


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

อ้างอิงจาก Javadocs:

องค์ประกอบสุดท้ายของอาร์เรย์แสดงถึงด้านล่างของสแต็กซึ่งเป็นการเรียกใช้วิธีล่าสุดอย่างน้อยที่สุดในลำดับ

StackTraceElementมีgetClassName(), getFileName(), และgetLineNumber()getMethodName()

คุณจะต้องทดสอบเพื่อกำหนดดัชนีที่คุณต้องการ (อาจจะstackTraceElements[1]หรือ[2])


7
ฉันควรทราบว่า getStackTrace () ยังคงสร้างข้อยกเว้นดังนั้นจึงไม่เร็วกว่านี้จริงๆ - สะดวกยิ่งขึ้น
Michael Myers

42
โปรดทราบว่าวิธีนี้จะไม่ให้ผู้โทรหาคุณ แต่เป็นประเภทของผู้โทรเท่านั้น คุณจะไม่มีการอ้างอิงไปยังวัตถุที่เรียกใช้วิธีการของคุณ
Joachim Sauer

3
เพียงแค่ข้อความด้านข้าง แต่ใน 1.5 JVM Thread.currentThread (). getStackTrace () ดูเหมือนจะช้ากว่าการสร้างข้อยกเว้นใหม่ () (ช้ากว่าประมาณ 3 เท่า) แต่ดังที่ได้กล่าวไปแล้วคุณไม่ควรใช้รหัสเช่นนี้ในส่วนที่สำคัญต่อประสิทธิภาพ ;) 1.6 JVM ดูเหมือนจะช้ากว่า ~ 10% และ Software Monkey กล่าวว่าเป็นการแสดงออกถึงเจตนาที่ดีกว่าวิธี "ข้อยกเว้นใหม่"
GaZ

21
@Eelco Thread.currentThread () ราคาถูก Thread.getStackTrace () มีราคาแพงเพราะต่างจาก Throwable.fillInStackTrace () ไม่มีการรับประกันวิธีการที่ถูกเรียกโดยเธรดเดียวกันที่กำลังตรวจสอบดังนั้น JVM จึงต้องสร้าง "safepoint" - ล็อคฮีปและสแต็ก ดูรายงานข้อผิดพลาดนี้: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
David Moles

7
@JoachimSauer คุณรู้วิธีที่จะได้รับการอ้างอิงไปยังวัตถุที่เรียกวิธีการหรือไม่
jophde

216

วิธีการแก้ปัญหาทางเลือกที่สามารถพบได้ในความคิดเห็นที่คำขอนี้สำหรับการเพิ่มประสิทธิภาพ มันใช้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.");
  }
}

ตัวอย่างผลลัพธ์จาก 2.4 GHz Intel Core 2 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.

5
ใช่ดูเหมือนว่า แต่โปรดทราบว่าการกำหนดเวลาที่ฉันให้ในตัวอย่างนั้นใช้สำหรับการโทรนับล้านครั้ง - ดังนั้นขึ้นอยู่กับว่าคุณใช้งานอย่างไรมันอาจไม่เป็นปัญหา
Johan Kaving

1
สำหรับฉันการลบการสะท้อนออกจากโครงการของฉันทำให้ความเร็วเพิ่มขึ้น 10 เท่า
Kevin Parker

1
ใช่การสะท้อนกลับโดยทั่วไปช้า (ดูเช่นstackoverflow.com/questions/435553/java-reflection-performance ) แต่ในกรณีนี้การใช้ sun.reflect.Reflection ระดับที่เร็วที่สุด
Johan Kaving

1
จริงๆแล้วมันไม่จำเป็นต้อง คุณสามารถตรวจสอบได้โดยการแก้ไขโค้ดด้านบนเพื่อพิมพ์ className คืน (และฉันขอแนะนำให้ลดจำนวนลูปเป็น 1) คุณจะเห็นว่าวิธีการทั้งหมดกลับ className เดียวกัน - TestGetCallerClassName
Johan Kaving

1
getCallerClass เลิกใช้แล้วและจะถูกลบใน 7u40 .. เศร้า :(
lyomi

36

ดูเหมือนว่าคุณกำลังพยายามหลีกเลี่ยงการอ้างอิงถึงthisวิธีการ การส่งthisเป็นวิธีที่ดีกว่าการค้นหาผู้เรียกผ่านการติดตามสแต็กปัจจุบัน การปรับเปลี่ยนการออกแบบ OO ให้ดียิ่งขึ้นนั้นดียิ่งขึ้น คุณไม่จำเป็นต้องรู้จักผู้โทร ผ่านวัตถุติดต่อกลับหากจำเป็น


6
++ การรู้จักผู้โทรเป็นข้อมูลมากเกินไป หากคุณต้องการคุณสามารถส่งผ่านอินเทอร์เฟซได้ แต่มีโอกาสดีที่จำเป็นต้องทำการปรับโครงสร้างใหม่ @satish ควรโพสต์รหัสของเขาและให้เราได้สนุกกับมัน :)
บิล K

15
เหตุผลที่ถูกต้องสำหรับการทำเช่นนี้มีอยู่ ฉันมีบางครั้งที่ฉันพบว่ามีประโยชน์ในระหว่างการทดสอบเช่น
Eelco

2
@chillenious ฉันรู้ว่า :) ฉันได้ทำมันเองเพื่อสร้างวิธีการLoggerFactory.getLogger(MyClass.class)ที่ฉันไม่ต้องผ่านตัวอักษรในชั้นเรียน ยังไม่ค่อยมีสิ่งที่ถูกต้องทำ
Craig P. Motlin

6
นี่เป็นคำแนะนำที่ดีโดยทั่วไป แต่ไม่ตอบคำถาม
Navin

1
ตัวอย่างที่ชัดเจนว่าเมื่อใดการตัดสินใจออกแบบ RIGHT เพื่อรับข้อมูลเกี่ยวกับผู้โทรคือเมื่อใช้งานINotifyPropertyChangedอินเตอร์เฟส. NET ในขณะที่ตัวอย่างเฉพาะนี้ไม่ได้อยู่ใน Java ปัญหาเดียวกันสามารถแสดงตัวเองเมื่อพยายามที่จะสร้างแบบจำลองเขตข้อมูล / getters เป็นสตริงสำหรับการสะท้อน
Chris Kerekes

31

Java 9 - JEP 259: Stack-Walking API

JEP 259มี API มาตรฐานที่มีประสิทธิภาพสำหรับการเดินสแต็กที่อนุญาตให้กรองได้ง่ายและเข้าถึงข้อมูลในร่องรอยสแต็กได้อย่างง่ายดาย ก่อน Stack-Walking API วิธีการเข้าถึงเฟรมสแต็กทั่วไปคือ:

Throwable::getStackTraceและThread::getStackTraceส่งกลับอาร์เรย์ของอ StackTraceElementอบเจกต์ซึ่งมีชื่อคลาสและชื่อเมธอดขององค์ประกอบการติดตามสแต็กแต่ละตัว

SecurityManager::getClassContextเป็นวิธีการป้องกันซึ่งช่วยให้ SecurityManagersubclass เข้าถึงบริบทของชั้นเรียน

sun.reflect.Reflection::getCallerClassวิธีการภายใน JDK ซึ่งคุณไม่ควรใช้อยู่ดี

การใช้ API เหล่านี้มักไม่มีประสิทธิภาพ:

API เหล่านี้ต้องการ VM เพื่อรวบรวมสแน็ปช็อตทั้งหมดของสแต็กอย่างกระตือรือร้นและส่งคืนข้อมูลที่แสดงถึงสแต็กทั้งหมด ไม่มีวิธีใดที่จะหลีกเลี่ยงค่าใช้จ่ายในการตรวจสอบเฟรมทั้งหมดหากผู้โทรสนใจเฉพาะในเฟรมไม่กี่อันดับแรกบนสแต็ก

เพื่อหาชั้นเรียนของผู้โทรทันทีก่อนอื่นให้ได้รับStackWalker:

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

จากนั้นโทรไปที่getCallerClass():

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

หรือและได้รับก่อนหน้านี้ครั้งแรก:walkStackFrameStackFrame

walker.walk(frames -> frames
      .map(StackWalker.StackFrame::getDeclaringClass)
      .skip(1)
      .findFirst());

15

Oneliner :

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

โปรดทราบว่าคุณอาจต้องแทนที่ 2 ด้วย 1


10

วิธีนี้ทำในสิ่งเดียวกัน แต่ก็ง่ายขึ้นและอาจมีประสิทธิภาพมากกว่าเล็กน้อยและในกรณีที่คุณใช้การสะท้อนมันจะข้ามเฟรมเหล่านั้นโดยอัตโนมัติ ปัญหาเดียวคือมันอาจไม่มีใน 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ค่าควรเป็นเวอร์ชัน Sun 1.5 และ 1.6 VM java.lang.Systemมีวิธีการป้องกันแพคเกจที่เรียกว่า getCallerClass () ซึ่งเรียกsun.reflect.Reflection.getCallerClass(3)แต่ในยูทิลิตี้คลาสผู้ช่วยเหลือของฉันฉันใช้ 4 เนื่องจากมีเฟรมเพิ่มของคลาสผู้ช่วย การภาวนา


16
การใช้ JVM เรียนการดำเนินงานเป็นจริงๆความคิดที่ดี
Lawrence Dol

7
ข้อสังเกต. ฉันระบุว่ามันไม่ใช่คลาสสาธารณะและเมธอดที่ได้รับการป้องกัน getCallerClass () ใน java.lang.System มีอยู่ในทุก 1.5+ VMs ที่ฉันได้ดูรวมถึง IBM, JRockit และ Sun แต่การยืนยันของคุณค่อนข้างอนุรักษ์นิยม .
นิโคลัส

6
@ ซอฟต์แวร์ Monkey เช่นเดียวกับปกติ "ทุกอย่างขึ้นอยู่กับ" ทำสิ่งนี้เพื่อช่วยในการดีบักหรือทดสอบการบันทึกโดยเฉพาะอย่างยิ่งถ้ามันไม่เคยจบลงด้วยรหัสการผลิตหรือถ้าเป้าหมายการปรับใช้นั้นเป็นเครื่องคอมพิวเตอร์ของนักพัฒนาซอฟต์แวร์อย่างเคร่งครัด ทุกคนที่ยังคงคิดอย่างอื่นแม้ในกรณีเช่นนี้: คุณต้องอธิบาย"เหตุผลที่ไม่ดีจริงๆ " ด้วยเหตุผลที่ดีกว่าเพียงแค่พูดว่ามันไม่ดี ...

8
นอกจากนี้ด้วยเหตุผลที่คล้ายกันคุณสามารถยืนยันได้ว่าเมื่อใดก็ตามที่คุณใช้คุณลักษณะเฉพาะของไฮเบอร์เนตที่ไม่เข้ากันได้กับ JPA นั่นเป็นแนวคิดที่ไม่ดีจริงๆ หรือถ้าคุณจะใช้คุณสมบัติเฉพาะของออราเคิลซึ่งไม่มีในฐานข้อมูลอื่นมันเป็น " แนวคิดที่ไม่ดีจริงๆ " แน่ใจว่าเป็นความคิดที่ปลอดภัยกว่าและคำแนะนำที่ดีสำหรับการใช้งานบางอย่างโดยอัตโนมัติ แต่ทิ้งเครื่องมือที่มีประโยชน์โดยอัตโนมัติเพียงเพราะมันจะไม่ทำงานกับการกำหนดค่าซอฟต์แวร์ที่คุณใช่มั้ย.. ไม่ได้ใช้เลย ? มันค่อนข้างยืดหยุ่นและงี่เง่านิดหน่อย

5
การใช้คลาสเฉพาะของผู้ขายที่ไม่ระวังจะนำเสนอโอกาสในการเกิดปัญหาที่สูงขึ้น แต่เราควรกำหนดเส้นทางที่จะลดระดับอย่างสง่างามหากไม่มีคำถามในชั้นเรียน (หรือเป็นสิ่งต้องห้ามด้วยเหตุผลบางประการ) นโยบายของการปฏิเสธแบบไม่เจาะจงที่จะใช้คลาสที่เฉพาะเจาะจงของผู้ขายใด ๆ คือในความคิดของฉันมีความไร้เดียงสาเล็กน้อย ลองเข้าไปดูในซอร์สโค้ดของไลบรารี่บางอันที่คุณใช้ในการผลิตและดูว่ามีไลบรารี่ตัวใดที่ทำเช่นนี้ (sun.misc.Unsafe อาจจะ?)
Nicholas

7
     /**
       * 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();
      }

ตัวอย่างเช่นถ้าคุณพยายามรับบรรทัดวิธีการโทรเพื่อจุดประสงค์ในการแก้ไขปัญหาคุณจะต้องผ่านคลาสยูทิลิตี้ที่คุณใช้รหัสวิธีการคงที่เหล่านั้น:
(รหัส 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;
      }

ฉันต้องการบางสิ่งที่ทำงานร่วมกับ Java 1.4 และคำตอบนี้มีประโยชน์มาก! ขอบคุณ!
RGO

6

ฉันเคยทำมาก่อน คุณสามารถสร้างข้อยกเว้นใหม่และคว้าการติดตามสแต็กโดยไม่ทิ้งมันจากนั้นตรวจสอบสแต็กการติดตาม อย่างที่คำตอบอื่นบอกไว้ว่ามันมีค่าใช้จ่ายสูงมาก - อย่าทำอย่างนั้น

ฉันเคยทำมาก่อนแล้วสำหรับยูทิลิตี้การบันทึกในแอปที่ประสิทธิภาพไม่ได้สำคัญอะไรมาก (ประสิทธิภาพไม่ค่อยสำคัญเท่าไหร่จริง ๆ แล้ว - ตราบใดที่คุณแสดงผลการดำเนินการเช่นการคลิกปุ่มอย่างรวดเร็ว)

ก่อนหน้านี้คุณจะได้รับ stack trace ยกเว้นว่ามี. printStackTrace () ดังนั้นฉันจึงต้องเปลี่ยนเส้นทาง System.out ไปยังกระแสการสร้างของฉันเองจากนั้น (new Exception ()) printStackTrace (); เปลี่ยนเส้นทาง System.out back และวิเคราะห์กระแสข้อมูล สิ่งที่สนุก.


เย็น; คุณไม่ต้องขว้างมันเหรอ?
krosenvold

ไม่อย่างน้อยนั่นเป็นวิธีที่ฉันจำได้ฉันยังไม่ได้ทำในช่วงสองสามปีที่ผ่านมา แต่ฉันค่อนข้างแน่ใจว่าการสร้างข้อยกเว้นใหม่คือการสร้างวัตถุและการโยนข้อยกเว้นไม่ได้ทำอะไรเลยยกเว้นผ่าน มันเพื่อจับข้อ ()
บิล K

เรียบร้อย ฉันมีแนวโน้มที่จะขว้างมันเพื่อจำลองข้อยกเว้นจริง
Sathish

ไม่เนื่องจาก Java 5 มีเมธอดบน Thread เพื่อรับสแต็กปัจจุบันเป็นอาร์เรย์ของ StackTraceElements มันยังไม่ถูก แต่ถูกกว่าการแยกวิเคราะห์แบบเก่า
Lawrence Dol

@ ซอฟต์แวร์ Monkey ถึงแม้ว่าฉันจะแน่ใจว่ามันเหมาะสมกว่า แต่ทำไมคุณถึงพูดว่ามันถูกกว่า? ฉันคิดว่าจะใช้กลไกเดียวกันและถ้าไม่ทำไมทำให้ช้าลงเมื่อทำสิ่งเดียวกัน
Bill K

1
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() );
         }
      }
   }

0

นี่คือส่วนหนึ่งของรหัสที่ฉันทำตามคำแนะนำที่แสดงในหัวข้อนี้ หวังว่ามันจะช่วย

(อย่าลังเลที่จะให้คำแนะนำในการปรับปรุงรหัสนี้โปรดบอกฉัน)

เคาน์เตอร์:

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
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

ใช้วิธีนี้: -

 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();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.