停止自定义 logback 异步附加程序的正确方法

2024-04-08

我使用 Amazon 的 Java SDK 创建了 Amazon SQS 和 SNS logback 附加程序。基本附加程序使用同步 Java API,但我还通过扩展创建了两者的异步版本ch.qos.logback.classic.AsyncAppender class.

但是,使用异步附加程序停止 logback 记录器上下文并不能按预期工作。当上下文停止时,所有异步附加程序都会在退出之前尝试刷新剩余事件。问题源于ch.qos.logback.core.AsyncAppenderBase#stop https://github.com/qos-ch/logback/blob/master/logback-core/src/main/java/ch/qos/logback/core/AsyncAppenderBase.java#L113-138方法,它会中断工作线程。当 Amazon SDK 仍在处理排队的事件并产生结果时,会触发中断com.amazonaws.AbortedException。在我的测试中AbortedException当 SDK 处理来自 API 的响应时发生,因此实际消息已通过,但情况可能并不总是如此。

即使工作人员仍应处理剩余的事件队列,logback 是否会中断工作线程?如果是这样,我该如何解决这个问题AbortedException中断引起的?我可以重写整个停止方法并删除中断,但这需要复制粘贴大部分实现。


我终于找到了一个解决方案,我认为这不是最佳的,而且远非简单,但它有效。

我的第一次尝试是将异步版本的 AWS SDK API 与 logback 提供的执行器一起使用,因为使用内部执行器可以避免中断问题。但这并没有成功,因为工作队列是共享的,在这种情况下,队列必须是特定于附加程序的才能正确停止它。所以我需要对每个附加程序使用自己的执行程序。

首先,我需要一个 AWS 客户端的执行程序。执行器的问题是所提供的线程工厂必须创建守护线程,否则如果使用 logback 的 JVM shutdown hook,它将无限期地阻塞。

public static ExecutorService newExecutor(Appender<?> appender, int threadPoolSize) {
    final String name = appender.getName();
    return Executors.newFixedThreadPool(threadPoolSize, new ThreadFactory() {

        private final AtomicInteger idx = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName(name + "-" + idx.getAndIncrement());
            thread.setDaemon(true);
            return thread;
        }
    });
}

下一个问题是如何通过中断正确停止追加器?这需要通过重试来处理中断异常,因为否则执行器将跳过等待队列刷新。

public static void shutdown(Appender<?> appender, ExecutorService executor, long waitMillis) {
    executor.shutdown();
    boolean completed = awaitTermination(appender, executor, waitMillis);
    if (!completed) {
        appender.addWarn(format("Executor for %s did not shut down in %d milliseconds, " +
                                "logging events might have been discarded",
                                appender.getName(), waitMillis));
    }
}

private static boolean awaitTermination(Appender<?> appender, ExecutorService executor, long waitMillis) {
    long started = System.currentTimeMillis();
    try {
        return executor.awaitTermination(waitMillis, TimeUnit.MILLISECONDS);
    } catch (InterruptedException ie1) {
        // the worker loop is stopped by interrupt, but the remaining queue should still be handled
        long waited = System.currentTimeMillis() - started;
        if (waited < waitMillis) {
            try {
                return executor.awaitTermination(waitMillis - waited, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ie2) {
                appender.addError(format("Shut down of executor for %s was interrupted",
                                         appender.getName()));
            }
        }
        Thread.currentThread().interrupt();
    }
    return false;
}

正常的 logback 附加程序预计以同步方式工作,因此即使没有适当的关闭挂钩,也不应该丢失日志记录事件。这是当前异步 AWS 开发工具包 API 调用的问题。我决定使用倒计时锁存器来提供阻塞附加器行为。

public class LoggingEventHandler<REQUEST extends AmazonWebServiceRequest, RESULT> implements AsyncHandler<REQUEST, RESULT> {

    private final ContextAware contextAware;
    private final CountDownLatch latch;
    private final String errorMessage;

    public LoggingEventHandler(ContextAware contextAware, CountDownLatch latch, String errorMessage) {
        this.contextAware = contextAware;
        this.latch = latch;
        this.errorMessage = errorMessage;
    }

    @Override
    public void onError(Exception exception) {
        contextAware.addWarn(errorMessage, exception);
        latch.countDown();
    }

    @Override
    public void onSuccess(REQUEST request, RESULT result) {
        latch.countDown();
    }
}

并用闩锁来处理等待。

public static void awaitLatch(Appender<?> appender, CountDownLatch latch, long waitMillis) {
    if (latch.getCount() > 0) {
        try {
            boolean completed = latch.await(waitMillis, TimeUnit.MILLISECONDS);
            if (!completed) {
                appender.addWarn(format("Appender '%s' did not complete sending event in %d milliseconds, " +
                                        "the event might have been lost",
                                        appender.getName(), waitMillis));
            }
        } catch (InterruptedException ex) {
            appender.addWarn(format("Appender '%s' was interrupted, " +
                                    "a logging event might have been lost or shutdown was initiated",
                                    appender.getName()));
            Thread.currentThread().interrupt();
        }
    }
}

然后全部捆绑在一起。以下示例是实际实现的简化版本,仅显示此问题的相关部分。

public class SqsAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    private AmazonSQSAsyncClient sqs;

    @Override
    public void start() {
        sqs = new AmazonSQSAsyncClient(
                getCredentials(),
                getClientConfiguration(),
                Executors.newFixedThreadPool(getThreadPoolSize())
        );
        super.start();
    }

    @Override
    public void stop() {
        super.stop();
        if (sqs != null) {
            AppenderExecutors.shutdown(this, sqs.getExecutorService(), getMaxFlushTime());
            sqs.shutdown();
            sqs = null;
        }
    }

    @Override
    protected void append(final ILoggingEvent eventObject) {
        SendMessageRequest request = ...
        CountDownLatch latch = new CountDownLatch(1);
        sqs.sendMessageAsync(request, new LoggingEventHandler<SendMessageRequest, SendMessageResult>(this, latch, "Error"));
        AppenderExecutors.awaitLatch(this, latch, getMaxFlushTime());
    }
}

