All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.qos.logback.more.appenders.KinesisStreamLogbackAppenderV2 Maven / Gradle / Ivy

There is a newer version: 1.8.8-JAVA9MODULE_SLF4J17
Show newest version
/**
 * Copyright (c) 2018 sndyuk 
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package ch.qos.logback.more.appenders;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.more.appenders.IntervalEmitter.EventMapper;
import ch.qos.logback.more.appenders.IntervalEmitter.IntervalAppender;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.kinesis.model.PutRecordsRequest;
import software.amazon.awssdk.services.kinesis.model.PutRecordsRequestEntry;
import software.amazon.awssdk.services.kinesis.model.PutRecordsResponse;
import software.amazon.awssdk.services.kinesis.model.PutRecordsResultEntry;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Appender for Kinesis Stream. It appends entries for every emitInterval.
 * 
 * @author sndyuk
 */
public class KinesisStreamLogbackAppenderV2 extends KinesisStreamAppenderBaseV2 {

    private IntervalEmitter emitter;
    private PartitionKey partitionKey = new RandomPartitionKey();
    private long emitInterval = 10000;

    public void setAwsConfig(AwsConfig config) {
        this.config = config;
    }

    public void setStreamName(String streamName) {
        this.streamName = streamName;
    }

    public void setShardCount(int shardCount) {
        this.shardCount = shardCount;
    }

    public void setCreateStreamDestination(boolean createStreamDestination) {
        this.createStreamDestination = createStreamDestination;
    }

    public void setPartitionKey(PartitionKey partitionKey) {
        this.partitionKey = partitionKey;
    }

    public void setEmitInterval(long emitInterval) {
        this.emitInterval = emitInterval;
    }

    public void setEncoder(Encoder encoder) {
        this.encoder = encoder;
    }

    @Override
    public void start() {
        this.emitter = new IntervalEmitter(emitInterval,
                new KinesisEventMapper(), new KinesisIntervalAppender());
        super.start();
    }

    @Override
    public void stop() {
        try {
            emitter.emitForShutdown(10000, 10);
        } catch (Exception e) {
            // Ignore
        }
        try {
            super.stop();
        } catch (Exception e) {
            // Ignore
        }
    }

    @Override
    protected void append(ILoggingEvent eventObject) {
        emitter.append(eventObject);
    }

    private final class KinesisEventMapper implements EventMapper {

        @Override
        public PutRecordsRequestEntry map(ILoggingEvent event) {
            PutRecordsRequestEntry entry  = PutRecordsRequestEntry.builder()
                    .data(SdkBytes.fromByteArray(encoder.encode(event)))
                            .partitionKey(partitionKey.get(event))
                    .build();
            return entry;
        }
    }

    private final class KinesisIntervalAppender implements IntervalAppender {

        @Override
        public boolean append(List entries) {
            if (!active) {
                ensureKinesisStream();
            }
            return append(entries, 0);
        }

        private boolean append(List entries, int retryCount) {
            if (retryCount > 3) {
                StringBuilder sb = new StringBuilder("Could not append the Kinesis stream entry. Failed entries:");
                for (PutRecordsRequestEntry entry : entries) {
                    sb.append(System.lineSeparator()).append(entry.data());
                }
                addError(sb.toString());
                return true; // true to forcibly remove unexpected failed entries.
            }
            try {
                if (retryCount > 0) {
                    Thread.sleep(1000 * retryCount);
                }
                PutRecordsRequest putRecordsRequest  = PutRecordsRequest.builder()
                        .streamName(streamName)
                        .records(entries)
                        .build();
                PutRecordsResponse putRecordsResult  = kinesis.putRecords(putRecordsRequest);
                if (putRecordsResult.failedRecordCount() == 0) {
                    return true;
                }
                if (putRecordsResult.failedRecordCount() == entries.size()) {
                    return false; // Leave it to logback retry mechanism.
                }
                // Retry
                List failedEntries = new ArrayList(putRecordsResult.failedRecordCount());
                for (int i = 0; i < entries.size(); i++) {
                    PutRecordsResultEntry resultEntry = putRecordsResult.records().get(i);
                    if (resultEntry.errorCode() != null) {
                        failedEntries.add(entries.get(i));
                    }
                    append(failedEntries, retryCount + 1);
                }
            } catch (RuntimeException e) {
                addError("Unexpected runtime error while appending kinesis entries.", e);
            } catch (InterruptedException e) {
                // pass
            }
            return true;
        }
    }

    public static class RandomPartitionKey implements PartitionKey {
        private Random rand;
        private int maxPartition;

        public RandomPartitionKey() {
            this.rand = new Random();
            this.maxPartition = 10000;
        }

        @Override
        public String get(ILoggingEvent event) {
            return String.valueOf(rand.nextInt(maxPartition));
        }
    }

    public static class SinglePartitionKey implements PartitionKey {
        @Override
        public String get(ILoggingEvent event) {
            return "0";
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy