概要
IObservable<T>.Debug
オペレーターを挟むことで、以下のタイミングでログを出力することができます。
public static IObservable<T> Debug<T>(this IObservable<T> source, string label = null); public static IObservable<T> Debug<T>(this IObservable<T> source, UniRx.Diagnostics.Logger logger);
ログ出力するのは以下のタイミング*1です。
- OnNext
- OnError
- OnComplete
- OnCancel
- OnSubscribe
利用サンプル
public class Tests { [Test] public void SimplePasses1() { var subject = new Subject<int>(); var observable = (IObservable<int>) subject; // stringを引数にとったDebugオペレーターを追加する observable = observable.Debug("MyDebug"); observable.Subscribe(x => {}); // [MyDebug]OnSubscribe var disposable = observable.Subscribe(x => { }); // [MyDebug]OnSubscribe subject.OnNext(1); // [MyDebug]OnNext(1), [MyDebug]OnNext(1) subject.OnNext(2); // [MyDebug]OnNext(2), [MyDebug]OnNext(2) disposable.Dispose(); // [MyDebug]OnCancel subject.OnNext(3); // [MyDebug]OnNext(3) subject.OnCompleted(); // [MyDebug]OnCompleted() } [Test] public void SimplePasses2() { var subject = new Subject<int>(); var observable = (IObservable<int>) subject; // stringを引数にとったDebugオペレーターを追加する observable = observable.Debug("MyDebug"); observable.Subscribe(x => {}); // [MyDebug]OnSubscribe subject.OnNext(1); // [MyDebug]OnNext(1) try { subject.OnError(new Exception("Hoge Hoge")); // [MyDebug]OnError(System.Exception) Assert.Fail(); } catch { Assert.Pass(); } } [Test] public void SimplePasses3() { var subject = new Subject<int>(); var observable = (IObservable<int>) subject; // Loggerを新規作成し、Loggerの出力をUnityEngine.Debug.Logするように購読する // デフォルトではUnityEngine.DebugLogの実行を購読していないみたいなので、自身で設定してあげる必要あり(staticのため共通のインスタンスなので注意) var logger = new UniRx.Diagnostics.Logger("MyLogger"); ObservableLogger.Listener.Subscribe(x => Debug.Log($"[{x.LoggerName}]{x.Message}")); // UniRx.Diagnostics.Loggerを引数にとったDebugオペレーター observable = observable.Debug(logger); observable.Subscribe(x => {}); // [MyLogger]OnSubscribe subject.OnNext(1); // [MyLogger]OnNext(1) subject.OnCompleted(); // [MyLogger]OnCompleted() } }
コメントにも書いておきましたが、UniRx.Diagnostics.Logger
を利用する場合は購読する処理も書かなければいけません。
ただObservableLogger.Listener
はstatic
で共通なので、他のLogger
インスタンスの動作にも影響が出てしまいます。
(LoggerのコンストラクタにてObservableLogger.logPublisher
のOnNext
と紐付けれており、Listener.Subscribe
メソッドはObservableLogger.logPublisher
を購読している)
仕組み
動作の理解のために中のコードを見てみましょう。
namespace UniRx.Diagnostics { public static class ObservableDebugExtensions { /// <summary> /// Debug helper of observbale stream. Works for only DEBUG symbol. /// </summary> public static IObservable<T> Debug<T>(this IObservable<T> source, string label = null) { #if DEBUG var l = (label == null) ? "" : "[" + label + "]"; return source.Materialize() .Do(x => UnityEngine.Debug.Log(l + x.ToString())) .Dematerialize() .DoOnCancel(() => UnityEngine.Debug.Log(l + "OnCancel")) .DoOnSubscribe(() => UnityEngine.Debug.Log(l + "OnSubscribe")); #else return source; #endif } /// <summary> /// Debug helper of observbale stream. Works for only DEBUG symbol. /// </summary> public static IObservable<T> Debug<T>(this IObservable<T> source, UniRx.Diagnostics.Logger logger) { #if DEBUG return source.Materialize() .Do(x => logger.Debug(x.ToString())) .Dematerialize() .DoOnCancel(() => logger.Debug("OnCancel")) .DoOnSubscribe(() => logger.Debug("OnSubscribe")); #else return source; #endif } } }
よく見てみると、以下のオペレーターが順番に実行されています。
オペレーター | 意味 |
---|---|
Materialize | OnNext・OnError・OnComplete ( を含むメッセージ*2 ) をNotification<T> というオブジェクトに変換してOnNextのメッセージとして出力する |
Do | OnNextのたびに呼ばれる |
Dematerialize | Materializeをもとに戻す |
DoOnCancel | 購読解除された時に呼ばれる |
DoOnSubscribe | 購読された時に呼ばれる |
これを考えると、なぜDebug
オペレーターが以下のタイミングでログ出力を行うかが分かります。
- OnNext
- OnError
- OnComplete
- OnCancel
- OnSubscribe