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

org.swisspush.reststorage.LoggingFileReadStream Maven / Gradle / Ivy

package org.swisspush.reststorage;

import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.file.AsyncFile;
import io.vertx.core.file.impl.AsyncFileImpl;
import io.vertx.core.streams.ReadStream;
import org.slf4j.Logger;

import static org.slf4j.LoggerFactory.getLogger;


/**
 * Decorator with the purpose to log what's going on in a reading stream of an
 * {@link AsyncFile}.
 */
public class LoggingFileReadStream implements ReadStream {

    private static final Logger log = getLogger(LoggingFileReadStream.class);
    private final long expectedSize;
    private final String path;
    private final AsyncFile delegate;
    private long writtenBytes = 0;

    /**
     * @param expectedSize Actual file size which is expected to be streamed through that stream
     *                     in bytes.
     * @param path         Token printed alongside the logs so when reading logs, we can see which
     *                     log belongs to which file. A possible candidate is to use the file path
     *                     but it theoretically can be anything  which  helps  you  to  find  logs
     *                     related to your observed file.
     * @param delegate     The file (or stream) we wanna observe.
     */
    LoggingFileReadStream(long expectedSize, String path, AsyncFile delegate) {
        this.expectedSize = expectedSize;
        this.path = path;
        this.delegate = delegate;
    }

    @Override
    public ReadStream exceptionHandler(Handler handler) {
        log.trace("exceptionHandler registered for reading '{}'", path);
        delegate.exceptionHandler( ex -> {
            log.debug("Got an exception at offset {} ({} bytes remaining) for '{}'",
                    writtenBytes, expectedSize - writtenBytes, path, ex);
            handler.handle(ex);
        });
        return this;
    }

    @Override
    public ReadStream handler(Handler handler) {
        log.trace("handler registered");
        delegate.handler(buf -> {
            if (weShouldLogThatChunk(buf)) {
                log.debug("Read {} bytes at offset {} of total {} from '{}'",
                        buf.length(), writtenBytes, expectedSize, path);
            }
            writtenBytes += buf.length();
            handler.handle(buf);
        });
        return this;
    }

    @Override
    public ReadStream pause() {
        log.debug("Pause reading at offset {} for '{}'", writtenBytes, path);
        delegate.pause();
        return this;
    }

    @Override
    public ReadStream resume() {
        log.debug("Resume reading at offset {} for '{}'", writtenBytes, path);
        delegate.resume();
        return this;
    }

    @Override
    public ReadStream fetch(long amount) {
        log.debug("fetch amount {}", amount);
        return delegate.fetch(amount);
    }

    @Override
    public ReadStream endHandler(Handler endHandler) {
        log.trace("endHandler registered.");
        delegate.endHandler(aVoid -> {
            log.debug("End handler called ({} bytes remaining) for '{}'", expectedSize - writtenBytes, path);
            endHandler.handle(aVoid);
        });
        return this;
    }

    /**
     * Determines if it is worth writing some details to the logs for that chunk.
     */
    private boolean weShouldLogThatChunk(Buffer buf) {

        if (log.isTraceEnabled()) {
            // Simply log everything.
            return true;
        }

        // Because trace is disabled, we only log near begin or end of  stream  to  not
        // flood the log too much. Especially  for  large  files  which  would  produce
        // hundreds of lines of output.

        if (writtenBytes <= AsyncFileImpl.DEFAULT_READ_BUFFER_SIZE) {
            // We'll log near the beginning.
            return true;
        }

        if ((expectedSize - writtenBytes - buf.length()) < AsyncFileImpl.DEFAULT_READ_BUFFER_SIZE) {
            // We'll log near the end.
            return true;
        }

        // Neither verbosity is configured, nor are we at a interesting position in the stream.
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy