Java监控本地日志并实现实时查看
Posted 言成言成啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java监控本地日志并实现实时查看相关的知识,希望对你有一定的参考价值。
每次查看日志,都需要去服务器上看,太麻烦了,所以简单实现一个在线日志实时监控功能,可以方便实时查看了。
参考
实现功能
- 指定目录下的日志文件查询。支持按照时间范围查询
- 下载日志
- 实时查看日志
实现逻辑
- 文件的按照时间查询,采用FileChannel的lastModified属性获取时间,查询lastModified在指定时间范围内即可。
- 下载功能,直接网上抄就行
- 实时查看日志,通过websocket实现。每个websocket请求进来会开启一个线程监听一个日志文件,请求断开线程关闭。考虑到这本身也不是一个常用的功能,使用了显式创建线程的方式。
查询与下载
放上具体的业务逻辑代码
@Service
public class LogMonitorServiceImpl implements LogMonitorService
private final Logger log = LoggerFactory.getLogger(LogMonitorServiceImpl.class);
/**
* 日志根目录、默认目录
*/
@Value("$log.monitor.defaultPath")
private String logRootPath;
/**
* 获取路径
*
* @param logPath
* @return
*/
private String getLogPath(String logPath) throws CommonException
String path = null;
if (ObjectUtils.isEmpty(logPath))
path = logRootPath;
else
if (!logPath.contains(logRootPath))
throw new CommonException(ResponseEnum.NO_ACCESS_FOR_THIS_PATH);
path = logPath;
return path;
/**
* 按照时间查询日志
*
* @param startTime
* @param endTime
* @param dir
* @return
*/
private List<String> queryLogByTime(Long startTime, Long endTime, File dir)
List<String> files = new LinkedList<>();
for (String s : Objects.requireNonNull(dir.list()))
File file = new File(dir, s);
long lastModified = file.lastModified();
if (startTime <= lastModified && endTime >= lastModified)
files.add(file.getAbsolutePath().replaceAll("\\\\\\\\", "/"));
log.info("queryLogByTime");
return files;
/**
* 查询所有日志
*
* @param dir
* @return
*/
private List<String> queryLogWithoutTime(File dir)
List<String> files = new LinkedList<>();
for (String s : Objects.requireNonNull(dir.list()))
File file = new File(dir, s);
files.add(file.getAbsolutePath().replaceAll("\\\\\\\\", "/"));
log.info("queryLogWithoutTime");
return files;
@Override
public List<String> queryLog(QueryLogRequest request) throws CommonException
String logPath = getLogPath(request.getLogPath());
File dir = new File(logPath);
if (!dir.exists() || !dir.isDirectory())
throw new CommonException(ResponseEnum.DIR_NOT_EXIST_OR_DIR_IS_A_FILE);
if (!ObjectUtils.isEmpty(request.getStartTime()) && !ObjectUtils.isEmpty(request.getEndTime()))
return queryLogByTime(request.getStartTime(), request.getEndTime(), dir);
else
return queryLogWithoutTime(dir);
@Override
public String downloadLog(DownloadLogRequest request, HttpServletResponse response) throws CommonException
String logPath = getLogPath(request.getLogPath());
File file = new File(logPath);
if (!file.exists() || !file.isFile())
throw new CommonException(ResponseEnum.FILE_NOT_EXIST_OR_FILE_IS_DIRECTORY);
// 实现文件下载
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try
// 配置文件下载
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
// 下载文件能正常显示中文
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(request.getDownloadName(), "UTF-8"));
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1)
os.write(buffer, 0, i);
i = bis.read(buffer);
log.info("下载文件成功!");
catch (Exception e)
log.info("下载文件失败!");
throw new CommonException(ResponseEnum.DOWNLOAD_FILE_FAILED);
finally
if (bis != null)
try
bis.close();
catch (IOException e)
e.printStackTrace();
if (fis != null)
try
fis.close();
catch (IOException e)
e.printStackTrace();
return null;
实时查看日志
websocket使用spring-websocket
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
websocket配置类
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer
@Value("$log.websocket")
private String path;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
registry.addHandler(new SocketEventHandler(), path).setAllowedOrigins("*");
websocket处理器
public class SocketEventHandler extends AbstractWebSocketHandler
private final Logger log = LoggerFactory.getLogger(SocketEventHandler.class);
@Override
public void afterConnectionEstablished(WebSocketSession session)
log.info("进行连接");
WebSocketUtils.addSessoin(session);
WebSocketUtils.startMonitor(session.getId());
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
log.info("关闭连接");
WebSocketUtils.reduceSession(session);
websocket工具类
public class WebSocketUtils
private static final Logger log = LoggerFactory.getLogger(WebSocketUtils.class);
/**
* 已连接的websocket
*/
private static Map<String, WebSocketSession> onlineSession = new HashMap<>();
/**
* 添加用户
*
* @param session
*/
public static void addSessoin(WebSocketSession session)
onlineSession.put(session.getId(), session);
log.info("的用户连接websocket", session.getId());
/**
* 移除用户
*
* @param session
*/
public static void reduceSession(WebSocketSession session)
onlineSession.remove(session.getId());
log.info("的用户断开websocket", session.getId());
/**
* 开启监测
* 本质是一监控一线程
*
* @param sessionId
*/
public static void startMonitor(String sessionId)
WebSocketSession session = onlineSession.get(sessionId);
String query = session.getUri().getQuery();
String logPath = query.substring(query.indexOf("=") + 1);
new FileMonitor(session.getId(), logPath);
/**
* 关闭监控
* session关闭,相应线程也会关闭
*
* @param sessionId
*/
public static void endMonitor(String sessionId)
WebSocketSession session = onlineSession.get(sessionId);
sendMessageTo(sessionId,"<error>ERROR 监控线程出现异常!</error>");
try
session.close();
catch (IOException e)
e.printStackTrace();
/**
* 发送消息给指定用户
*
* @param sessionId
* @param message
*/
public static void sendMessageTo(String sessionId, String message)
WebSocketSession session = onlineSession.get(sessionId);
try
session.sendMessage(new TextMessage(message));
catch (Exception e)
e.printStackTrace();
WebSocketUtils.endMonitor(sessionId);
/**
* session是否在线
* 用于决定线程是否关闭
*
* @param sessionId
* @return
*/
public static boolean currentSessionAlive(String sessionId)
return onlineSession.containsKey(sessionId);
文件监听器
public class FileMonitor
private static final Logger log = LoggerFactory.getLogger(FileMonitor.class);
/**
* 绑定的websocket
*/
private String sessionId;
/**
* 绑定的监控日志路径
*/
private String logPath;
/**
* 监控时间间隔,单位ms
*/
private Long monitorDelay;
public FileMonitor(String sessionId, String logPath)
this.sessionId = sessionId;
this.logPath = logPath;
this.monitorDelay = 500L;
startFileMonitor(monitorDelay);
public FileMonitor(String sessionId, String logPath, Long monitorDelay)
this.sessionId = sessionId;
this.logPath = logPath;
this.monitorDelay = monitorDelay;
startFileMonitor(monitorDelay);
private void startFileMonitor(Long monitorDelay)
Thread thread = new Thread(new FileMonitorRunnable(sessionId, logPath, monitorDelay));
thread.start();
文件监听线程Runnable
public class FileMonitorRunnable implements Runnable
private static final Logger log = LoggerFactory.getLogger(FileMonitorRunnable.class);
private ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 100);
private CharBuffer charBuffer = CharBuffer.allocate(1024 * 50);
private CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
private boolean isRunning = true;
private String sessionId;
private String logPath;
private Long monitorDelay;
public FileMonitorRunnable(String sessionId, String logPath, Long monitorDelay)
this.sessionId = sessionId;
this.logPath = logPath;
this.monitorDelay = monitorDelay;
@Override
public void run()
File file = new File(logPath);
FileChannel channel = null;
try
channel = new FileInputStream(file).getChannel();
channel.position(channel.size());
catch (Exception e)
log.info("监控文件失败,检查路径是否正确");
WebSocketUtils.endMonitor(sessionId);
e.printStackTrace();
long lastModified = file.lastModified();
//TODO: 初次连接将所有内容丢回去?这个考虑到数据如果很多先不丢
while (isRunning)
long now = file.lastModified();
// log.info("的连接正在通过线程监控文件",sessionId,Thread.currentThread().getName(),logPath);
if (now != lastModified)
log.info("的连接正在通过线程监控的文件update", sessionId, Thread.currentThread().getName(), logPath);
String newContent = getNewContent(channel);
WebSocketUtils.sendMessageTo(sessionId, newContent);
lastModified = now;
try
Thread.sleep(monitorDelay);
以上是关于Java监控本地日志并实现实时查看的主要内容,如果未能解决你的问题,请参考以下文章