程序员开发实例大全宝库

网站首页 > 编程文章 正文

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

zazugpt 2024-08-24 02:23:50 编程文章 16 ℃ 0 评论

有一个需求,在不影响现有 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 stdoutCopy = 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();
  }
}

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表