最近项目中遇到java进程意外终止,没有任何异常日志信息,不知道什么时候出现的进程终止。
在有完善的运维监控系统的情况下,通过监控系统,可以及时发现服务终止时间,这里所说的方法也就没有必要了。这里主要针对缺乏完善监控运维的情况下,采取的临时措施。
为了记录java进程最近一段时间的运行状态,可以采用循环记录的方式进行记录。
这里采用的方式是程序启动后,每间隔一段时间(比如5秒),先读取日志文件到队列中,在队列尾添加最新的状态信息,判断信息是否超过预设数量(比如30),如果超出,从队列头部删除第一条数据。然后将队列中的数据,从队列头部逐个写入文件。
实际项目中 ,可以开辟单独的线程进行处理,记录的信息也可以更丰富,比如增加对所提供的服务是否可用的判断,数据库连接是否可用,可用的内存空间大小等。作为一个基本的案例,这里只是记录了系统时间。
package com.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
public class QueueDemo {
private static String timeMinutes;
public static void main(String[] args) throws InterruptedException {
LinkedList<String> queue = new LinkedList<String>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
Long time = System.currentTimeMillis();
// 转换为分
time = time / 1000 / 60;
timeMinutes = time + "";
for (int i = 0; i < 60; i++)
{
restoreCheckPoint(queue);
String dateTime = sdf.format(System.currentTimeMillis());
//添加元素
queue.addLast(dateTime);
saveCheckPoint(queue);
Thread.sleep(5000);
}
}
/**
* 文件不存在时,不需要报错
*/
public static void restoreCheckPoint(LinkedList<String> queue) {
String fileName = "heartbeat" + timeMinutes + ".log";
String confPath = getConfFile(fileName);
try {
File confFile = new File(confPath);
// 文件不存在,不进行读取
if (!confFile.exists()) {
return;
}
InputStream inputStream = new FileInputStream(confFile);
// 设置1KB的缓冲区
byte buf[] = new byte[1024];
int read = inputStream.read(buf);
inputStream.close();
if(read > 0)
{
String buffInfo = new String(buf);
String confInfo = buffInfo.substring(0, read);
String splits[] = confInfo.split("\n");
for (String line : splits) {
queue.addLast(line);
System.out.println("恢复数据:" + line);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件不存在时,不影响正常使用
*
*/
public static void saveCheckPoint(LinkedList<String> queue) {
String fileName = "heartbeat" + timeMinutes + ".log";
String filePath = getConfFile(fileName);
try {
File confFile = new File(filePath);
if (!confFile.exists()) {
confFile.createNewFile();
}
FileOutputStream outputStream = new FileOutputStream(confFile);
while (queue.size() > 30) {
queue.removeFirst();
}
while (!queue.isEmpty())
{
String poll = queue.poll();
System.out.println("持久化成功到:" + poll);
String configInfo = poll + "\n";
outputStream.write(configInfo.getBytes(StandardCharsets.UTF_8));
}
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取日志文件路径
public static String getConfFile(String fileName) {
URL resource1 = QueueDemo.class.getResource("/");
String path = resource1.getPath();
System.out.println("path1 = " + path);
int endPos = path.indexOf(".jar!");
if (endPos > 0)
path = path.substring(0, endPos);
System.out.println("path2 = " + path);
endPos = path.lastIndexOf("/");
if (path.startsWith("file:/"))
path = path.substring(6, endPos);
else if (path.startsWith("/"))
path = path.substring(1, endPos);
System.out.println("path3 = " + path);
path = path + "/" + fileName;
System.out.println("path4 = " + path);
return path;
}
}
每次重启的时候,会根据系统时间到分为单位,生成独立的日志文件,这样不会覆盖原来的文件。确认文件没有作用后,可以手动删除文件。