collectd_plugin/
errors.rs

1use std::error;
2use std::fmt;
3use std::panic::PanicInfo;
4use std::str::Utf8Error;
5
6/// Error that occurred while translating the collectd config to rust structures.
7#[derive(Debug, Clone)]
8pub enum ConfigError {
9    /// The config type (eg: string, number, etc) denoted is unrecognized
10    UnknownType(i32),
11
12    /// The config string contains invalid UTF-8 characters
13    StringDecode(Utf8Error),
14}
15
16impl fmt::Display for ConfigError {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match *self {
19            ConfigError::UnknownType(type_) => {
20                write!(f, "unknown value ({}) for config enum", type_)
21            }
22            ConfigError::StringDecode(ref _e) => {
23                write!(f, "unable to convert config string to utf8")
24            }
25        }
26    }
27}
28
29impl error::Error for ConfigError {
30    fn description(&self) -> &str {
31        "error interpreting configuration values"
32    }
33
34    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
35        match *self {
36            ConfigError::StringDecode(ref e) => Some(e),
37            ConfigError::UnknownType(_) => None,
38        }
39    }
40}
41
42/// Error that occurred when converting a rust UTF-8 string to an array of `c_char` for collectd
43/// ingestion.
44#[derive(Debug, Clone)]
45pub enum ArrayError {
46    /// The UTF-8 string contained a null character
47    NullPresent(usize, String),
48
49    /// The UTF-8 string is too long to be ingested
50    TooLong(usize),
51}
52
53impl fmt::Display for ArrayError {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match *self {
56            ArrayError::NullPresent(pos, ref s) => {
57                write!(f, "null encountered (pos: {}) in string: {}", pos, s)
58            }
59            ArrayError::TooLong(len) => write!(f, "length of {} is too long", len),
60        }
61    }
62}
63
64impl error::Error for ArrayError {
65    fn description(&self) -> &str {
66        "error generating array"
67    }
68}
69
70/// Error that occurred while receiving values from collectd to write
71#[derive(Debug, Clone)]
72pub enum ReceiveError {
73    /// A plugin submitted a field that contained invalid UTF-8 characters
74    Utf8 {
75        /// plugin where the error originates
76        plugin: String,
77
78        /// the field that contained the utf8 error
79        field: &'static str,
80
81        /// the inner utf-8 error
82        err: Utf8Error,
83    },
84    Metadata {
85        /// plugin where the error originates
86        plugin: String,
87
88        /// metadata key or field where the error occurred
89        field: String,
90
91        /// Stringly typed error message
92        msg: &'static str,
93    },
94}
95
96impl fmt::Display for ReceiveError {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        match *self {
99            ReceiveError::Utf8 {
100                ref plugin,
101                ref field,
102                ..
103            } => {
104                write!(f, "plugin: {} submitted bad field: {}", plugin, field)
105            }
106            ReceiveError::Metadata {
107                ref plugin,
108                ref field,
109                msg,
110            } => {
111                write!(f, "plugin: {}, field: {}: {}", plugin, field, msg)
112            }
113        }
114    }
115}
116
117impl error::Error for ReceiveError {
118    fn description(&self) -> &str {
119        "error generating a value list"
120    }
121
122    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
123        match *self {
124            ReceiveError::Utf8 { ref err, .. } => Some(err),
125            ReceiveError::Metadata { .. } => None,
126        }
127    }
128}
129
130/// Errors that occur when submitting values to collectd
131#[derive(Debug, Clone)]
132pub enum SubmitError {
133    /// Contains the exit status that collectd returns when a submission fails
134    Dispatch(i32),
135
136    /// Error submitting a field
137    Field {
138        /// Name of field where error occurred
139        name: &'static str,
140
141        /// The underlying error
142        err: ArrayError,
143    },
144}
145
146impl fmt::Display for SubmitError {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        match *self {
149            SubmitError::Dispatch(code) => {
150                write!(f, "plugin_dispatch_values returned an error: {}", code)
151            }
152            SubmitError::Field { name, .. } => write!(f, "error submitting {}", name),
153        }
154    }
155}
156
157impl error::Error for SubmitError {
158    fn description(&self) -> &str {
159        "error generating array"
160    }
161
162    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
163        match *self {
164            SubmitError::Dispatch(_code) => None,
165            SubmitError::Field { ref err, .. } => Some(err),
166        }
167    }
168}
169
170/// If a plugin advertises that it supports a certain functionality, but doesn't implement the
171/// necessary `Plugin` function, this error is returned.
172#[derive(Clone, Copy, Debug)]
173pub struct NotImplemented;
174
175impl fmt::Display for NotImplemented {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        write!(f, "function is not implemented")
178    }
179}
180
181impl error::Error for NotImplemented {
182    fn description(&self) -> &str {
183        "function is not implemented"
184    }
185}
186
187/// Errors that occur when retrieving rates
188#[derive(Clone, Debug)]
189pub struct CacheRateError;
190
191impl fmt::Display for CacheRateError {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        write!(
194            f,
195            "unable to retrieve rate (see collectd logs for additional details)"
196        )
197    }
198}
199
200impl error::Error for CacheRateError {
201    fn description(&self) -> &str {
202        "unable to retrieve rate (see collectd logs for additional details)"
203    }
204}
205
206/// Errors that occur on the boundary between collectd and a plugin
207#[derive(Debug)]
208pub enum FfiError<'a> {
209    /// Error for implementing Rust's panic hook
210    PanicHook(&'a PanicInfo<'a>),
211
212    /// Represents a plugin that panicked. A plugin that panics has a logic bug that should be
213    /// fixed so that the plugin can better log and recover, else collectd decides
214    Panic,
215
216    /// An error from the plugin. This is a "normal" error that the plugin has caught. Like if the
217    /// database is down and the plugin has the proper error mechanisms
218    Plugin(Box<dyn error::Error>),
219
220    /// An error occurred outside the path of a plugin
221    Collectd(Box<dyn error::Error>),
222
223    /// When logging, collectd handed us a log level that was outside the known range
224    UnknownSeverity(i32),
225
226    /// Collectd gave us multiple configs to deserialize
227    MultipleConfig,
228
229    /// Collectd gave us field that contains invalid UTF-8 characters
230    Utf8(&'static str, Utf8Error),
231}
232
233impl<'a> fmt::Display for FfiError<'a> {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        match *self {
236            FfiError::Collectd(_) => write!(f, "unexpected collectd behavior"),
237            FfiError::UnknownSeverity(severity) => {
238                write!(f, "unrecognized severity level: {}", severity)
239            }
240            FfiError::MultipleConfig => write!(f, "duplicate config section"),
241            FfiError::Panic => write!(f, "plugin panicked"),
242            FfiError::PanicHook(info) => {
243                write!(f, "plugin panicked: ")?;
244                if let Some(location) = info.location() {
245                    write!(f, "({}: {}): ", location.file(), location.line(),)?;
246                }
247
248                if let Some(payload) = info.payload().downcast_ref::<&str>() {
249                    write!(f, "{}", payload)?;
250                }
251
252                Ok(())
253            }
254            FfiError::Plugin(_) => write!(f, "plugin encountered an error"),
255            FfiError::Utf8(field, ref _e) => write!(f, "UTF-8 error for field: {}", field),
256        }
257    }
258}
259
260impl<'a> error::Error for FfiError<'a> {
261    fn description(&self) -> &str {
262        "collectd plugin error"
263    }
264
265    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
266        match *self {
267            FfiError::Collectd(ref e) => Some(e.as_ref()),
268            FfiError::Plugin(ref e) => Some(e.as_ref()),
269            FfiError::Utf8(_field, ref e) => Some(e),
270            _ => None,
271        }
272    }
273}