2016-02-23 3 views
6

Я долгое время читатель, но первый писатель.Ошибка Java PrintWriter

В настоящее время я пытаюсь внедрить регистратор с AspectJ в нашей кодовой базе. AspectJ, похоже, работает хорошо, но я встречаю чрезвычайно странные ошибки Java. Я давний разработчик C++ и .NET, который все еще адаптируется к миру Java, поэтому я извиняюсь, если это глупый вопрос.

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

package mil.uscg.c3cen.vic.aspect; 

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.io.PrintWriter; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.JoinPoint; 

@Aspect 
public class LoggingAspect 
{ 
    private final String LOG_FILE = "aspectLog.txt"; 
    private final File file = new File(LOG_FILE); 

    private LoggingAspect() 
    { 

    } 

    private void logException(String msg) 
    { 
     try 
     { 
      if(!file.exists()) 
       file.createNewFile(); 
     } 
     catch(IOException e) 
     { 

     } 

     try (FileWriter fw = new FileWriter(file); 
       BufferedWriter bw = new BufferedWriter(fw); 
       PrintWriter pw = new PrintWriter(bw)) 
     { 
      pw.println(msg); 
     } 
     catch(IOException e) 
     { 

     } 
    } 

    private String getSimpleFunctionInfo(String className, String  function, Object[] args) 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.append(". Method: "); 
     builder.append(className); 
     builder.append("."); 
     builder.append(function); 

     if(args.length == 0) 
     { 
      builder.append("()"); 
      return builder.toString(); 
     } 

     builder.append("("); 

     for(Object o : args) 
     { 
      builder.append(o.toString()); 
      builder.append(","); 
     } 
     // Replace the comma for the last param with a closing parenthesis 
     int len = builder.length(); 
     builder.replace(len -1, len, ")"); 

     return builder.toString(); 
    } 

    // Returns a formatted exception. "Exception.ErrorMessage" 
    private String getSimpleExceptionInfo(String name, String msg) 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.append("Exception caught: "); 
     builder.append(name); 
     builder.append(". Message: "); 
     builder.append(msg); 
     return builder.toString(); 
    } 


    @AfterThrowing(pointcut = "execution(* mil.uscg.c3cen.*.*.*(..)) " 
     //+ "&& !within(mil.uscg.c3cen.vic.aspect.*) " 
     , throwing = "excep") 
    public void afterThrowing(JoinPoint jp, Throwable excep) throws Throwable 
    { 
     String ex = getSimpleExceptionInfo(excep.getClass().getSimpleName(), 
                  excep.getMessage()); 

     String name = getSimpleFunctionInfo(jp.getSignature().getDeclaringType().getSimpleName(), 
                   jp.getSignature().getName(), 
                   jp.getArgs()); 

     StringBuilder builder = new StringBuilder(); 
     builder.append(ex); 
     builder.append(name); 

     logException(builder.toString()); 
    } 
} 

Все выглядит так, как вы ожидали бы в файле класса, за исключением функции logException.

