【问题标题】:Best way to process/handle Error stream messages处理/处理错误流消息的最佳方式
【发布时间】:2015-11-09 17:40:16
【问题描述】:

我需要处理编译/运行时生成的不同错误/异常消息。

我执行一个 Java 程序并读取由此生成的流:

final Process p2 = builder.start();
BufferedReader in = new BufferedReader(new                                                 
        InputStreamReader(p2.getInputStream()));

对于每个成功,都会生成并显示输出。那里没问题。但我需要为每条错误消息显示自定义消息。

EG:

Error: Main method not found in class dummy.helloParse10.hello, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application

可自定义为:Error: Main method not found

我目前的做法非常丑陋和有限。我正在查看错误流中是否存在“异常”字符串,然后取出子字符串。类似于:

if(tError.contains("Exception"))            
      tError=tError.substring(tError.indexOf("main\"")+5,tError.indexOf("at"))
        + "( At Line: "+tError.substring(tError.indexOf(".java")+6);

但它并没有广泛地定制我的方法。

我能做的最好的事情是什么?

编辑

我认为我的问题不清楚。基本上我正在执行一个Java程序 ProcessBuilder.

    //Compile the program 
  Process p = Runtime.getRuntime().exec("javac filename ");

    // Now get the error stream if available :
  BufferedReader in = new BufferedReader(new            
                    InputStreamReader(p.getOutputStream()));

 String line = null;
 while ((line = in.readLine()) != null) {

       //process error in compilation.
       }
    ...
    ...
  // ProcessBuilder to execute the program.java
  //Read the output or runtime Exception  

进程的输出不能是 Java 程序的结果,也不能是从进程流中获取的异常/错误,并且是字符串形式。需要处理这些错误。

更新:

我现在可以按照@Miserable Variable 的建议通过Java Compiler API 解决编译时错误。我该如何类似地处理运行时异常?

编辑: 实际上不可能修改程序以在新进程中运行。它们是用户特定的。

【问题讨论】:

  • 我不确定您在寻找什么。如果您尝试使用自定义异常,那么您可以创建自定义异常(它应该扩展异常),将您的逻辑放在 try 块中,在 catch 块中始终使用自定义消息创建自定义异常并抛出它。
  • @bakki 我知道用户定义的异常。这些是在新进程中执行 Java 程序时产生的异常。我手头只有错误流。
  • 也许你希望实现一个自定义的UncaughtExceptionHandler
  • @jewelsea 我已经进行了编辑。现在问题可能更容易理解了。
  • 你看过Java Compiler API吗?

标签: java error-handling


【解决方案1】:

为了识别编译中的错误,而不是使用ProcessBuilder 运行javac,更好的选择可能是使用Java Compiler API

我自己从未使用过它,但它看起来很简单。

