12 LogLevel log_level = LogLevel::WARN;
13 LogLevel cons_log_level = LogLevel::WARN;
14 static bool log_to_console = false; // whether we should output log messages to
15 // console immediately
16 static bool log_current_line; // Whether the current line is being logged
18 static ServiceSet *service_set = nullptr; // Reference to service set
20 // Buffer for current log line:
21 constexpr static int log_linebuf_len = 120; // TODO needed?
22 static char * lineBuf = new char[log_linebuf_len];
23 static int lineBuf_idx = 0;
26 // Buffer of log lines:
27 // (One for main log, one for console)
28 static CPBuffer<4096> log_buffer[2];
30 // Each line is represented as a string of characters terminated by a
31 // newline. (If the newline is missing, the line is not complete).
33 constexpr static int DLOG_MAIN = 0; // main log facility
34 constexpr static int DLOG_CONS = 1; // console
36 constexpr static char DLOG_MAIN_FLAG = 1 << DLOG_MAIN;
37 constexpr static char DLOG_CONS_FLAG = 1 << DLOG_CONS;
39 static int current_index[2] = { 0, 0 }; // current/next message slot for (main, console)
41 static bool partway[2] = { false, false }; // part-way through writing log line?
42 static int msg_index[2] = { 0, 0 }; // index into the current message
44 static struct ev_io eviocb[2];
46 static bool discarded[2] = { false, false }; // have discarded a log line?
47 static bool special[2] = { false, false }; // writing from a special buffer?
48 static char *special_buf[2] = { nullptr, nullptr }; // special buffer
50 static void release_console()
52 ev_io_stop(ev_default_loop(EVFLAG_AUTO), &eviocb[DLOG_CONS]);
53 if (! log_to_console) {
54 int flags = fcntl(1, F_GETFL, 0);
55 fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
56 service_set->pullConsoleQueue();
60 static void log_conn_callback(struct ev_loop * loop, ev_io * w, int revents) noexcept
62 if (special[DLOG_CONS]) {
63 char * start = special_buf[DLOG_CONS] + msg_index[DLOG_CONS];
64 char * end = std::find(special_buf[DLOG_CONS] + msg_index[DLOG_CONS], (char *)nullptr, '\n');
65 int r = write(1, start, end - start + 1);
67 if (start + r > end) {
68 // All written: go on to next message in queue
69 special[DLOG_CONS] = false;
70 partway[DLOG_CONS] = false;
71 msg_index[DLOG_CONS] = 0;
74 msg_index[DLOG_CONS] += r;
79 // spurious readiness - EAGAIN or EWOULDBLOCK?
86 // Writing from the regular circular buffer
88 // TODO issue special message if we have discarded a log message
90 if (current_index[DLOG_CONS] == 0) {
95 char *ptr = log_buffer[DLOG_CONS].get_ptr(0);
96 int len = log_buffer[DLOG_CONS].get_contiguous_length(ptr);
97 char *creptr = ptr + len; // contiguous region end
98 char *eptr = std::find(ptr, creptr, '\n');
100 bool will_complete = false; // will complete this message?
101 if (eptr != creptr) {
102 eptr++; // include '\n'
103 will_complete = true;
108 int r = write(1, ptr, len);
111 // msg_index[DLOG_CONS] += r;
112 bool complete = (r == len) && will_complete;
113 log_buffer[DLOG_CONS].consume(len);
114 partway[DLOG_CONS] = ! complete;
116 current_index[DLOG_CONS] -= len;
117 if (current_index[DLOG_CONS] == 0 || !log_to_console) {
118 // No more messages buffered / stop logging to console:
125 // EAGAIN / EWOULDBLOCK?
131 // We've written something by the time we get here. We could fall through to below, but
132 // let's give other events a chance to be processed by returning now.
136 void init_log(ServiceSet *sset) noexcept
139 enable_console_log(true);
142 // Enable or disable console logging. If disabled, console logging will be disabled on the
143 // completion of output of the current message (if any), at which point the first service record
144 // queued in the service set will acquire the console.
145 void enable_console_log(bool enable) noexcept
147 if (enable && ! log_to_console) {
148 // Console is fd 1 - stdout
149 // Set non-blocking IO:
150 int flags = fcntl(1, F_GETFL, 0);
151 fcntl(1, F_SETFL, flags | O_NONBLOCK);
153 ev_io_init(&eviocb[DLOG_CONS], log_conn_callback, 1, EV_WRITE);
154 if (current_index[DLOG_CONS] > 0) {
155 ev_io_start(ev_default_loop(EVFLAG_AUTO), &eviocb[DLOG_CONS]);
157 log_to_console = true;
159 else if (! enable && log_to_console) {
160 log_to_console = false;
161 if (! partway[DLOG_CONS]) {
162 if (current_index[DLOG_CONS] > 0) {
163 // Try to flush any messages that are currently buffered. (Console is non-blocking
164 // so it will fail gracefully).
165 log_conn_callback(ev_default_loop(EVFLAG_AUTO), &eviocb[DLOG_CONS], EV_WRITE);
171 // (if we're partway through logging a message, we release the console when
177 // Variadic method to calculate the sum of string lengths:
178 static int sum_length(const char *arg) noexcept
180 return std::strlen(arg);
183 template <typename U, typename ... T> static int sum_length(U first, T ... args) noexcept
185 return sum_length(first) + sum_length(args...);
188 // Variadic method to append strings to a buffer:
189 static void append(CPBuffer<4096> &buf, const char *s)
191 buf.append(s, std::strlen(s));
194 template <typename U, typename ... T> static void append(CPBuffer<4096> &buf, U u, T ... t)
200 // Variadic method to log a sequence of strings as a single message:
201 template <typename ... T> static void do_log(T ... args) noexcept
203 int amount = sum_length(args...);
204 if (log_buffer[DLOG_CONS].get_free() >= amount) {
205 append(log_buffer[DLOG_CONS], args...);
207 bool was_first = (current_index[DLOG_CONS] == 0);
208 current_index[DLOG_CONS] += amount;
209 if (was_first && log_to_console) {
210 ev_io_start(ev_default_loop(EVFLAG_AUTO), & eviocb[DLOG_CONS]);
214 // TODO mark a discarded message
218 // Variadic method to potentially log a sequence of strings as a single message with the given log level:
219 template <typename ... T> static void do_log(LogLevel lvl, T ... args) noexcept
221 if (lvl >= cons_log_level) {
227 // Log a message. A newline will be appended.
228 void log(LogLevel lvl, const char *msg) noexcept
230 do_log(lvl, "dinit: ", msg, "\n");
233 // Log a multi-part message beginning
234 void logMsgBegin(LogLevel lvl, const char *msg) noexcept
237 log_current_line = lvl >= log_level;
238 if (log_current_line) {
239 if (log_to_console) {
240 std::cout << "dinit: " << msg;
245 // Continue a multi-part log message
246 void logMsgPart(const char *msg) noexcept
249 if (log_current_line) {
250 if (log_to_console) {
256 // Complete a multi-part log message
257 void logMsgEnd(const char *msg) noexcept
260 if (log_current_line) {
261 if (log_to_console) {
262 std::cout << msg << std::endl;
267 void logServiceStarted(const char *service_name) noexcept
269 do_log("[ OK ] ", service_name, "\n");
272 void logServiceFailed(const char *service_name) noexcept
274 do_log("[FAILED] ", service_name, "\n");
277 void logServiceStopped(const char *service_name) noexcept
279 do_log("[STOPPD] ", service_name, "\n");