/* Error */ 
    private void logException(String msg) 
    { 
    // Byte code: 
    // 0: aload_0 
    // 1: getfield 25 mil/uscg/c3cen/vic/aspect/LoggingAspect:file Ljava/io/File; 
    // 4: invokevirtual 32 java/io/File:exists()Z 
    // 7: ifne +15 -> 22 
    // 10: aload_0 
    // 11: getfield 25 mil/uscg/c3cen/vic/aspect/LoggingAspect:file Ljava/io/File; 
    // 14: invokevirtual 36 java/io/File:createNewFile ()Z 
    // 17: pop 
    // 18: goto +4 -> 22 
    // 21: pop 
    // 22: aconst_null 
    // 23: astore_2 
    // 24: aconst_null 
    // 25: astore_3 
    // 26: new 39 java/io/FileWriter 
    // 29: dup 
    // 30: aload_0 
    // 31: getfield 25 mil/uscg/c3cen/vic/aspect/LoggingAspect:file Ljava/io/File; 
    // 34: invokespecial 41 java/io/FileWriter:<init> (Ljava/io/File;)V 
    // 37: astore 4 
    // 39: new 44 java/io/BufferedWriter 
    // 42: dup 
    // 43: aload 4 
    // 45: invokespecial 46 java/io/BufferedWriter:<init> (Ljava/io/Writer;)V 
    // 48: astore 5 
    // 50: new 49 java/io/PrintWriter 
    // 53: dup 
    // 54: aload 5 
    // 56: invokespecial 51 java/io/PrintWriter:<init> (Ljava/io/Writer;)V 
    // 59: astore 6 
    // 61: aload 6 
    // 63: aload_1 
    // 64: invokevirtual 52 java/io/PrintWriter:println (Ljava/lang/String;)V 
    // 67: aload 6 
    // 69: ifnull +24 -> 93 
    // 72: aload 6 
    // 74: invokevirtual 55 java/io/PrintWriter:close ()V 
    // 77: goto +16 -> 93 
    // 80: astore_2 
    // 81: aload 6 
    // 83: ifnull +8 -> 91 
    // 86: aload 6 
    // 88: invokevirtual 55 java/io/PrintWriter:close ()V 
    // 91: aload_2 
    // 92: athrow 
    // 93: aload 5 
    // 95: ifnull +43 -> 138 
    // 98: aload 5 
    // 100: invokevirtual 58 java/io/BufferedWriter:close ()V 
    // 103: goto +35 -> 138 
    // 106: astore_3 
    // 107: aload_2 
    // 108: ifnonnull +8 -> 116 
    // 111: aload_3 
    // 112: astore_2 
    // 113: goto +13 -> 126 
    // 116: aload_2 
    // 117: aload_3 
    // 118: if_acmpeq +8 -> 126 
    // 121: aload_2 
    // 122: aload_3 
    // 123: invokevirtual 59 java/lang/Throwable:addSuppressed (Ljava/lang/Throwable;)V 
    // 126: aload 5 
    // 128: ifnull +8 -> 136 
    // 131: aload 5 
    // 133: invokevirtual 58 java/io/BufferedWriter:close ()V 
    // 136: aload_2 
    // 137: athrow 
    // 138: aload 4 
    // 140: ifnull +66 -> 206 
    // 143: aload 4 
    // 145: invokevirtual 65 java/io/FileWriter:close ()V 
    // 148: goto +58 -> 206 
    // 151: astore_3 
    // 152: aload_2 
    // 153: ifnonnull +8 -> 161 
    // 156: aload_3 
    // 157: astore_2 
    // 158: goto +13 -> 171 
    // 161: aload_2 
    // 162: aload_3 
    // 163: if_acmpeq +8 -> 171 
    // 166: aload_2 
    // 167: aload_3 
    // 168: invokevirtual 59 java/lang/Throwable:addSuppressed (Ljava/lang/Throwable;)V 
    // 171: aload 4 
    // 173: ifnull +8 -> 181 
    // 176: aload 4 
    // 178: invokevirtual 65 java/io/FileWriter:close ()V 
    // 181: aload_2 
    // 182: athrow 
    // 183: astore_3 
    // 184: aload_2 
    // 185: ifnonnull +8 -> 193 
    // 188: aload_3 
    // 189: astore_2 
    // 190: goto +13 -> 203 
    // 193: aload_2 
    // 194: aload_3 
    // 195: if_acmpeq +8 -> 203 
    // 198: aload_2 
    // 199: aload_3 
    // 200: invokevirtual 59 java/lang/Throwable:addSuppressed (Ljava/lang/Throwable;)V 
    // 203: aload_2 
    // 204: athrow 
    // 205: pop 
    // 206: return 
    // Line number table: 
    // Java source line #28 -> byte code offset #0 
    // Java source line #29 -> byte code offset #10 
    // Java source line #30 -> byte code offset #18 
    // Java source line #31 -> byte code offset #21 
    // Java source line #36 -> byte code offset #22 
    // Java source line #36 -> byte code offset #26 
    // Java source line #37 -> byte code offset #39 
    // Java source line #38 -> byte code offset #50 
    // Java source line #40 -> byte code offset #61 
    // Java source line #41 -> byte code offset #67 
    // Java source line #42 -> byte code offset #205 
    // Java source line #46 -> byte code offset #206 
    // Local variable table: 
    // start length slot name signature 
    // 0 207 0 this LoggingAspect 
    // 0 207 1 msg String 
    // 23 1 2 localObject1 Object 
    // 80 28 2 localObject2 Object 
    // 112 92 2 localObject3 Object 
    // 25 1 3 localObject4 Object 
    // 106 17 3 localThrowable1 Throwable 
    // 151 17 3 localThrowable2 Throwable 
    // 183 17 3 localThrowable3 Throwable 
    // 37 140 4 fw java.io.FileWriter 
    // 48 84 5 bw java.io.BufferedWriter 
    // 59 28 6 pw java.io.PrintWriter 
    // 21 1 12 localIOException1 java.io.IOException 
    // 205 1 13 localIOException2 java.io.IOException 
    // Exception table: 
    // from to target type 
    // 0 18 21 java/io/IOException 
    // 61 67 80 finally 
    // 50 93 106 finally 
    // 39 138 151 finally 
    // 26 183 183 finally 
    // 22 205 205 java/io/IOException 
    } 

