2020namespace Tests . ClientConcepts . Troubleshooting
2121{
2222 /**
23+ * [[diagnostic-source]]
2324 * === Diagnostic Source
2425 *
25- * Elasticsearch.Net and by proxy NEST ship with support for DiagnosticSource and Activity out of the box.
26- *
27- * To aid with their discover the topics you can subscribe on and the event names they emit are exposed as
28- * strongly typed strings under `Elasticsearch.Net.Diagnostics.DiagnosticSources`
26+ * Elasticsearch.Net and NEST support capturing diagnostics information using `DiagnosticSource` and `Activity` from the
27+ * `System.Diagnostics` namespace.
2928 *
29+ * To aid with the discoverability of the topics you can subscribe to and the event names they emit,
30+ * both topics and event names are exposed as strongly typed strings under `Elasticsearch.Net.Diagnostics.DiagnosticSources`
3031 */
3132 public class DiagnosticSourceUsageDocumentation : IClusterFixture < ReadOnlyCluster >
3233 {
3334 private readonly ReadOnlyCluster _cluster ;
3435
36+ // hide
3537 public DiagnosticSourceUsageDocumentation ( ReadOnlyCluster cluster ) => _cluster = cluster ;
3638
37-
3839 /**
3940 * Subscribing to DiagnosticSources means implementing `IObserver<DiagnosticListener>`
40- * or use `.Subscribe(observer, filter)` to opt in to the correct topic.
41- *
42- * Here we choose the more verbose `IObserver<>` implementation.
41+ * or using `.Subscribe(observer, filter)` to opt in to the correct topic.
4342 *
43+ * Here we choose the more verbose `IObserver<>` implementation
4444 */
45- private class ListenerObserver : IObserver < DiagnosticListener > , IDisposable
45+ public class ListenerObserver : IObserver < DiagnosticListener > , IDisposable
4646 {
4747 private long _messagesWrittenToConsole = 0 ;
4848 public long MessagesWrittenToConsole => _messagesWrittenToConsole ;
4949
5050 public Exception SeenException { get ; private set ; }
51- public void OnError ( Exception error ) => SeenException = error ;
5251
52+ public void OnError ( Exception error ) => SeenException = error ;
5353 public bool Completed { get ; private set ; }
5454 public void OnCompleted ( ) => Completed = true ;
5555
@@ -61,21 +61,9 @@ private void WriteToConsole<T>(string eventName, T data)
6161
6262 private List < IDisposable > Disposables { get ; } = new List < IDisposable > ( ) ;
6363
64- /**
65- * By inspecting the name we selectively subscribe only to topics `Elasticsearch.Net` emits.
66- *
67- * Thanks to `DiagnosticSources` you do not have to guess the topics we emit under.
68- *
69- * `DiagnosticListener.Subscribe` expects an `IObserver<KeyValuePair<string, object>>` which is useful to create
70- * a decoupled messaging contract but as a subscriber you would like to know what `object` is.
71- *
72- * Therefor each topic we ship with has a dedicated `Observer` implementation that takes an `onNext` lambda
73- * which is typed to the context object we actually emit.
74- *
75- */
7664 public void OnNext ( DiagnosticListener value )
7765 {
78- void TrySubscribe ( string sourceName , Func < IObserver < KeyValuePair < string , object > > > listener )
66+ void TrySubscribe ( string sourceName , Func < IObserver < KeyValuePair < string , object > > > listener ) // <1> By inspecting the name, we can selectively subscribe only to the topics `Elasticsearch.Net` emit
7967 {
8068 if ( value . Name != sourceName ) return ;
8169
@@ -88,11 +76,7 @@ void TrySubscribe(string sourceName, Func<IObserver<KeyValuePair<string, object>
8876
8977 TrySubscribe ( DiagnosticSources . Serializer . SourceName ,
9078 ( ) => new SerializerDiagnosticObserver ( v => WriteToConsole ( v . Key , v . Value ) ) ) ;
91- /**
92- * RequestPipeline emits a different context object for the start of the `Activity` then it does
93- * for the end of the `Activity` therefor `RequestPipelineDiagnosticObserver` accepts two `onNext` lambda's.
94- * One for the `.Start` events and one for the `.Stop` events.
95- */
79+
9680 TrySubscribe ( DiagnosticSources . RequestPipeline . SourceName ,
9781 ( ) => new RequestPipelineDiagnosticObserver (
9882 v => WriteToConsole ( v . Key , v . Value ) ,
@@ -111,37 +95,43 @@ public void Dispose()
11195 foreach ( var d in Disposables ) d . Dispose ( ) ;
11296 }
11397 }
114-
98+ /**
99+ * Thanks to `DiagnosticSources`, you do not have to guess the topics emitted.
100+ *
101+ * The `DiagnosticListener.Subscribe` method expects an `IObserver<KeyValuePair<string, object>>`
102+ * which is a rather generic message contract. As a subscriber, it's useful to know what `object` is in each case.
103+ * To help with this, each topic within the client has a dedicated `Observer` implementation that
104+ * takes an `onNext` delegate typed to the context object actually emitted.
105+ *
106+ * The RequestPipeline diagnostic source emits a different context objects the start and end of the `Activity`
107+ * For this reason, `RequestPipelineDiagnosticObserver` accepts two `onNext` delegates,
108+ * one for the `.Start` events and one for the `.Stop` events.
109+ *
110+ * [[subscribing-to-topics]]
111+ * ==== Subscribing to topics
112+ *
113+ * As a concrete example of subscribing to topics, let's hook into all diagnostic sources and use
114+ * `ListenerObserver` to only listen to the ones from `Elasticsearch.Net`
115+ */
115116 [ I ] public void SubscribeToTopics ( )
116117 {
117- /**
118- * Here we hook into all diagnostic sources and use `ListenerObserver` to only listen to the ones
119- * from `Elasticsearch.Net`
120- */
118+
121119 using ( var listenerObserver = new ListenerObserver ( ) )
122120 using ( var subscription = DiagnosticListener . AllListeners . Subscribe ( listenerObserver ) )
123121 {
124-
125- /**
126- * We'll use a Sniffing connection pool here since it sniffs on startup and pings before
127- * first usage, so our diagnostics are involved enough to showcase most topics.
128- */
129- var pool = new SniffingConnectionPool ( new [ ] { TestConnectionSettings . CreateUri ( ) } ) ;
122+ var pool = new SniffingConnectionPool ( new [ ] { TestConnectionSettings . CreateUri ( ) } ) ; // <1> use a sniffing connection pool that sniffs on startup and pings before first usage, so our diagnostics will emit most topics.
130123 var connectionSettings = new ConnectionSettings ( pool )
131124 . DefaultMappingFor < Project > ( i => i
132125 . IndexName ( "project" )
133126 ) ;
134127
135128 var client = new ElasticClient ( connectionSettings ) ;
136129
137- /**
138- * After issuing the following request
139- */
140- var response = client . Search < Project > ( s => s
130+ var response = client . Search < Project > ( s => s // <2> make a search API call
141131 . MatchAll ( )
142132 ) ;
143133
144- listenerObserver . SeenException . Should ( ) . BeNull ( ) ;
134+ listenerObserver . SeenException . Should ( ) . BeNull ( ) ; // <3> verify that the listener is picking up events
145135 listenerObserver . Completed . Should ( ) . BeFalse ( ) ;
146136 listenerObserver . MessagesWrittenToConsole . Should ( ) . BeGreaterThan ( 0 ) ;
147137 }
0 commit comments