当前位置:首页> 正文

linux查看实时日志命令(查看日志的三种命令分享)

linux查看实时日志命令(查看日志的三种命令分享)

  最近在做一个小工具,有个需求是在Web端能实时查看日志文件,也就是相当于在终端执行tail -f命令,对此没有找到好的解决方式,一开始想得直接通过FileInputStream来读取,因为他也能直接跳过n个字节来读取,就像下面这样。

  如果不跳过的话,那么每次读取全部内容并展示显然不现实,我们要做的是像tail一样,每次从后n行开始读取,并且会持续输出最新的行。

  还有一个问题就是对文件的变化要能感知到,所以最后选择直接调用tail命令,并且通过WebSocket输出到网页上。

  在java中调用tail命令后,拿到它的输入流并且包装成BufferedReader,如果通过readLine()读取不到数据,那么他会一直阻塞,并不会返回null,这也就代表日志文件中暂时还没有新数据写入,一旦readLine()方法返回,那么就代表有新数据到达了。另外一个问题就是如何终止,我们不可能让他一直读取,要在一个合适的时间终止,答案就是在WebSocket断开连接时,并且Process类提供了destroy()方法用来终止这个进程,相当于按下了Ctrl+C

  在Spring Boot中加入WebSocket功能有很多方式,目前感觉普遍的文章都是介绍以ServerEndpointExporter、@OnOpen、 @OnClose、@OnMessage这种方式来实现的,这种方式需要声明一个Bean,也就是ServerEndpointExporter,但是我记得如果要打包成war放入Tomcat中运行时,还需要把这个Bean取消掉,否则还会报错,非常的麻烦,当然也有办法解决。

  还有其他集成的办法,比如实现WebSocketConfigurer或者

  WebSocketMessageBrokerConfigurer接口,而我目前采用的是实现WebSocketMessageBrokerConfigurer接口,并且前端还需要两个库,SockJS和Stomp(更具选择,也可以不使用)。

  SockJS提供类似于WebSocket的对象,还有一套跨浏览器的API,可以在浏览器和Web服务器之间创建了低延迟,全双工,跨域的通信通道,如果浏览器不支持 WebSocket,它还可以模拟对WebSocket的支持。

  Stomp即Simple Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。

  首先看一下连接处理层的逻辑,其中一部分非必要的代码就不展示了。

  对于上面的几个接口可能没使用过他的人有点蒙,至少我在学习他的时候是这样的,看上面的代码,我们先要理清逻辑,才能明白为什么要这样写。

  首先是

  WebSocketMessageBrokerConfigurer接口,Spring Boot提供的一个WebSocket配置接口,只需要简简单单地配置两下,就可以实现一个WebSocket程序,这个接口中有8个方法,而我们只需要用到三个个。

  然后就是给出前端连接WebSocket所需要的地址,如果连连接地址都不给,后面步骤怎么继续?这个就是通过实现registerStompEndpoints方法来完成,只需要向StompEndpointRegistry中通过addEndpoint添加一个新的”连接点”就可以,还可以设置拦截器,也就是在前端试图连接的时候,如果后端发现这个连接不对劲,有猫腻,可以拒绝和他连接,这步可以通过addInterceptors来完成。

  切记如果使用了SocketJs库,那么一定要加入withSockJS。

  这一步是为了方便管理,比如主动断开连接,需要实现

  configureWebSocketTransport接口,但是这里的SessionId并不是服务端生成的会话ID,而是这个WebSocket的会话ID,每个WebSocket连接都是不同的。

  这里主要考虑到如果前端传过来的文件不存在,那么服务端要能主动断开连接。

  接着前端通过Stomp的API来订阅一个消息,那么我们怎么接收订阅的事件呢?就是通过 @EventListener注解来接收SessionSubscribeEvent事件。

  而前端订阅时就需要传入要监控的日志路径。这时候我们就能拿到这个WebSocket要监听的日志路径了。

  接着我们要为每个WebSocket都开启一个线程,用来执行tail命令。

  最终执行者,其中的stop()方法是在WebSocket断开连接时执行的。那么需要事先保存好sessionId和LogMonitorExecute的对应关系。当文件有新变化时,发送给对应的WebSocket。

  注意这里,要发送给指定的WebSocket,而不是订阅了这个路径的WebSocket,因为使用SimpMessagingTemplate在发送数据时,他可以给所有订阅了此路径的WebSocket,那么就导致如果一个浏览器开了2个监控,而且监控的都是同一个日志文件,那么每个监控都会收到两条同样的消息。

  所以要使用convertAndSendToUser方法而不是convertAndSend,这也就是为什么前面会通过setHandshakeHandler设置握手处理器为每个WebSocket连接取一个name的原因。

  下面是启动、关闭Tomcat的日志。

  如果不使用SimpMessagingTemplate,那么首先我们要拿到对应的WebSocketSession,它有个sendMessage方法用来发送数据,但是类型是WebSocketMessage,Spring Boot有几个默认的实现,比如TextMessage用来发送文本信息。

  但是如果使用了Stomp,那么单纯地使用他发送是不行的,数据虽然能过去,但是格式不对,Stomp解析不了,所以我们要按照Stomp的格式发送。

  但是经过查找,未能找到相关的资料,所以自己看了一下他的源码,其中设计到了StompEncoder这个类,看名字就知道他是Stomp编码的工具。Stomp协议分为三个部分,命令、头、消息体,命令有如下几个:

  紧跟着命令下一行是头,是键值对形式存在的,最后是消息体,末尾以空字符结尾。

  下面是发送的必要格式,否则StompEncoder也无法编码,将抛出异常,至于这个为什么这么写,详细就得看

  StompEncoderde.writeHeaders方法了,里面有几个验证,这种写完全是被他逼的。

  这是偶尔间的一个发现,当执行tail -f命令后,我们通过vim、gedit等工具编辑并保存这个文件,会发现tail -f并不会输出新的行,反而通过echo test>>xx.txt是正常的。

  那这里的蹊跷又在哪?

  其实,tail -f不管在文件移动、改名都会进行追踪,因为他跟踪的是文件描述符,引入维基百科的一句话:

  tail -f执行后会产生一个进程,可以在/proc/pid/fd路径下查看他所打开的文件描述符,下面来看一个GIF。

  在这个操作中,首先在终端1中创建一个1.txt,然后进行tail -f跟踪,接着在终端2中追加一行数据,可以看到终端1中是可以打印出来的。

  然后再看神奇的一幕,在终端2进行mv改名,接着向被改名后的文件追加新的一行,你会发现,终端1居然还是会打印的。

  如果查看一下这个进程的文件描述符,就不为奇了,在下面的命令中,显示了3号描述符追踪的是

  /home/HouXinLin/test/tail/2.txt。

  但是如果我们通过vim、等工具编辑这个文件后,那么这个文件描述符中会被记录为被删除,即使这个文件确实是存在的,此时在向2.txt文件中追加就会失效。

展开全文阅读

相关内容