Это действительно сильно меня озадачило, поэтому любая информация будет чрезвычайно оценена. Благодаря!

+0

Я подумал, что было правильный синтаксис для использования автоматизированного управления ресурсами в Java. Я понял, что это эквивалент использования функции на C#, где ресурсы гарантируются для закрытия, как это обычно делается в блоке finally. – FretlessMayhem

+1

@CalvinP. Эти строки находятся в круглых скобках, потому что они используют оператор try-with-resources. Пожалуйста, посмотрите здесь, чтобы раскрыть его код, если вы не знаете, что это значит [link] (https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) – angryip

+1

Ах, только что увидел javadocs, и вы правы. Оператор try должен быть успешным, если вы используете Java SE 7 или новее. @angryip буквально просто посмотрел, что до того, как вы предоставили ссылку, но спасибо. –

ответ

1

Ну, вы действительно не сказали, в чем проблема. Если у вас есть проблема с неперехваченным исключением в logException, то просто поймайте его и исследуйте. Скорее всего, это проблема с разрешением при открытии файла или, может быть, (например, в окнах), ОС не разрешает доступ к файлу несколькими потоками.

Для второго вопроса - байтовый код не работает. Первая часть довольно проста и следует за кодом, который вы написали. Вторая часть обрабатывает закрытие ресурсов и добавление потенциально зараженных исключений, как подавленных, к уже вызванному исключению. Это немного длиннее, но снова следует за инициализацией ресурсов в противоположном направлении.

+1

не уверен, как байт-код достаточно ясен. "/ * Ошибка * /" наверняка вызывает у меня флаг. – angryip

2

Хорошо, я пробовал использовать Java 8 и текущий AspectJ 1.8.8. Ваш аспект работает как ожидалось (я скомпилировал его без каких-либо изменений). Это немного сложнее и должно быть упрощено. Кроме того, вы можете просто ошибочно подсчитать количество .* в своем pointcut.

Если добавить System.out.println(jp); в начале вашего метода консультации с тем, чтобы увидеть что-то на консоли и запустить свой аспект в отношении этого образца класса драйвера ...

package mil.uscg.c3cen.foo; 

public class Application { 
    public static void main(String[] args) { 
     for (int i = 0; i < 3; i++) { 
      try { 
       doSomething(); 
      } 
      catch (Exception e) {} 
     } 
    } 

    public static void doSomething() { 
     System.out.println("Calculation result = " + multiply(add(3, 4), 5)); 
     System.out.println("Calculation result = " + divide(add(5, 6), 0)); 
    } 

    private static int add(int summand1, int summand2) { 
     return summand1 + summand2; 
    } 

    private static int multiply(int factor1, int factor2) { 
     return factor1 * factor2; 
    } 

    private static int divide(int dividend, int divisor) { 
     return dividend/divisor; 
    } 
} 

... бревно выглядит консоль это:

Calculation result = 35 
execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
execution(void mil.uscg.c3cen.foo.Application.doSomething()) 
Calculation result = 35 
execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
execution(void mil.uscg.c3cen.foo.Application.doSomething()) 
Calculation result = 35 
execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
execution(void mil.uscg.c3cen.foo.Application.doSomething()) 

Как вы можете видеть, только методы метания исключение вверх по иерархии вызовов (пока они не будут пойманы), регистрируются, как и ожидалось. Файл журнала aspectLog.txt имеет это содержание:

Exception caught: ArithmeticException. Message:/by zero. Method: Application.main([Ljava.lang.String;@f6f4d33) 

Что улучшить:

  • Может быть, вы хотите иметь более стабильную Pointcut пристреливать все подпакеты mil.uscg.c3cen. Синтаксис «всех методов выполнения внутри этого пакета и всех его подпакетов» будет execution(* mil.uscg.c3cen..*(..)).
  • В вашей логической логике есть ошибка: всякий раз, когда возникает первое исключение и файл журнала еще не существует, он регистрируется и файл журнала немедленно закрывается. Любое последующее исключение никогда не будет регистрироваться, что, вероятно, не то, что вы хотите.Вероятно, вы хотите, чтобы несколько исключений регистрировались в одном файле до тех пор, пока работает JVM. Таким образом, вы не хотите закрывать файл журнала после каждой записи, но позаботьтесь об этом в JVM-подключении в конце срока службы JVM. Попытка с (автоматически закрываемыми) ресурсами полезна только тогда, когда вы действительно хотите закрыть их после запуска определенной части кода. Кстати, вы можете избежать крюка отключения, регулярно промывая автора, например. после каждого println звонок.
  • Я не обсуждаю здесь проблемы с потоками и синхронизацией, что потребует еще большей осторожности. Предположим, у вас однопоточное приложение.
  • Возможно, вы также хотите перехватить исключения в конструкторах и добавить это к вашему pointcut.

Улучшенная & упрощенного аспект:

package mil.uscg.c3cen.vic.aspect; 

import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.PrintWriter; 

import org.aspectj.lang.JoinPoint; 
import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 

@Aspect 
public class LoggingAspect { 
    private static final String LOG_FILE = "aspectLog.txt"; 

    private final PrintWriter logWriter; 

    public LoggingAspect() throws FileNotFoundException { 
     logWriter = new PrintWriter(new FileOutputStream(LOG_FILE)); 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       logWriter.close(); 
      } 
     }); 
    } 

    @AfterThrowing(
     pointcut = 
      "(execution(* mil.uscg.c3cen..*(..)) || execution(mil.uscg.c3cen..new(..)))" + 
      " && !within(mil.uscg.c3cen.vic.aspect..*) ", 
     throwing = "excep" 
    ) 
    public void afterThrowing(JoinPoint jp, Throwable excep) throws Throwable { 
     //System.out.println(excep + " -> " + jp); 
     logWriter.println(excep + " -> " + jp); 
    } 
} 

Расширенный пример кода с конструктором, бросанием исключения:

package mil.uscg.c3cen.foo; 

public class Application { 
    public Application() { 
     System.out.println(1/0); 
    } 

    public static void doSomething() { 
     System.out.println("Calculation result = " + multiply(add(3, 4), 5)); 
     System.out.println("Calculation result = " + divide(add(5, 6), 0)); 
    } 

    private static int add(int summand1, int summand2) { 
     return summand1 + summand2; 
    } 

    private static int multiply(int factor1, int factor2) { 
     return factor1 * factor2; 
    } 

    private static int divide(int dividend, int divisor) { 
     return dividend/divisor; 
    } 

    public static void main(String[] args) { 
     for (int i = 0; i < 3; i++) { 
      try { 
       doSomething(); 
      } 
      catch (Exception e) {} 
     } 
     try { 
      new Application(); 
     } 
     catch (Exception e) {} 
    } 
} 

консоль журнал:

Calculation result = 35 
Calculation result = 35 
Calculation result = 35 

Файл журнала:

java.lang.ArithmeticException:/by zero -> execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
java.lang.ArithmeticException:/by zero -> execution(void mil.uscg.c3cen.foo.Application.doSomething()) 
java.lang.ArithmeticException:/by zero -> execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
java.lang.ArithmeticException:/by zero -> execution(void mil.uscg.c3cen.foo.Application.doSomething()) 
java.lang.ArithmeticException:/by zero -> execution(int mil.uscg.c3cen.foo.Application.divide(int, int)) 
java.lang.ArithmeticException:/by zero -> execution(void mil.uscg.c3cen.foo.Application.doSomething()) 
java.lang.ArithmeticException:/by zero -> execution(mil.uscg.c3cen.foo.Application()) 

Посмотрите на последней строке, там вы увидите исключение в конструкторе.

Если вы хотите украсить лог исключения немного, подобный тому, что делает ваш оригинальный аспект, сделайте следующее:

logWriter.println(excep.getClass().getSimpleName() + " -> " + jp.getSignature()); 

Тогда лог-файл становится:

ArithmeticException -> int mil.uscg.c3cen.foo.Application.divide(int, int) 
ArithmeticException -> void mil.uscg.c3cen.foo.Application.doSomething() 
ArithmeticException -> int mil.uscg.c3cen.foo.Application.divide(int, int) 
ArithmeticException -> void mil.uscg.c3cen.foo.Application.doSomething() 
ArithmeticException -> int mil.uscg.c3cen.foo.Application.divide(int, int) 
ArithmeticException -> void mil.uscg.c3cen.foo.Application.doSomething() 
ArithmeticException -> mil.uscg.c3cen.foo.Application() 
+0

Это помогло СУЩЕСТВЕННО. Огромное спасибо. Изменение pointcut к вашему предложению безупречно подключило систему. Я не понимал, что сопоставление подстановочных знаков было разным в этом случае. Кроме того, благодарю вас за отзыв о Try with resources. Это для меня все ново, и я не понимал, что это было только для одного использования. С помощью flush исправлена ​​проблема, и теперь она отлично записывается. – FretlessMayhem

+0

Я рад, что смогу помочь. Заранее благодарю за принятие и отсрочку ответа. – kriegaex

Смежные вопросы