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

com.artipie.http.slice.LoggingSlice Maven / Gradle / Ivy

There is a newer version: v1.17.16
Show newest version
/*
 * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
 * https://github.com/artipie/artipie/blob/master/LICENSE.txt
 */
package com.artipie.http.slice;

import com.artipie.http.Connection;
import com.artipie.http.Headers;
import com.artipie.http.Response;
import com.artipie.http.Slice;
import com.artipie.http.rs.RsStatus;
import com.jcabi.log.Logger;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.logging.Level;
import org.reactivestreams.Publisher;

/**
 * Slice that logs incoming requests and outgoing responses.
 *
 * @since 0.8
 */
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public final class LoggingSlice implements Slice {

    /**
     * Logging level.
     */
    private final Level level;

    /**
     * Delegate slice.
     */
    private final Slice slice;

    /**
     * Ctor.
     *
     * @param slice Slice.
     */
    public LoggingSlice(final Slice slice) {
        this(Level.FINE, slice);
    }

    /**
     * Ctor.
     *
     * @param level Logging level.
     * @param slice Slice.
     */
    public LoggingSlice(final Level level, final Slice slice) {
        this.level = level;
        this.slice = slice;
    }

    @Override
    public Response response(
        final String line,
        final Iterable> headers,
        final Publisher body
    ) {
        final StringBuilder msg = new StringBuilder(">> ").append(line);
        LoggingSlice.append(msg, headers);
        Logger.log(this.level, this.slice, msg.toString());
        return connection -> {
            try {
                return this.slice.response(line, headers, body)
                    .send(new LoggingConnection(connection))
                    .handle(
                        (value, throwable) -> {
                            final CompletableFuture result = new CompletableFuture<>();
                            if (throwable == null) {
                                result.complete(value);
                            } else {
                                this.log(throwable);
                                result.completeExceptionally(throwable);
                            }
                            return result;
                        }
                    )
                    .thenCompose(Function.identity());
            } catch (final Exception ex) {
                this.log(ex);
                throw ex;
            }
        };
    }

    /**
     * Writes throwable to logger.
     *
     * @param throwable Throwable to be logged.
     */
    private void log(final Throwable throwable) {
        Logger.log(this.level, this.slice, "Failure: %[exception]s", throwable);
    }

    /**
     * Append headers to {@link StringBuilder}.
     *
     * @param builder Target {@link StringBuilder}.
     * @param headers Headers to be appended.
     */
    private static void append(
        final StringBuilder builder,
        final Iterable> headers
    ) {
        for (final Map.Entry header : headers) {
            builder.append('\n').append(header.getKey()).append(": ").append(header.getValue());
        }
    }

    /**
     * Connection logging response prior to sending.
     *
     * @since 0.8
     */
    private final class LoggingConnection implements Connection {

        /**
         * Delegate connection.
         */
        private final Connection connection;

        /**
         * Ctor.
         *
         * @param connection Delegate connection.
         */
        private LoggingConnection(final Connection connection) {
            this.connection = connection;
        }

        @Override
        public CompletionStage accept(
            final RsStatus status,
            final Headers headers,
            final Publisher body
        ) {
            final StringBuilder msg = new StringBuilder("<< ").append(status);
            LoggingSlice.append(msg, headers);
            Logger.log(LoggingSlice.this.level, LoggingSlice.this.slice, msg.toString());
            return this.connection.accept(status, headers, body);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy