java监控本地日志并实现实时查看(代码片段)

言成言成啊 言成言成啊     2023-02-22     514

关键词:

每次查看日志,都需要去服务器上看,太麻烦了,所以简单实现一个在线日志实时监控功能,可以方便实时查看了。

源码meethigher/log-monitor

参考

实现功能

  1. 指定目录下的日志文件查询。支持按照时间范围查询
  2. 下载日志
  3. 实时查看日志

实现逻辑

  1. 文件的按照时间查询,采用FileChannel的lastModified属性获取时间,查询lastModified在指定时间范围内即可。
  2. 下载功能,直接网上抄就行
  3. 实时查看日志,通过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);
             使用jpom快速发布项目到服务器教程(代码片段)

简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件前言❝本文主要介绍:如何从零开始使用Jpom中的构建功能快速实现将项目从仓库中构建并发布到服务器中并启动项目。❞Jpom功能特点创建、修改、删除项目... 查看详情

elasticsearch:api网关apacheapisix集成elasticsearch实现实时日志监控(代码片段)

作者:王程程本文将为你介绍ApacheAPISIX的elasticsearch-logger插件的相关信息,以及如何通过此插件获取APISIX的实时日志。背景信息​ApacheAPISIX 是一个动态、实时、高性能的 API 网关,提供了负载均衡、动态上游、灰度... 查看详情

flinkonyarn实时日志收集最佳实践(代码片段)

...34;设为星标"再也不用担心错过重要文章 后台回复"监控",获取grafana监控Flink 最新的模板背景在Flinkonyarn的模式下,程序运行的日志会分散的存储在不同的DN上,当Flink任务发生异常的时候,我们需要查看日志来定位问题,一般... 查看详情

java编程:java.nio.file.watchservice实时监控文件变化并没有真正实时

...景需要实时监听文件的变化,如下:1、通过实时监控mysql的binlog日志实现数据同步2、修改配置文件后,希望系统可以实时感知3、应用系统将日志写入文件中,日志监控系统可以实时抓取日志,分析日志内容并... 查看详情

observability:从零开始创建java微服务并监控它(代码片段)

在本教程中,你将学习如何使用Elastic可观察性监控Java应用程序:日志、基础设施指标、APM和正常运行时间。通过本教程,你将学到:创建示例Java应用程序。使用Filebeat提取日志并在Kibana中查看你的日志。使用Metri... 查看详情

rainbond结合jpom实现云原生&本地一体化项目管理(代码片段)

...轻的低侵入式在线构建、自动部署、日常运维、项目运维监控软件。提供了:节点管理:集群节点,统一管理多节点的项目,实现快速一键分发项目文件项目管理:创建、启动、停止、实时查看项目控制台日志,管理项目文件SSH... 查看详情

flume监控hive日志文件(代码片段)

flume监控hive日志文件一:flume监控hive的日志1.1案例需求:1.实时监控某个日志文件,将数据收集到存储hdfs上面,此案例使用execsource,实时监控文件数据,使用MemoryChannel缓存数据,使用HDFSSink写入数据2.此案例实时监控hive日志文... 查看详情

sentinel实时监控不显示(代码片段)

问题如下:在控制台里,此应用可以出来,但是解决方式一(未解决):看到网上的解决办法是,使sentinel所在服务器的时间和应用所在机器的时间一致,修改docker容器时间。我就同步了一下,... 查看详情

实时监控:基于流计算oceanus(flink)实现系统和应用级实时监控(代码片段)

...工程师本文描述了如何使用腾讯云大数据组件来完成实时监控系统的设计和实现,通过实时采集并分析云服务器(CVM)及其App应用的CPU和内存等资源消耗数据,以短信、电话、微信消息等方式实时反馈监控告警信... 查看详情

flinkcdc--debezium实现kafka实时监控mysqlbinlog日志(代码片段)

...部分都分单机和集群模式,这次我配置的是kafka集群监控mysqlbinlog日志一.Zookeeper和Kafka集群部署我的服务器是三台节点  aliyun-bigdata-01 aliyun-bigdata-02 aliyun-bigdata-031.第一步实现kafka集群部署和zookeeper集群部署先启动zookeeper再启... 查看详情

linux查看日志的几种方法(代码片段)

...显示行号;相当于nl命令;例子如下:tail-100ftest.log实时监控10 查看详情

inotify+rsync实现实时同步并邮件通知(代码片段)

服务器之间文件实时同步,监控文件的变化,发送邮件通知,并实时同步文件。由于人工同步多台服务器的文件比较吃力,可以借助这样一套软件,自动化的实现这样的工作。并且可以事实监控变化发送邮件给系统管理人员。服... 查看详情

抓取日志并保存到本地(代码片段)

抓取日志并保存到本地通过代码抓取应用打印的日志并保存到本地目录下文章目录抓取日志并保存到本地前言一、参考方案1.通过在代码中执行Linux命令实现总结前言我们的应用在使用的过程中,有的时候随机出现异常,... 查看详情

canal数据库变更监控(代码片段)

背景:1.一些项目的基础功能会有AuditTrace,以记录系统用户所做过的所有记录。2.实时备份数据,比如mysql主从复制,一个用于面向应用,一个用于对应用数据库的实时备份。3.实时收集关系型数据库变更,将数据保存在nosql数据库... 查看详情

observability:从零开始创建java微服务并监控它(代码片段)

在本教程中,你将学习如何使用Elastic可观察性监控Java应用程序:日志、基础设施指标、APM和正常运行时间。通过本教程,你将学到:创建示例Java应用程序。使用Filebeat提取日志并在Kibana中查看你的日志。使用Metri... 查看详情

logstation支持浏览器实时查看日志(代码片段)

我们在logback分布式日志汇总中已经将日志输出到了all.logs,LogStation支持浏览器实时查看日志,适合研发和运维彼此独立的场景:研发没有服务器权限,却想看日志实时输出。再配合nginx提供的auth_basic简单授权就搞定了。快速上... 查看详情

实时查看容器日志信息dockerlogs-fcontainer_id(代码片段)

实时查看容器日志信息dockerlogs-fcontainer_ID 查看详情

如何实时查看linux下的日志

参考技术A1.如下图所示,先cd到我们需要监控的日志目录。2.这里我们先使用cat命令查看下日志信息,方便与动态监控进行对比。3.下面先讲解下tail命令实现查看最后一部分日志的方法。tail文件名,默认显示最后10行。4.接着我们... 查看详情