1 // fish logging
2 #include "config.h"
3 
4 #include "flog.h"
5 
6 #include <vector>
7 
8 #include "common.h"
9 #include "enum_set.h"
10 #include "global_safety.h"
11 #include "parse_util.h"
12 #include "wcstringutil.h"
13 #include "wildcard.h"
14 
15 namespace flog_details {
16 
17 // Note we are relying on the order of global initialization within this file.
18 // It is important that 'all' be initialized before 'g_categories', because g_categories wants to
19 // append to all in the ctor.
20 /// This is not modified after initialization.
21 static std::vector<category_t *> s_all_categories;
22 
23 /// When a category is instantiated it adds itself to the 'all' list.
category_t(const wchar_t * name,const wchar_t * desc,bool enabled)24 category_t::category_t(const wchar_t *name, const wchar_t *desc, bool enabled)
25     : name(name), description(desc), enabled(enabled) {
26     s_all_categories.push_back(this);
27 }
28 
29 /// Instantiate all categories.
30 /// This is deliberately leaked to avoid pointless destructor registration.
31 category_list_t *const category_list_t::g_instance = new category_list_t();
32 
logger_t()33 logger_t::logger_t() : file_(stderr) {}
34 
35 owning_lock<logger_t> g_logger;
36 
log1(const wchar_t * s)37 void logger_t::log1(const wchar_t *s) { std::fputws(s, file_); }
38 
log1(const char * s)39 void logger_t::log1(const char *s) {
40     // Note glibc prohibits mixing narrow and wide I/O, so always use wide-printing functions.
41     // See #5900.
42     std::fwprintf(file_, L"%s", s);
43 }
44 
log1(wchar_t c)45 void logger_t::log1(wchar_t c) { std::fputwc(c, file_); }
46 
log1(char c)47 void logger_t::log1(char c) { std::fwprintf(file_, L"%c", c); }
48 
log1(int64_t v)49 void logger_t::log1(int64_t v) { std::fwprintf(file_, L"%lld", v); }
50 
log1(uint64_t v)51 void logger_t::log1(uint64_t v) { std::fwprintf(file_, L"%llu", v); }
52 
log_fmt(const category_t & cat,const wchar_t * fmt,...)53 void logger_t::log_fmt(const category_t &cat, const wchar_t *fmt, ...) {
54     va_list va;
55     va_start(va, fmt);
56     log1(cat.name);
57     log1(L": ");
58     std::vfwprintf(file_, fmt, va);
59     log1(L'\n');
60     va_end(va);
61 }
62 
log_fmt(const category_t & cat,const char * fmt,...)63 void logger_t::log_fmt(const category_t &cat, const char *fmt, ...) {
64     // glibc dislikes mixing wide and narrow output functions.
65     // So construct a narrow string in-place and output that via wide functions.
66     va_list va;
67     va_start(va, fmt);
68     int ret = vsnprintf(nullptr, 0, fmt, va);
69     va_end(va);
70 
71     if (ret < 0) {
72         perror("vsnprintf");
73         return;
74     }
75     size_t len = static_cast<size_t>(ret) + 1;
76     std::unique_ptr<char[]> buff(new char[len]);
77 
78     va_start(va, fmt);
79     ret = vsnprintf(buff.get(), len, fmt, va);
80     va_end(va);
81     if (ret < 0) {
82         perror("vsnprintf");
83         return;
84     }
85     log_fmt(cat, L"%s", buff.get());
86 }
87 
88 }  // namespace flog_details
89 
90 using namespace flog_details;
91 
92 /// For each category, if its name matches the wildcard, set its enabled to the given sense.
apply_one_wildcard(const wcstring & wc_esc,bool sense)93 static void apply_one_wildcard(const wcstring &wc_esc, bool sense) {
94     wcstring wc = parse_util_unescape_wildcards(wc_esc);
95     bool match_found = false;
96     for (category_t *cat : s_all_categories) {
97         if (wildcard_match(cat->name, wc)) {
98             cat->enabled = sense;
99             match_found = true;
100         }
101     }
102     if (!match_found) {
103         fprintf(stderr, "Failed to match debug category: %ls\n", wc_esc.c_str());
104     }
105 }
106 
activate_flog_categories_by_pattern(const wcstring & inwc)107 void activate_flog_categories_by_pattern(const wcstring &inwc) {
108     // Normalize underscores to dashes, allowing the user to be sloppy.
109     wcstring wc = inwc;
110     std::replace(wc.begin(), wc.end(), L'_', L'-');
111     for (const wcstring &s : split_string(wc, L',')) {
112         if (string_prefixes_string(L"-", s)) {
113             apply_one_wildcard(s.substr(1), false);
114         } else {
115             apply_one_wildcard(s, true);
116         }
117     }
118 }
119 
set_flog_output_file(FILE * f)120 void set_flog_output_file(FILE *f) { g_logger.acquire()->set_file(f); }
121 
log_extra_to_flog_file(const wcstring & s)122 void log_extra_to_flog_file(const wcstring &s) { g_logger.acquire()->log_extra(s.c_str()); }
123 
get_flog_categories()124 std::vector<const category_t *> get_flog_categories() {
125     std::vector<const category_t *> result(s_all_categories.begin(), s_all_categories.end());
126     std::sort(result.begin(), result.end(), [](const category_t *a, const category_t *b) {
127         return wcscmp(a->name, b->name) < 0;
128     });
129     return result;
130 }
131