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