【问题标题】:Is there a way to stream a log to a Spring Boot application?有没有办法将日志流式传输到 Spring Boot 应用程序?
【发布时间】:2018-02-09 17:52:36
【问题描述】:

我有一个用作事件记录器的 Spring Boot 应用程序。每个客户端通过 REST api 发送不同的事件,然后将其保存在数据库中。但除了简单的事件之外,我还需要客户端将他们的执行日志发送到 Spring Boot。

现在,在客户端完成执行后上传日志很容易,并且有很多示例。我需要的是在客户端执行时逐行流式传输日志,而不是等到客户端完成。

我花了很长时间在谷歌上搜索可能的答案,但找不到任何适合我需要的东西。任何建议如何使用 Spring Boot(包括未来版本)来做到这一点?可行吗?

【问题讨论】:

  • 您能更清楚地了解涉及到什么样的客户吗?假设您正在编写客户端并控制它们的行为,并且它们也是用 Java 编写的,那么您可以为您正在使用的任何日志框架创建自己的 Appender,并且 Appender 可以针对每个日志语句访问您的 REST api,而不是写入文件。我建议通过在 Appender 实现中生成另一个线程来异步执行实际的 REST 调用。
  • @John 客户是 Java SE,是的,我正在为他们编写代码。 Andrew 已经提出了类似的建议,所以附加程序将是我要尝试的第一件事。感谢您的回复!

标签: java spring spring-boot upload stream


【解决方案1】:

我在这里看到了几种可能性。首先,考虑在客户端中使用 logback(默认 Spring Boot 日志实现)SocketAppender 或 ServerSocketAppender。请参阅:https://logback.qos.ch/manual/appenders.html。这将允许您将日志消息发送到任何日志记录服务。 但是我可能会建议您不要登录到 Spring Boot Event App,因为我怀疑这会不必要地增加您的应用程序的复杂性,并且我可以看到 Event App 中存在一些错误然后导致客户端记录一堆错误又全部返回到事件应用程序,从而难以确定初始错误。

我会恭敬地建议您改为登录到日志服务器 - 例如logstash:https://www.elastic.co/products/logstash,或者如果您已经有一个要保存事件的数据库,那么可以使用日志 DBAppender 并编写直接记录到数据库。

【讨论】:

  • 我已经考虑过 logstash(以及整个 elk 堆栈),只是不知道它如何适合我的安全设置。而且我不想给项目增加额外的复杂性。另一方面,appenders 似乎是一个非常直接的解决方案,所以我将首先尝试那个。谢谢你的回答!
  • 如果你看一下,你可能会找到一个 logback http appender:github.com/kewang/logback-http-appenderstackoverflow.com/questions/38271359/…
  • 最后我还是选择了Logstash。我在我的 Spring Boot 应用程序中添加了一个日志控制器,它接受来自我客户端的 http 日志附加器的请求。控制器从客户端接收的所有内容都记录到单独的日志文件中。然后我简单地将 Logstash 连接到该日志文件。我不知道这是否是理想的设置,但它至少允许我使用我现有的 Spring Boot 安全性。
【解决方案2】:

我写了here 一个关于如何在 Spring Boot 端点中流式传输文件更新的示例。唯一的区别是代码使用 Java WatchService API 来触发给定文件的文件更新。

但是,在您的情况下,我还会选择日志附加程序直接向连接的客户端发送消息(使用 sse - 从那里调用 template.broadcast),而不是像我描述的那样监视更改.

端点:

@GetMapping(path = "/logs", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamSseMvc() {
    return sseService.newSseEmitter();
}

服务:

public class LogsSseService {
private static final Logger log = LoggerFactory.getLogger(LogsSseService.class);

private static final String TOPIC = "logs";
private final SseTemplate template;
private static final AtomicLong COUNTER = new AtomicLong(0);

public LogsSseService(SseTemplate template, MonitoringFileService monitoringFileService) {
    this.template = template;
    monitoringFileService.listen(file -> {

        try {
            Files.lines(file)
                    .skip(COUNTER.get())
                    .forEach(line ->
                            template.broadcast(TOPIC, SseEmitter.event()
                                    .id(String.valueOf(COUNTER.incrementAndGet()))
                                    .data(line)));
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
}

public SseEmitter newSseEmitter() {
    return template.newSseEmitter(TOPIC);
}

}

自定义附加程序(您必须将其添加到您的记录器 - 检查here):

public class StreamAppender extends UnsynchronizedAppenderBase<ILoggingEvent> implements SmartLifecycle {
public static final String TOPIC = "logs";

private final SseTemplate template;

public StreamAppender(SseTemplate template) {
    this.template = template;
}

@Override
protected void append(ILoggingEvent event) {
    template.broadcast(TOPIC, SseEmitter.event()
            .id(event.getThreadName())
            .name("log")
            .data(event.getFormattedMessage()));
}

@Override
public boolean isRunning() {
    return isStarted();
}

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    相关资源
    最近更新 更多