1 /// The flogger: debug logging support for fish.
2 #ifndef FISH_FLOG_H
3 #define FISH_FLOG_H
4 
5 #include "config.h"  // IWYU pragma: keep
6 
7 #include <stdio.h>
8 
9 #include <string>
10 #include <type_traits>
11 #include <utility>
12 
13 #include "global_safety.h"
14 
15 using wcstring = std::wstring;
16 using wcstring_list_t = std::vector<wcstring>;
17 
18 template <typename T>
19 class owning_lock;
20 
21 namespace flog_details {
22 
23 class category_list_t;
24 class category_t {
25     friend category_list_t;
26     category_t(const wchar_t *name, const wchar_t *desc, bool enabled = false);
27 
28    public:
29     /// The name of this category.
30     const wchar_t *const name;
31 
32     /// A (non-localized) description of the category.
33     const wchar_t *const description;
34 
35     /// Whether the category is enabled.
36     relaxed_atomic_bool_t enabled;
37 };
38 
39 class category_list_t {
40     category_list_t() = default;
41 
42    public:
43     /// The singleton global category list instance.
44     static category_list_t *const g_instance;
45 
46     /// What follows are the actual logging categories.
47     /// To add a new category simply define a new field, following the pattern.
48 
49     category_t error{L"error", L"Serious unexpected errors (on by default)", true};
50 
51     category_t debug{L"debug", L"Debugging aid (on by default)", true};
52 
53     category_t warning{L"warning", L"Warnings (on by default)", true};
54     category_t warning_path{
55         L"warning-path", L"Warnings about unusable paths for config/history (on by default)", true};
56 
57     category_t config{L"config", L"Finding and reading configuration"};
58 
59     category_t event{L"event", L"Firing events"};
60 
61     category_t exec_job_status{L"exec-job-status", L"Jobs changing status"};
62 
63     category_t exec_job_exec{L"exec-job-exec", L"Jobs being executed"};
64 
65     category_t exec_fork{L"exec-fork", L"Calls to fork()"};
66 
67     category_t output_invalid{L"output-invalid", L"Trying to print invalid output"};
68     category_t ast_construction{L"ast-construction", L"Parsing fish AST"};
69 
70     category_t proc_job_run{L"proc-job-run", L"Jobs getting started or continued"};
71 
72     category_t proc_termowner{L"proc-termowner", L"Terminal ownership events"};
73 
74     category_t proc_internal_proc{L"proc-internal-proc", L"Internal (non-forked) process events"};
75 
76     category_t proc_reap_internal{L"proc-reap-internal",
77                                   L"Reaping internal (non-forked) processes"};
78 
79     category_t proc_reap_external{L"proc-reap-external", L"Reaping external (forked) processes"};
80     category_t proc_pgroup{L"proc-pgroup", L"Process groups"};
81 
82     category_t env_locale{L"env-locale", L"Changes to locale variables"};
83 
84     category_t env_export{L"env-export", L"Changes to exported variables"};
85 
86     category_t env_dispatch{L"env-dispatch", L"Reacting to variables"};
87 
88     category_t uvar_file{L"uvar-file", L"Writing/reading the universal variable store"};
89     category_t uvar_notifier{L"uvar-notifier", L"Notifications about universal variable changes"};
90 
91     category_t topic_monitor{L"topic-monitor", L"Internal details of the topic monitor"};
92     category_t char_encoding{L"char-encoding", L"Character encoding issues"};
93 
94     category_t history{L"history", L"Command history events"};
95     category_t history_file{L"history-file", L"Reading/Writing the history file"};
96 
97     category_t profile_history{L"profile-history", L"History performance measurements"};
98 
99     category_t iothread{L"iothread", L"Background IO thread events"};
100     category_t fd_monitor{L"fd-monitor", L"FD monitor events"};
101 
102     category_t term_support{L"term-support", L"Terminal feature detection"};
103 
104     category_t reader{L"reader", L"The interactive reader/input system"};
105     category_t reader_render{L"reader-render", L"Rendering the command line"};
106     category_t complete{L"complete", L"The completion system"};
107     category_t path{L"path", L"Searching/using paths"};
108 
109     category_t screen{L"screen", L"Screen repaints"};
110 };
111 
112 /// The class responsible for logging.
113 /// This is protected by a lock.
114 class logger_t {
115     FILE *file_;
116 
117     void log1(const wchar_t *);
118     void log1(const char *);
119     void log1(wchar_t);
120     void log1(char);
121     void log1(int64_t);
122     void log1(uint64_t);
123 
log1(const wcstring & s)124     void log1(const wcstring &s) { log1(s.c_str()); }
log1(const std::string & s)125     void log1(const std::string &s) { log1(s.c_str()); }
126 
127     template <typename T,
128               typename Enabler = typename std::enable_if<std::is_integral<T>::value>::type>
log1(T v)129     void log1(T v) {
130         if (std::is_signed<T>::value) {
131             log1(static_cast<int64_t>(v));
132         } else {
133             log1(static_cast<uint64_t>(v));
134         }
135     }
136 
137     template <typename T>
log_args_impl(const T & arg)138     void log_args_impl(const T &arg) {
139         log1(arg);
140     }
141 
142     template <typename T, typename... Ts>
log_args_impl(const T & arg,const Ts &...rest)143     void log_args_impl(const T &arg, const Ts &...rest) {
144         log1(arg);
145         log1(' ');
146         log_args_impl<Ts...>(rest...);
147     }
148 
149    public:
set_file(FILE * f)150     void set_file(FILE *f) { file_ = f; }
151 
152     logger_t();
153 
154     template <typename... Args>
log_args(const category_t & cat,const Args &...args)155     void log_args(const category_t &cat, const Args &...args) {
156         log1(cat.name);
157         log1(": ");
158         log_args_impl(args...);
159         log1('\n');
160     }
161 
162     void log_fmt(const category_t &cat, const wchar_t *fmt, ...);
163     void log_fmt(const category_t &cat, const char *fmt, ...);
164 
165     // Log outside of the usual flog usage.
log_extra(const wchar_t * s)166     void log_extra(const wchar_t *s) { log1(s); }
167 };
168 
169 extern owning_lock<logger_t> g_logger;
170 
171 }  // namespace flog_details
172 
173 /// Set the active flog categories according to the given wildcard \p wc.
174 void activate_flog_categories_by_pattern(const wcstring &wc);
175 
176 /// Set the file that flog should output to.
177 /// flog does not close this file.
178 void set_flog_output_file(FILE *f);
179 
180 /// \return a list of all categories, sorted by name.
181 std::vector<const flog_details::category_t *> get_flog_categories();
182 
183 /// Print some extra stuff to the flog file (stderr by default).
184 /// This is used by the tracing machinery.
185 void log_extra_to_flog_file(const wcstring &s);
186 
187 /// Output to the fish log a sequence of arguments, separated by spaces, and ending with a newline.
188 /// We save and restore errno because we don't want this to affect other code.
189 #define FLOG(wht, ...)                                                        \
190     do {                                                                      \
191         if (flog_details::category_list_t::g_instance->wht.enabled) {         \
192             auto old_errno = errno;                                           \
193             flog_details::g_logger.acquire()->log_args(                       \
194                 flog_details::category_list_t::g_instance->wht, __VA_ARGS__); \
195             errno = old_errno;                                                \
196         }                                                                     \
197     } while (0)
198 
199 /// Output to the fish log a printf-style formatted string.
200 #define FLOGF(wht, ...)                                                       \
201     do {                                                                      \
202         if (flog_details::category_list_t::g_instance->wht.enabled) {         \
203             auto old_errno = errno;                                           \
204             flog_details::g_logger.acquire()->log_fmt(                        \
205                 flog_details::category_list_t::g_instance->wht, __VA_ARGS__); \
206             errno = old_errno;                                                \
207         }                                                                     \
208     } while (0)
209 
210 #endif
211 
212 #define should_flog(wht) (flog_details::category_list_t::g_instance->wht.enabled)
213