如何为AWS CloudWatch创建一个Quarkus扩展

100 阅读6分钟

为AWS CloudWatch创建一个Quarkus扩展

为AWS CloudWatch创建Quarkus扩展

我们最近遇到的情况是,我们想把我们的Quarkus应用日志记录到AWS CloudWatch上。基本上这需要一些时间,但不是什么大问题。添加一个CloudWatch的依赖关系,创建一个日志处理程序,通过提供的AWS CloudWatch API将日志推送到CloudWatch。但如果你想和别人分享呢?当然,你可以把它作为项目的一部分放在GitHub上,这样别人就可以复制和粘贴它,但这并不是与他人分享代码的最优雅的方式。

这就是为什么我们实现了Quarkus扩展,这样其他人可以更容易地使用它,而不需要重新发明轮子或需要复制粘贴东西。如何做到这一点呢?在这里,Quarkiverse Hub就出现了。Quarkiverse是一个GitHub组织,开发者可以在这里托管并与他人分享他们的扩展。在Quarkiverse中托管扩展,而不是用自己做所有事情的老方法,你可以免费得到几个好处。通过使用Quarkiverse,你不需要构建工件,用Sonatype Nexus Manager(或类似的东西)发布它,也不需要在Maven Central和其他资源库中发布它。Quarkiverse自带所有这些东西,所以你可以专注于实现扩展本身。下面的文章描述了初始化、实现和分享CloudWatch Quarkus扩展所需要做的事情。

如果你想使用Quarkiverse在Hub上发布你的扩展(这是我们的建议),并利用所有使用它的优势,你只需要在quarkusio/quarkus GitHub组织中打开一个新的扩展建议问题。这样做,大部分的要求都已经满足了,因为会有一个模板为你生成,你只需要实现你的扩展代码。如果你使用一个现有的项目作为模板,有一些要求你需要照顾到。为了自动发布扩展和发布文档,Quarkiverse组织下的项目有一些规则需要遵循。

  • 扩展库应该被命名为quarkus-

  • Quarkiverse扩展必须属于io.quarkiverse.组Id

  • 根 pom.xml必须继承 io.quarkiverse:quarkiverse-parent

  • 一个Quarkiverse扩展包含以下文件夹和文件:

    • 部署

    • 运行时间

    • 集成-测试

    • 文档

    • pom.xml

    • 许可证

    • 阅读手册

这篇文章只涉及运行时和部署内容,因为其他东西是可选的,已经由项目模板生成,甚至是重要的,但当你想了解如何创建Quarkus扩展时,这不是最重要和实用的东西。让我们从部署部分开始。它包含了Quarkus扩展的初始化所需的类。如果没有这个初始化类,当启动你的Quarkus应用程序时,你的扩展将无法被识别。

class LoggingCloudwatchProcessor {

    @BuildStep
    FeatureBuildItem feature() {
        return new FeatureBuildItem("logging-cloudwatch");
    }

    @BuildStep
    @Record(ExecutionTime.RUNTIME_INIT)
    LogHandlerBuildItem addCloudwatchLogHandler(final LoggingCloudWatchConfig config,
            final LoggingCloudWatchHandlerValueFactory cloudWatchHandlerValueFactory) {
        return new LogHandlerBuildItem(cloudWatchHandlerValueFactory.create(config));
    }
}

在上面的片段中,你可以看到一个feature()方法,它被@BuildStep注解并返回一个新的FeatureBuildItem。它暴露了在应用程序启动过程中显示在日志中的功能名称(logging-cloudwatch)。第二个方法*addCloudWatchHandler()*初始化了由LoggingCloudWatchConfig和LoggingCloudWatchHandlerValueFactory类提供的扩展运行时配置。幸运的是,有一个LogHandlerBuildItem提供,所以我们可以通过添加我们自己的实现来覆盖现有的日志处理器。还有很多其他的BuildItems提供,所以如果你想创建自己的扩展,绝对值得一看。这个方法的参数是一个配置类,将在下面的片段中描述。

@ConfigRoot(phase = ConfigPhase.RUN_TIME, name = "log.cloudwatch")
public class LoggingCloudWatchConfig {

    @ConfigItem(defaultValue = "true")
    boolean enabled;

    @ConfigItem
    public String region;

    // ...
}

LoggingCloudWatchConfig在扩展本身和使用该扩展的Quarkus应用程序之间建立了桥梁。它将Quarkus应用程序中的application.properties条目与我们的扩展相结合。这意味着通过这个类你可以定义application.properties文件中的属性,并使扩展可以从外部配置。@ConfigRoot定义了application.properties中属性的前缀,@ConfigItems是后缀。我们用这个类接受的一个application.properties条目是log.cloudwatch.enabled,例如。

除了LoggingCloudWatchConfig,还有一个*addCloudwatchLogHandler()*方法的参数。它是相应的工厂类。

@Recorder
public class LoggingCloudWatchHandlerValueFactory {

    public RuntimeValue<Optional<Handler>> create(final LoggingCloudWatchConfig config) {
        if (!config.enabled) {
            return new RuntimeValue<>(Optional.empty());
        }

        AWSLogsClientBuilder clientBuilder = AWSLogsClientBuilder.standard();
        clientBuilder.setCredentials(new CloudWatchCredentialsProvider(config));

        // …

        AWSLogs awsLogs = clientBuilder.build();

        // …

        LoggingCloudWatchHandler handler = new LoggingCloudWatchHandler(awsLogs, config.logGroup.get(),
                config.logStreamName.get(), token);
        // …

        return new RuntimeValue<>(Optional.of(handler));
    }
}

LoggingCloudWatchHandlerValueFactory是扩展的实际业务逻辑之间的粘合剂:处理应用程序的日志并将这些日志放到AWS和前面提到的application.properties文件的配置。正如你在*create()*方法中看到的,配置项被检查并用于初始化CloudWatch连接。

现在,我们已经通过添加application.properties条目使扩展用户可以配置扩展,暴露了扩展名称,并向处理程序类提供了配置,该处理程序创建了将日志信息放入AWS CloudWatch所需的AWS CloudWatch对象,我们只需要添加一个缺失的部分。日志处理程序本身。在上面的片段中,在LoggingCloudWatchHandlerValueFactory中,我们已经创建了它,并将其作为RuntimeValue返回,我们在LoggingCloudwatchProcessor类中使用。这就是覆盖现有默认日志处理程序所需的调用链。

class LoggingCloudWatchHandler extends Handler {

    private AWSLogs awsLogs;
    private String logStreamName;
    private String logGroupName;
    private String sequenceToken;

    // ...

    LoggingCloudWatchHandler(AWSLogs awsLogs, String logGroup, String logStreamName, String token) {
        this.logGroupName = logGroup;
        this.awsLogs = awsLogs;
        this.logStreamName = logStreamName;
        this.sequenceToken = token;
    }

    @Override
    public void publish(LogRecord record) {

        // ...

        InputLogEvent logEvent = new InputLogEvent()
                .withMessage(body)
                .withTimestamp(System.currentTimeMillis());
        awsLogs.putLogEvents(request);
    }
}

这个日志处理程序是一个java.util.LogHandler,它将LogRecord对象作为发布方法的参数,在应用程序中写入日志时将会被调用。例如像log.info("I Love Open Source!");。如果配置正确,这个日志处理程序将在写入日志时被调用。由于我们想把日志信息放到AWS CloudWatch中,我们需要添加这样做的逻辑。因此,我们创建一个InputLogEvent并调用putLogEvents(),将日志信息放到CloudWatch中。这基本上就是了。

本文中的片段有点简短,但基本上这就是扩展包含的内容。

让我们把它总结一下。有一个初始化扩展的处理器类,一个使扩展可配置的配置类,一个接受这些配置并创建AWS CloudWatch连接的值工厂类,以及一个将每个日志消息推送到CloudWatch的自定义LogHandler类。

做完所有这些事情后,唯一缺少的就是发布扩展的版本。这可以通过打开一个Pull Request来完成,它可以更新*.github文件夹中的project.yml文件的当前版本和下一个版本*条目。合并该拉动请求后,会触发一些GitHub动作,将你的新版本带到Maven中心,最后其他人也可以使用你的扩展 :-)

总结

正如你所看到的,创建、实现和与他人分享Quarkus扩展其实非常容易。因此,如果你有一个对社区有用的扩展想法,请随时在quarkusio/quarkus GitHub问题部分创建一个新的扩展建议问题来提出你的想法 :-)