mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-28 13:53:10 +00:00
Add async-safe flog support
This allows using flog in a limited way after calling fork, or from signal handlers.
This commit is contained in:
parent
727934c6b6
commit
28bf44d698
2 changed files with 75 additions and 1 deletions
56
src/flog.cpp
56
src/flog.cpp
|
@ -20,6 +20,10 @@ namespace flog_details {
|
|||
/// This is not modified after initialization.
|
||||
static std::vector<category_t *> s_all_categories;
|
||||
|
||||
/// The fd underlying the flog output file.
|
||||
/// This is separated out for flogf_async_safe.
|
||||
static int s_flog_file_fd{STDERR_FILENO};
|
||||
|
||||
/// When a category is instantiated it adds itself to the 'all' list.
|
||||
category_t::category_t(const wchar_t *name, const wchar_t *desc, bool enabled)
|
||||
: name(name), description(desc), enabled(enabled) {
|
||||
|
@ -85,6 +89,52 @@ void logger_t::log_fmt(const category_t &cat, const char *fmt, ...) {
|
|||
log_fmt(cat, L"%s", buff.get());
|
||||
}
|
||||
|
||||
inline void async_safe_write_str(const char *s, size_t len = std::string::npos) {
|
||||
if (s_flog_file_fd < 0) return;
|
||||
if (len == std::string::npos) len = std::strlen(s);
|
||||
ignore_result(write(s_flog_file_fd, s, len));
|
||||
}
|
||||
|
||||
// static
|
||||
void logger_t::flogf_async_safe(const char *category, const char *fmt, const char *param1,
|
||||
const char *param2, const char *param3, const char *param4,
|
||||
const char *param5, const char *param6, const char *param7,
|
||||
const char *param8, const char *param9, const char *param10,
|
||||
const char *param11, const char *param12) {
|
||||
const char *const params[] = {param1, param2, param3, param4, param5, param6,
|
||||
param7, param8, param9, param10, param11, param12};
|
||||
const size_t max_params = sizeof params / sizeof *params;
|
||||
|
||||
// Can't call fwprintf, that may allocate memory.
|
||||
// Just call write() over and over.
|
||||
async_safe_write_str(category);
|
||||
async_safe_write_str(": ");
|
||||
|
||||
size_t param_idx = 0;
|
||||
const char *cursor = fmt;
|
||||
while (*cursor != '\0') {
|
||||
const char *end = std::strchr(cursor, '%');
|
||||
if (end == nullptr) end = cursor + std::strlen(cursor);
|
||||
if (end > cursor) async_safe_write_str(cursor, end - cursor);
|
||||
if (end[0] == '%' && end[1] == 's') {
|
||||
// Handle a format string.
|
||||
const char *param = (param_idx < max_params ? params[param_idx++] : nullptr);
|
||||
if (!param) param = "(null)";
|
||||
async_safe_write_str(param);
|
||||
cursor = end + 2;
|
||||
} else if (end[0] == '\0') {
|
||||
// Must be at the end of the string.
|
||||
cursor = end;
|
||||
} else {
|
||||
// Some other format specifier, just skip it.
|
||||
cursor = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// We always append a newline.
|
||||
async_safe_write_str("\n");
|
||||
}
|
||||
|
||||
} // namespace flog_details
|
||||
|
||||
using namespace flog_details;
|
||||
|
@ -117,7 +167,11 @@ void activate_flog_categories_by_pattern(const wcstring &inwc) {
|
|||
}
|
||||
}
|
||||
|
||||
void set_flog_output_file(FILE *f) { g_logger.acquire()->set_file(f); }
|
||||
void set_flog_output_file(FILE *f) {
|
||||
assert(f && "Null file");
|
||||
g_logger.acquire()->set_file(f);
|
||||
s_flog_file_fd = fileno(f);
|
||||
}
|
||||
|
||||
void log_extra_to_flog_file(const wcstring &s) { g_logger.acquire()->log_extra(s.c_str()); }
|
||||
|
||||
|
|
20
src/flog.h
20
src/flog.h
|
@ -164,6 +164,15 @@ class logger_t {
|
|||
|
||||
// Log outside of the usual flog usage.
|
||||
void log_extra(const wchar_t *s) { log1(s); }
|
||||
|
||||
// Variant of flogf which is async safe. This is intended to be used after fork().
|
||||
static void flogf_async_safe(const char *category, const char *fmt,
|
||||
const char *param1 = nullptr, const char *param2 = nullptr,
|
||||
const char *param3 = nullptr, const char *param4 = nullptr,
|
||||
const char *param5 = nullptr, const char *param6 = nullptr,
|
||||
const char *param7 = nullptr, const char *param8 = nullptr,
|
||||
const char *param9 = nullptr, const char *param10 = nullptr,
|
||||
const char *param11 = nullptr, const char *param12 = nullptr);
|
||||
};
|
||||
|
||||
extern owning_lock<logger_t> g_logger;
|
||||
|
@ -207,6 +216,17 @@ void log_extra_to_flog_file(const wcstring &s);
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
/// Variant of FLOG which is safe to use after fork().
|
||||
/// Only %s specifiers are supported.
|
||||
#define FLOGF_SAFE(wht, ...) \
|
||||
do { \
|
||||
if (flog_details::category_list_t::g_instance->wht.enabled) { \
|
||||
auto old_errno = errno; \
|
||||
flog_details::logger_t::flogf_async_safe(#wht, __VA_ARGS__); \
|
||||
errno = old_errno; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#define should_flog(wht) (flog_details::category_list_t::g_instance->wht.enabled)
|
||||
|
|
Loading…
Reference in a new issue