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

com.epam.reportportal.service.LaunchLoggingContext Maven / Gradle / Ivy

Go to download

A application used as an example on how to set up pushing its components to the Central Repository .

The newest version!
/*
 * Copyright 2019 EPAM Systems
 *
 * 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 com.epam.reportportal.service;

import com.epam.reportportal.listeners.ListenerParameters;
import com.epam.reportportal.message.TypeAwareByteSource;
import com.epam.reportportal.service.logs.LogBatchingFlowable;
import com.epam.reportportal.service.logs.LoggingSubscriber;
import com.epam.reportportal.utils.RetryWithDelay;
import com.epam.reportportal.utils.http.HttpRequestUtils;
import com.epam.ta.reportportal.ws.model.BatchSaveOperatingRS;
import com.epam.ta.reportportal.ws.model.log.SaveLogRQ;
import io.reactivex.*;
import io.reactivex.functions.Function;
import io.reactivex.internal.operators.flowable.FlowableFromObservable;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.subjects.PublishSubject;
import org.reactivestreams.Publisher;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static com.epam.reportportal.utils.files.ImageConverter.convert;
import static com.epam.reportportal.utils.files.ImageConverter.isImage;
import static com.google.common.io.ByteSource.wrap;

/**
 * Logging context holds {@link ConcurrentHashMap} context for launch logging and converts
 * {@link SaveLogRQ} to multipart HTTP request to ReportPortal
 * Basic flow:
 * After start some launch context should be initialized with observable of
 * launch ID and ReportPortal client.
 * Before actual finish of launch, context should be closed/completed.
 * Context consists of {@link Flowable} with buffering back-pressure strategy to be able
 * to batch incoming log messages into one request
 *
 * @author Ihar Kahadouski
 * @see #init(Maybe, ReportPortalClient, Scheduler)
 */
public class LaunchLoggingContext {
    private static final int DEFAULT_RETRY_COUNT = 5;
    private static final int DEFAULT_RETRY_TIMEOUT = 2;
    static final String DEFAULT_LAUNCH_KEY = "default";

    private static final ConcurrentHashMap loggingContextMap = new ConcurrentHashMap<>();
    /* Log emitter */
    private final PublishSubject> emitter;
    /* a UUID of Launch in ReportPortal */
    private final Maybe launchUuid;
    /* Whether Image should be converted to BlackAndWhite */
    private final boolean convertImages;

    private LaunchLoggingContext(@Nonnull final Maybe launchUuid, @Nonnull final ReportPortalClient client,
                                 @Nonnull final Scheduler scheduler, @Nonnull final ListenerParameters parameters,
                                 @Nonnull final FlowableSubscriber loggingSubscriber) {
        this.launchUuid = launchUuid;
        this.emitter = PublishSubject.create();
        this.convertImages = parameters.isConvertImage();
        RxJavaPlugins.onAssembly(new LogBatchingFlowable(
                        new FlowableFromObservable<>(emitter).flatMap((Function, Publisher>) Maybe::toFlowable),
                        parameters
                ))
                .flatMap((Function, Flowable>) rqs -> client.log(HttpRequestUtils.buildLogMultiPartRequest(
                        rqs)).toFlowable().retry(new RetryWithDelay((e) -> true, DEFAULT_RETRY_COUNT,
                        TimeUnit.SECONDS.toMillis(DEFAULT_RETRY_TIMEOUT))))
                .observeOn(scheduler)
                .onBackpressureBuffer(parameters.getRxBufferSize(), false, true)
                .subscribe(loggingSubscriber);
    }

    @Nullable
    public static LaunchLoggingContext context(@Nonnull final String key) {
        return loggingContextMap.get(key);
    }

