Coverage Report

Created: 2025-11-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ztunnel/src/metrics/process.rs
Line
Count
Source
1
// Copyright Istio Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use nix::sys::resource::{Resource, getrlimit};
16
use prometheus_client::collector::Collector;
17
use prometheus_client::encoding::{DescriptorEncoder, EncodeMetric};
18
use prometheus_client::metrics;
19
use tracing::error;
20
21
// Track open fds
22
#[derive(Debug)]
23
pub struct ProcessMetrics {}
24
const FD_PATH: &str = "/dev/fd";
25
26
impl ProcessMetrics {
27
0
    pub fn new() -> Self {
28
0
        Self {}
29
0
    }
30
31
0
    fn encode_open_fds(&self, encoder: &mut DescriptorEncoder) -> Result<(), std::fmt::Error> {
32
        // Count open fds by listing /proc/self/fd
33
0
        let open_fds = match std::fs::read_dir(FD_PATH) {
34
0
            Ok(entries) => entries.count() as u64,
35
0
            Err(e) => {
36
0
                error!("Failed to read {}: {}", FD_PATH, e);
37
0
                0
38
            }
39
        };
40
        // exclude the fd used to read the directory
41
0
        let gauge = metrics::gauge::ConstGauge::new(open_fds - 1);
42
0
        let metric_encoder = encoder.encode_descriptor(
43
0
            "process_open_fds",
44
0
            "Number of open file descriptors",
45
0
            None,
46
0
            gauge.metric_type(),
47
0
        )?;
48
0
        gauge.encode(metric_encoder)?;
49
0
        Ok(())
50
0
    }
51
52
0
    fn encode_max_fds(&self, encoder: &mut DescriptorEncoder) -> Result<(), std::fmt::Error> {
53
0
        let fds = match getrlimit(Resource::RLIMIT_NOFILE) {
54
0
            Ok((soft_limit, _)) => soft_limit,
55
0
            Err(e) => {
56
0
                error!("Failed to get rlimit: {}", e);
57
0
                return Ok(());
58
            }
59
        };
60
0
        let gauge = metrics::gauge::ConstGauge::new(fds);
61
0
        let metric_encoder = encoder.encode_descriptor(
62
0
            "process_max_fds",
63
0
            "Maximum number of file descriptors",
64
0
            None,
65
0
            gauge.metric_type(),
66
0
        )?;
67
0
        gauge.encode(metric_encoder)?;
68
0
        Ok(())
69
0
    }
70
}
71
72
impl Default for ProcessMetrics {
73
0
    fn default() -> Self {
74
0
        Self::new()
75
0
    }
76
}
77
78
impl Collector for ProcessMetrics {
79
0
    fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
80
0
        match self.encode_open_fds(&mut encoder) {
81
0
            Ok(_) => {}
82
0
            Err(e) => {
83
0
                error!("Failed to encode open fds: {}", e);
84
0
                return Ok(());
85
            }
86
        }
87
0
        match self.encode_max_fds(&mut encoder) {
88
0
            Ok(_) => {}
89
0
            Err(e) => {
90
0
                error!("Failed to encode max fds: {}", e);
91
            }
92
        }
93
0
        Ok(())
94
0
    }
95
}