2016-06-20 43 views
18

Vì mục đích ghi nhật ký, tôi đã tạo phương thức logTitle() để in ra tên phương thức gọi cho các bài kiểm tra TestNG của chúng tôi. Mã mẫu dưới đây.Nhận các kết quả khác nhau cho getStackTrace() [2] .getMethodName()

@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); 
    } 
} 

Vấn đề là tôi nhận được kết quả khác nhau cho logTitle() trên hai máy khác nhau. máy tính xách tay

của mọi người trở về một cách chính xác:

2016-06-20 14:22:06 INFO - ------------------------------------ 
2016-06-20 14:22:06 INFO - method1 
2016-06-20 14:22:06 INFO - ------------------------------------ 

chúng tôi dev unix hộp lợi nhuận khác nhau:

2016-06-20 14:42:26 INFO - ------------------------------------ 
2016-06-20 14:42:26 INFO - logTitle 
2016-06-20 14:42:26 INFO - ------------------------------------ 

này hoạt động một cách chính xác trên máy tính xách tay tất cả mọi người khác, chỉ cần không phải là dev unix hộp. Tôi nghĩ rằng hộp dev unix đang sử dụng phiên bản Java của IBM, trong khi mọi người khác đang sử dụng phiên bản Java của Oracle, nhưng không chắc liệu đó có phải là thủ phạm hay không.

Bất kỳ ý tưởng nào?

+2

có thể có một số nội tuyến phương pháp1 đang diễn ra? – MeBigFatGuy

+4

Làm thế nào để in toàn bộ dấu vết ngăn xếp (không chỉ tên phương thức của phần tử thứ 3), và thấy sự khác biệt là gì? –

+1

Tôi nghi ngờ rằng [câu trả lời này] (http://stackoverflow.com/a/1069150/3788176) và nhận xét của nó sẽ giúp bạn. –

Trả lời

1

Tôi nghĩ rằng độ sâu cụ thể của nó đang gây ra vấn đề đó là 2 trong kịch bản của bạn.

Vì vậy, thay vì viết

String method = Thread.currentThread().getStackTrace()[2].getMethodName(); 

nếu bạn viết

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"); 
    } 

Nó sẽ chỉ làm việc cho JDK 1.5 +

Các tùy chọn khác là như sau:

String method = new Object(){}.getClass().getEnclosingMethod().getName(); 

Hoặc một o chậm hơn ption sẽ là:

String method = new Exception().getStackTrace()[0].getMethodName(); 

Vì điều này sẽ tạo ra một trường hợp ngoại lệ mọi lúc.

Hy vọng sẽ giúp bạn.

+0

Bằng chứng cho 'new Object() {} getClass() getEnclosingMethod(). GetName()' nhanh hơn 'ngoại lệ mới(). GetStackTrace() [0] .getMethodName()'? –

+1

@SteffenHarbich Với tùy chọn đầu tiên, nó sẽ chỉ cần tạo một đối tượng Object trong khi đối với tùy chọn thứ hai, nó sẽ cần tạo ra ba cá thể: Exception, Throwable và Object. Để chứng minh, tôi đã viết một chương trình để lấy và in tên 1000000 lần và ở đây chúng tôi đi ... ra đến là: 1) cho đối tượng mới() {}. GetClass(). GetEnclosingMethod(). GetName() Tổng thời gian thực hiện là 7 giây, 701 mili giây 476129 nano giây trong khi đối với 2) cho ngoại lệ mới(). getStackTrace() [0] .getMethodName() Tổng thời gian thực hiện là 12 giây, 222 mili giây 112528 nano giây –

+1

Âm thanh người đàn ông tốt. –

4

Cách đơn giản hơn để có tên phương pháp thử là sử dụng @BeforeMethod và tiêm Method. Xem tài liệu của TestNG, here.

Chỉ cần lưu trữ tên một nơi nào đó và sử dụng nó trong nhật ký của mình (tại sao không ở trong một @AfterMethod?)

+0

Tôi đã làm điều đó để đăng nhập tên của một phương pháp thử nghiệm, nhưng ở trên, chúng tôi đang nói về phương pháp không thử nghiệm. – Greg

+1

Tôi nghĩ bạn nên giải thích nhu cầu của bạn nhiều hơn: tại sao bạn muốn làm điều đó? Bạn đã cân nhắc sử dụng khía cạnh cho điều đó chưa? – juherr

+0

để hỗ trợ khắc phục sự cố khi xem qua tệp nhật ký. các tệp nhật ký của chúng tôi là khá lớn và chúng tôi có rất nhiều phương pháp, biết nơi chúng tôi là người cuối cùng hoặc phương thức nào đang in những tuyên bố nào, đôi khi giúp chúng tôi. xin lỗi, không quen thuộc với khía cạnh, nhưng có thể nhìn vào nó. – Greg

1

Tôi đoán hành vi này là JVM cụ thể. Trong quá khứ tôi đã đưa ra giải pháp này:

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

Đoạn trích sử dụng Google Guava vì đây là Java 7. Nếu bạn có Java 8, bạn có thể sử dụng API luồng và lambdas. Tôi đã thực hiện kiểm tra ste.isPresent() vì tôi đã gặp phải một dấu vết ngăn xếp trống một lần. Theo như tôi nhớ Oracle JVM là bỏ qua dấu vết ngăn xếp khi cùng một ngoại lệ được ném hơn và hơn nữa.

EDIT: Java 8 cách

Optional<StackTraceElement> ste = Arrays.stream(new RuntimeException().getStackTrace()) 
      .filter(x -> !x.getClassName().equals(Utils.class.getName())) 
      .findFirst(); 
0

Log4J finds the method name bằng cách tìm kiếm xuống stack trace cho đến khi nó tìm thấy tên lớp đối tượng đó phải được thông qua, sau đó đọc tên phương pháp.

Trong code của bạn, bạn có thể sử dụng một kỹ thuật tương tự - thay vì một phương pháp tĩnh Utils bạn có thể tạo một thể hiện trong thử nghiệm của bạn, đi qua trong lớp của thử nghiệm:

Utils utils = new Utils(MyTest.class); 

Sau đó sử dụng đã đề cập trước kỹ thuật tìm kiếm theo phương pháp Utils.logTitle().

Utils.logTitle() sẽ tìm kiếm chuyển tiếp thông qua phần tử theo dõi ngăn xếp của Throwable mới được tạo cho đến khi tìm thấy phần tử đầu tiên với lớp đích mong muốn.

3

Từ Javadoc:

Một số máy ảo có thể, trong một số trường hợp, bỏ qua một hoặc nhiều khung stack từ stack trace.Trong trường hợp cực đoan, một máy ảo không có thông tin dấu vết ngăn xếp liên quan đến việc ném này được phép trả về một mảng có độ dài bằng không từ phương thức này.

Vì vậy, cách đảm bảo duy nhất để thực hiện việc này là sử dụng các khía cạnh hoặc thu thập dấu vết ngăn xếp bằng một số cách tùy chỉnh khác. Nhưng bạn có thể kết hợp phương pháp này với dự phòng cho một số cách để nhận tên của phương thức hiện tại (đối với trường hợp khi phương thức logTitle của bạn sẽ được gạch chân). Nó có thể được tìm thấy here, ví dụ. Một lần nữa, không có bảo đảm, nhưng cơ hội tốt hơn.

2

Đoán của tôi và được đề cập bởi MeBigFatGuy. Điều này có thể xảy ra do sự khác nhau trong việc triển khai/mặc định của trình biên dịch JIT của IBM/Oracle JVM khi thực hiện phương thức tối ưu hóa nội tuyến.

Tôi đề nghị chạy mã trong dev unix hộp với

-Xjit:disableInlining 

và xem nếu vấn đề biến mất.

Nếu điều này có hiệu quả đối với bạn, bạn có thể thử nghiệm, nhưng như đã đề cập trong câu hỏi Alexey Adamovskiy, chúng tôi không thể tin tưởng java được bao gồm trong khung ngăn xếp.

Xem thêm:

0

Log4j 2 sử dụng tên lớp đầy đủ của Trình ghi nhật ký để định vị lớp và phương thức mà Trình ghi nhật ký được gọi. Mã để tìm vị trí sau đây. Hãy sử dụng nó.

Lưu ý rằng vòng lặp bắt đầu từ cuối ngăn xếp chồng; điều này là cần thiết để phát hiện các trường hợp đặc biệt mà logger được gọi đệ quy (có lẽ từ phương thức toString() của một đối tượng đã được ghi lại). Trong trường hợp này, chúng tôi muốn báo cáo lớp/phương thức đầu tiên được gọi là Trình ghi nhật ký, không phải là tệp cuối cùng, vì vậy chúng tôi không có lựa chọn nào khác ngoài việc đi theo dấu vết ngăn xếp từ dưới lên.

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; 
} 
Các vấn đề liên quan