    /**
     * Initializes new logging context and attaches it to current thread
     *
     * @param launchUuid        a UUID of a Launch
     * @param client            Client of ReportPortal
     * @param scheduler         a {@link Scheduler} to use with this LoggingContext
     * @param parameters        Report Portal client configuration parameters
     * @param loggingSubscriber RxJava subscriber on logging results
     * @return New Logging Context
     */
    public static LaunchLoggingContext init(@Nonnull final Maybe launchUuid, @Nonnull final ReportPortalClient client,
                                            @Nonnull final Scheduler scheduler, @Nonnull final ListenerParameters parameters,
                                            @Nonnull final FlowableSubscriber loggingSubscriber) {
        LaunchLoggingContext context = new LaunchLoggingContext(launchUuid, client, scheduler, parameters, loggingSubscriber);
        loggingContextMap.put(DEFAULT_LAUNCH_KEY, context);
        return context;
    }

    /**
     * Initializes new logging context and attaches it to current thread
     *
     * @param launchUuid a UUID of a Launch
     * @param client     Client of ReportPortal
     * @param scheduler  a {@link Scheduler} to use with this LoggingContext
     * @param parameters Report Portal client configuration parameters
     * @return New Logging Context
     */
    public static LaunchLoggingContext init(@Nonnull final Maybe launchUuid, @Nonnull final ReportPortalClient client,
                                            @Nonnull final Scheduler scheduler, @Nonnull final ListenerParameters parameters) {
        return init(launchUuid, client, scheduler, parameters, new LoggingSubscriber());
    }

    /**
     * Initializes new logging context and attaches it to current thread
     *
     * @param launchUuid Launch UUID
     * @param client     Client of ReportPortal
     * @param scheduler  a {@link Scheduler} to use with this LoggingContext
     * @return New Logging Context
     */
    static LaunchLoggingContext init(@Nonnull final Maybe launchUuid, final ReportPortalClient client,
                                     @Nonnull final Scheduler scheduler) {
        return init(launchUuid, client, scheduler, LoggingContext.DEFAULT_LOG_BATCH_SIZE, false);
    }

    /**
     * Initializes new logging context and attaches it to current thread
     *
     * @param launchUuid    Launch UUID
     * @param client        Client of ReportPortal
     * @param scheduler     a {@link Scheduler} to use with this LoggingContext
     * @param batchLogsSize Size of a log batch
     * @param convertImages Whether Image should be converted to BlackAndWhite
     * @return New Logging Context
     */
    static LaunchLoggingContext init(@Nonnull final Maybe launchUuid, @Nonnull final ReportPortalClient client,
                                     @Nonnull final Scheduler scheduler, final int batchLogsSize, final boolean convertImages) {
        ListenerParameters params = new ListenerParameters();
        params.setBatchLogsSize(batchLogsSize);
        params.setConvertImage(convertImages);
        return init(launchUuid, client, scheduler, params, new LoggingSubscriber());
    }

    /**
     * Completes context attached to the current thread
     *
     * @return Waiting queue to be able to track request sending completion
     */
    public static Completable complete() {
        final LaunchLoggingContext loggingContext = loggingContextMap.get(DEFAULT_LAUNCH_KEY);
        if (null != loggingContext) {
            return loggingContext.completed();
        } else {
            return Maybe.empty().ignoreElement();
        }
    }

    /**
     * Emits log. Basically, put it into processing pipeline
     *
     * @param logSupplier Log Message Factory. Key if the function is actual test item ID
     */
    void emit(@Nonnull final java.util.function.Function logSupplier) {
        emitter.onNext(launchUuid.map(input -> {
            final SaveLogRQ rq = logSupplier.apply(input);
            SaveLogRQ.File file = rq.getFile();
            if (convertImages && null != file && isImage(file.getContentType())) {
                final TypeAwareByteSource source = convert(wrap(file.getContent()));
                file.setContent(source.read());
                file.setContentType(source.getMediaType());
            }
            return rq;
        }));
    }

    /**
     * Marks flow as completed
     *
     * @return {@link Completable}
     */
    private Completable completed() {
        emitter.onComplete();
        return emitter.ignoreElements();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy