Use the same infrastructure used for local logs. Also with structured metadata.
Signed-off-by: 胡玮文 <huww98@outlook.com>
}
};
+class LogEntryEncoder : public EntryEncoderBase {
+ public:
+ void encode(const LogEntry& le)
+ {
+ meta_buf.clear();
+ fmt::format_to(meta_buf,
+ R"(PRIORITY={:d}
+TIMESTAMP={}
+CEPH_NAME={}
+CEPH_RANK={}
+CEPH_SEQ={}
+CEPH_CHANNEL={}
+MESSAGE
+)",
+ clog_type_to_syslog_level(le.prio),
+ le.stamp.to_nsec(),
+ le.name.to_str(),
+ le.rank,
+ le.seq,
+ le.channel);
+
+ uint64_t msg_len = htole64(le.msg.size());
+ meta_buf.resize(meta_buf.size() + sizeof(msg_len));
+ *(reinterpret_cast<uint64_t*>(meta_buf.end()) - 1) = htole64(le.msg.size());
+
+ m_msg_vec[0].iov_base = meta_buf.data();
+ m_msg_vec[0].iov_len = meta_buf.size();
+
+ m_msg_vec[1].iov_base = (void *)le.msg.data();
+ m_msg_vec[1].iov_len = le.msg.size();
+ }
+};
+
enum class JournaldClient::MemFileMode {
MEMFD_CREATE,
OPEN_TMPFILE,
return client.send();
}
+JournaldClusterLogger::JournaldClusterLogger() :
+ m_log_entry_encoder(make_unique<detail::LogEntryEncoder>())
+{
+ client.m_msghdr.msg_iov = m_log_entry_encoder->iovec();
+ client.m_msghdr.msg_iovlen = m_log_entry_encoder->iovec_len();
+}
+
+JournaldClusterLogger::~JournaldClusterLogger() = default;
+
+int JournaldClusterLogger::log_log_entry(const LogEntry &le)
+{
+ m_log_entry_encoder->encode(le);
+ return client.send();
+}
+
}
#include <sys/types.h>
#include <sys/socket.h>
+struct LogEntry;
+
namespace ceph {
namespace logging {
namespace detail {
class EntryEncoder;
+class LogEntryEncoder;
class JournaldClient {
public:
* Logger to send local logs to journald
*
* local logs means @code dout(0) << ... @endcode and similars
+ *
+ * @see JournaldClusterLogger
*/
class JournaldLogger {
public:
const SubsystemMap * m_subs;
};
+/**
+ * Logger to send cluster log recieved by MON to journald
+ *
+ * @see JournaldLogger
+ */
+class JournaldClusterLogger {
+ public:
+ JournaldClusterLogger();
+ ~JournaldClusterLogger();
+
+ /**
+ * @returns 0 if log entry is successfully sent, -1 otherwise.
+ */
+ int log_log_entry(const LogEntry &le);
+
+ private:
+ detail::JournaldClient client;
+
+ std::unique_ptr<detail::LogEntryEncoder> m_log_entry_encoder;
+};
}
}
.set_description("Graylog port for cluster log messages")
.add_see_also("mon_cluster_log_to_graylog"),
+ Option("mon_cluster_log_to_journald", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+ .set_default("false")
+ .set_flag(Option::FLAG_RUNTIME)
+ .add_service("mon")
+ .set_description("Make monitor send cluster log to journald"),
+
Option("enable_experimental_unrecoverable_data_corrupting_features", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_flag(Option::FLAG_RUNTIME)
.set_default("")
#include "messages/MLog.h"
#include "messages/MLogAck.h"
#include "common/Graylog.h"
+#include "common/Journald.h"
#include "common/errno.h"
#include "common/strtol.h"
#include "include/ceph_assert.h"
return graylogs[channel];
}
+ceph::logging::JournaldClusterLogger &LogMonitor::log_channel_info::get_journald()
+{
+ dout(25) << __func__ << dendl;
+
+ if (!journald) {
+ journald = std::make_unique<ceph::logging::JournaldClusterLogger>();
+ }
+ return *journald;
+}
+
+void LogMonitor::log_channel_info::clear()
+{
+ log_to_syslog.clear();
+ syslog_level.clear();
+ syslog_facility.clear();
+ log_file.clear();
+ expanded_log_file.clear();
+ log_file_level.clear();
+ log_to_graylog.clear();
+ log_to_graylog_host.clear();
+ log_to_graylog_port.clear();
+ log_to_journald.clear();
+ graylogs.clear();
+ journald.reset();
+}
+
+LogMonitor::log_channel_info::log_channel_info() = default;
+LogMonitor::log_channel_info::~log_channel_info() = default;
+
#undef dout_prefix
#define dout_prefix _prefix(_dout, mon, get_last_committed())
<< " host:" << channels.log_to_graylog_host << dendl;
}
+ if (channels.do_log_to_journald(channel)) {
+ auto &journald = channels.get_journald();
+ journald.log_log_entry(le);
+ dout(7) << "journald: " << channel << dendl;
+ }
+
if (g_conf()->mon_cluster_log_to_file) {
string log_file = channels.get_log_file(channel);
dout(20) << __func__ << " logging for channel '" << channel
return;
}
+ r = get_conf_str_map_helper(
+ g_conf().get_val<string>("mon_cluster_log_to_journald"), oss,
+ &channels.log_to_journald,
+ CLOG_CONFIG_DEFAULT_KEY);
+ if (r < 0) {
+ derr << __func__ << " error parsing 'mon_cluster_log_to_journald'"
+ << dendl;
+ return;
+ }
+
channels.expand_channel_meta();
}
changed.count("mon_cluster_log_file_level") ||
changed.count("mon_cluster_log_to_graylog") ||
changed.count("mon_cluster_log_to_graylog_host") ||
- changed.count("mon_cluster_log_to_graylog_port")) {
+ changed.count("mon_cluster_log_to_graylog_port") ||
+ changed.count("mon_cluster_log_to_journald")) {
update_log_channels();
}
}
namespace ceph {
namespace logging {
class Graylog;
+ class JournaldClusterLogger;
}
}
std::map<std::string,std::string> log_to_graylog;
std::map<std::string,std::string> log_to_graylog_host;
std::map<std::string,std::string> log_to_graylog_port;
+ std::map<std::string,std::string> log_to_journald;
std::map<std::string, std::shared_ptr<ceph::logging::Graylog>> graylogs;
+ std::unique_ptr<ceph::logging::JournaldClusterLogger> journald;
uuid_d fsid;
std::string host;
- void clear() {
- log_to_syslog.clear();
- syslog_level.clear();
- syslog_facility.clear();
- log_file.clear();
- expanded_log_file.clear();
- log_file_level.clear();
- log_to_graylog.clear();
- log_to_graylog_host.clear();
- log_to_graylog_port.clear();
- graylogs.clear();
- }
+ log_channel_info();
+ ~log_channel_info();
+
+ void clear();
/** expands $channel meta variable on all maps *EXCEPT* log_file
*
}
std::shared_ptr<ceph::logging::Graylog> get_graylog(const std::string &channel);
+
+ bool do_log_to_journald(const std::string &channel) {
+ return (get_str_map_key(log_to_journald, channel,
+ &CLOG_CONFIG_DEFAULT_KEY) == "true");
+ }
+
+ ceph::logging::JournaldClusterLogger &get_journald();
} channels;
void update_log_channels();
"mon_cluster_log_to_graylog",
"mon_cluster_log_to_graylog_host",
"mon_cluster_log_to_graylog_port",
+ "mon_cluster_log_to_journald",
NULL
};
return KEYS;