有一个需求,在不影响现有 Jvm 标准输出和标准错误输出的情况下,将标准输出和标准错误输出保存下来。考虑到项目包含了 Java 和 Scala 代码,所以采用 pipe2 和 dup2 系统调用的方式来实现重定向,流程如下:
- 复制标准输出和标准错误输出
- 建立非阻塞读写管道
- 复制标准输出和标准错误输出到写管道
- 启动另外一个线程从读管道读取内容并保存到文件,同时将内容输出到之前复制的标准输出;如果结束标志为 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();
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)