所有这些都是为了正确处理以下情况:

  • 使用异步附加器包装器时,在 logback 上下文停止或关闭挂钩上刷新剩余事件队列
  • 使用logback的延迟关闭钩子时不要无限期阻塞
  • 不使用异步附加程序时提供阻塞行为
  • 避免异步追加器停止的中断,该中断导致所有 AWS SDK 流实现中断

以上是在开源项目中使用的登录扩展 https://github.com/trautonen/logback-ext,我是它的维护者。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

停止自定义 logback 异步附加程序的正确方法 的相关文章

  • 如何在 IDEA Intellij 上使用 Spring-boot 自动重新加载

    我写了一个基于Spring boot tomcat freemarker的项目 我运行成功 但是每当我修改一些模板和java类时 我必须重新启动服务器或使用Intellij上的 重新加载更改的类 菜单才能使更改生效 浪费很多时间 然后我尝试
  • 记录共享和映射的诊断上下文

    据我所知 其他人做了什么来解决 Commons Logging 项目 针对 NET 和 Java 不支持映射或嵌套诊断上下文这一事实 执行摘要 我们选择直接使用实现者日志框架 在我们的例子中为 log4j 长答案 您是否需要一个抽象日志框架
  • 无法从资源加载图片

    So I am trying to load a image file from a resource so that when I export my application into a jar file it could be use
  • Java:SortedMap、TreeMap、可比较?如何使用?

    我有一个对象列表 需要根据其中一个字段的属性进行排序 我听说 SortedMap 和 Comparator 是实现此目的的最佳方法 我是否要与正在排序的类实现 Comparable 还是创建一个新类 如何实例化 SortedMap 并传入
  • 在尝试使用 GPS 之前如何检查 GPS 是否已启用

    我有以下代码 但效果不好 因为有时 GPS 需要很长时间 我该如何执行以下操作 检查GPS是否启用 如果启用了 GPS 请使用 GPS 否则请使用网络提供商 如果 GPS 时间超过 30 秒 请使用网络 我可以使用时间或 Thread sl
  • 正确签名的 JNLP 应用程序无法在 Java 7 中运行

    我有一个 JNLP 应用程序 由于证书过期需要更新 我有一个经过 CA 验证的新证书 我已将新证书导入到我的密钥库中 我已导入完整的证书链 我的构建文件对构建中的 jar 进行签名和时间戳
  • 在 Java 中创建 XML 文件的最佳方法是什么?

    我们目前使用 dom4j 来创建 XML 文件 不过 我猜现在有更好的东西了 如果我们使用的是 Java 1 6 或更高版本 那么在编写 XML 文件时最好使用什么类 运行速度最快 使用简单 我不需要构建一个 DOM 然后编写整个 DOM
  • 在 java 中运行外部应用程序但不要等待它完成

    我正在用java编写一个应用程序 允许我运行其他应用程序 为此 我使用了 Process 类对象 但当我这样做时 应用程序会等待进程结束 然后再退出 有没有办法在 Java 中运行外部应用程序 但不等待它完成 public static v
  • 嵌套字段的 Comparator.comparing(...)

    假设我有一个这样的域模型 class Lecture Course course getters class Course Teacher teacher int studentSize getters class Teacher int
  • 如何从intellij项目视图中隐藏不必要的文件?

    给定一个示例 gradle 项目 其项目结构如下所示 正如你所看到的 有很多东西你实际上不需要在想法中看到 但你需要它们存在 我知道下面被忽略的文件 文件夹类型Editor File Types但这些正在影响库和项目 idea 会在各处忽略
  • BadPaddingException:无效的密文

    我需要一些帮助 因为这是我第一次编写加密代码 加密代码似乎工作正常 但解密会引发错误 我得到的错误是 de flexiprovider api exceptions BadPaddingException 无效的密文 in the 解密函数
  • 当底层连接是有状态时如何使用 Apache HttpClient?

    我在谷歌上搜索了很多关于如何使用 HttpClient 进行多线程处理的信息 他们中的大多数人建议使用 ThreadSafeClientConnManager 但我的应用程序必须登录某个主机 登录表单页面 以便 HttpClient 获得底
  • 删除 JFX 中选项卡后面的灰色背景

    So is there any way to remove the gray area behind the tab s 我尝试过用 CSS 来做到这一点 但没有找到方法 要设置 tabpane 标题的背景颜色 请在 CSS 文件中写入 t
  • Collections.sort(list) 和 list.sort(Comparator) 之间的区别

    有什么理由让我应该选择Collections sort list 方法而不是简单地调用list sort 内部Collections sort只是调用sort的方法List无论如何 上课 令人惊讶的是几乎每个人都告诉我使用Collectio
  • spring data jpa复合键重复键记录插入导致更新

    我有一个具有复合键的实体 我试图通过使用 spring data jpa 存储库到 mysql 数据库来持久化它 如下所示 Embeddable public class MobileVerificationKey implements S
  • 在实现使用原始类型的接口时如何避免警告?

    我正在实施流程工厂 http help eclipse org ganymede index jsp topic org eclipse platform doc isv reference api org eclipse debug co
  • Java/MongoDB 按日期查询

    我将一个值作为 java util Date 存储在我的集合中 但是当我查询以获取两个特定日期之间的值时 我最终得到的值超出了范围 这是我的代码 插入 BasicDBObject object new BasicDBObject objec
  • 如何建立与 FileZilla Server 1.2.0 的 FTPS 数据连接

    使用 Apache commons net 的 Java FTPSClient 进行会话恢复是一个已知问题 会话恢复是 FTPS 服务器数据连接所需的一项安全功能 Apache FTPSClient 不支持会话恢复 并且 JDK API 使
  • mybatis:使用带有 XML 配置的映射器接口作为全局参数

    我喜欢使用 XML 表示法来指定全局参数 例如连接字符串 我也喜欢 Mapper 注释 当我尝试将两者结合起来时 我得到这个例外 https stackoverflow com questions 4263832 type interfac
  • java中void的作用是什么?

    返回类型 方法返回值的数据类型 如果方法不返回值 则返回 void http download oracle com javase tutorial java javaOO methods html http download oracle

随机推荐

  • 获取手机方向,但将屏幕方向固定为纵向

    我想要获得手机方向 但将屏幕方向保持为纵向 因此 无论用户将手机转向横向还是纵向 视图都保持不变 但我可以知道它是转向横向还是纵向 将活动设置为android screenOrientation 肖像 将解决这两个问题 但我无法通过以下方式
  • PHP 检测无用文件或无用代码的工具

    我有一个非常大的 PHP 项目 我认为有很多无用的东西 您是否有一些技巧或工具来检测无用的代码部分或无用的文件 PHP 混乱检测器 PHPMD https phpmd org 可能的错误 次优代码 表达式过于复杂 未使用的参数 方法 属性
  • 无法从源代码构建 qtermwidget

    我正在尝试建立qterm小部件 https github com lxqt qtermwidget来自源头 但它给了我错误 我已经成功构建了lxqt 构建工具 https github com lxqt lxqt build tools 然
  • 如何编辑 Angular CLI 项目的 Service Worker 文件

    我已经添加了 angular pwa使用以下命令打包到我的 Angular CLI 项目ng add angular pwa project project name 所以它变成了一个渐进式 Web 应用程序 这增加了我认识的服务人员 我想
  • Nodejs:如何向浏览器发送可读流

    如果我查询框 REST API 并返回可读流 处理它的最佳方法是什么 怎么发送到浏览器呢 免责声明 我对流和缓冲区很陌生 所以其中一些代码非常理论化 你能在响应中传递readStream并让浏览器处理它吗 或者您是否必须将块流式传输到缓冲区
  • Delphi-如何获取目录中所有文件的列表

    我正在使用 delphi 当我执行 openpicturedialog 时 我想要一个目录中所有文件的列表 即 当执行打开对话框时并且 我从中选择一个文件 我想要 目录中所有文件的列表 所选文件的 您甚至可以建议我从中获取目录名称FileN
  • 当 net462 应用程序引用 netstandard1.5 库时,出现“无法加载文件或程序集”错误。但为什么?

    我试图找出在这个示例项目中我可能做错了什么 当我的net462应用参考文献anetstandard1 5图书馆 该应用程序依赖于 System Collections Immutable 1 3 0 根据 Nuget 的说法 它的目标是 N
  • C++ 释放结构体使用的所有内存

    快速提问 我已经用谷歌搜索并找到了一些答案 但我有点偏执 所以我想确定一下 考虑这种情况 struct CoordLocation float X float Y float Z int main CoordLocation coord n
  • 如何清除货物缓存?

    当我跑步时cargo build 各种库存储在文件夹中 usr local lib rustlib 清除这些库的正确方法是什么 我可以rm手动这些文件 但是这样做正确吗 我注意到 usr local lib rustlib manifest
  • 使用 AWS Kinesis Firehose 写入 S3 存储桶中的特定文件夹

    我希望能够根据数据内的内容将数据发送到 kinesis firehose 例如 如果我发送此 JSON 数据 name John id 345 我想根据 id 过滤数据并将其发送到我的 s3 存储桶的子文件夹 例如 S3 myS3Bucke
  • ViewHolder 在偶数和奇数位置上膨胀布局

    我需要两个布局来使用 ViewHolder 根据列表视图项的偶数和奇数位置进行膨胀 在偶数位置 我需要不同的布局 在奇数位置 另一个具有相同元素但布局不同的布局 然而 我实现了它 它给了我不同位置的随机布局 无论它们的位置如何 需要做什么来
  • 如何制作一个在滑动/滑动时在视图之间翻转的 Android 视图

    我想在我的 Android 应用程序中创建一个视图 通过滑动 滑动在多个视图之间切换 我希望它的行为或多或少类似于 Android 启动器在视图之间切换时的行为 尤其 它应该在滑动时翻转视图 一般来说 滑动会在一个视图和下一个视图之间翻转
  • Java 中的转义双引号[重复]

    这个问题在这里已经有答案了 可能的重复 在Java中 有没有一种方法可以编写字符串文字而不必转义引号 https stackoverflow com questions 3034186 in java is there a way to w
  • HTML - 在表格单元格内启用滚动

    假设我有一个具有固定宽度和高度的表格单元格 并且我的数据超出了单元格的固定尺寸 td width 500 height 300 lots of data that exceeds the dimensions td 我可以在单元格内启用此数
  • 仅当“xxxx”是活动工作表时,“With Worksheets("xxxx")”才有效

    我对 Excel VBA 还很陌生 到目前为止 我已经在这个网站上阅读并学到了很多东西 但还没有找到解决我的问题的方法 作为宏的一部分 我有以下代码 With Worksheets Oracle On error resume next A
  • 如何在 Visual Studio 代码资源管理器文件树结构中添加更多缩进?

    如何在文件树结构中添加更多缩进 它有一点缩进 我想增加更多 就像 NetBeans 一样 检查图像 Go to 文件 gt 首选项 gt 设置并选择 工作台 树 缩进 控制树缩进 以像素为单位 或者 在你的settings json中直接输
  • 如何在R中将向量或列切割成间隔[重复]

    这个问题在这里已经有答案了 我在数据框中有以下列 每行之间的差异为 0 012 s Time 0 0 012 0 024 0 036 0 048 0 060 0 072 0 084 0 096 0 108 我想提出从开始开始增加 0 030
  • 如何禁用 AlertDialog 内的按钮?

    我正在尝试写一个AlertDialog有 3 个按钮 如果不满足特定条件 我希望禁用中间的中性按钮 Code int playerint settings getPlayerInt int monsterint settings getMo
  • PHP 缓存标头覆盖

    我已经在这里找到了 100 多个答案 有很多尝试 但没有任何效果 拥有一个基于 PHP 的网站 除了少数几个文件外 我需要关闭所有 php 文件的缓存 因此 在 htaccess 中 我有以下内容 ExpiresActive On Elim
  • 停止自定义 logback 异步附加程序的正确方法

    我使用 Amazon 的 Java SDK 创建了 Amazon SQS 和 SNS logback 附加程序 基本附加程序使用同步 Java API 但我还通过扩展创建了两者的异步版本ch qos logback classic Asyn