Skip to main content

tuwunel_core/metrics/
dump.rs

1//! Exit-time dumps of runtime metrics and resource usage.
2//!
3//! Each file is a small JSON envelope: a `meta` block (pid, timestamp,
4//! version, scope) and a `payload` string holding the Debug output of the
5//! source struct verbatim.
6
7use std::{fs, path::Path, process};
8
9use serde::Serialize;
10#[cfg(tokio_unstable)]
11use tokio_metrics::RuntimeMetrics;
12
13use crate::{
14	Result, debug_info, error,
15	utils::{sys::Usage, time::now_millis},
16	version,
17};
18
19#[cfg(tokio_unstable)]
20const RUNTIME_METRICS_PREFIX: &str = "tuwunel.runtime_metrics";
21const RUNTIME_USAGE_PREFIX: &str = "tuwunel.runtime_usage";
22
23#[derive(Serialize)]
24struct Dump<'a> {
25	meta: DumpMeta,
26	payload: &'a str,
27}
28
29#[derive(Serialize)]
30struct DumpMeta {
31	pid: u32,
32	wrote_at_ms: u64,
33	tuwunel_version: &'static str,
34	scope: &'static str,
35}
36
37impl DumpMeta {
38	fn new(scope: &'static str) -> Self {
39		Self {
40			pid: process::id(),
41			wrote_at_ms: now_millis(),
42			tuwunel_version: version(),
43			scope,
44		}
45	}
46}
47
48#[cfg(tokio_unstable)]
49pub fn write_runtime_metrics(dir: &Path, metrics: &RuntimeMetrics) {
50	let pid = process::id();
51	let path = dir.join(format!("{RUNTIME_METRICS_PREFIX}.{pid}.json"));
52	let payload = format!("{metrics:?}");
53	let dump = Dump {
54		meta: DumpMeta::new("runtime_metrics"),
55		payload: &payload,
56	};
57
58	report(&path, "runtime_metrics", write_json(&path, &dump));
59}
60
61pub fn write_resource_usage(dir: &Path, usage: &Usage) {
62	let pid = process::id();
63	let path = dir.join(format!("{RUNTIME_USAGE_PREFIX}.{pid}.json"));
64	let payload = format!("{usage:?}");
65	let dump = Dump {
66		meta: DumpMeta::new("runtime_usage"),
67		payload: &payload,
68	};
69
70	report(&path, "runtime_usage", write_json(&path, &dump));
71}
72
73fn write_json<T: Serialize>(path: &Path, value: &T) -> Result {
74	if let Some(parent) = path.parent()
75		&& !parent.as_os_str().is_empty()
76	{
77		fs::create_dir_all(parent)?;
78	}
79
80	let json = serde_json::to_string_pretty(value)?;
81	fs::write(path, json)?;
82
83	Ok(())
84}
85
86fn report(path: &Path, scope: &'static str, result: Result) {
87	match result {
88		| Ok(()) => debug_info!(?path, %scope, "Wrote metrics."),
89		| Err(error) => error!(?path, %scope, %error, "Failed to write metrics."),
90	}
91}