【讨论】:

    【解决方案2】:

    如果您的程序依赖于日志框架以某种日志格式记录其错误,IMO 会容易得多。您可以通过使用日志解析器从中受益。 write your own 应该很简单,也许您可​​以定义分配给特定程序的常见错误模式字典。

    如果您的程序不遵循明确定义的日志模式并且您想要一种可扩展的方法,那么一种可能的方法是实现基于Grok 的解决方案。 Grok 用于像 Logstash 这样的强大工具。请参阅 this post 了解如何在 Java 中执行此操作。

    【讨论】:

    • 我无法对要运行的程序进行更改。否则有很多可用的选项。编辑。
    • @joeyrohan 在这种情况下,如果适合您的需要,也许您可​​以探索 Grok 选项。
    【解决方案3】:

    对于运行时异常,您可以查看 ProcessBuilder 并流式传输错误异常,然后检查是否相同。以下是一些可以帮助您的示例:

    http://examples.javacodegeeks.com/core-java/lang/processbuilder/java-lang-processbuilder-example/

    【讨论】:

      【解决方案4】:

      我的理解是这样的,

      问题陈述:

      编写一个 Java 程序,它应该能够 1.将输入作为Java程序文件 2. 编译,如果不能编译会报错 3.运行上一步生成的class文件 4. 产生调用产生的运行时异常(如果有) 假设: 1. 类将包含一个“main”方法,否则它不能使用“java”程序运行

      鉴于上述问题陈述,我已经提出了一个解决方案。在编写代码之前,我认为按照它的执行顺序来解释它的作用是个好主意。

      解决步骤:

      1. 使用编译器 API 编译 Java 代码(使用 ToolProvider.getSystemJavaCompiler()) 2. 使用 DiagnosticCollector 收集可能发生的任何编译错误。 3. 如果编译成功,则将生成的类加载到字节数组中。 4. 使用 ClassLoader.defineClass() 将类文件从字节数组加载到 JVM 运行时。 5.一旦类被加载,使用反射查找main方法,如果不存在则抛出main not found相关异常。 6. 运行 main 方法,并报告任何产生的运行时异常。 注意:如果需要,可以将标准输入和输出流重定向到 新程序和原始对象可以保存为原始对象 主程序。我没有做过,但做起来很简单。

      工作代码:

      import java.io.ByteArrayOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.lang.reflect.Modifier;
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      
      import javax.tools.Diagnostic;
      import javax.tools.DiagnosticCollector;
      import javax.tools.JavaCompiler;
      import javax.tools.JavaFileObject;
      import javax.tools.StandardJavaFileManager;
      import javax.tools.ToolProvider;
      
      public class JreMain {
          private static final String PATH_TO_JAVA_FILE = "src/main/java/MyProgram.java";
      
          public static void main(String[] args) {
              JreMain main = new JreMain();
              System.out.println("Running a java program");
      
              String filePath = PATH_TO_JAVA_FILE;
      
      
              File javaFile = new File(filePath);
              /*compiling the file */
              List<String> errorList = main.compile(Arrays.asList(javaFile));
      
              if(errorList.size() != 0) {
                  System.out.println("file could not be compiled, check below for errors");
      
                  for(String error : errorList) {
                      System.err.println("Error : " + error);
                  }
              } else {
                  main.runJavaClass(filePath, new String[] {});
              }
          }
      
          @SuppressWarnings({"rawtypes", "unchecked"})
          private void runJavaClass(String filePath, String[] mainArguments) {
      
              System.out.println("Running " + filePath);
      
              ClassLoader classLoader = getClass().getClassLoader();
              Class klass = null;
              String fileNameWithoutExtension = filePath.substring(0, filePath.length() - ".java".length());
              String className = getClassName(fileNameWithoutExtension);
              /* loading defineClass method in Classloader through reflection, since it's 'protected' */
              try {
                  /* signature of defineClass method: protected final Class<?> defineClass(String name, byte[] b, int off, int len)*/
      
                  Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
                  defineClassMethod.setAccessible(true);
      
                  /* attempting to load our class in JVM via byte array */
      
                  byte[] classBytes = getClassBytes(fileNameWithoutExtension + ".class");
                  klass = (Class)defineClassMethod.invoke(classLoader, className, classBytes, 0, classBytes.length);
      
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (SecurityException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (IllegalArgumentException e) {
                  e.printStackTrace();
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
              }
      
              /* checking if main method exists, in the loaded class, and running main if exists*/
      
              if(klass != null) {
                  try {
                      Method mainMethod = klass.getMethod("main", String[].class);
                      Class returnType = mainMethod.getReturnType();
      
                      /*Checking for main method modifiers and return type*/
      
                      if( !Modifier.isStatic(mainMethod.getModifiers()) || !Modifier.isPublic(mainMethod.getModifiers()) || !(returnType.equals(Void.TYPE) || returnType.equals(Void.class))) {
                          throw new RuntimeException("Main method signature incorrect, expected : \"public static void main(String[] args)\",");
                      }
      
                      /* finally invoking the main method **/
                      mainMethod.invoke(null, new Object[]{mainArguments});
      
                  } catch (NoSuchMethodException e) {
                      throw new RuntimeException("Class " + klass.getCanonicalName() + " does not declare main method");
                  } catch (SecurityException e) {
                      e.printStackTrace();
                  } catch (IllegalAccessException e) {
                      e.printStackTrace();
                  } catch (IllegalArgumentException e) {
                      e.printStackTrace();
                  } catch (InvocationTargetException e) {
                      /*e.printStackTrace();*/
                      System.err.println("Exception in main :");
                      throw new RuntimeException(e.getCause());
                  }
              }
          }
      
          private String getClassName(String fileNameWithoutExtension) {
              String className = null;
              int lastIndex = -1;
              if( ( lastIndex = fileNameWithoutExtension.lastIndexOf(File.separator)) != -1) {
                  className = fileNameWithoutExtension.substring(lastIndex + 1);
              } if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("\\")) != -1) {
                  className = fileNameWithoutExtension.substring(lastIndex + 1);
              } else if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("/")) != -1) {
                  className = fileNameWithoutExtension.substring(lastIndex + 1);
              }
              return className;
          }
      
          private byte[] getClassBytes(String classFilePath) {
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              File classFile = new File(classFilePath);
              if(!classFile.exists()) {
                  throw new RuntimeException("Class file does not exist : " + classFile.getAbsolutePath());
              }
      
              byte[] buffer = new byte[2048];
              int readLen = -1;
              FileInputStream fis = null;
              try {
                  fis = new FileInputStream(classFile);
                  while( (readLen = fis.read(buffer)) != -1) {
                      baos.write(buffer, 0, readLen);
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  if(fis != null) {
                      try {
                          fis.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
      
              return baos.toByteArray();
          }
      
          @SuppressWarnings("restriction")
          public List<String> compile (List<File> javaFileList) {
              System.out.println("Started compilation");
              List<String> errorList = new ArrayList<String>();
              JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
      
              DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
              StandardJavaFileManager fileManager = compiler.getStandardFileManager(
                      diagnostics, null, null);
      
              Iterable<? extends JavaFileObject> compilationUnits = fileManager
                      .getJavaFileObjectsFromFiles(javaFileList);
              compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits)
                      .call();
      
              for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics
                      .getDiagnostics()) {
                  String diagnosticMessage = String.format("Error on line %d in %s%n",
                          diagnostic.getLineNumber(), diagnostic.getSource().toUri() + " : \n\t" + diagnostic.getMessage(null));
      
                  /*Following gives out of box good message, but I used above to show the custom use of diagnostic
                   * String diagnosticMessage = diagnostic.toString();*/
      
                  errorList.add(diagnosticMessage);
              }
              try {
                  fileManager.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              return errorList;
          }
      }
      

      感谢@Miserable 变量,我几乎准备好为 javac 程序创建一个进程,但你的回答为我节省了一些丑陋的代码。

      ** *已编辑 **

      命令行参数

      编译:

      //in the JreMain.compile()    
      List<String> compilerOptionsList = Arrays.asList("-classpath", "jar/slf4j-api-1.7.10.jar", "-verbose");
      JavaCompiler.CompilationTask compilationTask = compiler.getTask(null,
                  fileManager, diagnostics, compilerOptionsList, null,
                  compilationUnits);
      

      对于 Java 运行时参数:

      They will have to be passed to our JareMain program itself.
      java -classpath "jar/slf4j-api-1.7.10.jar;" -verbose JreMain
      

      对于新程序的main方法:

      //second argument here is going to main method
       main.runJavaClass(filePath, new String[] {});
      

      【讨论】:

      • 感谢您的回答!我可以通过这种方法传递命令行参数吗?我还没有完成代码。不过,我已经提供了赏金,因为它即将过期。所有的信仰。
      • 感谢您的信任,更新了我的答案以包括如何添加命令行选项。
      猜你喜欢
      • 1970-01-01
      • 2018-10-24
      • 2012-09-30
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-25
      • 2010-11-29
      相关资源
      最近更新 更多