/
flog.rs
263 lines (212 loc) · 8.39 KB
/
flog.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
use crate::wchar::prelude::*;
use crate::wildcard::wildcard_match;
use crate::wutil::write_to_fd;
use crate::{parse_util::parse_util_unescape_wildcards, wutil::wwrite_to_fd};
use libc::c_int;
use std::sync::atomic::{AtomicI32, Ordering};
#[rustfmt::skip::macros(category)]
pub mod categories {
use super::wstr;
use crate::wchar::L;
use std::sync::atomic::AtomicBool;
pub struct category_t {
pub name: &'static wstr,
pub description: &'static wstr,
pub enabled: AtomicBool,
}
/// Macro to declare a static variable identified by $var,
/// with the given name and description, and optionally enabled by default.
macro_rules! declare_category {
(
($var:ident, $name:literal, $description:literal, $enabled:expr)
) => {
pub static $var: category_t = category_t {
name: L!($name),
description: L!($description),
enabled: AtomicBool::new($enabled),
};
};
(
($var:ident, $name:expr, $description:expr)
) => {
declare_category!(($var, $name, $description, false));
};
}
/// Macro to extract the variable name for a category.
macro_rules! category_name {
(($var:ident, $name:literal, $description:literal, $enabled:expr)) => {
$var
};
(($var:ident, $name:literal, $description:literal)) => {
$var
};
}
macro_rules! categories {
(
// A repetition of categories, separated by semicolons.
$($cats:tt);*
// Allow trailing semicolon.
$(;)?
) => {
// Declare each category.
$(
declare_category!($cats);
)*
// Define a function which gives you a Vector of all categories.
pub fn all_categories() -> Vec<&'static category_t> {
vec![
$(
& category_name!($cats),
)*
]
}
};
}
categories!(
(error, "error", "Serious unexpected errors (on by default)", true);
(debug, "debug", "Debugging aid (on by default)", true);
(warning, "warning", "Warnings (on by default)", true);
(warning_path, "warning-path", "Warnings about unusable paths for config/history (on by default)", true);
(deprecated_test, "deprecated-test", "Warning about using test's zero- or one-argument modes (`test -d $foo`), which will be changed in future.");
(config, "config", "Finding and reading configuration");
(event, "event", "Firing events");
(exec, "exec", "Errors reported by exec (on by default)", true);
(exec_job_status, "exec-job-status", "Jobs changing status");
(exec_job_exec, "exec-job-exec", "Jobs being executed");
(exec_fork, "exec-fork", "Calls to fork()");
(output_invalid, "output-invalid", "Trying to print invalid output");
(ast_construction, "ast-construction", "Parsing fish AST");
(proc_job_run, "proc-job-run", "Jobs getting started or continued");
(proc_termowner, "proc-termowner", "Terminal ownership events");
(proc_internal_proc, "proc-internal-proc", "Internal (non-forked) process events");
(proc_reap_internal, "proc-reap-internal", "Reaping internal (non-forked) processes");
(proc_reap_external, "proc-reap-external", "Reaping external (forked) processes");
(proc_pgroup, "proc-pgroup", "Process groups");
(env_locale, "env-locale", "Changes to locale variables");
(env_export, "env-export", "Changes to exported variables");
(env_dispatch, "env-dispatch", "Reacting to variables");
(uvar_file, "uvar-file", "Writing/reading the universal variable store");
(uvar_notifier, "uvar-notifier", "Notifications about universal variable changes");
(topic_monitor, "topic-monitor", "Internal details of the topic monitor");
(char_encoding, "char-encoding", "Character encoding issues");
(history, "history", "Command history events");
(history_file, "history-file", "Reading/Writing the history file");
(profile_history, "profile-history", "History performance measurements");
(iothread, "iothread", "Background IO thread events");
(fd_monitor, "fd-monitor", "FD monitor events");
(term_support, "term-support", "Terminal feature detection");
(term_protocols, "term-protocols", "Terminal protocol negotiation");
(reader, "reader", "The interactive reader/input system");
(reader_render, "reader-render", "Rendering the command line");
(complete, "complete", "The completion system");
(path, "path", "Searching/using paths");
(screen, "screen", "Screen repaints");
(abbrs, "abbrs", "Abbreviation expansion");
(refcell, "refcell", "Refcell dynamic borrowing");
);
}
/// FLOG formats values. By default we would like to use Display, and fall back to Debug.
/// However that would require specialization. So instead we make two "separate" traits, bring them both in scope,
/// and let Rust figure it out.
/// Clients can opt a Debug type into Floggable by implementing FloggableDebug:
/// impl FloggableDebug for MyType {}
pub trait FloggableDisplay {
/// Return a string representation of this thing.
fn to_flog_str(&self) -> String;
}
impl<T: std::fmt::Display> FloggableDisplay for T {
fn to_flog_str(&self) -> String {
self.to_string()
}
}
pub trait FloggableDebug: std::fmt::Debug {
fn to_flog_str(&self) -> String {
format!("{:?}", self)
}
}
/// Write to our FLOG file.
pub fn flog_impl(s: &str) {
let fd = get_flog_file_fd();
if fd < 0 {
return;
}
let _ = write_to_fd(s.as_bytes(), fd);
}
/// The entry point for flogging.
#[macro_export]
macro_rules! FLOG {
($category:ident, $($elem:expr),+ $(,)*) => {
if $crate::flog::categories::$category.enabled.load(std::sync::atomic::Ordering::Relaxed) {
#[allow(unused_imports)]
use $crate::flog::{FloggableDisplay, FloggableDebug};
let mut vs = vec![format!("{}:", $crate::flog::categories::$category.name)];
$(
{
vs.push($elem.to_flog_str())
}
)+
// We don't use locking here so we have to append our own newline to avoid multiple writes.
let mut v = vs.join(" ");
v.push('\n');
$crate::flog::flog_impl(&v);
}
};
}
#[macro_export]
macro_rules! FLOGF {
($category:ident, $fmt: expr, $($elem:expr),+ $(,)*) => {
$crate::flog::FLOG!($category, $crate::wutil::sprintf!($fmt, $($elem),*))
}
}
#[macro_export]
macro_rules! should_flog {
($category:ident) => {
$crate::flog::categories::$category
.enabled
.load(std::sync::atomic::Ordering::Relaxed)
};
}
pub use {should_flog, FLOG, FLOGF};
/// For each category, if its name matches the wildcard, set its enabled to the given sense.
fn apply_one_wildcard(wc_esc: &wstr, sense: bool) {
let wc = parse_util_unescape_wildcards(wc_esc);
let mut match_found = false;
for cat in categories::all_categories() {
if wildcard_match(cat.name, &wc, false) {
cat.enabled.store(sense, Ordering::Relaxed);
match_found = true;
}
}
if !match_found {
eprintln!("Failed to match debug category: {wc_esc}");
}
}
/// Set the active flog categories according to the given wildcard \p wc.
pub fn activate_flog_categories_by_pattern(wc_ptr: &wstr) {
let mut wc: WString = wc_ptr.into();
// Normalize underscores to dashes, allowing the user to be sloppy.
for c in wc.as_char_slice_mut() {
if *c == '_' {
*c = '-';
}
}
for s in wc.split(',') {
if s.starts_with('-') {
apply_one_wildcard(s.slice_from(1), false);
} else {
apply_one_wildcard(s, true);
}
}
}
/// The flog output fd. Defaults to stderr. A value < 0 disables flog.
static FLOG_FD: AtomicI32 = AtomicI32::new(libc::STDERR_FILENO);
pub fn set_flog_file_fd(fd: c_int) {
FLOG_FD.store(fd, Ordering::Relaxed);
}
#[inline]
pub fn get_flog_file_fd() -> c_int {
FLOG_FD.load(Ordering::Relaxed)
}
pub fn log_extra_to_flog_file(s: &wstr) {
wwrite_to_fd(s, get_flog_file_fd());
}