aboutsummaryrefslogtreecommitdiff
path: root/bbot/diagnostics.cxx
blob: 12c8438c85e9e94d1b946988a9bb7c82ab40e474 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// file      : bbot/diagnostics.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license   : TBC; see accompanying LICENSE file

#include <bbot/diagnostics.hxx>

#ifdef __linux__
#  define BBOT_SYSTEMD
#endif

#ifdef BBOT_SYSTEMD
#  include <dlfcn.h>
#  include <syslog.h> // LOG_*
#endif

#include <bbot/utility.hxx>

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 "<N>..." 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<decltype(sd_journal_print)> (
        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);
    }
  }
}