|
37 | 37 | import io.opentelemetry.context.Context; |
38 | 38 | import io.opentelemetry.context.Scope; |
39 | 39 | import io.reactivex.rxjava3.core.Completable; |
| 40 | +import io.reactivex.rxjava3.core.CompletableObserver; |
40 | 41 | import io.reactivex.rxjava3.core.CompletableSource; |
41 | 42 | import io.reactivex.rxjava3.core.CompletableTransformer; |
42 | 43 | import io.reactivex.rxjava3.core.Flowable; |
43 | 44 | import io.reactivex.rxjava3.core.FlowableTransformer; |
44 | 45 | import io.reactivex.rxjava3.core.Maybe; |
| 46 | +import io.reactivex.rxjava3.core.MaybeObserver; |
45 | 47 | import io.reactivex.rxjava3.core.MaybeSource; |
46 | 48 | import io.reactivex.rxjava3.core.MaybeTransformer; |
47 | 49 | import io.reactivex.rxjava3.core.Single; |
| 50 | +import io.reactivex.rxjava3.core.SingleObserver; |
48 | 51 | import io.reactivex.rxjava3.core.SingleSource; |
49 | 52 | import io.reactivex.rxjava3.core.SingleTransformer; |
| 53 | +import io.reactivex.rxjava3.disposables.Disposable; |
50 | 54 | import java.util.ArrayList; |
51 | 55 | import java.util.HashMap; |
52 | 56 | import java.util.List; |
|
58 | 62 | import java.util.function.Consumer; |
59 | 63 | import java.util.function.Supplier; |
60 | 64 | import org.reactivestreams.Publisher; |
| 65 | +import org.reactivestreams.Subscriber; |
| 66 | +import org.reactivestreams.Subscription; |
61 | 67 | import org.slf4j.Logger; |
62 | 68 | import org.slf4j.LoggerFactory; |
63 | 69 |
|
@@ -550,4 +556,185 @@ public CompletableSource apply(Completable upstream) { |
550 | 556 | }); |
551 | 557 | } |
552 | 558 | } |
| 559 | + |
| 560 | + /** |
| 561 | + * Returns a transformer that re-activates a given context for the duration of the stream's |
| 562 | + * subscription. |
| 563 | + * |
| 564 | + * @param context The context to re-activate. |
| 565 | + * @param <T> The type of the stream. |
| 566 | + * @return A transformer that re-activates the context. |
| 567 | + */ |
| 568 | + public static <T> ContextTransformer<T> withContext(Context context) { |
| 569 | + return new ContextTransformer<>(context); |
| 570 | + } |
| 571 | + |
| 572 | + /** |
| 573 | + * A transformer that re-activates a given context for the duration of the stream's subscription. |
| 574 | + * |
| 575 | + * @param <T> The type of the stream. |
| 576 | + */ |
| 577 | + public static final class ContextTransformer<T> |
| 578 | + implements FlowableTransformer<T, T>, |
| 579 | + SingleTransformer<T, T>, |
| 580 | + MaybeTransformer<T, T>, |
| 581 | + CompletableTransformer { |
| 582 | + private final Context context; |
| 583 | + |
| 584 | + private ContextTransformer(Context context) { |
| 585 | + this.context = context; |
| 586 | + } |
| 587 | + |
| 588 | + @Override |
| 589 | + public Publisher<T> apply(Flowable<T> upstream) { |
| 590 | + return upstream.lift(subscriber -> TracingObserver.wrap(context, subscriber)); |
| 591 | + } |
| 592 | + |
| 593 | + @Override |
| 594 | + public SingleSource<T> apply(Single<T> upstream) { |
| 595 | + return upstream.lift(observer -> TracingObserver.wrap(context, observer)); |
| 596 | + } |
| 597 | + |
| 598 | + @Override |
| 599 | + public MaybeSource<T> apply(Maybe<T> upstream) { |
| 600 | + return upstream.lift(observer -> TracingObserver.wrap(context, observer)); |
| 601 | + } |
| 602 | + |
| 603 | + @Override |
| 604 | + public CompletableSource apply(Completable upstream) { |
| 605 | + return upstream.lift(observer -> TracingObserver.wrap(context, observer)); |
| 606 | + } |
| 607 | + } |
| 608 | + |
| 609 | + /** |
| 610 | + * An observer that wraps another observer and ensures that the OpenTelemetry context is active |
| 611 | + * during all callback methods. |
| 612 | + * |
| 613 | + * <p>This implementation only wraps the data-flow callbacks (`onNext`, `onSuccess`, etc.). The |
| 614 | + * `Subscription.request/cancel` and `Disposable.dispose` calls are not wrapped in the context. If |
| 615 | + * the upstream logic depends on the context during these signals, they might lose trace |
| 616 | + * information. Given this is a manual `withContext` utility, this might be an acceptable |
| 617 | + * trade-off for simplicity/performance, but worth keeping in mind. |
| 618 | + * |
| 619 | + * @param <T> The type of the items emitted by the stream. |
| 620 | + */ |
| 621 | + private static final class TracingObserver<T> |
| 622 | + implements Subscriber<T>, SingleObserver<T>, MaybeObserver<T>, CompletableObserver { |
| 623 | + private final Context context; |
| 624 | + private final Subscriber<? super T> subscriber; |
| 625 | + private final SingleObserver<? super T> singleObserver; |
| 626 | + private final MaybeObserver<? super T> maybeObserver; |
| 627 | + private final CompletableObserver completableObserver; |
| 628 | + |
| 629 | + private TracingObserver( |
| 630 | + Context context, |
| 631 | + Subscriber<? super T> subscriber, |
| 632 | + SingleObserver<? super T> singleObserver, |
| 633 | + MaybeObserver<? super T> maybeObserver, |
| 634 | + CompletableObserver completableObserver) { |
| 635 | + this.context = context; |
| 636 | + this.subscriber = subscriber; |
| 637 | + this.singleObserver = singleObserver; |
| 638 | + this.maybeObserver = maybeObserver; |
| 639 | + this.completableObserver = completableObserver; |
| 640 | + } |
| 641 | + |
| 642 | + static <T> TracingObserver<T> wrap(Context context, Subscriber<? super T> subscriber) { |
| 643 | + return new TracingObserver<>(context, subscriber, null, null, null); |
| 644 | + } |
| 645 | + |
| 646 | + static <T> TracingObserver<T> wrap(Context context, SingleObserver<? super T> observer) { |
| 647 | + return new TracingObserver<>(context, null, observer, null, null); |
| 648 | + } |
| 649 | + |
| 650 | + static <T> TracingObserver<T> wrap(Context context, MaybeObserver<? super T> observer) { |
| 651 | + return new TracingObserver<>(context, null, null, observer, null); |
| 652 | + } |
| 653 | + |
| 654 | + static <T> TracingObserver<T> wrap(Context context, CompletableObserver observer) { |
| 655 | + return new TracingObserver<>(context, null, null, null, observer); |
| 656 | + } |
| 657 | + |
| 658 | + private void runInContext(Runnable action) { |
| 659 | + try (Scope scope = context.makeCurrent()) { |
| 660 | + action.run(); |
| 661 | + } |
| 662 | + } |
| 663 | + |
| 664 | + @Override |
| 665 | + public void onSubscribe(Subscription s) { |
| 666 | + runInContext( |
| 667 | + () -> { |
| 668 | + if (subscriber != null) { |
| 669 | + subscriber.onSubscribe(s); |
| 670 | + } |
| 671 | + }); |
| 672 | + } |
| 673 | + |
| 674 | + @Override |
| 675 | + public void onSubscribe(Disposable d) { |
| 676 | + runInContext( |
| 677 | + () -> { |
| 678 | + if (singleObserver != null) { |
| 679 | + singleObserver.onSubscribe(d); |
| 680 | + } else if (maybeObserver != null) { |
| 681 | + maybeObserver.onSubscribe(d); |
| 682 | + } else if (completableObserver != null) { |
| 683 | + completableObserver.onSubscribe(d); |
| 684 | + } |
| 685 | + }); |
| 686 | + } |
| 687 | + |
| 688 | + @Override |
| 689 | + public void onNext(T t) { |
| 690 | + runInContext( |
| 691 | + () -> { |
| 692 | + if (subscriber != null) { |
| 693 | + subscriber.onNext(t); |
| 694 | + } |
| 695 | + }); |
| 696 | + } |
| 697 | + |
| 698 | + @Override |
| 699 | + public void onSuccess(T t) { |
| 700 | + runInContext( |
| 701 | + () -> { |
| 702 | + if (singleObserver != null) { |
| 703 | + singleObserver.onSuccess(t); |
| 704 | + } else if (maybeObserver != null) { |
| 705 | + maybeObserver.onSuccess(t); |
| 706 | + } |
| 707 | + }); |
| 708 | + } |
| 709 | + |
| 710 | + @Override |
| 711 | + public void onError(Throwable t) { |
| 712 | + runInContext( |
| 713 | + () -> { |
| 714 | + if (subscriber != null) { |
| 715 | + subscriber.onError(t); |
| 716 | + } else if (singleObserver != null) { |
| 717 | + singleObserver.onError(t); |
| 718 | + } else if (maybeObserver != null) { |
| 719 | + maybeObserver.onError(t); |
| 720 | + } else if (completableObserver != null) { |
| 721 | + completableObserver.onError(t); |
| 722 | + } |
| 723 | + }); |
| 724 | + } |
| 725 | + |
| 726 | + @Override |
| 727 | + public void onComplete() { |
| 728 | + runInContext( |
| 729 | + () -> { |
| 730 | + if (subscriber != null) { |
| 731 | + subscriber.onComplete(); |
| 732 | + } else if (maybeObserver != null) { |
| 733 | + maybeObserver.onComplete(); |
| 734 | + } else if (completableObserver != null) { |
| 735 | + completableObserver.onComplete(); |
| 736 | + } |
| 737 | + }); |
| 738 | + } |
| 739 | + } |
553 | 740 | } |
0 commit comments