Logstash【从无到有从有到无】【L24】贡献了Java插件

目录

1.贡献了Java插件

1.1.流程概述

1.2.如何编写Java输入插件

1.2.1.设置环境

1.2.2.代码插件

1.2.3.打包和部署

1.2.4.与Java输入插件运行Logstash

1.2.5.反馈

1.3.如何编写一个Java编解码器插件

1.3.1.设置环境

1.3.2.代码插件

1.3.2.打包和部署

1.3.3.运行Logstash与Java编解码器插件

1.3.4.反馈

1.4.如何编写一个Java插件过滤器

1.4.1.设置环境

1.4.2.代码插

1.4.3.打包和部署

1.4.4.运行Logstash与Java插件过滤器

1.4.5.反馈

1.5.如何编写一个Java插件输出

1.5.1.设置环境

1.5.2.代码插件

1.5.3.打包和部署

1.5.4.与Java插件输出运行Logstash

1.5.5.反馈


1.贡献了Java插件

现在,您可以编写自己的Java插件以与Logstash一起使用。我们提供了说明和GitHub示例,以帮助您起步。

Logstash对Java插件的本机支持包括几个组件,包括:

  • Java执行引擎的扩展,以支持在Logstash管道中运行Java插件
  • 用于开发Java插件的API。API在co.elastic.logstash.api包装中。如果Java插件引用该包外部的类或API接口的特定具体实现,则它可能会损坏。API包之外的类的实现可能随时更改。
  • Logstash中用于自动打包和部署Java插件的工具。

1.1.流程概述

步骤如下:

  1. 选择您要创建的插件类型:输入,编解码器,过滤器或输出。
  2. 设置您的环境。
  3. 对插件进行编码。
  4. 打包并部署插件。
  5. 使用新插件运行Logstash。

让我们开始

这是示例存储库:

以下是说明:

1.2.如何编写Java输入插件

要为Logstash开发新的Java输入,您需要编写一个符合Logstash Java Inputs API的新Java类,将其打包,并使用logstash-plugin实用程序进行安装。我们将完成所有这些步骤。

1.2.1.设置环境

复制的例子回购

首先复制示例输入插件。插件API当前是Logstash代码库的一部分,因此您必须具有可用的本地副本。您可以使用以下git命令获取Logstash代码库的副本:

git clone --branch <branch_name> --single-branch https://github.com/elastic/logstash.git <target_folder>

branch_name应当对应于包含Java插件API的优选版本的版本Logstash的。

7.1 Logstash代码库的及更高版本的分支中提供了Java插件API的GA版本。

target_folder为您的Logstash代码库的本地副本指定。如果不指定target_folder,则默认为logstash 当前文件夹下的新文件夹。

生成.jar文件

获得Logstash代码库的相应修订版的副本之后,需要对其进行编译以生成包含Java插件API的.jar文件。从Logstash代码库的根目录($ LS_HOME),您可以使用./gradlew assemble(或gradlew.bat assemble如果您在Windows上运行)对其进行编译。这将产生 $LS_HOME/logstash-core/build/libs/logstash-core-x.y.z.jarwhere x,,y并 z引用Logstash的版本。

成功编译Logstash后,您需要告诉Java插件在哪里可以找到该logstash-core-x.y.z.jar文件。gradle.properties在插件项目的根文件夹中创建一个名为的新文件 。该文件应该只有一行:

LOGSTASH_CORE_PATH=<target_folder>/logstash-core

这里target_folder是当地的Logstash代码库的副本的根文件夹。

1.2.2.代码插件

示例输入插件在终止之前会生成可配置数量的简单事件。让我们在示例输入中查看主类。

@LogstashPlugin(name="java_input_example")
public class JavaInputExample implements Input {

    public static final PluginConfigSpec<Long> EVENT_COUNT_CONFIG =
            PluginConfigSpec.numSetting("count", 3);

    public static final PluginConfigSpec<String> PREFIX_CONFIG =
            PluginConfigSpec.stringSetting("prefix", "message");

    private String id;
    private long count;
    private String prefix;
    private final CountDownLatch done = new CountDownLatch(1);
    private volatile boolean stopped;


    public JavaInputExample(String id, Configuration config, Context context) {
            this.id = id;
        count = config.get(EVENT_COUNT_CONFIG);
        prefix = config.get(PREFIX_CONFIG);
    }

    @Override
    public void start(Consumer<Map<String, Object>> consumer) {
        int eventCount = 0;
        try {
            while (!stopped && eventCount < count) {
                eventCount++;
                consumer.accept.push(Collections.singletonMap("message",
                        prefix + " " + StringUtils.center(eventCount + " of " + count, 20)));
            }
        } finally {
            stopped = true;
            done.countDown();
        }
    }

    @Override
    public void stop() {
        stopped = true; // set flag to request cooperative stop of input
    }

    @Override
    public void awaitStop() throws InterruptedException {
        done.await(); // blocks until input has stopped
    }

    @Override
    public Collection<PluginConfigSpec<?>> configSchema() {
        return Arrays.asList(EVENT_COUNT_CONFIG, PREFIX_CONFIG);
    }

    @Override
    public String getId() {
        return this.id;
    }
}

让我们逐步学习该类的每个部分。

类声明

@LogstashPlugin(name="java_input_example")
public class JavaInputExample implements Input {

有关类声明的注意事项:

  • 所有Java插件都必须带有@LogstashPlugin注释。另外:

    • name注解的属性必须提供,并定义插件的名称,因为它将在Logstash管道定义中使用。例如,此输入将在Logstash管道定义的输入部分中引用为input { java_input_example => { .... } }
    • name属性的值必须与该类的名称匹配,但不包括大小写和下划线。
  • 该类必须实现co.elastic.logstash.api.Input接口。
  • 不得在org.logstashco.elastic.logstash软件包中创建Java插件,以防止与Logstash本身中的类发生潜在冲突。

插件设置

下面的代码片段包含设置定义和引用它的方法。

public static final PluginConfigSpec<Long> EVENT_COUNT_CONFIG =
        PluginConfigSpec.numSetting("count", 3);

public static final PluginConfigSpec<String> PREFIX_CONFIG =
        PluginConfigSpec.stringSetting("prefix", "message");

@Override
public Collection<PluginConfigSpec<?>> configSchema() {
    return Arrays.asList(EVENT_COUNT_CONFIG, PREFIX_CONFIG);
}

PluginConfigSpec级允许开发人员指定一个插件支持完整的设置名称,数据类型,弃用状态,需要的状态,并且默认值设置。在此示例中,count设置定义将要生成的事件数,并且prefix设置定义要包括在事件字段中的可选前缀。都不需要设置,如果未明确设置,则设置分别默认为3和 message

configSchema方法必须返回插件支持的所有设置的列表。在Java插件项目的未来阶段,Logstash执行引擎将验证是否存在所有必需的设置,并且不存在不支持的设置。

构造函数和初始化

private String id;
private long count;
private String prefix;

public JavaInputExample(String id, Configuration config, Context context) {
    this.id = id;
    count = config.get(EVENT_COUNT_CONFIG);
    prefix = config.get(PREFIX_CONFIG);
}

所有Java输入插件都必须具有带Stringid和 Configurationand Context参数的构造函数。这是构造函数,将用于在运行时实例化它们。所有插件设置的检索和验证都应在此构造方法中进行。在此示例中,将检索两个插件设置的值并将其存储在局部变量中,以供以后在该start方法中使用。

任何其他初始化也可能在构造函数中发生。如果在配置或初始化输入插件时遇到任何无法恢复的错误,则应抛出描述性异常。该异常将被记录,并阻止Logstash启动。

启动方法

@Override
public void start(Consumer<Map<String, Object>> consumer) {
    int eventCount = 0;
    try {
        while (!stopped && eventCount < count) {
            eventCount++;
            consumer.accept.push(Collections.singletonMap("message",
                    prefix + " " + StringUtils.center(eventCount + " of " + count, 20)));
        }
    } finally {
        stopped = true;
        done.countDown();
    }
}

start方法在输入中开始事件产生循环。输入是灵活的,可以通过许多不同的机制来产生事件,包括:

  • 拉机制,例如外部数据库的定期查询
  • 推送机制,例如从客户端发送到本地网络端口的事件
  • 定时计算,例如心跳
  • 产生有用事件流的任何其他机制。事件流可以是有限的也可以是无限的。如果输入产生无限的事件流,则此方法应循环执行,直到通过该stop方法发出停止请求为止。如果输入产生有限的事件流,则在产生流中的最后一个事件或发出停止请求(以先到者为准)时,此方法应终止。

应将事件构造为该方法的实例,Map<String, Object>并通过该Consumer<Map<String, Object>>.accept()方法将其推入事件管道 。为了减少分配和GC压力,输入可以通过修改两次调用之间的字段来重用同一地图实例,Consumer<Map<String, Object>>.accept()因为事件管道将基于地图数据的副本创建事件。

Stop和awaitStop方法

private final CountDownLatch done = new CountDownLatch(1);
private volatile boolean stopped;

@Override
public void stop() {
    stopped = true; // set flag to request cooperative stop of input
}

@Override
public void awaitStop() throws InterruptedException {
    done.await(); // blocks until input has stopped
}

stop方法通知输入停止产生事件。停止机制可以通过遵守API合同的任何方式来实现,尽管 volatile boolean标志在许多用例中效果很好。

输入异步和协作停止。使用该awaitStop方法进行阻塞,直到输入完成停止过程为止。注意,此方法应该没有信号输入到停止的stop方法一样。尽管CountDownLatch可以在许多用例中很好地发挥作用,但是可以通过遵守API合同的任何方式来实现awaitStop机制。

getId method

@Override
public String getId() {
    return id;
}

对于输入插件,该getId方法应始终返回实例化时通过其构造函数提供给插件的ID。

单元测试

最后,但同样重要的是,强烈建议进行单元测试。示例输入插件包括一个 示例单元测试,您可以将其用作自己的模板。

1.2.3.打包和部署

Java插件被打包为Ruby gem,以进行依赖关系管理以及与Ruby插件的互操作性。一旦将它们打包为gem,就可以logstash-plugin像Ruby插件一样将它们与实用程序一起安装。因为不需要Java插件开发就不需要Ruby或其工具链知识,所以通过示例Java插件随附的Gradle构建文件中的自定义任务,将Java插件打包为Ruby gem的过程已实现自动化。以下各节描述了如何配置和执行该打包任务以及如何在Logstash中安装打包的Java插件。

配置摇篮的包装任务

以下部分显示build.gradle在示例Java插件随附的文件顶部附近:

// ===========================================================================
// plugin info
// ===========================================================================
group                      'org.logstashplugins' // must match the package of the main plugin class
version                    "${file("VERSION").text.trim()}" // read from required VERSION file
description                = "Example Java filter implementation"
pluginInfo.licenses        = ['Apache-2.0'] // list of SPDX license IDs
pluginInfo.longDescription = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using \$LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
pluginInfo.authors         = ['Elasticsearch']
pluginInfo.email           = ['info@elastic.co']
pluginInfo.homepage        = "http://www.elastic.co/guide/en/logstash/current/index.html"
pluginInfo.pluginType      = "filter"
pluginInfo.pluginClass     = "JavaFilterExample"
pluginInfo.pluginName      = "java_filter_example"
// ===========================================================================

您应该为您的插件配置以上值。

  • version值将自动从VERSION插件代码库根目录中的文件中读取。
  • pluginInfo.pluginType应设置为一个inputfiltercodec,或output
  • pluginInfo.pluginName必须与@LogstashPlugin 主插件类的注释上指定的名称匹配。Gradle打包任务将对此进行验证,如果不匹配,则返回错误。

运行摇篮包装任务

要将插件打包为Ruby gem,需要几个Ruby源文件以及一个gemspec文件和一个文件Gemfile。这些Ruby文件仅用于定义Ruby gem结构,或者在Logstash启动时用于注册Java插件。在运行时事件处理期间不使用它们。Gradle打包任务会根据上一节中配置的值自动生成所有这些文件。

您可以使用以下命令运行Gradle打包任务:

./gradlew gem

对于Windows平台:替换gradlew.bat./gradlew在命令适当。

该任务将在您插件代码库的根目录中生成一个名为gem的文件 logstash-{plugintype}-<pluginName>-<version>.gem

安装在Logstash Java插件

将Java插件打包为Ruby gem之后,可以使用以下命令将其安装在Logstash中:

bin/logstash-plugin install --no-verify --local /path/to/javaPlugin.gem

对于Windows平台:在命令中适当地用反斜杠代替正斜杠。

1.2.4.与Java输入插件运行Logstash

以下是最低的Logstash配置,可用于测试Java输入插件是否已正确安装并运行。

input {
  java_input_example {}
}
output {
  stdout { codec => rubydebug }
}

将上述Logstash配置复制到一个文件,例如java_input.conf。使用以下命令启动Logstash:

bin/logstash -f /path/to/java_input.conf

需要Java执行引擎,这是Logstash 7.0以来的默认执行引擎,因为Ruby执行引擎不支持Java插件。

具有上述配置的预期Logstash输出(不包括初始化)为:

{
      "@version" => "1",
       "message" => "message        1 of 3       ",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ
}
{
      "@version" => "1",
       "message" => "message        2 of 3       ",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ
}
{
      "@version" => "1",
       "message" => "message        3 of 3       ",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ
}

1.2.5.反馈

如果您对Logstash中的Java插件支持有任何反馈,请评论我们的 主要Github问题或在 Logstash论坛中发布

1.3.如何编写一个Java编解码器插件

目前仅Java输入和输出插件支持Java编解码器。它们不适用于Ruby输入或输出插件。

要为Logstash开发新的Java编解码器,请编写一个符合Logstash Java编解码器API的新Java类,将其打包,并使用logstash-plugin实用程序进行安装。我们将完成所有这些步骤。

1.3.1.设置环境

复制的例子回购

首先复制示例编解码器插件。插件API当前是Logstash代码库的一部分,因此您必须具有可用的本地副本。您可以使用以下git命令获取Logstash代码库的副本:

git clone --branch <branch_name> --single-branch https://github.com/elastic/logstash.git <target_folder>

branch_name应当对应于包含Java插件API的优选版本的版本Logstash的。

7.1 Logstash代码库的及更高版本的分支中提供了Java插件API的GA版本。

target_folder为您的Logstash代码库的本地副本指定。如果不指定target_folder,则默认为logstash 当前文件夹下的新文件夹。

生成.jar文件

获得Logstash代码库的相应修订版的副本之后,需要对其进行编译以生成包含Java插件API的.jar文件。从Logstash代码库的根目录($ LS_HOME),您可以使用./gradlew assemble(或gradlew.bat assemble如果您在Windows上运行)对其进行编译。这将产生 $LS_HOME/logstash-core/build/libs/logstash-core-x.y.z.jarwhere x,,y并 z引用Logstash的版本。

成功编译Logstash后,您需要告诉Java插件在哪里可以找到该logstash-core-x.y.z.jar文件。gradle.properties在插件项目的根文件夹中创建一个名为的新文件 。该文件应该只有一行:

LOGSTASH_CORE_PATH=<target_folder>/logstash-core

这里target_folder是当地的Logstash代码库的副本的根文件夹。

1.3.2.代码插件

示例编解码器插件对由可配置的分隔符分隔的消息进行解码,并通过编写由分隔符分隔的字符串表示形式对消息进行编码。例如,如果编解码器被配置有/作为分隔符,输入的文本event1/event2/将被解码成两个单独的事件 message的字段event1event2分别。请注意,这仅是示例编解码器,并未涵盖生产级编解码器应涵盖的所有极端情况。

让我们看一下该编解码器过滤器中的主类:

@LogstashPlugin(name="java_codec_example")
public class JavaCodecExample implements Codec {

    public static final PluginConfigSpec<String> DELIMITER_CONFIG =
            PluginConfigSpec.stringSetting("delimiter", ",");

    private final String id;
    private final String delimiter;

    public JavaCodecExample(final Configuration config, final Context context) {
        this(config.get(DELIMITER_CONFIG));
    }

    private JavaCodecExample(String delimiter) {
        this.id = UUID.randomUUID().toString();
        this.delimiter = delimiter;
    }

    @Override
    public void decode(ByteBuffer byteBuffer, Consumer<Map<String, Object>> consumer) {
        // a not-production-grade delimiter decoder
        byte[] byteInput = new byte[byteBuffer.remaining()];
        byteBuffer.get(byteInput);
        if (byteInput.length > 0) {
            String input = new String(byteInput);
            String[] split = input.split(delimiter);
            for (String s : split) {
                Map<String, Object> map = new HashMap<>();
                map.put("message", s);
                consumer.accept(map);
            }
        }
    }

    @Override
    public void flush(ByteBuffer byteBuffer, Consumer<Map<String, Object>> consumer) {
        // if the codec maintains any internal state such as partially-decoded input, this
        // method should flush that state along with any additional input supplied in
        // the ByteBuffer

        decode(byteBuffer, consumer); // this is a simplistic implementation
    }

    @Override
    public void encode(Event event, OutputStream outputStream) throws IOException {
        outputStream.write((event.toString() + delimiter).getBytes(Charset.defaultCharset()));
    }

    @Override
    public Collection<PluginConfigSpec<?>> configSchema() {
        // should return a list of all configuration options for this plugin
        return Collections.singletonList(DELIMITER_CONFIG);
    }

    @Override
    public Codec cloneCodec() {
        return new JavaCodecExample(this.delimiter);
    }

    @Override
    public String getId() {
        return this.id;
    }

}

让我们逐步学习该类的每个部分。

类声明

@LogstashPlugin(name="java_codec_example")
public class JavaCodecExample implements Codec {

有关类声明的注意事项:

  • 所有Java插件都必须带有@LogstashPlugin注释。另外:

    • name注解的属性必须提供,并定义插件的名称,因为它将在Logstash管道定义中使用。例如,此编解码器将在Logstash管道定义中适当输入或输出的编解码器部分中引用为codec => java_codec_example { }
    • name属性的值必须与该类的名称匹配,但不包括大小写和下划线。
  • 该类必须实现co.elastic.logstash.api.Codec接口。
  • 不得在org.logstashco.elastic.logstash软件包中创建Java插件,以防止与Logstash本身中的类发生潜在冲突。

插件设置

下面的代码片段包含设置定义和引用它的方法:

public static final PluginConfigSpec<String> DELIMITER_CONFIG =
        PluginConfigSpec.stringSetting("delimiter", ",");

@Override
public Collection<PluginConfigSpec<?>> configSchema() {
    return Collections.singletonList(DELIMITER_CONFIG);
}

PluginConfigSpec级允许开发人员指定一个插件支持完整的设置名称,数据类型,弃用状态,需要的状态,并且默认值设置。在此示例中,该delimiter设置定义了编解码器将在其上分割事件的定界符。这不是必需的设置,如果未显式设置,则默认值为,

configSchema方法必须返回插件支持的所有设置的列表。Logstash执行引擎将验证是否存在所有必需的设置,并且不存在不支持的设置。

构造函数和初始化

private final String id;
private final String delimiter;

public JavaCodecExample(final Configuration config, final Context context) {
    this(config.get(DELIMITER_CONFIG));
}

private JavaCodecExample(String delimiter) {
    this.id = UUID.randomUUID().toString();
    this.delimiter = delimiter;
}

所有Java编解码器插件都必须具有带Configuration和 Context参数的构造函数。这是构造函数,将用于在运行时实例化它们。所有插件设置的检索和验证都应在此构造方法中进行。在此示例中,用于定界事件的定界符从其设置中检索并存储在局部变量中,以便以后可以在decodeencode方法中使用。编解码器的ID初始化为随机UUID(大多数编解码器都应这样做),并且encoder初始化局部变量以使用指定的字符集进行编码和解码。

任何其他初始化也可能在构造函数中发生。如果在编解码器插件的配置或初始化过程中遇到任何无法恢复的错误,则应抛出描述性异常。该异常将被记录,并阻止Logstash启动。

编解码器的方法

@Override
public void decode(ByteBuffer byteBuffer, Consumer<Map<String, Object>> consumer) {
    // a not-production-grade delimiter decoder
    byte[] byteInput = new byte[byteBuffer.remaining()];
    byteBuffer.get(byteInput);
    if (byteInput.length > 0) {
        String input = new String(byteInput);
        String[] split = input.split(delimiter);
        for (String s : split) {
            Map<String, Object> map = new HashMap<>();
            map.put("message", s);
            consumer.accept(map);
        }
    }
}

@Override
public void flush(ByteBuffer byteBuffer, Consumer<Map<String, Object>> consumer) {
    // if the codec maintains any internal state such as partially-decoded input, this
    // method should flush that state along with any additional input supplied in
    // the ByteBuffer

    decode(byteBuffer, consumer); // this is a simplistic implementation
}

@Override
public void encode(Event event, OutputStream outputStream) throws IOException {
    outputStream.write((event.toString() + delimiter).getBytes(Charset.defaultCharset()));
}

decodeflushencode方法提供编解码器的核心功能。输入可以使用编解码器将字节序列或字节流解码为事件,也可以使用输出将事件编码为字节序列。

decode方法对指定事件进行解码ByteBuffer,并将其传递给提供的Consumer。输入必须提供一个ByteBuffer准备好读取的信号,并byteBuffer.position()指示要读取的下一个位置以及byteBuffer.limit()指示缓冲区中不安全读取的第一个字节。编解码器必须确保byteBuffer.position()将最后读取的位置反映在将控制权返回给输入之前。然后,输入负责通过恢复写入之前byteBuffer.clear()byteBuffer.compact()之前使缓冲区返回写入模式 。在上面的示例中,该decode方法仅在指定的分隔符上拆分传入的字节流。生产级编解码器,例如 java-line 不会做出简化的假设,即所提供的字节流的末尾与事件的末尾相对应。

应将事件构造为该方法的实例,Map<String, Object>并通过该Consumer<Map<String, Object>>.accept()方法将其推入事件管道 。为了减少分配和GC的压力,编解码器可以通过修改两次调用之间的字段来重用同一映射实例,Consumer<Map<String, Object>>.accept()因为事件管道将基于映射数据的副本创建事件。

flush方法与该decode方法协同工作,以解码来自指定对象的所有剩余事件 ByteBuffer以及在先前调用该decode方法之后可能残留的任何内部状态。作为编解码器可能维护的内部状态的示例,请考虑event1/event2/event3使用定界符为的字节输入流 /。由于缓冲或其他原因,输入可能会提供部分字节流,例如 event1/eve 编解码器的decode方法。在这种情况下,编解码器可以保存eve 第二个事件的开头三个字符,而不必假设提供的字节流在事件边界处结束。如果下一次调用decode 提供了nt2/ev字节,则编解码器将在保存的文件之前添加eve个字节以产生完整event2事件,然后在提供该事件的其余ev字节时保存其余字节以进行解码。调用会向 flush编解码器发信号,告知所提供的字节代表事件流的结尾,所有剩余字节应解码为事件。flush上面的示例是一种简化的实现,它在对的调用中不维护有关部分提供的字节流的任何状态decode

encode方法将事件编码为字节序列,然后将其写入指定的OutputStream。因为一个编解码器实例在Logstash管道的输出级在所有流水线工人共享,编解码器应该 不会在调用他们的保留状态encode的方法。

cloneCodec方法

@Override
public Codec cloneCodec() {
    return new JavaCodecExample(this.delimiter);
}

cloneCodec方法应返回编解码器的相同实例,但其ID除外。因为编解码器可能在其decode方法的调用中处于有状态状态,所以多线程的输入插件应通过cloneCodec每个线程的方法使用每个编解码器的单独实例。因为一个编解码器实例在Logstash管道的输出级在所有流水线工人共享,编解码器应该不会在调用他们的保留状态encode的方法。在上面的示例中,使用相同的定界符但使用不同的ID克隆了编解码器。

的getId方法

@Override
public String getId() {
    return id;
}

对于编解码器插件,该getId方法应始终返回实例化时设置的ID。这通常是一个UUID。

单元测试

最后,但同样重要的是,强烈建议进行单元测试。示例编解码器插件包括一个 示例单元测试,您可以将其用作自己的模板。

1.3.2.打包和部署

Java插件被打包为Ruby gem,以进行依赖关系管理以及与Ruby插件的互操作性。一旦将它们打包为gem,就可以logstash-plugin像Ruby插件一样将它们与实用程序一起安装。因为不需要Java插件开发就不需要Ruby或其工具链知识,所以通过示例Java插件随附的Gradle构建文件中的自定义任务,将Java插件打包为Ruby gem的过程已实现自动化。以下各节描述了如何配置和执行该打包任务以及如何在Logstash中安装打包的Java插件。

配置摇篮的包装任务

以下部分显示build.gradle在示例Java插件随附的文件顶部附近:

// ===========================================================================
// plugin info
// ===========================================================================
group                      'org.logstashplugins' // must match the package of the main plugin class
version                    "${file("VERSION").text.trim()}" // read from required VERSION file
description                = "Example Java filter implementation"
pluginInfo.licenses        = ['Apache-2.0'] // list of SPDX license IDs
pluginInfo.longDescription = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using \$LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
pluginInfo.authors         = ['Elasticsearch']
pluginInfo.email           = ['info@elastic.co']
pluginInfo.homepage        = "http://www.elastic.co/guide/en/logstash/current/index.html"
pluginInfo.pluginType      = "filter"
pluginInfo.pluginClass     = "JavaFilterExample"
pluginInfo.pluginName      = "java_filter_example"
// ===========================================================================

您应该为您的插件配置以上值。

  • version值将自动从VERSION插件代码库根目录中的文件中读取。
  • pluginInfo.pluginType应设置为一个inputfiltercodec,或output
  • pluginInfo.pluginName必须与@LogstashPlugin 主插件类的注释上指定的名称匹配。Gradle打包任务将对此进行验证,如果不匹配,则返回错误。

运行摇篮包装任务

要将插件打包为Ruby gem,需要几个Ruby源文件以及一个gemspec文件和一个文件Gemfile。这些Ruby文件仅用于定义Ruby gem结构,或者在Logstash启动时用于注册Java插件。在运行时事件处理期间不使用它们。Gradle打包任务会根据上一节中配置的值自动生成所有这些文件。

您可以使用以下命令运行Gradle打包任务:

./gradlew gem

对于Windows平台:替换gradlew.bat./gradlew在命令适当。

该任务将在您插件代码库的根目录中生成一个名为gem的文件 logstash-{plugintype}-<pluginName>-<version>.gem

安装在Logstash Java插件

将Java插件打包为Ruby gem之后,可以使用以下命令将其安装在Logstash中:

bin/logstash-plugin install --no-verify --local /path/to/javaPlugin.gem

对于Windows平台:在命令中适当地用反斜杠代替正斜杠。

1.3.3.运行Logstash与Java编解码器插件

要测试插件,请使用以下命令启动Logstash:

echo "foo,bar" | bin/logstash -e 'input { java_stdin { codec => java_codec_example } } }'

需要Java执行引擎,这是Logstash 7.0以来的默认执行引擎,因为Ruby执行引擎不支持Java插件。

具有上述配置的预期Logstash输出(不包括初始化)为:

{
      "@version" => "1",
       "message" => "foo",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ,
          "host" => "<yourHostName>"
}
{
      "@version" => "1",
       "message" => "bar\n",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ,
          "host" => "<yourHostName>"
}

1.3.4.反馈

如果您对Logstash中的Java插件支持有任何反馈,请评论我们的 主要Github问题或在 Logstash论坛中发布

1.4.如何编写一个Java插件过滤器

要为Logstash开发新的Java过滤器,请编写一个符合Logstash Java过滤器API的新Java类,将其打包,然后使用logstash-plugin实用程序进行安装。我们将完成所有这些步骤。

1.4.1.设置环境

复制的例子回购

首先复制示例过滤器插件。插件API当前是Logstash代码库的一部分,因此您必须具有可用的本地副本。您可以使用以下git命令获取Logstash代码库的副本:

git clone --branch <branch_name> --single-branch https://github.com/elastic/logstash.git <target_folder>

branch_name应当对应于包含Java插件API的优选版本的版本Logstash的。

7.1 Logstash代码库的及更高版本的分支中提供了Java插件API的GA版本。

target_folder为您的Logstash代码库的本地副本指定。如果不指定target_folder,则默认为logstash 当前文件夹下的新文件夹。

生成.jar文件

获得Logstash代码库的相应修订版的副本之后,需要对其进行编译以生成包含Java插件API的.jar文件。从Logstash代码库的根目录($ LS_HOME),您可以使用./gradlew assemble(或gradlew.bat assemble如果您在Windows上运行)对其进行编译。这将产生 $LS_HOME/logstash-core/build/libs/logstash-core-x.y.z.jarwhere x,,y并 z引用Logstash的版本。

成功编译Logstash后,您需要告诉Java插件在哪里可以找到该logstash-core-x.y.z.jar文件。gradle.properties在插件项目的根文件夹中创建一个名为的新文件 。该文件应该只有一行:

LOGSTASH_CORE_PATH=<target_folder>/logstash-core

这里target_folder是当地的Logstash代码库的副本的根文件夹。

1.4.2.代码插

示例过滤器插件允许您在每个将被逆转的事件中配置一个字段。例如,如果将过滤器配置为反转该day_of_week字段,则事件day_of_week: "Monday"将转换为day_of_week: "yadnoM"。让我们看一下示例过滤器中的主类:

@LogstashPlugin(name = "java_filter_example")
public class JavaFilterExample implements Filter {

    public static final PluginConfigSpec<String> SOURCE_CONFIG =
            PluginConfigSpec.stringSetting("source", "message");

    private String id;
    private String sourceField;

    public JavaFilterExample(String id, Configuration config, Context context) {
        this.id = id;
        this.sourceField = config.get(SOURCE_CONFIG);
    }

    @Override
    public Collection<Event> filter(Collection<Event> events, FilterMatchListener matchListener) {
        for (Event e : events) {
            Object f = e.getField(sourceField);
            if (f instanceof String) {
                e.setField(sourceField, StringUtils.reverse((String)f));
                matchListener.filterMatched(e);
            }
        }
        return events;
    }

    @Override
    public Collection<PluginConfigSpec<?>> configSchema() {
        return Collections.singletonList(SOURCE_CONFIG);
    }

    @Override
    public String getId() {
        return this.id;
    }
}

让我们逐步学习该类的每个部分。

类声明

@LogstashPlugin(name = "java_filter_example")
public class JavaFilterExample implements Filter {

有关类声明的注意事项:

  • 所有Java插件都必须带有@LogstashPlugin注释。另外:

    • name注解的属性必须提供,并定义插件的名称,因为它将在Logstash管道定义中使用。例如,该过滤器将在Logstash管道定义的过滤器部分中引用为filter { java_filter_example => { .... } }
    • name属性的值必须与该类的名称匹配,但不包括大小写和下划线。
  • 该类必须实现co.elastic.logstash.api.Filter接口。
  • 不得在org.logstashco.elastic.logstash软件包中创建Java插件,以防止与Logstash本身中的类发生潜在冲突。

插件设置

下面的代码片段包含设置定义和引用它的方法:

public static final PluginConfigSpec<String> SOURCE_CONFIG =
        PluginConfigSpec.stringSetting("source", "message");

@Override
public Collection<PluginConfigSpec<?>> configSchema() {
    return Collections.singletonList(SOURCE_CONFIG);
}

PluginConfigSpec级允许开发人员指定一个插件支持完整的设置名称,数据类型,弃用状态,需要的状态,并且默认值设置。在此示例中,该source设置定义了每个将被反转的事件中的字段名称。这不是必需的设置,如果未显式设置,则默认值为message

configSchema方法必须返回插件支持的所有设置的列表。在Java插件项目的未来阶段,Logstash执行引擎将验证是否存在所有必需的设置,并且不存在不支持的设置。

构造函数和初始化

private String id;
private String sourceField;

public JavaFilterExample(String id, Configuration config, Context context) {
    this.id = id;
    this.sourceField = config.get(SOURCE_CONFIG);
}

所有Java过滤器插件必须具有带Stringid和a Configuration和自Context变量的构造函数。这是构造函数,将用于在运行时实例化它们。所有插件设置的检索和验证都应在此构造方法中进行。在此示例中,从每个事件的设置中检索要反转的字段的名称,并将其存储在局部变量中,以便以后可以在该filter方法中使用。

任何其他初始化也可能在构造函数中发生。如果在过滤器插件的配置或初始化过程中遇到任何无法恢复的错误,则应抛出描述性异常。该异常将被记录,并阻止Logstash启动。

筛选方法

@Override
public Collection<Event> filter(Collection<Event> events, FilterMatchListener matchListener) {
    for (Event e : events) {
        Object f = e.getField(sourceField);
        if (f instanceof String) {
            e.setField(sourceField, StringUtils.reverse((String)f));
            matchListener.filterMatched(e);
        }
    }
    return events;

最后,我们介绍filter了Logstash执行引擎在事件流通过事件处理管道时对一批事件调用的方法。events参数中提供了要过滤的事件,该方法应返回已过滤事件的集合。过滤器在流过管道时可以对事件执行各种操作,包括:

  • 突变-事件中的字段可以通过过滤器添加,删除或更改。对于在事件上执行各种丰富化的过滤器,这是最常见的情况。在这种情况下,由于events集合中的事件在原地发生了突变,因此传入的集合可能会未经修改地返回。
  • 删除-可以通过过滤器从事件管道中删除事件,以便后续的过滤器和输出不会接收到它们。在这种情况下,要删除的事件必须在返回之前从已过滤事件的集合中删除。
  • 创建-过滤器可以将新事件插入事件管道,只有随后的过滤器和输出才能看到新事件。在这种情况下,新事件必须在返回之前添加到已过滤事件的集合中。
  • 观察-事件可以由过滤器通过事件管道不变地传递。这在过滤器基于事件管道中观察到的事件执行外部动作(例如,更新外部缓存)的场景中可能有用。在这种情况下,events由于未进行任何更改,传入的集合可能未经修改就返回。

在上面的示例中,source从每个事件中检索字段的值,如果它是字符串值,则取反。因为每个事件都在适当位置发生了变异,所以events可以返回传入的集合。

matchListener是过滤器用来指示哪些事件“匹配”的机制。过滤器的常见操作(例如add_field和)add_tag仅应用于指定为“匹配”的事件。一些过滤器(例如grok过滤器) 对构成匹配事件的定义有明确的定义,并且只会将匹配事件通知给侦听器。其他过滤器(例如 UUID过滤器) 没有特定的匹配条件,应针对每个过滤的事件通知侦听器。在此示例中,过滤器会向匹配侦听器通知Stringsource字段中具有值并因此可以被反转的任何事件。

的getId方法

@Override
public String getId() {
    return id;
}

对于过滤器插件,该getId方法应始终返回实例化时通过其构造函数提供给插件的ID。

单元测试

最后,但同样重要的是,强烈建议进行单元测试。示例过滤器插件包括一个 示例单元测试,您可以将其用作自己的模板。

1.4.3.打包和部署

Java插件被打包为Ruby gem,以进行依赖关系管理以及与Ruby插件的互操作性。一旦将它们打包为gem,就可以logstash-plugin像Ruby插件一样将它们与实用程序一起安装。因为不需要Java插件开发就不需要Ruby或其工具链知识,所以通过示例Java插件随附的Gradle构建文件中的自定义任务,将Java插件打包为Ruby gem的过程已实现自动化。以下各节描述了如何配置和执行该打包任务以及如何在Logstash中安装打包的Java插件。

配置摇篮的包装任务

以下部分显示build.gradle在示例Java插件随附的文件顶部附近:

// ===========================================================================
// plugin info
// ===========================================================================
group                      'org.logstashplugins' // must match the package of the main plugin class
version                    "${file("VERSION").text.trim()}" // read from required VERSION file
description                = "Example Java filter implementation"
pluginInfo.licenses        = ['Apache-2.0'] // list of SPDX license IDs
pluginInfo.longDescription = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using \$LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
pluginInfo.authors         = ['Elasticsearch']
pluginInfo.email           = ['info@elastic.co']
pluginInfo.homepage        = "http://www.elastic.co/guide/en/logstash/current/index.html"
pluginInfo.pluginType      = "filter"
pluginInfo.pluginClass     = "JavaFilterExample"
pluginInfo.pluginName      = "java_filter_example"
// ===========================================================================

您应该为您的插件配置以上值。

  • version值将自动从VERSION插件代码库根目录中的文件中读取。
  • pluginInfo.pluginType应设置为一个inputfiltercodec,或output
  • pluginInfo.pluginName必须与@LogstashPlugin 主插件类的注释上指定的名称匹配。Gradle打包任务将对此进行验证,如果不匹配,则返回错误。

运行摇篮包装任务

要将插件打包为Ruby gem,需要几个Ruby源文件以及一个gemspec文件和一个文件Gemfile。这些Ruby文件仅用于定义Ruby gem结构,或者在Logstash启动时用于注册Java插件。在运行时事件处理期间不使用它们。Gradle打包任务会根据上一节中配置的值自动生成所有这些文件。

您可以使用以下命令运行Gradle打包任务:

./gradlew gem

对于Windows平台:替换gradlew.bat./gradlew在命令适当。

该任务将在您插件代码库的根目录中生成一个名为gem的文件 logstash-{plugintype}-<pluginName>-<version>.gem

安装在Logstash Java插件

将Java插件打包为Ruby gem之后,可以使用以下命令将其安装在Logstash中:

bin/logstash-plugin install --no-verify --local /path/to/javaPlugin.gem

对于Windows平台:在命令中适当地用反斜杠代替正斜杠。

1.4.4.运行Logstash与Java插件过滤器

以下是最低的Logstash配置,可用于测试Java筛选器插件是否已正确安装并运行。

input {
  generator { message => "Hello world!" count => 1 }
}
filter {
  java_filter_example {}
}
output {
  stdout { codec => rubydebug }
}

将上述Logstash配置复制到一个文件,例如java_filter.conf。使用以下命令启动Logstash:

bin/logstash -f /path/to/java_filter.conf

需要Java执行引擎,这是Logstash 7.0以来的默认执行引擎,因为Ruby执行引擎不支持Java插件。

具有上述配置的预期Logstash输出(不包括初始化)为:

{
      "sequence" => 0,
      "@version" => "1",
       "message" => "!dlrow olleH",
    "@timestamp" => yyyy-MM-ddThh:mm:ss.SSSZ,
          "host" => "<yourHostName>"
}

1.4.5.反馈

如果您对Logstash中的Java插件支持有任何反馈,请评论我们的 主要Github问题或在 Logstash论坛中发布

1.5.如何编写一个Java插件输出

要为Logstash开发新的Java输出,您可以编写一个符合Logstash Java Outputs API的新Java类,将其打包,并使用logstash-plugin实用程序进行安装。我们将完成所有这些步骤。

1.5.1.设置环境

复制的例子回购

首先复制示例输出插件。插件API当前是Logstash代码库的一部分,因此您必须具有可用的本地副本。您可以使用以下git命令获取Logstash代码库的副本:

git clone --branch <branch_name> --single-branch https://github.com/elastic/logstash.git <target_folder>

branch_name应当对应于包含Java插件API的优选版本的版本Logstash的。

7.1 Logstash代码库的及更高版本的分支中提供了Java插件API的GA版本。

target_folder为您的Logstash代码库的本地副本指定。如果不指定target_folder,则默认为logstash 当前文件夹下的新文件夹。

生成.jar文件

获得Logstash代码库的相应修订版的副本之后,需要对其进行编译以生成包含Java插件API的.jar文件。从Logstash代码库的根目录($ LS_HOME),您可以使用./gradlew assemble(或gradlew.bat assemble如果您在Windows上运行)对其进行编译。这将产生 $LS_HOME/logstash-core/build/libs/logstash-core-x.y.z.jarwhere x,,y并 z引用Logstash的版本。

成功编译Logstash后,您需要告诉Java插件在哪里可以找到该logstash-core-x.y.z.jar文件。gradle.properties在插件项目的根文件夹中创建一个名为的新文件 。该文件应该只有一行:

LOGSTASH_CORE_PATH=<target_folder>/logstash-core

这里target_folder是当地的Logstash代码库的副本的根文件夹。

1.5.2.代码插件

示例输出插件使用事件的toString方法将事件打印到控制台 。让我们看一下示例输出中的主类:

@LogstashPlugin(name = "java_output_example")
public class JavaOutputExample implements Output {

    public static final PluginConfigSpec<String> PREFIX_CONFIG =
            PluginConfigSpec.stringSetting("prefix", "");

    private final String id;
    private String prefix;
    private PrintStream printer;
    private final CountDownLatch done = new CountDownLatch(1);
    private volatile boolean stopped = false;

    public JavaOutputExample(final String id, final Configuration configuration, final Context context) {
        this(id, configuration, context, System.out);
    }

    JavaOutputExample(final String id, final Configuration config, final Context context, OutputStream targetStream) {
        this.id = id;
        prefix = config.get(PREFIX_CONFIG);
        printer = new PrintStream(targetStream);
    }

    @Override
    public void output(final Collection<Event> events) {
      Iterator<Event> z = events.iterator();
      while (z.hasNext() && !stopped) {
          String s = prefix + z.next();
          printer.println(s);
        }
    }

    @Override
    public void stop() {
        stopped = true;
        done.countDown();
    }

    @Override
    public void awaitStop() throws InterruptedException {
        done.await();
    }

    @Override
    public Collection<PluginConfigSpec<?>> configSchema() {
        return Collections.singletonList(PREFIX_CONFIG);
    }

    @Override
    public String getId() {
        return id;
    }
}

让我们逐步学习该类的每个部分。

类声明

@LogstashPlugin(name="java_output_example")
public class JavaOutputExample implements Output {

有关类声明的注意事项:

  • 所有Java插件都必须带有@LogstashPlugin注释。另外:

    • name注解的属性必须提供,并定义插件的名称,因为它将在Logstash管道定义中使用。例如,此输出将在Logstash管道定义的输出部分中引用为output { java_output_example => { .... } }
    • name属性的值必须与该类的名称匹配,但不包括大小写和下划线。
  • 该类必须实现co.elastic.logstash.api.Output接口。
  • 不得在org.logstashco.elastic.logstash软件包中创建Java插件,以防止与Logstash本身中的类发生潜在冲突。

插件设置

下面的代码片段包含设置定义和引用它的方法:

public static final PluginConfigSpec<String> PREFIX_CONFIG =
        PluginConfigSpec.stringSetting("prefix", "");

@Override
public Collection<PluginConfigSpec<?>> configSchema() {
    return Collections.singletonList(PREFIX_CONFIG);
}

PluginConfigSpec级允许开发人员指定一个插件支持完整的设置名称,数据类型,弃用状态,需要的状态,并且默认值设置。在此示例中,该prefix设置定义了一个可选的前缀,以包括在事件的输出中。该设置不是必需的,如果未显式设置,则默认为空字符串。

configSchema方法必须返回插件支持的所有设置的列表。在Java插件项目的未来阶段,Logstash执行引擎将验证是否存在所有必需的设置,并且不存在不支持的设置。

构造函数和初始化

private final String id;
private String prefix;
private PrintStream printer;

public JavaOutputExample(final String id, final Configuration configuration, final Context context) {
    this(configuration, context, System.out);
}

JavaOutputExample(final String id, final Configuration config, final Context context, OutputStream targetStream) {
    this.id = id;
    prefix = config.get(PREFIX_CONFIG);
    printer = new PrintStream(targetStream);
}

所有Java输出插件都必须具有带Stringid和a Configuration和自Context变量的构造函数。这是构造函数,将用于在运行时实例化它们。所有插件设置的检索和验证都应在此构造方法中进行。在此示例中,prefix检索设置的值 并将其存储在局部变量中,以供以后在该output方法中使用。在这个例子中,第二,pacakge私有构造函数被定义,它是具有一个单元测试有用Stream以外System.out

任何其他初始化也可能在构造函数中发生。如果在输出插件的配置或初始化中遇到任何无法恢复的错误,则应抛出描述性异常。该异常将被记录,并阻止Logstash启动。

输出方式

@Override
public void output(final Collection<Event> events) {
    Iterator<Event> z = events.iterator();
    while (z.hasNext() && !stopped) {
        String s = prefix + z.next();
        printer.println(s);
    }
}

输出可以将事件发送到本地接收器(例如控制台或文件)或发送到远程系统(例如Elasticsearch或其他外部系统)。在此示例中,事件被打印到本地控制台。

Stop和awaitStop方法

private final CountDownLatch done = new CountDownLatch(1);
private volatile boolean stopped;

@Override
public void stop() {
    stopped = true;
    done.countDown();
}

@Override
public void awaitStop() throws InterruptedException {
    done.await();
}

stop方法通知输出停止发送事件。停止机制可以通过遵守API合同的任何方式来实现,尽管volatile boolean标志在许多用例中效果很好。因为此输出示例非常简单,所以其output方法不检查停止标志。

输出异步和协作停止。使用该awaitStop方法进行阻塞,直到输出完成停止过程为止。注意,此方法应该没有信号输出停止的stop方法一样。尽管CountDownLatch可以在许多用例中很好地发挥作用,但是可以通过遵守API合同的任何方式来实现awaitStop机制。

的getId方法

@Override
public String getId() {
    return id;
}

对于输出插件,该getId方法应始终返回实例化时通过其构造函数提供给插件的ID。

单元测试

最后,但同样重要的是,强烈建议进行单元测试。示例输出插件包括一个 示例单元测试,您可以将其用作自己的模板。

1.5.3.打包和部署

Java插件被打包为Ruby gem,以进行依赖关系管理以及与Ruby插件的互操作性。一旦将它们打包为gem,就可以logstash-plugin像Ruby插件一样将它们与实用程序一起安装。因为不需要Java插件开发就不需要Ruby或其工具链知识,所以通过示例Java插件随附的Gradle构建文件中的自定义任务,将Java插件打包为Ruby gem的过程已实现自动化。以下各节描述了如何配置和执行该打包任务以及如何在Logstash中安装打包的Java插件。

配置摇篮的包装任务

以下部分显示build.gradle在示例Java插件随附的文件顶部附近:

// ===========================================================================
// plugin info
// ===========================================================================
group                      'org.logstashplugins' // must match the package of the main plugin class
version                    "${file("VERSION").text.trim()}" // read from required VERSION file
description                = "Example Java filter implementation"
pluginInfo.licenses        = ['Apache-2.0'] // list of SPDX license IDs
pluginInfo.longDescription = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using \$LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
pluginInfo.authors         = ['Elasticsearch']
pluginInfo.email           = ['info@elastic.co']
pluginInfo.homepage        = "http://www.elastic.co/guide/en/logstash/current/index.html"
pluginInfo.pluginType      = "filter"
pluginInfo.pluginClass     = "JavaFilterExample"
pluginInfo.pluginName      = "java_filter_example"
// ===========================================================================

您应该为您的插件配置以上值。

  • version值将自动从VERSION插件代码库根目录中的文件中读取。
  • pluginInfo.pluginType应设置为一个inputfiltercodec,或output
  • pluginInfo.pluginName必须与@LogstashPlugin 主插件类的注释上指定的名称匹配。Gradle打包任务将对此进行验证,如果不匹配,则返回错误。

运行摇篮包装任务

要将插件打包为Ruby gem,需要几个Ruby源文件以及一个gemspec文件和一个文件Gemfile。这些Ruby文件仅用于定义Ruby gem结构,或者在Logstash启动时用于注册Java插件。在运行时事件处理期间不使用它们。Gradle打包任务会根据上一节中配置的值自动生成所有这些文件。

您可以使用以下命令运行Gradle打包任务:

./gradlew gem

对于Windows平台:替换gradlew.bat./gradlew在命令适当。

该任务将在您插件代码库的根目录中生成一个名为gem的文件 logstash-{plugintype}-<pluginName>-<version>.gem

安装在Logstash Java插件

将Java插件打包为Ruby gem之后,可以使用以下命令将其安装在Logstash中:

bin/logstash-plugin install --no-verify --local /path/to/javaPlugin.gem

对于Windows平台:在命令中适当地用反斜杠代替正斜杠。

1.5.4.​​​​​​​与Java插件输出运行Logstash

以下是最低的Logstash配置,可用于测试Java输出插件是否已正确安装并运行。

input {
  generator { message => "Hello world!" count => 1 }
}
output {
  java_output_example {}
}

将上述Logstash配置复制到一个文件,例如java_output.conf。Logstash然后应以以下内容开头:

bin/logstash -f /path/to/java_output.conf

需要Java执行引擎,这是Logstash 7.0以来的默认执行引擎,因为Ruby执行引擎不支持Java插件。

具有上述配置的预期Logstash输出(不包括初始化)为:

{"@timestamp":"yyyy-MM-ddTHH:mm:ss.SSSZ","message":"Hello world!","@version":"1","host":"<yourHostname>","sequence":0}

1.5.5.反馈

如果您对Logstash中的Java插件支持有任何反馈,请评论我们的 主要Github问题或在 Logstash论坛中发布

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值