1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 #include "common/errno.h"
7 #include "common/safe_io.h"
8 #include "common/Graylog.h"
9 #include "common/Journald.h"
10 #include "common/valgrind.h"
12 #include "include/ceph_assert.h"
13 #include "include/compat.h"
14 #include "include/on_exit.h"
15 #include "include/uuid.h"
19 #include "SubsystemMap.h"
21 #include <boost/container/vector.hpp>
32 #include <fmt/format.h>
33 #include <fmt/ostream.h>
35 #define MAX_LOG_BUF 65536
40 static OnExitManager exit_callbacks
;
42 static void log_on_exit(void *p
)
47 delete (Log
**)p
;// Delete allocated pointer (not Log object, the pointer only!)
50 Log::Log(const SubsystemMap
*s
)
51 : m_indirect_this(nullptr),
53 m_recent(DEFAULT_MAX_RECENT
)
55 m_log_buf
.reserve(MAX_LOG_BUF
);
61 if (m_indirect_this
) {
62 *m_indirect_this
= nullptr;
65 ceph_assert(!is_started());
67 VOID_TEMP_FAILURE_RETRY(::close(m_fd
));
72 void Log::_configure_stderr()
76 if (int rc
= fstat(m_fd_stderr
, &info
); rc
== -1) {
77 std::cerr
<< "failed to stat stderr: " << cpp_strerror(errno
) << std::endl
;
81 if (S_ISFIFO(info
.st_mode
)) {
82 /* Set O_NONBLOCK on FIFO stderr file. We want to ensure atomic debug log
83 * writes so they do not get partially read by e.g. buggy container
84 * runtimes. See also IEEE Std 1003.1-2017 and Log::_log_stderr below.
86 * This isn't required on Windows.
88 int flags
= fcntl(m_fd_stderr
, F_GETFL
);
90 std::cerr
<< "failed to get fcntl flags for stderr: " << cpp_strerror(errno
) << std::endl
;
93 if (!(flags
& O_NONBLOCK
)) {
95 flags
= fcntl(m_fd_stderr
, F_SETFL
, flags
);
97 std::cerr
<< "failed to set fcntl flags for stderr: " << cpp_strerror(errno
) << std::endl
;
101 do_stderr_poll
= true;
108 void Log::set_coarse_timestamps(bool coarse
) {
109 std::scoped_lock
lock(m_flush_mutex
);
111 Entry::clock().coarsen();
113 Entry::clock().refine();
116 void Log::set_flush_on_exit()
118 std::scoped_lock
lock(m_flush_mutex
);
119 // Make sure we flush on shutdown. We do this by deliberately
120 // leaking an indirect pointer to ourselves (on_exit() can't
121 // unregister a callback). This is not racy only becuase we
122 // assume that exit() won't race with ~Log().
123 if (m_indirect_this
== NULL
) {
124 m_indirect_this
= new (Log
*)(this);
125 exit_callbacks
.add_callback(log_on_exit
, m_indirect_this
);
129 void Log::set_max_new(std::size_t n
)
131 std::scoped_lock
lock(m_queue_mutex
);
135 void Log::set_max_recent(std::size_t n
)
137 std::scoped_lock
lock(m_flush_mutex
);
138 m_recent
.set_capacity(n
);
141 void Log::set_log_file(std::string_view fn
)
143 std::scoped_lock
lock(m_flush_mutex
);
147 void Log::set_log_stderr_prefix(std::string_view p
)
149 std::scoped_lock
lock(m_flush_mutex
);
150 m_log_stderr_prefix
= p
;
153 void Log::reopen_log_file()
155 std::scoped_lock
lock(m_flush_mutex
);
159 m_flush_mutex_holder
= pthread_self();
161 VOID_TEMP_FAILURE_RETRY(::close(m_fd
));
164 if (m_log_file
.length()) {
165 m_fd
= ::open(m_log_file
.c_str(), O_CREAT
|O_WRONLY
|O_APPEND
|O_CLOEXEC
, 0644);
166 if (m_fd
>= 0 && (m_uid
|| m_gid
)) {
167 if (::fchown(m_fd
, m_uid
, m_gid
) < 0) {
169 std::cerr
<< "failed to chown " << m_log_file
<< ": " << cpp_strerror(e
)
174 m_flush_mutex_holder
= 0;
177 void Log::chown_log_file(uid_t uid
, gid_t gid
)
179 std::scoped_lock
lock(m_flush_mutex
);
181 int r
= ::fchown(m_fd
, uid
, gid
);
184 std::cerr
<< "failed to chown " << m_log_file
<< ": " << cpp_strerror(r
)
190 void Log::set_syslog_level(int log
, int crash
)
192 std::scoped_lock
lock(m_flush_mutex
);
194 m_syslog_crash
= crash
;
197 void Log::set_stderr_level(int log
, int crash
)
199 std::scoped_lock
lock(m_flush_mutex
);
201 m_stderr_crash
= crash
;
204 void Log::set_graylog_level(int log
, int crash
)
206 std::scoped_lock
lock(m_flush_mutex
);
208 m_graylog_crash
= crash
;
211 void Log::start_graylog(const std::string
& host
,
214 std::scoped_lock
lock(m_flush_mutex
);
215 if (! m_graylog
.get()) {
216 m_graylog
= std::make_shared
<Graylog
>(m_subs
, "dlog");
217 m_graylog
->set_hostname(host
);
218 m_graylog
->set_fsid(fsid
);
223 void Log::stop_graylog()
225 std::scoped_lock
lock(m_flush_mutex
);
229 void Log::set_journald_level(int log
, int crash
)
231 std::scoped_lock
lock(m_flush_mutex
);
232 m_journald_log
= log
;
233 m_journald_crash
= crash
;
236 void Log::start_journald_logger()
238 std::scoped_lock
lock(m_flush_mutex
);
240 m_journald
= std::make_unique
<JournaldLogger
>(m_subs
);
244 void Log::stop_journald_logger()
246 std::scoped_lock
lock(m_flush_mutex
);
250 void Log::submit_entry(Entry
&& e
)
252 std::unique_lock
lock(m_queue_mutex
);
253 m_queue_mutex_holder
= pthread_self();
255 if (unlikely(m_inject_segv
))
256 *(volatile int *)(0) = 0xdead;
258 // wait for flush to catch up
259 while (is_started() &&
260 m_new
.size() > m_max_new
) {
261 if (m_stop
) break; // force addition
262 m_cond_loggers
.wait(lock
);
265 m_new
.emplace_back(std::move(e
));
266 m_cond_flusher
.notify_all();
267 m_queue_mutex_holder
= 0;
272 std::scoped_lock
lock1(m_flush_mutex
);
273 m_flush_mutex_holder
= pthread_self();
276 std::scoped_lock
lock2(m_queue_mutex
);
277 m_queue_mutex_holder
= pthread_self();
278 assert(m_flush
.empty());
280 m_cond_loggers
.notify_all();
281 m_queue_mutex_holder
= 0;
284 _flush(m_flush
, false);
285 m_flush_mutex_holder
= 0;
288 void Log::_log_safe_write(std::string_view sv
)
292 int r
= safe_write(m_fd
, sv
.data(), sv
.size());
293 if (r
!= m_fd_last_error
) {
295 std::cerr
<< "problem writing to " << m_log_file
296 << ": " << cpp_strerror(r
)
302 void Log::set_stderr_fd(int fd
)
308 void Log::_log_stderr(std::string_view strv
)
310 if (do_stderr_poll
) {
311 auto& prefix
= m_log_stderr_prefix
;
312 size_t const len
= prefix
.size() + strv
.size();
313 boost::container::small_vector
<char, PIPE_BUF
> buf
;
314 buf
.resize(len
+1, '\0');
315 memcpy(buf
.data(), prefix
.c_str(), prefix
.size());
316 memcpy(buf
.data()+prefix
.size(), strv
.data(), strv
.size());
318 char const* const start
= buf
.data();
319 char const* current
= start
;
320 while ((size_t)(current
-start
) < len
) {
321 auto chunk
= std::min
<ssize_t
>(PIPE_BUF
, len
-(ssize_t
)(current
-start
));
323 ssize_t rc
= write(m_fd_stderr
, current
, chunk
);
328 /* According to IEEE Std 1003.1-2017, this cannot happen:
330 * Write requests to a pipe or FIFO shall be handled in the same way as a regular file with the following exceptions:
332 * If the O_NONBLOCK flag is set ...
334 * A write request for {PIPE_BUF} or fewer bytes shall have the
335 * following effect: if there is sufficient space available in
336 * the pipe, write() shall transfer all the data and return the
337 * number of bytes requested. Otherwise, write() shall transfer
338 * no data and return -1 with errno set to [EAGAIN].
340 * In any case, handle misbehavior gracefully by incrementing current.
344 } else if (rc
== -1) {
345 if (errno
== EAGAIN
) {
346 struct pollfd pfd
[1];
347 pfd
[0].fd
= m_fd_stderr
;
348 pfd
[0].events
= POLLOUT
;
350 /* ignore errors / success, just retry the write */
351 } else if (errno
== EINTR
) {
354 /* some other kind of error, no point logging if stderr writes fail */
361 fmt::print(std::cerr
, "{}{}", m_log_stderr_prefix
, strv
);
365 void Log::_flush_logbuf()
367 if (m_log_buf
.size()) {
368 _log_safe_write(std::string_view(m_log_buf
.data(), m_log_buf
.size()));
373 void Log::_flush(EntryVector
& t
, bool crash
)
377 assert(m_log_buf
.empty());
384 auto prio
= e
.m_prio
;
385 auto stamp
= e
.m_stamp
;
386 auto sub
= e
.m_subsys
;
387 auto thread
= e
.m_thread
;
390 bool should_log
= crash
|| m_subs
->get_log_level(sub
) >= prio
;
391 bool do_fd
= m_fd
>= 0 && should_log
;
392 bool do_syslog
= m_syslog_crash
>= prio
&& should_log
;
393 bool do_stderr
= m_stderr_crash
>= prio
&& should_log
;
394 bool do_graylog2
= m_graylog_crash
>= prio
&& should_log
;
395 bool do_journald
= m_journald_crash
>= prio
&& should_log
;
397 if (do_fd
|| do_syslog
|| do_stderr
) {
398 const std::size_t cur
= m_log_buf
.size();
399 std::size_t used
= 0;
400 const std::size_t allocated
= e
.size() + 80;
401 m_log_buf
.resize(cur
+ allocated
);
403 char* const start
= m_log_buf
.data();
404 char* pos
= start
+ cur
;
407 used
+= (std::size_t)snprintf(pos
+ used
, allocated
- used
, "%6ld> ", -(--len
));
409 used
+= (std::size_t)append_time(stamp
, pos
+ used
, allocated
- used
);
410 used
+= (std::size_t)snprintf(pos
+ used
, allocated
- used
, " %lx %2d ", (unsigned long)thread
, prio
);
411 memcpy(pos
+ used
, str
.data(), str
.size());
414 ceph_assert((used
+ 1 /* '\n' */) < allocated
);
417 syslog(LOG_USER
|LOG_INFO
, "%s", pos
);
420 /* now add newline */
424 _log_stderr(std::string_view(pos
, used
));
428 m_log_buf
.resize(cur
+ used
);
433 if (m_log_buf
.size() > MAX_LOG_BUF
) {
438 if (do_graylog2
&& m_graylog
) {
439 m_graylog
->log_entry(e
);
442 if (do_journald
&& m_journald
) {
443 m_journald
->log_entry(e
);
446 m_recent
.push_back(std::move(e
));
453 void Log::_log_message(std::string_view s
, bool crash
)
456 std::string b
= fmt::format("{}\n", s
);
457 int r
= safe_write(m_fd
, b
.data(), b
.size());
459 std::cerr
<< "problem writing to " << m_log_file
<< ": " << cpp_strerror(r
) << std::endl
;
461 if ((crash
? m_syslog_crash
: m_syslog_log
) >= 0) {
462 syslog(LOG_USER
|LOG_INFO
, "%.*s", static_cast<int>(s
.size()), s
.data());
465 if ((crash
? m_stderr_crash
: m_stderr_log
) >= 0) {
466 std::cerr
<< s
<< std::endl
;
471 static uint64_t tid_to_int(T tid
)
473 if constexpr (std::is_pointer_v
<T
>) {
474 return reinterpret_cast<std::uintptr_t>(tid
);
480 void Log::dump_recent()
482 std::scoped_lock
lock1(m_flush_mutex
);
483 m_flush_mutex_holder
= pthread_self();
486 std::scoped_lock
lock2(m_queue_mutex
);
487 m_queue_mutex_holder
= pthread_self();
488 assert(m_flush
.empty());
490 m_queue_mutex_holder
= 0;
493 _flush(m_flush
, false);
495 _log_message("--- begin dump of recent events ---", true);
496 std::set
<pthread_t
> recent_pthread_ids
;
499 t
.insert(t
.end(), std::make_move_iterator(m_recent
.begin()), std::make_move_iterator(m_recent
.end()));
501 for (const auto& e
: t
) {
502 recent_pthread_ids
.emplace(e
.m_thread
);
507 _log_message("--- logging levels ---", true);
508 for (const auto& p
: m_subs
->m_subsys
) {
509 _log_message(fmt::format(" {:2d}/{:2d} {}",
510 p
.log_level
, p
.gather_level
, p
.name
), true);
512 _log_message(fmt::format(" {:2d}/{:2d} (syslog threshold)",
513 m_syslog_log
, m_syslog_crash
), true);
514 _log_message(fmt::format(" {:2d}/{:2d} (stderr threshold)",
515 m_stderr_log
, m_stderr_crash
), true);
517 _log_message("--- pthread ID / name mapping for recent threads ---", true);
518 for (const auto pthread_id
: recent_pthread_ids
)
520 char pthread_name
[16] = {0}; //limited by 16B include terminating null byte.
521 ceph_pthread_getname(pthread_id
, pthread_name
, sizeof(pthread_name
));
522 // we want the ID to be printed in the same format as we use for a log entry.
523 // The reason is easier grepping.
524 _log_message(fmt::format(" {:x} / {}",
525 tid_to_int(pthread_id
), pthread_name
), true);
528 _log_message(fmt::format(" max_recent {:9}", m_recent
.capacity()), true);
529 _log_message(fmt::format(" max_new {:9}", m_max_new
), true);
530 _log_message(fmt::format(" log_file {}", m_log_file
), true);
532 _log_message("--- end dump of recent events ---", true);
534 assert(m_log_buf
.empty());
536 m_flush_mutex_holder
= 0;
541 ceph_assert(!is_started());
543 std::scoped_lock
lock(m_queue_mutex
);
553 std::scoped_lock
lock(m_queue_mutex
);
555 m_cond_flusher
.notify_one();
556 m_cond_loggers
.notify_all();
566 std::unique_lock
lock(m_queue_mutex
);
567 m_queue_mutex_holder
= pthread_self();
569 if (!m_new
.empty()) {
570 m_queue_mutex_holder
= 0;
574 m_queue_mutex_holder
= pthread_self();
578 m_cond_flusher
.wait(lock
);
580 m_queue_mutex_holder
= 0;
586 bool Log::is_inside_log_lock()
589 pthread_self() == m_queue_mutex_holder
||
590 pthread_self() == m_flush_mutex_holder
;
593 void Log::inject_segv()
595 m_inject_segv
= true;
598 void Log::reset_segv()
600 m_inject_segv
= false;