lib.rs 12.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

17
18
19
20
21
//! Polkadot Jaeger related primitives
//!
//! Provides primitives used by Polkadot for interfacing with Jaeger.
//!
//! # Integration
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//!
//! See <https://www.jaegertracing.io/> for an introduction.
//!
//! The easiest way to try Jaeger is:
//!
//! - Start a docker container with the all-in-one docker image (see below).
//! - Open your browser and navigate to <https://localhost:16686> to acces the UI.
//!
//! The all-in-one image can be started with:
//!
//! ```not_rust
//! podman login docker.io
//! podman run -d --name jaeger \
//!  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
//!  -p 5775:5775/udp \
//!  -p 6831:6831/udp \
//!  -p 6832:6832/udp \
//!  -p 5778:5778 \
//!  -p 16686:16686 \
//!  -p 14268:14268 \
//!  -p 14250:14250 \
//!  -p 9411:9411 \
//!  docker.io/jaegertracing/all-in-one:1.21
//! ```

47
use sp_core::traits::SpawnNamed;
48
49
50
use polkadot_primitives::v1::{CandidateHash, Hash, PoV, ValidatorIndex};
use sc_network::PeerId;

51
use parking_lot::RwLock;
52
use std::{sync::Arc, result};
53

54
55
56
57
58
59
60
61
62
63
/// A description of an error causing the chain API request to be unservable.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum JaegerError {
	#[error("Already launched the collector thread")]
	AlreadyLaunched,

	#[error("Missing jaeger configuration")]
	MissingConfiguration,
}
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

lazy_static::lazy_static! {
	static ref INSTANCE: RwLock<Jaeger> = RwLock::new(Jaeger::None);
}

/// Configuration for the jaeger tracing.
#[derive(Clone)]
pub struct JaegerConfig {
	node_name: String,
	agent_addr: std::net::SocketAddr,
}

impl std::default::Default for JaegerConfig {
	fn default() -> Self {
		Self {
			node_name: "unknown_".to_owned(),
			agent_addr: "127.0.0.1:6831".parse().expect(r#"Static "127.0.0.1:6831" is a valid socket address string. qed"#),
		}
	}
}

impl JaegerConfig {
	/// Use the builder pattern to construct a configuration.
	pub fn builder() -> JaegerConfigBuilder {
		JaegerConfigBuilder::default()
	}
}


/// Jaeger configuration builder.
#[derive(Default)]
pub struct JaegerConfigBuilder {
	inner: JaegerConfig
}

impl JaegerConfigBuilder {
	/// Set the name for this node.
	pub fn named<S>(mut self, name: S) -> Self where S: AsRef<str> {
		self.inner.node_name = name.as_ref().to_owned();
		self
	}

	/// Set the agent address to send the collected spans to.
	pub fn agent<U>(mut self, addr: U) -> Self where U: Into<std::net::SocketAddr> {
		self.inner.agent_addr = addr.into();
		self
	}

	/// Construct the configuration.
	pub fn build(self) -> JaegerConfig {
		self.inner
	}
}

118
119
120
121
122
123
124
125
126
127
/// A special "per leaf span".
///
/// Essentially this span wraps two spans:
///
/// 1. The span that is created per leaf in the overseer.
/// 2. Some child span of the per-leaf span.
///
/// This just works as auxiliary structure to easily store both.
#[derive(Debug)]
pub struct PerLeafSpan {
128
129
	leaf_span: Arc<Span>,
	span: Span,
130
131
132
133
134
135
136
137
}

impl PerLeafSpan {
	/// Creates a new instance.
	///
	/// Takes the `leaf_span` that is created by the overseer per leaf and a name for a child span.
	/// Both will be stored in this object, while the child span is implicitly accessible by using the
	/// [`Deref`](std::ops::Deref) implementation.
138
	pub fn new(leaf_span: Arc<Span>, name: &'static str) -> Self {
139
140
141
142
143
144
145
146
147
		let span = leaf_span.child(name);

		Self {
			span,
			leaf_span,
		}
	}

	/// Returns the leaf span.
148
	pub fn leaf_span(&self) -> &Arc<Span> {
149
150
151
152
153
154
		&self.leaf_span
	}
}

/// Returns a reference to the child span.
impl std::ops::Deref for PerLeafSpan {
155
	type Target = Span;
156

157
	fn deref(&self) -> &Span {
158
159
160
161
		&self.span
	}
}

162
163
164
165
166
167
168
169
170
171

/// A helper to annotate the stage with a numerical value
/// to ease the life of the tooling team creating viable
/// statistical metrics for which stage of the inclusion
/// pipeline drops a significant amount of candidates,
/// statistically speaking.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[non_exhaustive]
pub enum Stage {
172
173
174
175
176
177
178
179
180
181
182
183
	CandidateSelection = 1,
	CandidateBacking = 2,
	StatementDistribution = 3,
	PoVDistribution = 4,
	AvailabilityDistribution = 5,
	AvailabilityRecovery = 6,
	BitfieldDistribution = 7,
	// Expand as needed, numbers should be ascending according to the stage
	// through the inclusion pipeline, or according to the descriptions
	// in [the path of a para chain block]
	// (https://polkadot.network/the-path-of-a-parachain-block/)
	// see [issue](https://github.com/paritytech/polkadot/issues/2389)
184
185
186
187
188
}

/// Builder pattern for children and root spans to unify
/// information annotations.
pub struct SpanBuilder {
189
	span: Span,
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
}

impl SpanBuilder {
	/// Attach a peer id to the span.
	#[inline(always)]
	pub fn with_peer_id(mut self, peer: &PeerId) -> Self {
		self.span.add_string_tag("peer-id", &peer.to_base58());
		self
	}
	/// Attach a candidate hash to the span.
	#[inline(always)]
	pub fn with_candidate(mut self, candidate_hash: &CandidateHash) -> Self  {
		self.span.add_string_tag("candidate-hash", &format!("{:?}", candidate_hash.0));
		self
	}
	/// Attach a candidate stage.
	/// Should always come with a `CandidateHash`.
	#[inline(always)]
208
209
	pub fn with_stage(mut self, stage: Stage) -> Self {
		self.span.add_stage(stage);
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
		self
	}

