1use std::{
12 sync::Arc,
13 time::{Duration, Instant},
14};
15
16use blake2b_simd::Params as Blake2bParams;
17use serde_json::{Map, Number, Value};
18use zakura_jsonl_trace::{JsonlTracer, JsonlWriteEvent};
19
20use super::{ZakuraPeerId, ZakuraRejectReason};
21
22#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24pub struct ZakuraTraceTable {
25 table: &'static str,
26 file_name: &'static str,
27}
28
29impl ZakuraTraceTable {
30 pub fn table(self) -> &'static str {
32 self.table
33 }
34
35 pub fn file_name(self) -> &'static str {
37 self.file_name
38 }
39}
40
41pub const HANDSHAKE_TABLE: ZakuraTraceTable = ZakuraTraceTable {
43 table: "handshake",
44 file_name: "handshake.jsonl",
45};
46
47pub const CONN_TABLE: ZakuraTraceTable = ZakuraTraceTable {
49 table: "conn",
50 file_name: "conn.jsonl",
51};
52
53pub const STREAM_TABLE: ZakuraTraceTable = ZakuraTraceTable {
55 table: "stream",
56 file_name: "stream.jsonl",
57};
58
59pub const DISCOVERY_TABLE: ZakuraTraceTable = ZakuraTraceTable {
61 table: "discovery",
62 file_name: "discovery.jsonl",
63};
64
65pub const RATELIMIT_TABLE: ZakuraTraceTable = ZakuraTraceTable {
67 table: "ratelimit",
68 file_name: "ratelimit.jsonl",
69};
70
71pub const HEADER_SYNC_TABLE: ZakuraTraceTable = ZakuraTraceTable {
73 table: "header_sync",
74 file_name: "header_sync.jsonl",
75};
76
77pub const LEGACY_REQUEST_TABLE: ZakuraTraceTable = ZakuraTraceTable {
79 table: "legacy_request",
80 file_name: "legacy_request.jsonl",
81};
82
83pub const BLOCK_SYNC_TABLE: ZakuraTraceTable = ZakuraTraceTable {
85 table: "block_sync",
86 file_name: "block_sync.jsonl",
87};
88
89pub const COMMIT_STATE_TABLE: ZakuraTraceTable = ZakuraTraceTable {
91 table: "commit_state",
92 file_name: "commit_state.jsonl",
93};
94
95pub const QUEUE_SEND_TABLE: ZakuraTraceTable = ZakuraTraceTable {
97 table: "queue_send",
98 file_name: "queue_send.jsonl",
99};
100
101pub mod queue_send_trace {
103 pub const EVENT: &str = "event";
105 pub const QUEUE_SEND_FAILED: &str = "queue_send_failed";
107 pub const SERVICE: &str = "service";
109 pub const MESSAGE: &str = "message";
111 pub const PEER: &str = "peer";
113 pub const SOURCE_PEER: &str = "source_peer";
115 pub const DESTINATION_PEER: &str = "destination_peer";
117 pub const ERROR: &str = "error";
119 pub const REASON: &str = "reason";
121 pub const QUEUE_CAPACITY: &str = "queue_capacity";
123 pub const QUEUE_MAX_CAPACITY: &str = "queue_max_capacity";
125 pub const RANGE_START: &str = "range_start";
127 pub const RANGE_COUNT: &str = "range_count";
129 pub const RETURNED: &str = "returned";
131 pub const HEIGHT: &str = "height";
133 pub const HASH: &str = "hash";
135}
136
137pub mod block_sync_trace {
148 pub const EVENT: &str = "event";
150 pub const PEER: &str = "peer";
152 pub const KIND: &str = "kind";
154 pub const HEIGHT: &str = "height";
156 pub const HASH: &str = "hash";
158 pub const RANGE_START: &str = "range_start";
160 pub const RANGE_COUNT: &str = "range_count";
162 pub const EXPECTED_COUNT: &str = "expected_count";
164 pub const ESTIMATED_BYTES: &str = "estimated_bytes";
166 pub const SERIALIZED_BYTES: &str = "serialized_bytes";
168 pub const DECODED_ATTRIBUTED_MEMORY_SIZE_BYTES: &str = "decoded_attributed_memory_size_bytes";
170 pub const SEQUENCER_INPUT_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
172 "sequencer_input_decoded_attributed_memory_bytes";
173 pub const REORDER_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
175 "reorder_decoded_attributed_memory_bytes";
176 pub const APPLYING_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
178 "applying_decoded_attributed_memory_bytes";
179 pub const ACTIVE_PIPELINE_DECODED_ATTRIBUTED_MEMORY_BYTES: &str =
181 "active_pipeline_decoded_attributed_memory_bytes";
182 pub const ELAPSED_MS: &str = "elapsed_ms";
184 pub const PREPARE_ELAPSED_MS: &str = "prepare_elapsed_ms";
186 pub const SEND_ELAPSED_MS: &str = "send_elapsed_ms";
188 pub const RESULT: &str = "result";
190 pub const REASON: &str = "reason";
192 pub const ERROR: &str = "error";
194 pub const APPLY_TOKEN: &str = "apply_token";
196 pub const REQUEST_FLOOR: &str = "request_floor";
198 pub const BODY_DOWNLOAD_FLOOR: &str = "body_download_floor";
200 pub const FLOOR_GAP_HEIGHT: &str = "floor_gap_height";
202 pub const FLOOR_GAP_STATE: &str = "floor_gap_state";
204 pub const FLOOR_GAP_SERVABLE_PEERS: &str = "floor_gap_servable_peers";
206 pub const FLOOR_GAP_AVAILABLE_PEERS: &str = "floor_gap_available_peers";
208 pub const FLOOR_GAP_OUTSTANDING_PEERS: &str = "floor_gap_outstanding_peers";
210 pub const FLOOR_GAP_OLDEST_OUTSTANDING_MS: &str = "floor_gap_oldest_outstanding_ms";
212 pub const FLOOR_GAP_NEXT_DEADLINE_MS: &str = "floor_gap_next_deadline_ms";
214 pub const VERIFIED_BLOCK_TIP: &str = "verified_block_tip";
216 pub const BEST_HEADER_TIP: &str = "best_header_tip";
218 pub const BODY_LAG: &str = "body_lag";
220 pub const APPLYING: &str = "applying";
222 pub const SUBMITTED_APPLIES: &str = "submitted_applies";
224 pub const REORDER: &str = "reorder";
226 pub const OUTSTANDING: &str = "outstanding";
228 pub const BUDGET_AVAILABLE: &str = "budget_available";
230 pub const BUDGET_RESERVED: &str = "budget_reserved";
232 pub const BUDGET_RESERVED_AFTER: &str = "budget_reserved_after";
235 pub const RECEIVED_BYTES_PER_SEC: &str = "received_bytes_per_sec";
237 pub const RECEIVED_BLOCKS_PER_SEC: &str = "received_blocks_per_sec";
239 pub const COMMITTED_BYTES_PER_SEC: &str = "committed_bytes_per_sec";
241 pub const COMMITTED_BLOCKS_PER_SEC: &str = "committed_blocks_per_sec";
243 pub const DOWNLOAD_BLOCKED_ON_BUDGET: &str = "download_blocked_on_budget";
246 pub const PEERS_WANTING_SLOTS: &str = "peers_wanting_slots";
248 pub const PEERS: &str = "peers";
250 pub const ACTIVE_CONNECTIONS: &str = "active_connections";
252 pub const PEERS_WITH_STATUS: &str = "peers_with_status";
254 pub const NEEDED_MIN: &str = "needed_min";
256 pub const NEEDED_COUNT: &str = "needed_count";
258 pub const QUEUE_LEN: &str = "queue_len";
260 pub const QUEUE_BLOCKS: &str = "queue_blocks";
262 pub const QUEUE_MIN_START: &str = "queue_min_start";
264 pub const ASSIGNED_LEN: &str = "assigned_len";
266 pub const LOCAL_BODY_WORK: &str = "local_body_work";
268 pub const REFILL_LOW_WATER: &str = "refill_low_water";
270 pub const COVERED_MAX_END: &str = "covered_max_end";
272
273 pub const BLOCK_STATUS_RECEIVED: &str = "block_status_received";
275 pub const BLOCK_STATUS_SENT: &str = "block_status_sent";
277 pub const BLOCK_STATUS_SEND_FAILED: &str = "block_status_send_failed";
279 pub const BLOCK_PEER_CONNECTED: &str = "block_peer_connected";
281 pub const BLOCK_PEER_DISCONNECTED: &str = "block_peer_disconnected";
283 pub const BLOCK_PEER_PROTOCOL_REJECT: &str = "block_peer_protocol_reject";
285 pub const BLOCK_GET_BLOCKS_SENT: &str = "block_get_blocks_sent";
287 pub const BLOCK_EVENT_RECEIVED: &str = "block_event_received";
289 pub const BLOCK_MESSAGE_RECEIVED: &str = "block_message_received";
291 pub const BLOCK_MESSAGE_SENT: &str = "block_message_sent";
293 pub const BLOCK_ACTION_DISPATCHED: &str = "block_action_dispatched";
295 pub const BLOCK_BODY_RECEIVED: &str = "block_body_received";
297 pub const BLOCK_BODY_SEQUENCER_SENT: &str = "block_body_sequencer_sent";
299 pub const BLOCK_BODY_DECODE_PERMIT: &str = "block_body_decode_permit";
301 pub const BLOCK_BODY_ACCEPTED: &str = "block_body_accepted";
303 pub const BLOCK_SEQUENCER_CONTROL_SENT: &str = "block_sequencer_control_sent";
305 pub const BLOCK_BODY_SUBMITTED: &str = "block_body_submitted";
307 pub const BLOCK_BODY_SUBMISSION_RETRY_SCHEDULED: &str = "block_body_submission_retry_scheduled";
309 pub const BLOCK_APPLY_FINISHED: &str = "block_apply_finished";
311 pub const BLOCK_RANGE_UNAVAILABLE: &str = "block_range_unavailable";
313 pub const BLOCK_RANGE_RESPONSE_SENT: &str = "block_range_response_sent";
315 pub const BLOCK_WORK_EXTENDED: &str = "block_work_extended";
317 pub const BLOCK_WORK_TAKEN: &str = "block_work_taken";
319 pub const BLOCK_WORK_RETURNED: &str = "block_work_returned";
321 pub const BLOCK_FLOOR_WATCHDOG_CANCELLED: &str = "block_floor_watchdog_cancelled";
323 pub const BLOCK_FILL_STOP: &str = "block_fill_stop";
327 pub const FILL_STOP_REASON: &str = "fill_stop_reason";
331 pub const FILL_SENT: &str = "fill_sent";
333 pub const BLOCK_FRONTIERS_CHANGED: &str = "block_frontiers_changed";
335 pub const BLOCK_CHAIN_TIP_RESET: &str = "block_chain_tip_reset";
337 pub const BLOCK_FRONTIER_RESET_CLASSIFIED: &str = "block_frontier_reset_classified";
339 pub const BLOCK_SYNC_STATE: &str = "block_sync_state";
341 pub const BLOCK_PEER_BBR: &str = "block_peer_bbr";
345}
346
347pub mod discovery_trace {
349 pub const EVENT: &str = "event";
351 pub const PEER: &str = "peer";
353 pub const RESULT: &str = "result";
355
356 pub const DISCOVERY_DIAL_RESULT: &str = "discovery_dial_result";
358}
359
360pub mod header_sync_trace {
362 pub const EVENT: &str = "event";
364 pub const PEER: &str = "peer";
366 pub const KIND: &str = "kind";
368 pub const SOURCE_PEER: &str = "source_peer";
370 pub const HEIGHT: &str = "height";
372 pub const HASH: &str = "hash";
374 pub const ANCHOR_HASH: &str = "anchor_hash";
376 pub const RANGE_START: &str = "range_start";
378 pub const RANGE_COUNT: &str = "range_count";
380 pub const VALIDATION_STAGE: &str = "validation_stage";
382 pub const ERROR_KIND: &str = "error_kind";
384 pub const ADVERTISED_CAP: &str = "advertised_cap";
386 pub const EXPECTED_COUNT: &str = "expected_count";
388 pub const IN_FLIGHT_COUNT: &str = "in_flight_count";
390 pub const WANT_TREE_AUX_ROOTS: &str = "want_tree_aux_roots";
392 pub const FINALIZED: &str = "finalized";
394 pub const RANGE_PRIORITY: &str = "range_priority";
396 pub const VERIFIED_BLOCK_TIP: &str = "verified_block_tip";
398 pub const FINALIZED_HEIGHT: &str = "finalized_height";
400 pub const BEST_HEADER_TIP: &str = "best_header_tip";
402 pub const TREE_AUX_ROOTS_LEN: &str = "tree_aux_roots_len";
404 pub const FIRST_ROOT_HEIGHT: &str = "first_root_height";
406 pub const LAST_ROOT_HEIGHT: &str = "last_root_height";
408 pub const ROOT_MISMATCH_OFFSET: &str = "root_mismatch_offset";
410 pub const EXPECTED_ROOT_HEIGHT: &str = "expected_root_height";
412 pub const ACTUAL_ROOT_HEIGHT: &str = "actual_root_height";
414 pub const DESTINATION_PEER_COUNT: &str = "destination_peer_count";
416 pub const ACTIVE_CONNECTIONS: &str = "active_connections";
418 pub const SESSION_ID: &str = "session_id";
420 pub const REQUEST_ID: &str = "request_id";
422 pub const STREAM_VERSION: &str = "stream_version";
424 pub const REASON: &str = "reason";
426
427 pub const HEADER_EVENT_RECEIVED: &str = "header_event_received";
429 pub const HEADER_ACTION_DISPATCHED: &str = "header_action_dispatched";
431 pub const HEADER_STATUS_SENT: &str = "header_status_sent";
433 pub const HEADER_STATUS_RECEIVED: &str = "header_status_received";
435 pub const HEADER_PEER_CONNECTED: &str = "header_peer_connected";
437 pub const HEADER_PEER_DISCONNECTED: &str = "header_peer_disconnected";
439 pub const HEADER_GET_HEADERS_SENT: &str = "header_get_headers_sent";
441 pub const HEADER_HEADERS_RECEIVED: &str = "header_headers_received";
443 pub const HEADER_HEADERS_SERVED: &str = "header_headers_served";
445 pub const HEADER_RANGE_COMMITTED: &str = "header_range_committed";
447 pub const HEADER_RANGE_REJECTED: &str = "header_range_rejected";
449 pub const HEADER_NEW_BLOCK_RECEIVED: &str = "header_new_block_received";
451 pub const HEADER_NEW_BLOCK_FORWARDED: &str = "header_new_block_forwarded";
453 pub const HEADER_NEW_BLOCK_DEDUPED: &str = "header_new_block_deduped";
455 pub const HEADER_PEER_VIOLATION: &str = "header_peer_violation";
457 pub const HEADER_PEER_VIOLATION_RECORDED: &str = "header_peer_violation_recorded";
459 pub const HEADER_FRONTIER_ADVANCED: &str = "header_frontier_advanced";
461 pub const HEADER_FRONTIER_REANCHORED: &str = "header_frontier_reanchored";
463 pub const HEADER_MISSING_BODIES_REPORTED: &str = "header_missing_bodies_reported";
465 pub const HEADER_MAINTENANCE_WAKEUP: &str = "header_maintenance_wakeup";
467}
468
469pub mod commit_state_trace {
471 pub const EVENT: &str = "event";
473 pub const SOURCE: &str = "source";
475 pub const HEIGHT: &str = "height";
477 pub const HASH: &str = "hash";
479 pub const RANGE_START: &str = "range_start";
481 pub const RANGE_COUNT: &str = "range_count";
483 pub const TREE_AUX_ROOTS_LEN: &str = "tree_aux_roots_len";
485 pub const RESULT: &str = "result";
487 pub const ERROR_VARIANT: &str = "error_variant";
489 pub const ERROR_DEBUG: &str = "error_debug";
491 pub const REASON: &str = "reason";
493 pub const APPLY_TOKEN: &str = "apply_token";
495 pub const APPLY_CLASS: &str = "apply_class";
497 pub const FINALIZED_HEIGHT: &str = "finalized_height";
499 pub const VERIFIED_BLOCK_TIP: &str = "verified_block_tip";
501 pub const VERIFIED_BLOCK_HASH: &str = "verified_block_hash";
503 pub const BEST_HEADER_TIP: &str = "best_header_tip";
505 pub const ELAPSED_MS: &str = "elapsed_ms";
507 pub const COMMIT_STALL_REASON: &str = "commit_stall_reason";
511 pub const COMMITTED_MARKER: &str = "committed_marker";
513 pub const FIRED_HIGH_WATER: &str = "fired_high_water";
517 pub const COMMITS_IN_FLIGHT: &str = "commits_in_flight";
519 pub const PEER: &str = "peer";
521 pub const QUEUE_LEN: &str = "queue_len";
523 pub const IN_FLIGHT_COUNT: &str = "in_flight_count";
525 pub const ACTION: &str = "action";
527 pub const LOCAL_FRONTIER: &str = "local_frontier";
529
530 pub const ACTION_RECEIVED: &str = "action_received";
532 pub const STATE_READ_START: &str = "state_read_start";
534 pub const STATE_READ_SUCCESS: &str = "state_read_success";
536 pub const STATE_READ_ERROR: &str = "state_read_error";
538 pub const STATE_READ_TIMEOUT: &str = "state_read_timeout";
540 pub const BLOCK_SUBMIT_QUEUED: &str = "block_submit_queued";
542 pub const COMMIT_START: &str = "commit_start";
544 pub const COMMIT_STALLED: &str = "commit_stalled";
546 pub const COMMIT_STALL_CONTIGUOUS_HEAD: &str = "contiguous_head";
551 pub const COMMIT_STALL_BEHIND_PREFIX: &str = "behind_committed_prefix";
555 pub const COMMIT_FINISH: &str = "commit_finish";
557 pub const FRONTIER_QUERY_START: &str = "frontier_query_start";
559 pub const FRONTIER_QUERY_FINISH: &str = "frontier_query_finish";
561 pub const REACTOR_EVENT_SENT: &str = "reactor_event_sent";
563 pub const CHECKPOINT_REFRESH_ATTEMPT: &str = "checkpoint_refresh_attempt";
565 pub const CHECKPOINT_REFRESH_SENT: &str = "checkpoint_refresh_sent";
567 pub const BLOCK_SYNC_NOTIFY_SENT: &str = "block_sync_notify_sent";
569 pub const CHAIN_TIP_ACTION: &str = "chain_tip_action";
571 pub const FRONTIER_DERIVED: &str = "frontier_derived";
573 pub const SYNC_FRONTIER_TRANSITION: &str = "sync_frontier_transition";
575 pub const SEQUENCE: &str = "sequence";
577 pub const CAUSE: &str = "cause";
579 pub const OLD_FINALIZED_HEIGHT: &str = "old_finalized_height";
581 pub const OLD_FINALIZED_HASH: &str = "old_finalized_hash";
583 pub const OLD_VERIFIED_BODY_HEIGHT: &str = "old_verified_body_height";
585 pub const OLD_VERIFIED_BODY_HASH: &str = "old_verified_body_hash";
587 pub const OLD_BEST_HEADER_HEIGHT: &str = "old_best_header_height";
589 pub const OLD_BEST_HEADER_HASH: &str = "old_best_header_hash";
591 pub const NEW_FINALIZED_HEIGHT: &str = "new_finalized_height";
593 pub const NEW_FINALIZED_HASH: &str = "new_finalized_hash";
595 pub const NEW_VERIFIED_BODY_HEIGHT: &str = "new_verified_body_height";
597 pub const NEW_VERIFIED_BODY_HASH: &str = "new_verified_body_hash";
599 pub const NEW_BEST_HEADER_HEIGHT: &str = "new_best_header_height";
601 pub const NEW_BEST_HEADER_HASH: &str = "new_best_header_hash";
603}
604
605#[derive(Clone, Debug)]
607pub struct ZakuraTrace {
608 tracer: JsonlTracer,
609 node: Arc<str>,
610 started: Instant,
611}
612
613impl ZakuraTrace {
614 pub fn noop() -> Self {
616 Self::new(JsonlTracer::noop(), zakura_jsonl_trace::node_id())
617 }
618
619 pub fn new(tracer: JsonlTracer, node: impl Into<Arc<str>>) -> Self {
621 Self {
622 tracer,
623 node: node.into(),
624 started: Instant::now(),
625 }
626 }
627
628 pub fn tracer(&self) -> &JsonlTracer {
630 &self.tracer
631 }
632
633 pub fn is_enabled(&self) -> bool {
635 self.tracer.is_enabled()
636 }
637
638 pub fn emit(&self, table: ZakuraTraceTable, event: ZakuraTraceEvent<'_>) {
640 self.emit_with(table, |row| event.insert_into(row));
641 }
642
643 pub fn emit_with(&self, table: ZakuraTraceTable, build: impl FnOnce(&mut Map<String, Value>)) {
649 let Ok(permit) = self.tracer.try_reserve() else {
650 return;
651 };
652
653 let mut row = Map::new();
654 row.insert("ts".to_string(), elapsed_micros(self.started.elapsed()));
655 row.insert("node".to_string(), Value::String(self.node.to_string()));
656 build(&mut row);
657
658 if let Ok(line) = serde_json::to_vec(&Value::Object(row)) {
659 permit.send(JsonlWriteEvent {
660 table: table.table(),
661 file_name: table.file_name(),
662 line,
663 });
664 }
665 }
666}
667
668impl Default for ZakuraTrace {
669 fn default() -> Self {
670 Self::noop()
671 }
672}
673
674#[derive(Clone, Debug)]
676pub struct ZakuraTraceEvent<'a> {
677 event: &'static str,
678 conn: Option<u64>,
679 stream: Option<u64>,
680 payload_len: Option<u64>,
681 frame_len: Option<u64>,
682 max_frame_bytes: Option<u64>,
683 peer: Option<&'a str>,
684 role: Option<&'static str>,
685 phase: Option<&'static str>,
686 reason: Option<&'static str>,
687 selected_protocol: Option<u16>,
688 direction: Option<&'static str>,
689 stream_kind: Option<&'static str>,
690 network: Option<&'static str>,
691}
692
693impl<'a> ZakuraTraceEvent<'a> {
694 pub fn new(event: &'static str) -> Self {
696 Self {
697 event,
698 conn: None,
699 stream: None,
700 payload_len: None,
701 frame_len: None,
702 max_frame_bytes: None,
703 peer: None,
704 role: None,
705 phase: None,
706 reason: None,
707 selected_protocol: None,
708 direction: None,
709 stream_kind: None,
710 network: None,
711 }
712 }
713
714 pub fn conn(mut self, conn: u64) -> Self {
716 self.conn = Some(conn);
717 self
718 }
719
720 pub fn stream(mut self, stream: u64) -> Self {
722 self.stream = Some(stream);
723 self
724 }
725
726 pub fn payload_len(mut self, payload_len: u64) -> Self {
728 self.payload_len = Some(payload_len);
729 self
730 }
731
732 pub fn frame_len(mut self, frame_len: u64) -> Self {
734 self.frame_len = Some(frame_len);
735 self
736 }
737
738 pub fn max_frame_bytes(mut self, max_frame_bytes: u64) -> Self {
740 self.max_frame_bytes = Some(max_frame_bytes);
741 self
742 }
743
744 pub fn peer(mut self, peer: &'a str) -> Self {
746 self.peer = Some(peer);
747 self
748 }
749
750 pub fn maybe_peer(mut self, peer: Option<&'a str>) -> Self {
752 self.peer = peer;
753 self
754 }
755
756 pub fn role(mut self, role: &'static str) -> Self {
758 self.role = Some(role);
759 self
760 }
761
762 pub fn phase(mut self, phase: &'static str) -> Self {
764 self.phase = Some(phase);
765 self
766 }
767
768 pub fn reason(mut self, reason: &'static str) -> Self {
770 self.reason = Some(reason);
771 self
772 }
773
774 pub fn selected_protocol(mut self, selected_protocol: u16) -> Self {
776 self.selected_protocol = Some(selected_protocol);
777 self
778 }
779
780 pub fn direction(mut self, direction: &'static str) -> Self {
782 self.direction = Some(direction);
783 self
784 }
785
786 pub fn stream_kind(mut self, stream_kind: &'static str) -> Self {
788 self.stream_kind = Some(stream_kind);
789 self
790 }
791
792 pub fn network(mut self, network: &'static str) -> Self {
794 self.network = Some(network);
795 self
796 }
797
798 fn insert_into(self, row: &mut Map<String, Value>) {
799 row.insert("event".to_string(), Value::String(self.event.to_string()));
800 insert_optional_u64(row, "conn", self.conn);
801 insert_optional_u64(row, "stream", self.stream);
802 insert_optional_u64(row, "payload_len", self.payload_len);
803 insert_optional_u64(row, "frame_len", self.frame_len);
804 insert_optional_u64(row, "max_frame_bytes", self.max_frame_bytes);
805 insert_optional_str(row, "peer", self.peer);
806 insert_optional_str(row, "role", self.role);
807 insert_optional_str(row, "phase", self.phase);
808 insert_optional_str(row, "reason", self.reason);
809 insert_optional_u64(
810 row,
811 "selected_protocol",
812 self.selected_protocol.map(u64::from),
813 );
814 insert_optional_str(row, "direction", self.direction);
815 insert_optional_str(row, "stream_kind", self.stream_kind);
816 insert_optional_str(row, "network", self.network);
817 }
818}
819
820pub fn peer_label(peer_id: &ZakuraPeerId) -> String {
822 let hash = Blake2bParams::new()
823 .hash_length(8)
824 .personal(b"zakura-peer-lbl")
825 .hash(peer_id.as_bytes());
826 format!("peer:{}", hex::encode(hash.as_bytes()))
827}
828
829pub fn reject_reason_label(reason: ZakuraRejectReason) -> &'static str {
831 match reason {
832 ZakuraRejectReason::UnsupportedPreludeVersion => "unsupported_prelude_version",
833 ZakuraRejectReason::IncompatibleZakuraProtocol => "incompatible_zakura_protocol",
834 ZakuraRejectReason::WrongNetwork => "wrong_network",
835 ZakuraRejectReason::WrongChain => "wrong_chain",
836 ZakuraRejectReason::MissingRequiredCapability => "missing_required_capability",
837 ZakuraRejectReason::ResourceLimit => "resource_limit",
838 ZakuraRejectReason::AlreadyConnected => "already_connected",
839 ZakuraRejectReason::TemporaryUnavailable => "temporary_unavailable",
840 }
841}
842
843pub(crate) fn ordered_send_error_label(
845 error: &crate::zakura::transport::OrderedSendError,
846) -> &'static str {
847 match error {
848 crate::zakura::transport::OrderedSendError::Full => "full",
849 crate::zakura::transport::OrderedSendError::Closed => "closed",
850 crate::zakura::transport::OrderedSendError::Encode(_) => "encode",
851 }
852}
853
854fn elapsed_micros(elapsed: Duration) -> Value {
855 let micros = u64::try_from(elapsed.as_micros()).unwrap_or(u64::MAX);
856 Value::Number(Number::from(micros))
857}
858
859fn insert_optional_str(row: &mut Map<String, Value>, key: &'static str, value: Option<&str>) {
860 row.insert(
861 key.to_string(),
862 value.map_or(Value::Null, |value| Value::String(value.to_string())),
863 );
864}
865
866fn insert_optional_u64(row: &mut Map<String, Value>, key: &'static str, value: Option<u64>) {
867 row.insert(
868 key.to_string(),
869 value.map_or(Value::Null, |value| Value::Number(Number::from(value))),
870 );
871}
872
873#[cfg(test)]
874mod tests {
875 use std::sync::{
876 atomic::{AtomicBool, Ordering},
877 Arc,
878 };
879
880 use tokio::sync::mpsc;
881
882 use super::*;
883
884 #[test]
885 fn noop_trace_does_not_build_rows() {
886 let trace = ZakuraTrace::noop();
887 let called = Arc::new(AtomicBool::new(false));
888 let called_in_emit = called.clone();
889
890 trace.emit_with(CONN_TABLE, |_| {
891 called_in_emit.store(true, Ordering::SeqCst);
892 });
893
894 assert!(!called.load(Ordering::SeqCst));
895 }
896
897 #[test]
898 fn full_queue_does_not_build_rows() {
899 let (tx, _rx) = mpsc::channel(1);
901 let tracer = JsonlTracer::new(tx);
902 let trace = ZakuraTrace::new(tracer, "node-full");
903
904 trace.emit(CONN_TABLE, ZakuraTraceEvent::new("conn.fill"));
905
906 let called = Arc::new(AtomicBool::new(false));
907 let called_in_emit = called.clone();
908 trace.emit_with(CONN_TABLE, |_| {
909 called_in_emit.store(true, Ordering::SeqCst);
910 });
911
912 assert!(
913 !called.load(Ordering::SeqCst),
914 "build closure must not run when the queue is full"
915 );
916 }
917
918 #[test]
919 fn closed_queue_does_not_build_rows() {
920 let (tx, rx) = mpsc::channel(1);
922 let tracer = JsonlTracer::new(tx);
923 let trace = ZakuraTrace::new(tracer, "node-closed");
924 drop(rx);
925
926 let called = Arc::new(AtomicBool::new(false));
927 let called_in_emit = called.clone();
928 trace.emit_with(CONN_TABLE, |_| {
929 called_in_emit.store(true, Ordering::SeqCst);
930 });
931
932 assert!(
933 !called.load(Ordering::SeqCst),
934 "build closure must not run when the queue is closed"
935 );
936 assert!(
937 !trace.is_enabled(),
938 "trace must report disabled once the receiver is dropped"
939 );
940 }
941}