// file : bbot/diagnostics.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #ifdef __linux__ # define BBOT_SYSTEMD #endif #ifdef BBOT_SYSTEMD # include # include // LOG_* #endif #include using namespace std; using namespace butl; namespace bbot { // Diagnostics verbosity level. // uint16_t verb; // Diagnostic facility, project specifics. // void simple_prologue_base:: operator() (const diag_record& r) const { if (type_ != nullptr) r << type_; if (name_ != nullptr) r << name_; if (data_ != nullptr) r << '(' << data_ << ')'; if (name_ != nullptr || data_ != nullptr) r << ": "; } const char* trace_type = "trace: "; const char* trace_indent = "\n "; trace_mark_base:: trace_mark_base (const char* name, const char* data) : basic_mark_base (trace_type, trace_indent, name, data) { } basic_mark error ("error: "); basic_mark warn ("warning: "); basic_mark info ("info: "); basic_mark text (nullptr); fail_mark fail ("error: "); const fail_end endf; #ifndef BBOT_SYSTEMD void systemd_diagnostics (bool) { fail << "systemd mode only supported on Linux"; } #else static const char systemd_indent[] = "\n"; static const char libsystemd[] = "libsystemd.so.0"; static int (*sd_journal_print) (int priority, const char *format, ...); static void (*default_writer) (const diag_record&); static void systemd_writer (const diag_record& dr) { const string& m (dr.os.str ()); const char* s (m.c_str ()); // Map "..." prefix to priority. Absent is info. // int p (LOG_INFO); if (m.size () > 2 && m[0] == '<' && m[2] == '>') { char c (m[1]); p = (c == '0' ? LOG_EMERG : c == '1' ? LOG_ALERT : c == '2' ? LOG_CRIT : c == '3' ? LOG_ERR : c == '4' ? LOG_WARNING : c == '5' ? LOG_NOTICE : c == '6' ? LOG_INFO : c == '7' ? LOG_DEBUG : LOG_ALERT); s += 3; } if (sd_journal_print (p, "%s", s) != 0) default_writer (dr); } void systemd_diagnostics (bool with_critical) { trace_indent = fail.indent_ = error.indent_ = warn.indent_ = info.indent_ = text.indent_ = systemd_indent; // We don't always see colorized output so keep 'error: ', etc. // // Note that lucky for us the default level is info (<6>) and so we can // omit it. Failed that, we would see the '<6>' prefixes in "followup" // entries (unless we clean them up in systemd_writer() or some such). // fail.type_ = with_critical ? "<2>fatal: " : "<3>error: "; error.type_ = "<3>error: "; warn.type_ = "<4>warning: "; info.type_ = nullptr; trace_type = "<7>"; // Using sd_journal_print() allows us to print multi-line records nicely. // However, requiring libsystemd-dev (or equivalent) is a bit of a schlep // so we use the dynamic loading trick. // // Note that we could also fallback to stderr output (which is how it // worked before) if we cannot load the library or resolve the symbol for // some reason. But for now we fail to keep things simple. // void* libsd (dlopen (libsystemd, RTLD_LAZY | RTLD_GLOBAL)); if (libsd == nullptr) fail << "unable to load " << libsystemd << ": " << dlerror (); sd_journal_print = function_cast ( dlsym (libsd, "sd_journal_print")); if (sd_journal_print == nullptr) fail << "unable to lookup sd_journal_print() in " << libsystemd << ": " << dlerror (); default_writer = diag_record::writer; diag_record::writer = &systemd_writer; } #endif // tracer // void tracer:: operator() (const char* const args[], size_t n) const { if (verb >= 3) { diag_record dr (*this); process::print (dr.os, args, n); } } }