	#[inline(always)]
	pub fn with_validator_index(mut self, validator: ValidatorIndex) -> Self {
		self.span.add_string_tag("validator-index", &validator.to_string());
		self
	}

	#[inline(always)]
	pub fn with_chunk_index(mut self, chunk_index: u32) -> Self {
		self.span.add_string_tag("chunk-index", &format!("{}", chunk_index));
		self
	}

	#[inline(always)]
	pub fn with_relay_parent(mut self, relay_parent: &Hash) -> Self {
		self.span.add_string_tag("relay-parent", &format!("{:?}", relay_parent));
		self
	}

	#[inline(always)]
	pub fn with_claimed_validator_index(mut self, claimed_validator_index: ValidatorIndex) -> Self {
		self.span.add_string_tag(
			"claimed-validator",
			&claimed_validator_index.to_string(),
		);
		self
	}

240
241
242
243
244
245
	#[inline(always)]
	pub fn with_pov(mut self, pov: &PoV) -> Self {
		self.span.add_pov(pov);
		self
	}

246
247
	/// Complete construction.
	#[inline(always)]
248
	pub fn build(self) -> Span {
249
250
251
252
253
		self.span
	}
}


254
255
256
/// A wrapper type for a span.
///
/// Handles running with and without jaeger.
257
pub enum Span {
258
259
260
261
262
263
	/// Running with jaeger being enabled.
	Enabled(mick_jaeger::Span),
	/// Running with jaeger disabled.
	Disabled,
}

264
impl Span {
265
	/// Derive a child span from `self`.
266
	pub fn child(&self, name: &'static str) -> Self {
267
268
269
270
271
		match self {
			Self::Enabled(inner) => Self::Enabled(inner.child(name)),
			Self::Disabled => Self::Disabled,
		}
	}
272

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
	pub fn child_builder(&self, name: &'static str) -> SpanBuilder {
		SpanBuilder {
			span: self.child(name),
		}
	}

	/// Derive a child span from `self` but add a candidate annotation.
	/// A shortcut for
	///
	/// ```rust,no_run
	/// # use polkadot_primitives::v1::CandidateHash;
	/// # use polkadot_node_jaeger::candidate_hash_span;
	/// # let hash = CandidateHash::default();
	/// # let span = candidate_hash_span(&hash, "foo");
	/// let _span = span.child_builder("name").with_candidate(&hash).build();
	/// ```
	#[inline(always)]
	pub fn child_with_candidate(&self, name: &'static str, candidate_hash: &CandidateHash) -> Self {
		self.child_builder(name).with_candidate(candidate_hash).build()
	}

294
295
296
297
298
299
300
301
302
303
304
305
	/// Add candidate stage annotation.
	pub fn add_stage(&mut self, stage: Stage) {
		self.add_string_tag("candidate-stage", &format!("{}", stage as u8));
	}

	pub fn add_pov(&mut self, pov: &PoV) {
		if self.is_enabled() {
			// avoid computing the pov hash if jaeger is not enabled
			self.add_string_tag("pov", &format!("{:?}", pov.hash()));
		}
	}

306
307
308
309
310
311
312
	/// Add an additional tag to the span.
	pub fn add_string_tag(&mut self, tag: &str, value: &str) {
		match self {
			Self::Enabled(ref mut inner) => inner.add_string_tag(tag, value),
			Self::Disabled => {},
		}
	}
313
314
315
316
317
318
319
320

	/// Adds the `FollowsFrom` relationship to this span with respect to the given one.
	pub fn add_follows_from(&mut self, other: &Self) {
		match (self, other) {
			(Self::Enabled(ref mut inner), Self::Enabled(ref other_inner)) => inner.add_follows_from(&other_inner),
			_ => {},
		}
	}
321
322
323
324
325
326
327
328
329

	/// Helper to check whether jaeger is enabled
	/// in order to avoid computational overhead.
	pub const fn is_enabled(&self) -> bool {
		match self {
			Span::Enabled(_) => true,
			_ => false,
		}
	}
330
331
}

332
impl std::fmt::Debug for Span {
333
334
335
336
337
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "<jaeger span>")
	}
}

