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

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

There is a newer version: 5.2.21
Show 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.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 static com.epam.reportportal.utils.files.ImageConverter.convert;
import static com.epam.reportportal.utils.files.ImageConverter.isImage;
import static com.epam.reportportal.utils.files.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 {
	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())
				.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        ReportPortal 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 ReportPortal 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