您的位置 首页 java

混合编程—JNA 实现输出重定向

有一个需求,在不影响现有 Jvm 标准输出和标准错误输出的情况下,将标准输出和标准错误输出保存下来。考虑到项目包含了 Java Scala 代码,所以采用 pipe2 和 dup2 系统调用的方式来实现重定向,流程如下:

  1. 复制标准输出和标准错误输出
  2. 建立非阻塞读写管道
  3. 复制标准输出和标准错误输出到写管道
  4. 启动另外一个 线程 从读管道读取内容并保存到文件,同时将内容输出到之前复制的标准输出;如果结束标志为 1,且读取多次后都失败,结束读取

样例代码如下:

 /*
for linux only
 */

import com.sun.jna.*;
import com.sun.jna.Memory;


/**
 * @author mumu
 */
public class StdCollector {
   private  final  static  int STDOUT = 1;
  private final static int STDERR = 2;

  private final static int O_CREAT = 64;
  private final static int O_WRONLY = 1;
  private final static int O_NONBLOCK = 2048;
  private final static int P600 = 384;

  private static int READ_BATCH = 1024;
  private static int READ_INTERVAL_MS = 500;
  private static int READ_RETRY = 3;

  private Thread writeThread;
  private volatile int stopFlag;

  public interface CLibrary  extends  Library {
    CLibrary INSTANCE = Native.loadLibrary(null, CLibrary.class);

    int pipe2(Pointer p, int flag);

    int dup(int fd);

    int dup2(int fd1, int fd2);

    int open(String path, int flag, int permission);

    int read(int fd, Pointer p, int nbyte);

    int write(int fd, Pointer p, int nbyte);

    int close(int fd);
  }

  public StdCollector() {
    this.stopFlag = 0;
  }

  public  void  start(String path) {
    new java.io. File (path).delete();

    int  stdout Copy = CLibrary.INSTANCE.dup( STDOUT );
    int stderrCopy = CLibrary.INSTANCE.dup( STDERR );

    // 新建管道
    Pointer pipePointer = new Memory(2 * Native.getNativeSize(Integer.TYPE));
    CLibrary.INSTANCE.pipe2(pipePointer, O_NONBLOCK);

    // 重定向
    int readFd = pipePointer.getInt(0);
    int writeFd = pipePointer.getInt(Native.getNativeSize(Integer.TYPE));
    CLibrary.INSTANCE.dup2(writeFd, STDOUT);
    CLibrary.INSTANCE.dup2(writeFd, STDERR);

    // 启动写线程
    writeThread = new Thread(() -> write(path, readFd, stdoutCopy, stderrCopy));
    writeThread.start();
  }

  public void stop() {
    stopFlag = 1;
    try {
      writeThread.join();
    } catch (Exception e) {

    }
  }

  private void write(String path, int readFd, int stdoutCopy, int stderrCopy) {
    int fd = CLibrary.INSTANCE.open(path, O_CREAT | O_WRONLY, P600);

    if (fd < 0) {
      CLibrary.INSTANCE.dup2(stdoutCopy, STDOUT);
      CLibrary.INSTANCE.dup2(stderrCopy, STDERR);
      System.err.println("建立重定向文件失败");
      return;
    }

    Pointer p = new Memory(READ_BATCH);
    try {
      int retry = READ_RETRY;
      while (true) {
        int readCount = CLibrary.INSTANCE.read(readFd, p, READ_BATCH);
        if (readCount < 0) {
          Thread.sleep(READ_INTERVAL_MS);
          if (stopFlag == 1 && retry <= 0) {
            break;
          }
          retry -= 1;
          continue;
        }

        CLibrary.INSTANCE.write(stdoutCopy, p, readCount);
        CLibrary.INSTANCE.write(fd, p, readCount);
        retry = READ_RETRY;
      }
    } catch (Exception e) {
      CLibrary.INSTANCE.dup2(stdoutCopy, STDOUT);
      CLibrary.INSTANCE.dup2(stderrCopy, STDERR);
      e.printStackTrace();
    }

    CLibrary.INSTANCE.close(stdoutCopy);
    CLibrary.INSTANCE.close(fd);
  }

  public static void main(String[] args) {
    StdCollector sc = new StdCollector();

    sc.start("/tmp/1");
    for (int i = 0; i < 100; i++) {
      System.out.println(String.format("qqqqq: %d", i));
      System.err.println(String.format("wwwww: %d", i));
      try { Thread.sleep(10); } catch (Exception e) {}
    }
    sc.stop();
  }
}  

文章来源:智云一二三科技

文章标题:混合编程—JNA 实现输出重定向

文章地址:https://www.zhihuclub.com/184070.shtml

关于作者: 智云科技

热门文章

网站地图