338
impl From<Option<mick_jaeger::Span>> for Span {
339
340
341
342
343
344
345
346
347
	fn from(src: Option<mick_jaeger::Span>) -> Self {
		if let Some(span) = src {
			Self::Enabled(span)
		} else {
			Self::Disabled
		}
	}
}

348
impl From<mick_jaeger::Span> for Span {
349
350
351
352
353
	fn from(src: mick_jaeger::Span) -> Self {
		Self::Enabled(src)
	}
}

354
/// Shortcut for [`hash_span`] with the hash of the `Candidate` block.
355
356
pub fn candidate_hash_span(candidate_hash: &CandidateHash, span_name: &'static str) -> Span {
	let mut span: Span = INSTANCE.read_recursive()
357
358
359
360
		.span(|| { candidate_hash.0 }, span_name).into();

	span.add_string_tag("candidate-hash", &format!("{:?}", candidate_hash.0));
	span
361
362
363
364
}

/// Shortcut for [`hash_span`] with the hash of the `PoV`.
#[inline(always)]
365
366
367
368
369
370
pub fn pov_span(pov: &PoV, span_name: &'static str) -> Span {
	let mut span: Span = INSTANCE.read_recursive()
		.span(|| { pov.hash() }, span_name).into();

	span.add_pov(pov);
	span
371
372
373
374
}

/// Creates a `Span` referring to the given hash. All spans created with [`hash_span`] with the
/// same hash (even from multiple different nodes) will be visible in the same view on Jaeger.
375
376
///
/// This span automatically has the `relay-parent` tag set.
377
#[inline(always)]
378
379
pub fn hash_span(hash: &Hash, span_name: &'static str) -> Span {
	let mut span: Span = INSTANCE.read_recursive().span(|| { *hash }, span_name).into();
380
381
	span.add_string_tag("relay-parent", &format!("{:?}", hash));
	span
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
}

/// Stateful convenience wrapper around [`mick_jaeger`].
pub enum Jaeger {
	/// Launched and operational state.
	Launched {
		/// [`mick_jaeger`] provided API to record spans to.
		traces_in: Arc<mick_jaeger::TracesIn>,
	},
	/// Preparation state with the necessary config to launch the collector.
	Prep(JaegerConfig),
	/// Uninitialized, suggests wrong API usage if encountered.
	None,
}

impl Jaeger {
	/// Spawn the jaeger instance.
	pub fn new(cfg: JaegerConfig) -> Self {
		Jaeger::Prep(cfg)
	}

	/// Spawn the background task in order to send the tracing information out via udp
	#[cfg(target_os = "unknown")]
	pub fn launch<S: SpawnNamed>(self, _spawner: S) -> result::Result<(), JaegerError> {
		Ok(())
	}

	/// Spawn the background task in order to send the tracing information out via udp
	#[cfg(not(target_os = "unknown"))]
	pub fn launch<S: SpawnNamed>(self, spawner: S) -> result::Result<(), JaegerError> {
		let cfg = match self {
			Self::Prep(cfg) => Ok(cfg),
			Self::Launched{ .. } => {
				return Err(JaegerError::AlreadyLaunched)
			}
			Self::None => Err(JaegerError::MissingConfiguration),
		}?;

		let jaeger_agent = cfg.agent_addr;

		log::info!("🐹 Collecting jaeger spans for {:?}", &jaeger_agent);

		let (traces_in, mut traces_out) = mick_jaeger::init(mick_jaeger::Config {
425
			service_name: format!("polkadot-{}", cfg.node_name),
426
427
428
429
		});

		// Spawn a background task that pulls span information and sends them on the network.
		spawner.spawn("jaeger-collector", Box::pin(async move {
430
			match async_std::net::UdpSocket::bind("0.0.0.0:0").await {
431
432
433
				Ok(udp_socket) => loop {
					let buf = traces_out.next().await;
					// UDP sending errors happen only either if the API is misused or in case of missing privilege.
434
435
					if let Err(e) = udp_socket.send_to(&buf, jaeger_agent).await {
						log::debug!(target: "jaeger", "UDP send error: {}", e);
436
437
438
					}
				}
				Err(e) => {
439
					log::warn!(target: "jaeger", "UDP socket open error: {}", e);
440
441
442
443
444
445
446
447
448
449
				}
			}
		}));

		*INSTANCE.write() = Self::Launched {
			traces_in,
		};
		Ok(())
	}

450
	fn span<F>(&self, lazy_hash: F, span_name: &'static str) -> Option<mick_jaeger::Span>
451
452
453
454
455
456
457
458
459
460
461
462
463
464
	where
		F: Fn() -> Hash,
	{
		if let Self::Launched { traces_in , .. } = self {
			let hash = lazy_hash();
			let mut buf = [0u8; 16];
			buf.copy_from_slice(&hash.as_ref()[0..16]);
			let trace_id = std::num::NonZeroU128::new(u128::from_be_bytes(buf))?;
			Some(traces_in.span(trace_id, span_name))
		} else {
			None
		}
	}
}