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

org.opensearch.migrations.replay.tracing.ReplayContexts Maven / Gradle / Ivy

package org.opensearch.migrations.replay.tracing;

import java.time.Duration;
import java.time.Instant;
import java.util.Optional;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;

import org.opensearch.migrations.replay.datatypes.ISourceTrafficChannelKey;
import org.opensearch.migrations.replay.datatypes.ITrafficStreamKey;
import org.opensearch.migrations.replay.datatypes.UniqueReplayerRequestKey;
import org.opensearch.migrations.tracing.BaseNestedSpanContext;
import org.opensearch.migrations.tracing.CommonScopedMetricInstruments;
import org.opensearch.migrations.tracing.DirectNestedSpanContext;
import org.opensearch.migrations.tracing.IScopedInstrumentationAttributes;

import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class ReplayContexts extends IReplayContexts {

    public static final String COUNT_UNIT_STR = "count";
    public static final String BYTES_UNIT_STR = "bytes";

    public static class SocketContext extends DirectNestedSpanContext<
        RootReplayerContext,
        ChannelKeyContext,
        IChannelKeyContext> implements ISocketContext {

        protected SocketContext(ChannelKeyContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
            meterIncrementEvent(getMetrics().channelCreatedCounter);
            meterDeltaEvent(getMetrics().activeSocketConnectionsCounter, 1);
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            final LongUpDownCounter activeSocketConnectionsCounter;
            final LongCounter channelCreatedCounter;
            final LongCounter channelClosedCounter;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                activeSocketConnectionsCounter = meter.upDownCounterBuilder(MetricNames.ACTIVE_TARGET_CONNECTIONS)
                    .build();
                channelCreatedCounter = meter.counterBuilder(MetricNames.CONNECTIONS_OPENED).build();
                channelClosedCounter = meter.counterBuilder(MetricNames.CONNECTIONS_CLOSED).build();
            }
        }

        @Override
        public void sendMeterEventsForEnd() {
            super.sendMeterEventsForEnd();
            meterIncrementEvent(getMetrics().channelClosedCounter);
            meterDeltaEvent(getMetrics().activeSocketConnectionsCounter, -1);
        }

        public static MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        @Override
        public MetricInstruments getMetrics() {
            return getRootInstrumentationScope().socketInstruments;
        }
    }

    public static class ChannelKeyContext extends BaseNestedSpanContext<
        RootReplayerContext,
        IScopedInstrumentationAttributes> implements IReplayContexts.IChannelKeyContext {
        @Getter
        final ISourceTrafficChannelKey channelKey;

        SocketContext socketContext;

        public ChannelKeyContext(
            RootReplayerContext rootScope,
            IScopedInstrumentationAttributes enclosingScope,
            ISourceTrafficChannelKey channelKey
        ) {
            super(rootScope, enclosingScope);
            this.channelKey = channelKey;
            initializeSpan();
            meterDeltaEvent(getMetrics().activeChannelCounter, 1);
        }

        @Override
        public ISocketContext createSocketContext() {
            return new SocketContext(this);
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            final LongUpDownCounter activeChannelCounter;
            final LongCounter unretryableConnectionFailures;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                activeChannelCounter = meter.upDownCounterBuilder(MetricNames.ACTIVE_CHANNELS_YET_TO_BE_FULLY_DISCARDED)
                    .build();
                unretryableConnectionFailures = meter.counterBuilder(MetricNames.NONRETRYABLE_CONNECTION_FAILURES).build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().channelKeyInstruments;
        }

        @Override
        public String toString() {
            return channelKey.toString();
        }

        @Override
        public void sendMeterEventsForEnd() {
            super.sendMeterEventsForEnd();
            meterDeltaEvent(getMetrics().activeChannelCounter, -1);
        }

        @Override
        public void addFailedChannelCreation() {
            meterIncrementEvent(getMetrics().unretryableConnectionFailures);
        }
    }

    public static class KafkaRecordContext extends BaseNestedSpanContext
        implements
            IReplayContexts.IKafkaRecordContext {

        final String recordId;

        public KafkaRecordContext(
            RootReplayerContext rootReplayerContext,
            IChannelKeyContext enclosingScope,
            String recordId,
            int recordSize
        ) {
            super(rootReplayerContext, enclosingScope);
            this.recordId = recordId;
            initializeSpan();
            meterIncrementEvent(getMetrics().recordCounter);
            meterIncrementEvent(getMetrics().bytesCounter, recordSize);
        }

        @Override
        public IChannelKeyContext getLogicalEnclosingScope() {
            return (IChannelKeyContext) getEnclosingScope();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            final LongCounter recordCounter;
            final LongCounter bytesCounter;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                recordCounter = meter.counterBuilder(MetricNames.KAFKA_RECORD_READ).setUnit("records").build();
                bytesCounter = meter.counterBuilder(MetricNames.KAFKA_BYTES_READ).setUnit(BYTES_UNIT_STR).build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().kafkaRecordInstruments;
        }

        @Override
        public String getRecordId() {
            return recordId;
        }

        @Override
        public IReplayContexts.ITrafficStreamsLifecycleContext createTrafficLifecyleContext(ITrafficStreamKey tsk) {
            return new TrafficStreamLifecycleContext(this.getRootInstrumentationScope(), this, tsk);
        }
    }

    public static class TrafficStreamLifecycleContext extends BaseNestedSpanContext<
        RootReplayerContext,
        IScopedInstrumentationAttributes> implements IReplayContexts.ITrafficStreamsLifecycleContext {
        private final ITrafficStreamKey trafficStreamKey;

        protected TrafficStreamLifecycleContext(
            RootReplayerContext rootScope,
            IScopedInstrumentationAttributes enclosingScope,
            ITrafficStreamKey trafficStreamKey
        ) {
            super(rootScope, enclosingScope);
            this.trafficStreamKey = trafficStreamKey;
            initializeSpan();
            meterIncrementEvent(getMetrics().streamsRead);
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private final LongCounter streamsRead;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                streamsRead = meter.counterBuilder(MetricNames.TRAFFIC_STREAMS_READ).setUnit("objects").build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().trafficStreamLifecycleInstruments;
        }

        @Override
        public IReplayContexts.IChannelKeyContext getChannelKeyContext() {
            return getLogicalEnclosingScope();
        }

        @Override
        public HttpTransactionContext createHttpTransactionContext(
            UniqueReplayerRequestKey requestKey,
            Instant sourceTimestamp
        ) {
            return new ReplayContexts.HttpTransactionContext(
                getRootInstrumentationScope(),
                this,
                requestKey,
                sourceTimestamp
            );
        }

        @Override
        public ITrafficStreamKey getTrafficStreamKey() {
            return trafficStreamKey;
        }

        @Override
        public IReplayContexts.IChannelKeyContext getLogicalEnclosingScope() {
            var parent = getEnclosingScope();
            while (!(parent instanceof IReplayContexts.IChannelKeyContext)) {
                parent = parent.getEnclosingScope();
            }
            return (IReplayContexts.IChannelKeyContext) parent;
        }
    }

    public static class HttpTransactionContext extends BaseNestedSpanContext<
        RootReplayerContext,
        IReplayContexts.ITrafficStreamsLifecycleContext> implements IReplayContexts.IReplayerHttpTransactionContext {
        final UniqueReplayerRequestKey replayerRequestKey;
        @Getter
        final Instant timeOfOriginalRequest;
        int numTransactionContextsCreated;

        public HttpTransactionContext(
            RootReplayerContext rootScope,
            IReplayContexts.ITrafficStreamsLifecycleContext enclosingScope,
            UniqueReplayerRequestKey replayerRequestKey,
            Instant timeOfOriginalRequest
        ) {
            super(rootScope, enclosingScope);
            this.replayerRequestKey = replayerRequestKey;
            this.timeOfOriginalRequest = timeOfOriginalRequest;
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            final LongCounter numRetries;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                numRetries = meter.counterBuilder(MetricNames.NUM_REQUEST_RETRIES).setUnit(COUNT_UNIT_STR).build();
            }

        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().httpTransactionInstruments;
        }

        public IReplayContexts.IChannelKeyContext getChannelKeyContext() {
            return getLogicalEnclosingScope();
        }

        @Override
        public UniqueReplayerRequestKey getReplayerRequestKey() {
            return replayerRequestKey;
        }

        @Override
        public String toString() {
            return replayerRequestKey.toString();
        }

        @Override
        public IReplayContexts.IChannelKeyContext getLogicalEnclosingScope() {
            return getImmediateEnclosingScope().getLogicalEnclosingScope();
        }

        @Override
        public IReplayContexts.IRequestAccumulationContext createRequestAccumulationContext() {
            return new ReplayContexts.RequestAccumulationContext(this);
        }

        @Override
        public IReplayContexts.IResponseAccumulationContext createResponseAccumulationContext() {
            return new ReplayContexts.ResponseAccumulationContext(this);
        }

        @Override
        public RequestTransformationContext createTransformationContext() {
            return new ReplayContexts.RequestTransformationContext(this);
        }

        @Override
        public TargetRequestContext createTargetRequestContext() {
            if (numTransactionContextsCreated > 0) {
                meterIncrementEvent(getMetrics().numRetries);
            }
            ++numTransactionContextsCreated;
            return new ReplayContexts.TargetRequestContext(this);
        }

        @Override
        public IReplayContexts.IScheduledContext createScheduledContext(Instant timestamp) {
            return new ReplayContexts.ScheduledContext(this, Duration.between(Instant.now(), timestamp).toNanos());
        }

        @Override
        public IReplayContexts.ITupleHandlingContext createTupleContext() {
            return new ReplayContexts.TupleHandlingContext(this);
        }
    }

    public static class RequestAccumulationContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.IRequestAccumulationContext {
        public RequestAccumulationContext(HttpTransactionContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().requestAccumInstruments;
        }
    }

    public static class ResponseAccumulationContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.IResponseAccumulationContext {
        public ResponseAccumulationContext(HttpTransactionContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().responseAccumInstruments;
        }
    }

    public static class RequestTransformationContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.IRequestTransformationContext {
        public RequestTransformationContext(HttpTransactionContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private final LongCounter headerParses;
            private final LongCounter payloadParses;
            private final LongCounter payloadSuccessParses;
            private final LongCounter jsonPayloadParses;
            private final LongCounter jsonTransformSuccess;
            private final LongCounter payloadBytesIn;
            private final LongCounter uncompressedBytesIn;
            private final LongCounter uncompressedBytesOut;
            private final LongCounter finalPayloadBytesOut;
            private final LongCounter transformSuccess;
            private final LongCounter transformSkipped;
            private final LongCounter transformError;
            private final LongCounter transformBytesIn;
            private final LongCounter transformChunksIn;
            private final LongCounter transformBytesOut;
            private final LongCounter transformChunksOut;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                headerParses = meter.counterBuilder(MetricNames.TRANSFORM_HEADER_PARSE).setUnit(COUNT_UNIT_STR).build();
                payloadParses = meter.counterBuilder(MetricNames.TRANSFORM_PAYLOAD_PARSE_REQUIRED)
                    .setUnit(COUNT_UNIT_STR)
                    .build();
                payloadSuccessParses = meter.counterBuilder(MetricNames.TRANSFORM_PAYLOAD_PARSE_SUCCESS)
                    .setUnit(COUNT_UNIT_STR)
                    .build();
                jsonPayloadParses = meter.counterBuilder(MetricNames.TRANSFORM_JSON_REQUIRED)
                    .setUnit(COUNT_UNIT_STR)
                    .build();
                jsonTransformSuccess = meter.counterBuilder(MetricNames.TRANSFORM_JSON_SUCCEEDED)
                    .setUnit(COUNT_UNIT_STR)
                    .build();
                payloadBytesIn = meter.counterBuilder(MetricNames.TRANSFORM_PAYLOAD_BYTES_IN)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                uncompressedBytesIn = meter.counterBuilder(MetricNames.TRANSFORM_UNCOMPRESSED_BYTES_IN)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                uncompressedBytesOut = meter.counterBuilder(MetricNames.TRANSFORM_UNCOMPRESSED_BYTES_OUT)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                finalPayloadBytesOut = meter.counterBuilder(MetricNames.TRANSFORM_FINAL_PAYLOAD_BYTES_OUT)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                transformSuccess = meter.counterBuilder(MetricNames.TRANSFORM_SUCCESS).setUnit(COUNT_UNIT_STR).build();
                transformSkipped = meter.counterBuilder(MetricNames.TRANSFORM_SKIPPED).setUnit(COUNT_UNIT_STR).build();
                transformError = meter.counterBuilder(MetricNames.TRANSFORM_ERROR).setUnit(COUNT_UNIT_STR).build();
                transformBytesIn = meter.counterBuilder(MetricNames.TRANSFORM_BYTES_IN).setUnit(BYTES_UNIT_STR).build();
                transformChunksIn = meter.counterBuilder(MetricNames.TRANSFORM_CHUNKS_IN)
                    .setUnit(COUNT_UNIT_STR)
                    .build();
                transformBytesOut = meter.counterBuilder(MetricNames.TRANSFORM_BYTES_OUT)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                transformChunksOut = meter.counterBuilder(MetricNames.TRANSFORM_CHUNKS_OUT)
                    .setUnit(COUNT_UNIT_STR)
                    .build();

            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().transformationInstruments;
        }

        @Override
        public void onHeaderParse() {
            meterIncrementEvent(getMetrics().headerParses);
        }

        @Override
        public void onPayloadParse() {
            meterIncrementEvent(getMetrics().payloadParses);
        }

        @Override
        public void onPayloadParseSuccess() {
            meterIncrementEvent(getMetrics().payloadSuccessParses);
        }

        @Override
        public void onJsonPayloadParseRequired() {
            meterIncrementEvent(getMetrics().jsonPayloadParses);
        }

        @Override
        public void onJsonPayloadParseSucceeded() {
            meterIncrementEvent(getMetrics().jsonTransformSuccess);
        }

        @Override
        public void onPayloadBytesIn(int inputSize) {
            meterIncrementEvent(getMetrics().payloadBytesIn, inputSize);
        }

        @Override
        public void onUncompressedBytesIn(int inputSize) {
            meterIncrementEvent(getMetrics().uncompressedBytesIn, inputSize);
        }

        @Override
        public void onUncompressedBytesOut(int inputSize) {
            meterIncrementEvent(getMetrics().uncompressedBytesOut, inputSize);
        }

        @Override
        public void onFinalBytesOut(int inputSize) {
            meterIncrementEvent(getMetrics().finalPayloadBytesOut, inputSize);
        }

        @Override
        public void onTransformSuccess() {
            meterIncrementEvent(getMetrics().transformSuccess);
        }

        @Override
        public void onTransformSkip() {
            meterIncrementEvent(getMetrics().transformSkipped);
        }

        @Override
        public void onTransformFailure() {
            meterIncrementEvent(getMetrics().transformError);
        }

        @Override
        public void aggregateInputChunk(int sizeInBytes) {
            meterIncrementEvent(getMetrics().transformBytesIn, sizeInBytes);
            meterIncrementEvent(getMetrics().transformChunksIn);
        }

        @Override
        public void aggregateOutputChunk(int sizeInBytes) {
            meterIncrementEvent(getMetrics().transformBytesOut, sizeInBytes);
            meterIncrementEvent(getMetrics().transformChunksOut);
        }
    }

    public static class ScheduledContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.IScheduledContext {
        private final long scheduledForNanoTime;

        public ScheduledContext(HttpTransactionContext enclosingScope, long scheduledForNanoTime) {
            super(enclosingScope);
            this.scheduledForNanoTime = scheduledForNanoTime;
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            DoubleHistogram lag;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                lag = meter.histogramBuilder(MetricNames.NETTY_SCHEDULE_LAG).setUnit("ms").build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().scheduledInstruments;
        }

        @Override
        public void sendMeterEventsForEnd() {
            super.sendMeterEventsForEnd();
            meterHistogramMillis(
                getMetrics().lag,
                Duration.ofNanos(Math.max(0, System.nanoTime() - scheduledForNanoTime))
            );
        }
    }

    public static class TargetRequestContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.ITargetRequestContext {
        public TargetRequestContext(HttpTransactionContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
            meterHistogramMillis(
                getMetrics().sourceTargetGap,
                Duration.between(enclosingScope.getTimeOfOriginalRequest(), Instant.now())
            );
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {

            private final DoubleHistogram sourceTargetGap;
            private final LongCounter bytesWritten;
            private final LongCounter bytesRead;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                sourceTargetGap = meter.histogramBuilder(MetricNames.SOURCE_TO_TARGET_REQUEST_LAG)
                    .setUnit("ms")
                    .build();
                bytesWritten = meter.counterBuilder(MetricNames.BYTES_WRITTEN_TO_TARGET)
                    .setUnit(BYTES_UNIT_STR)
                    .build();
                bytesRead = meter.counterBuilder(MetricNames.BYTES_READ_FROM_TARGET).setUnit(BYTES_UNIT_STR).build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().targetRequestInstruments;
        }

        @Override
        public void onBytesSent(int size) {
            meterIncrementEvent(getMetrics().bytesWritten, size);
        }

        @Override
        public void onBytesReceived(int size) {
            meterIncrementEvent(getMetrics().bytesRead, size);
        }

        @Override
        public IRequestConnectingContext createHttpConnectingContext() {
            return new ReplayContexts.RequestConnectingContext(this);
        }

        @Override
        public IRequestSendingContext createHttpSendingContext() {
            return new ReplayContexts.RequestSendingContext(this);
        }

        @Override
        public IReplayContexts.IReceivingHttpResponseContext createHttpReceivingContext() {
            return new ReplayContexts.ReceivingHttpResponseContext(this);
        }

        @Override
        public IReplayContexts.IWaitingForHttpResponseContext createWaitingForResponseContext() {
            return new ReplayContexts.WaitingForHttpResponseContext(this);
        }
    }

    public static class RequestConnectingContext extends DirectNestedSpanContext<
        RootReplayerContext,
        TargetRequestContext,
        IReplayContexts.ITargetRequestContext> implements IReplayContexts.IRequestConnectingContext {
        public RequestConnectingContext(TargetRequestContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().requestConnectingInstruments;
        }
    }

    public static class RequestSendingContext extends DirectNestedSpanContext<
        RootReplayerContext,
        TargetRequestContext,
        IReplayContexts.ITargetRequestContext> implements IReplayContexts.IRequestSendingContext {
        public RequestSendingContext(TargetRequestContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().requestSendingInstruments;
        }
    }

    public static class WaitingForHttpResponseContext extends DirectNestedSpanContext<
        RootReplayerContext,
        TargetRequestContext,
        IReplayContexts.ITargetRequestContext> implements IReplayContexts.IWaitingForHttpResponseContext {
        public WaitingForHttpResponseContext(TargetRequestContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().waitingForHttpResponseInstruments;
        }

    }

    public static class ReceivingHttpResponseContext extends DirectNestedSpanContext<
        RootReplayerContext,
        TargetRequestContext,
        IReplayContexts.ITargetRequestContext> implements IReplayContexts.IReceivingHttpResponseContext {
        public ReceivingHttpResponseContext(TargetRequestContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().receivingHttpInstruments;
        }

    }

    @Getter
    @Setter
    public static class TupleHandlingContext extends DirectNestedSpanContext<
        RootReplayerContext,
        HttpTransactionContext,
        IReplayContexts.IReplayerHttpTransactionContext> implements IReplayContexts.ITupleHandlingContext {
        Integer sourceStatus;
        Integer targetStatus;
        String method;

        public TupleHandlingContext(HttpTransactionContext enclosingScope) {
            super(enclosingScope);
            initializeSpan();
        }

        public static class MetricInstruments extends CommonScopedMetricInstruments {
            private final LongCounter resultCounter;

            private MetricInstruments(Meter meter, String activityName) {
                super(meter, activityName);
                resultCounter = meter.counterBuilder(MetricNames.TUPLE_COMPARISON).build();
            }
        }

        public static @NonNull MetricInstruments makeMetrics(Meter meter) {
            return new MetricInstruments(meter, ACTIVITY_NAME);
        }

        public @NonNull MetricInstruments getMetrics() {
            return getRootInstrumentationScope().tupleHandlingInstruments;
        }

        static final AttributeKey TARGET_STATUS_CODE_ATTR = AttributeKey.longKey("targetStatusCode");

        public AttributesBuilder getSharedAttributes(AttributesBuilder attributesBuilder) {
            final var sourceOp = Optional.ofNullable(sourceStatus);
            final var targetOp = Optional.ofNullable(targetStatus);
            final boolean didMatch = sourceOp.flatMap(ss -> targetOp.map(ss::equals)).orElse(false);
            return addAttributeIfPresent(
                addAttributeIfPresent(
                    addAttributeIfPresent(attributesBuilder, METHOD_KEY, Optional.ofNullable(method)),
                    SOURCE_STATUS_CODE_KEY,
                    sourceOp.map(TupleHandlingContext::categorizeStatus)
                ),
                TARGET_STATUS_CODE_KEY,
                targetOp.map(TupleHandlingContext::categorizeStatus)
            ).put(STATUS_CODE_MATCH_KEY, didMatch);
        }

        @Override
        public AttributesBuilder fillExtraAttributesForThisSpan(AttributesBuilder builder) {
            return getSharedAttributes(super.fillExtraAttributesForThisSpan(builder));
        }

        @Override
        public void sendMeterEventsForEnd() {
            super.sendMeterEventsForEnd();
            AttributesBuilder attributesBuilderForAggregate = getSharedAttributes(Attributes.builder());
            setAllAttributes(attributesBuilderForAggregate.build());
            meterIncrementEvent(getMetrics().resultCounter, 1, attributesBuilderForAggregate);
        }

        /**
         * Convert everything in the 2xx range to 200; 300-399 to 300
         *
         * @param status
         * @return
         */
        public static long categorizeStatus(int status) {
            return (status / 100L) * 100L;
        }

        /**
         * Like httpVersion, Endpoint doesn't have a field because it isn't used as an attribute for metrics
         * (it would create too much cardinality pressure).  So just drop an attribute into a span instead of
         * stashing it for both the span and final metric.
         */
        @Override
        public void setEndpoint(String endpointUrl) {
            setAttribute(ENDPOINT_KEY, endpointUrl);
        }

        /**
         * Like Endpoint, httpVersion doesn't have a field because it isn't used as an attribute for metrics
         * (it just isn't expected to be super-useful and could create too much cardinality pressure).
         * So just drop an attribute into a span instead of stashing it for both the span and final metric.
         */
        @Override
        public void setHttpVersion(String httpVersion) {
            setAttribute(HTTP_VERSION_KEY, httpVersion);
        }

        @Override
        public String toString() {
            return getReplayerRequestKey().toString();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy