
org.appenders.log4j2.elasticsearch.hc.BatchRequest Maven / Gradle / Ivy
Show all versions of log4j2-elasticsearch-hc Show documentation
package org.appenders.log4j2.elasticsearch.hc;
/*-
* #%L
* log4j2-elasticsearch
* %%
* Copyright (C) 2019 Rafal Foltynski
* %%
* 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.
* #L%
*/
import com.fasterxml.jackson.databind.ObjectWriter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import org.appenders.log4j2.elasticsearch.ItemSource;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* {@link org.appenders.log4j2.elasticsearch.ByteBufItemSource}-backed /_bulk request.
* Allows to index multiple {@link org.appenders.log4j2.elasticsearch.hc.IndexRequest} documents
* in a single request.
*/
public class BatchRequest implements Request {
public static final String HTTP_METHOD_NAME = "POST";
public static final char LINE_SEPARATOR = '\n';
private final ObjectWriter objectWriter;
private ItemSource buffer;
protected final Collection indexRequests;
protected BatchRequest(Builder builder) {
this.indexRequests = builder.items;
this.objectWriter = builder.objectWriter;
this.buffer = builder.buffer;
}
/**
* Serializes and writes {@link #indexRequests} into {@link #buffer}
*
* @return underlying buffer filled with serialized indexRequests
* @throws IOException if serialization failed
*/
public ItemSource serialize() throws IOException {
ByteBufOutputStream byteBufOutputStream = new ByteBufOutputStream(buffer.getSource());
// in current impl with no IDs, it's possible to reduce serialization by reusing first action
IndexRequest identicalAction = uniformAction(indexRequests);
byte[] actionTemplate = identicalAction != null ? objectWriter.writeValueAsBytes(identicalAction) : null;
for (IndexRequest action : indexRequests) {
if (actionTemplate == null) {
objectWriter.writeValue((OutputStream) byteBufOutputStream, action);
} else {
byteBufOutputStream.write(actionTemplate);
}
byteBufOutputStream.writeByte(LINE_SEPARATOR);
ByteBuf source = action.getSource().getSource();
buffer.getSource().writeBytes(source);
byteBufOutputStream.writeByte(LINE_SEPARATOR);
}
return buffer;
}
/**
* Checks if all items in given collection are equal
* ({@link IndexRequest#index} and {@link IndexRequest#type} are the same for all elements)
*
* @param indexRequests collection of items to be checked
* @return {@link IndexRequest} first action in given collection if all items are equal, null otherwise
*/
IndexRequest uniformAction(Collection indexRequests) {
IndexRequest current = null;
for (IndexRequest indexRequest : indexRequests) {
if (current == null) {
current = indexRequest;
continue;
}
if (!current.index.equals(indexRequest.index) || !current.type.equals(indexRequest.type)) {
// fail fast and serialize each item
return null;
}
}
return current;
}
/**
* Clears underlying collection of indexRequests and releases all {@link ItemSource} instances.
* MUST be called when request is completed. Otherwise it may lead to excessive resource usage and memory leaks
*/
public void completed() {
for (IndexRequest indexRequest : indexRequests) {
indexRequest.release();
}
indexRequests.clear();
buffer.release();
buffer = null;
}
public Collection getIndexRequests() {
return indexRequests;
}
@Override
public String getURI() {
return "/_bulk";
}
@Override
public String getHttpMethodName() {
return HTTP_METHOD_NAME;
}
public static class Builder {
protected final Collection items = new ConcurrentLinkedQueue<>();
private ItemSource buffer;
private ObjectWriter objectWriter;
public Builder add(IndexRequest item) {
this.items.add(item);
return this;
}
public Builder add(Collection extends IndexRequest> items) {
this.items.addAll(items);
return this;
}
public BatchRequest build() {
if (buffer == null) {
throw new IllegalArgumentException("buffer cannot be null");
}
if (objectWriter == null) {
throw new IllegalArgumentException("objectWriter cannot be null");
}
return new BatchRequest(this);
}
public Builder withBuffer(ItemSource buffer) {
this.buffer = buffer;
return this;
}
public Builder withObjectWriter(ObjectWriter objectWriter) {
this.objectWriter = objectWriter;
return this;
}
}
}