From c3fe808e11b02968091609360199cb313b4ab7c3 Mon Sep 17 00:00:00 2001 From: kkondaka <41027584+kkondaka@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:14:43 -0800 Subject: [PATCH] Add truncate string processor (#3924) * Add truncate string processor Signed-off-by: Krishna Kondaka * Addressed review comments Signed-off-by: Krishna Kondaka * Added check for negative numbers in the config input Signed-off-by: Krishna Kondaka * Fixed checkstyle error Signed-off-by: Krishna Kondaka * Modified to make truncate processor a top level processor, not specific to strings Signed-off-by: Krishna Kondaka * Addressed review comments Signed-off-by: Krishna Kondaka * Updated documentation with correct configuration Signed-off-by: Krishna Kondaka * Fixed typos in the documentation Signed-off-by: Krishna Kondaka * Modified to allow more than one source keys in the config Signed-off-by: Krishna Kondaka * Modified to allow multiple entries under configuration Signed-off-by: Krishna Kondaka --------- Signed-off-by: Krishna Kondaka Co-authored-by: Krishna Kondaka --- .../truncate-processor/README.md | 76 +++++++++ .../truncate-processor/build.gradle | 17 +++ .../processor/truncate/TruncateProcessor.java | 100 ++++++++++++ .../truncate/TruncateProcessorConfig.java | 81 ++++++++++ .../TruncateProcessorConfigTests.java | 115 ++++++++++++++ .../truncate/TruncateProcessorTests.java | 144 ++++++++++++++++++ settings.gradle | 1 + 7 files changed, 534 insertions(+) create mode 100644 data-prepper-plugins/truncate-processor/README.md create mode 100644 data-prepper-plugins/truncate-processor/build.gradle create mode 100644 data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessor.java create mode 100644 data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfig.java create mode 100644 data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfigTests.java create mode 100644 data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorTests.java diff --git a/data-prepper-plugins/truncate-processor/README.md b/data-prepper-plugins/truncate-processor/README.md new file mode 100644 index 0000000000..e4ea59115d --- /dev/null +++ b/data-prepper-plugins/truncate-processor/README.md @@ -0,0 +1,76 @@ +# Truncate Processor + +This is a processor that truncates key's value at the beginning or at the end or at both sides of a string as per the configuration. If the key's value is a list, then each of the string members of the list are truncated. Non-string members of the list are left untouched. If `truncate_when` option is provided, the truncation of the input is done only when the condition specified is true for the event being processed. + +## Basic Usage +To get started, create the following `pipeline.yaml`. +```yaml +pipeline: + source: + file: + path: "/full/path/to/logs_json.log" + record_type: "event" + format: "json" + processor: + - truncate: + entries: + - source_keys: ["message1", "message2"] + length: 5 + - source_keys: ["info"] + length: 6 + start_at: 4 + - source_keys: ["log"] + start_at: 5 + sink: + - stdout: +``` + +Create the following file named `logs_json.log` and replace the `path` in the file source of your `pipeline.yaml` with the path of this file. + +```json +{"message1": "hello,world", "message2": "test message", "info", "new information", "log": "test log message"} +``` +When you run Data Prepper with this `pipeline.yaml`, you should see the following output: + +```json +{"message1":"hello", "message2":"test ", "info":"inform", "log": "log message"} +``` +where `message1` and `message2` have input values truncated to length 5, starting from index 0, `info` input value truncated to length 6 starting from index 4 and `log` input value truncated at the front by 5 characters. + +Example configuration with `truncate_when` option: +```yaml +pipeline: + source: + file: + path: "/full/path/to/logs_json.log" + record_type: "event" + format: "json" + processor: + - truncate: + entries: + - source: ["message"] + length: 5 + start_at: 8 + truncate_when: '/id == 1' + sink: + - stdout: +``` + +When the pipeline started with the above configuration receives the following two events +```json +{"message": "hello, world", "id": 1} +{"message": "hello, world,not-truncated", "id": 2} +``` +the output would be +```json +{"message": "world", "id": 1} +{"message": "hello, world,not-truncated", "id": 2} +``` + +### Configuration +* `entries` - (required) - A list of entries to add to an event + * `source_keys` - (required) - The list of key to be modified + * `truncate_when` - (optional) - a condition, when it is true the truncate operation is performed. + * `start_at` - (optional) - starting index of the string. Defaults to 0. + * `length` - (optional) - length of the string after truncation. Defaults to end of the string. +Either `start_at` or `length` or both must be present diff --git a/data-prepper-plugins/truncate-processor/build.gradle b/data-prepper-plugins/truncate-processor/build.gradle new file mode 100644 index 0000000000..59ecee5524 --- /dev/null +++ b/data-prepper-plugins/truncate-processor/build.gradle @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +plugins { + id 'java' +} + +dependencies { + implementation project(':data-prepper-api') + implementation project(':data-prepper-test-common') + implementation project(':data-prepper-plugins:common') + implementation 'com.fasterxml.jackson.core:jackson-databind' + testImplementation libs.commons.lang3 +} + diff --git a/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessor.java b/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessor.java new file mode 100644 index 0000000000..8449675791 --- /dev/null +++ b/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessor.java @@ -0,0 +1,100 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.plugins.processor.truncate; + +import org.opensearch.dataprepper.expression.ExpressionEvaluator; +import org.opensearch.dataprepper.metrics.PluginMetrics; +import org.opensearch.dataprepper.model.annotations.DataPrepperPlugin; +import org.opensearch.dataprepper.model.annotations.DataPrepperPluginConstructor; +import org.opensearch.dataprepper.model.event.Event; +import org.opensearch.dataprepper.model.record.Record; +import org.opensearch.dataprepper.model.processor.AbstractProcessor; +import org.opensearch.dataprepper.model.processor.Processor; + +import java.util.Collection; +import java.util.ArrayList; +import java.util.List; + +/** + * This processor takes in a key and truncates its value to a string with + * characters from the front or at the end or at both removed. + * If the value is not a string, no action is performed. + */ +@DataPrepperPlugin(name = "truncate", pluginType = Processor.class, pluginConfigurationType = TruncateProcessorConfig.class) +public class TruncateProcessor extends AbstractProcessor, Record>{ + private final ExpressionEvaluator expressionEvaluator; + private final List entries; + + @DataPrepperPluginConstructor + public TruncateProcessor(final PluginMetrics pluginMetrics, final TruncateProcessorConfig config, final ExpressionEvaluator expressionEvaluator) { + super(pluginMetrics); + this.expressionEvaluator = expressionEvaluator; + this.entries = config.getEntries(); + } + + private String getTruncatedValue(final String value, final int startIndex, final Integer length) { + String truncatedValue = + (length == null || startIndex+length >= value.length()) ? + value.substring(startIndex) : + value.substring(startIndex, startIndex + length); + + return truncatedValue; + } + + @Override + public Collection> doExecute(final Collection> records) { + for(final Record record : records) { + final Event recordEvent = record.getData(); + for (TruncateProcessorConfig.Entry entry: entries) { + final List sourceKeys = entry.getSourceKeys(); + final String truncateWhen = entry.getTruncateWhen(); + final int startIndex = entry.getStartAt() == null ? 0 : entry.getStartAt(); + final Integer length = entry.getLength(); + if (truncateWhen != null && !expressionEvaluator.evaluateConditional(truncateWhen, recordEvent)) { + continue; + } + for (String sourceKey: sourceKeys) { + if (!recordEvent.containsKey(sourceKey)) { + continue; + } + + final Object value = recordEvent.get(sourceKey, Object.class); + if (value instanceof String) { + recordEvent.put(sourceKey, getTruncatedValue((String)value, startIndex, length)); + } else if (value instanceof List) { + List result = new ArrayList<>(); + for (Object listItem: (List)value) { + if (listItem instanceof String) { + result.add(getTruncatedValue((String)listItem, startIndex, length)); + } else { + result.add(listItem); + } + } + recordEvent.put(sourceKey, result); + } + } + } + } + + return records; + } + + @Override + public void prepareForShutdown() { + + } + + @Override + public boolean isReadyForShutdown() { + return true; + } + + @Override + public void shutdown() { + + } +} + diff --git a/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfig.java b/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfig.java new file mode 100644 index 0000000000..1172da77b6 --- /dev/null +++ b/data-prepper-plugins/truncate-processor/src/main/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfig.java @@ -0,0 +1,81 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.plugins.processor.truncate; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.Valid; + +import java.util.List; + +public class TruncateProcessorConfig { + public static class Entry { + @NotEmpty + @NotNull + @JsonProperty("source_keys") + private List sourceKeys; + + @JsonProperty("start_at") + private Integer startAt; + + @JsonProperty("length") + private Integer length; + + @JsonProperty("truncate_when") + private String truncateWhen; + + public Entry(final List sourceKeys, final Integer startAt, final Integer length, final String truncateWhen) { + this.sourceKeys = sourceKeys; + this.startAt = startAt; + this.length = length; + this.truncateWhen = truncateWhen; + } + + public Entry() {} + + public List getSourceKeys() { + return sourceKeys; + } + + public Integer getStartAt() { + return startAt; + } + + public Integer getLength() { + return length; + } + + public String getTruncateWhen() { + return truncateWhen; + } + + @AssertTrue(message = "source_keys must be specified and at least one of start_at or length or both must be specified and the values must be positive integers") + public boolean isValidConfig() { + if (length == null && startAt == null) { + return false; + } + if (length != null && length < 0) { + return false; + } + if (startAt != null && startAt < 0) { + return false; + } + return true; + } + } + + @NotEmpty + @NotNull + private List<@Valid Entry> entries; + + public List getEntries() { + return entries; + } + +} + diff --git a/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfigTests.java b/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfigTests.java new file mode 100644 index 0000000000..3032db6b8b --- /dev/null +++ b/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorConfigTests.java @@ -0,0 +1,115 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.plugins.processor.truncate; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.apache.commons.lang3.RandomStringUtils; +import static org.opensearch.dataprepper.test.helper.ReflectivelySetField.setField; + +import java.util.List; +import java.util.Random; + +class TruncateProcessorConfigTests { + TruncateProcessorConfig truncateProcessorConfig; + + Random random; + + @BeforeEach + void setUp() { + truncateProcessorConfig = new TruncateProcessorConfig(); + random = new Random(); + } + + @Test + void testDefaults() { + assertThat(truncateProcessorConfig.getEntries(), equalTo(null)); + } + + @Test + void testEntryDefaults() { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + assertThat(entry.getStartAt(), equalTo(null)); + assertThat(entry.getLength(), equalTo(null)); + assertThat(entry.getTruncateWhen(), equalTo(null)); + } + + @Test + void testValidConfiguration_withStartAt() throws NoSuchFieldException, IllegalAccessException { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + String source = RandomStringUtils.randomAlphabetic(10); + List sourceKeys = List.of(source); + setField(TruncateProcessorConfig.Entry.class, entry, "sourceKeys", sourceKeys); + int startAt = random.nextInt(100); + setField(TruncateProcessorConfig.Entry.class, entry, "startAt", startAt); + assertThat(entry.getSourceKeys(), equalTo(sourceKeys)); + assertThat(entry.getStartAt(), equalTo(startAt)); + setField(TruncateProcessorConfig.class, truncateProcessorConfig, "entries", List.of(entry)); + assertTrue(entry.isValidConfig()); + } + + @Test + void testValidConfiguration_withLength() throws NoSuchFieldException, IllegalAccessException { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + String source1 = RandomStringUtils.randomAlphabetic(10); + String source2 = RandomStringUtils.randomAlphabetic(10); + List sourceKeys = List.of(source1, source2); + setField(TruncateProcessorConfig.Entry.class, entry, "sourceKeys", sourceKeys); + int length = random.nextInt(100); + setField(TruncateProcessorConfig.Entry.class, entry, "length", length); + assertThat(entry.getSourceKeys(), equalTo(sourceKeys)); + assertThat(entry.getLength(), equalTo(length)); + setField(TruncateProcessorConfig.class, truncateProcessorConfig, "entries", List.of(entry)); + assertTrue(entry.isValidConfig()); + } + + @Test + void testValidConfiguration_withLength_withTruncateWhen() throws NoSuchFieldException, IllegalAccessException { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + String source = RandomStringUtils.randomAlphabetic(10); + String condition = RandomStringUtils.randomAlphabetic(10); + List sourceKeys = List.of(source); + setField(TruncateProcessorConfig.Entry.class, entry, "sourceKeys", sourceKeys); + int length = random.nextInt(100); + int startAt = random.nextInt(100); + setField(TruncateProcessorConfig.Entry.class, entry, "length", length); + setField(TruncateProcessorConfig.Entry.class, entry, "startAt", startAt); + setField(TruncateProcessorConfig.Entry.class, entry, "truncateWhen", condition); + assertThat(entry.getSourceKeys(), equalTo(sourceKeys)); + assertThat(entry.getLength(), equalTo(length)); + assertThat(entry.getStartAt(), equalTo(startAt)); + assertThat(entry.getTruncateWhen(), equalTo(condition)); + assertTrue(entry.isValidConfig()); + } + + @Test + void testInvalidConfiguration_StartAt_Length_BothNull() throws NoSuchFieldException, IllegalAccessException { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + String source = RandomStringUtils.randomAlphabetic(10); + setField(TruncateProcessorConfig.Entry.class, entry, "sourceKeys", List.of(source)); + assertFalse(entry.isValidConfig()); + } + + @Test + void testInvalidConfiguration_StartAt_Length_Negative() throws NoSuchFieldException, IllegalAccessException { + TruncateProcessorConfig.Entry entry = new TruncateProcessorConfig.Entry(); + String source = RandomStringUtils.randomAlphabetic(10); + int length = random.nextInt(100); + int startAt = random.nextInt(100); + setField(TruncateProcessorConfig.Entry.class, entry, "sourceKeys", List.of(source)); + setField(TruncateProcessorConfig.Entry.class, entry, "startAt", -startAt); + assertFalse(entry.isValidConfig()); + setField(TruncateProcessorConfig.Entry.class, entry, "startAt", startAt); + setField(TruncateProcessorConfig.Entry.class, entry, "length", -length); + assertFalse(entry.isValidConfig()); + } +} diff --git a/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorTests.java b/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorTests.java new file mode 100644 index 0000000000..7717181864 --- /dev/null +++ b/data-prepper-plugins/truncate-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/truncate/TruncateProcessorTests.java @@ -0,0 +1,144 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.dataprepper.plugins.processor.truncate; + +import org.opensearch.dataprepper.expression.ExpressionEvaluator; +import org.opensearch.dataprepper.metrics.PluginMetrics; +import org.opensearch.dataprepper.model.event.Event; +import org.opensearch.dataprepper.model.event.JacksonEvent; +import org.opensearch.dataprepper.model.record.Record; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import static org.junit.jupiter.params.provider.Arguments.arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class TruncateProcessorTests { + + @Mock + private PluginMetrics pluginMetrics; + + @Mock + private TruncateProcessorConfig config; + + @Mock + private ExpressionEvaluator expressionEvaluator; + + private TruncateProcessor createObjectUnderTest() { + return new TruncateProcessor(pluginMetrics, config, expressionEvaluator); + } + + @ParameterizedTest + @ArgumentsSource(TruncateArgumentsProvider.class) + void testTruncateProcessor(final Object messageValue, final Integer startAt, final Integer truncateLength, final Object truncatedMessage) { + + when(config.getEntries()).thenReturn(Collections.singletonList(createEntry(List.of("message"), startAt, truncateLength, null))); + final TruncateProcessor truncateProcessor = createObjectUnderTest(); + final Record record = createEvent("message", messageValue); + final List> truncatedRecords = (List>) truncateProcessor.doExecute(Collections.singletonList(record)); + assertThat(truncatedRecords.get(0).getData().get("message", Object.class), notNullValue()); + assertThat(truncatedRecords.get(0).getData().get("message", Object.class), equalTo(truncatedMessage)); + } + + @ParameterizedTest + @ArgumentsSource(MultipleTruncateArgumentsProvider.class) + void testTruncateProcessorMultipleEntries(final Object messageValue, final Integer startAt1, final Integer truncateLength1, final Integer startAt2, final Integer truncateLength2, final Object truncatedMessage1, final Object truncatedMessage2) { + TruncateProcessorConfig.Entry entry1 = createEntry(List.of("message1"), startAt1, truncateLength1, null); + TruncateProcessorConfig.Entry entry2 = createEntry(List.of("message2"), startAt2, truncateLength2, null); + when(config.getEntries()).thenReturn(List.of(entry1, entry2)); + final Record record1 = createEvent("message1", messageValue); + final Record record2 = createEvent("message2", messageValue); + final TruncateProcessor truncateProcessor = createObjectUnderTest(); + final List> truncatedRecords = (List>) truncateProcessor.doExecute(List.of(record1, record2)); + assertThat(truncatedRecords.get(0).getData().get("message1", Object.class), notNullValue()); + assertThat(truncatedRecords.get(1).getData().get("message2", Object.class), notNullValue()); + assertThat(truncatedRecords.get(0).getData().get("message1", Object.class), equalTo(truncatedMessage1)); + assertThat(truncatedRecords.get(1).getData().get("message2", Object.class), equalTo(truncatedMessage2)); + } + + @Test + void test_event_is_the_same_when_truncateWhen_condition_returns_false() { + final String truncateWhen = UUID.randomUUID().toString(); + final String message = UUID.randomUUID().toString(); + + when(config.getEntries()).thenReturn(Collections.singletonList(createEntry(List.of("message"), null, 5, truncateWhen))); + + final TruncateProcessor truncateProcessor = createObjectUnderTest(); + final Record record = createEvent("message", message); + when(expressionEvaluator.evaluateConditional(truncateWhen, record.getData())).thenReturn(false); + final List> truncatedRecords = (List>) truncateProcessor.doExecute(Collections.singletonList(record)); + + assertThat(truncatedRecords.get(0).getData().toMap(), equalTo(record.getData().toMap())); + } + + private TruncateProcessorConfig.Entry createEntry(final List sourceKeys, final Integer startAt, final Integer length, final String truncateWhen) { + return new TruncateProcessorConfig.Entry(sourceKeys, startAt, length, truncateWhen); + } + + private Record createEvent(final String key, final Object value) { + final Map eventData = new HashMap<>(); + eventData.put(key, value); + return new Record<>(JacksonEvent.builder() + .withEventType("event") + .withData(eventData) + .build()); + } + + static class TruncateArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + arguments("hello,world,no-truncate", 0, 100, "hello,world,no-truncate"), + arguments("hello,world,no-truncate", 6, 100, "world,no-truncate"), + arguments("hello,world,no-truncate", 6, 16, "world,no-truncat"), + arguments("hello,world,no-truncate", 6, 17, "world,no-truncate"), + arguments("hello,world,no-truncate", 6, 18, "world,no-truncate"), + arguments("hello,world,no-truncate", 6, 5, "world"), + arguments("hello,world,no-truncate", 6, null, "world,no-truncate"), + + arguments("hello,world,no-truncate", null, 100, "hello,world,no-truncate"), + arguments("hello,world,truncate", null, 11, "hello,world"), + arguments("hello,world", null, 1, "h"), + arguments("hello", null, 0, ""), + arguments(List.of("hello_one", "hello_two", "hello_three"), null, 5, List.of("hello", "hello", "hello")), + arguments(List.of("hello_one", 2, "hello_three"), null, 5, List.of("hello", 2, "hello")) + ); + } + } + + static class MultipleTruncateArgumentsProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + return Stream.of( + arguments("hello,world,no-truncate", 0, 100, 1, 10, "hello,world,no-truncate", "ello,world"), + arguments("hello,world,no-truncate", 6, 100, null, 5, "world,no-truncate", "hello"), + arguments("hello,world,no-truncate", 6, 16, 12, null, "world,no-truncat", "no-truncate") + ); + } + } + +} + diff --git a/settings.gradle b/settings.gradle index 05d6ab936a..e5b091a14d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -122,6 +122,7 @@ include 'data-prepper-plugins:csv-processor' include 'data-prepper-plugins:parse-json-processor' include 'data-prepper-plugins:trace-peer-forwarder-processor' include 'data-prepper-plugins:translate-processor' +include 'data-prepper-plugins:truncate-processor' include 'data-prepper-plugins:dynamodb-source-coordination-store' include 'release' include 'release:archives'