#include "include/types.h"
+#include "include/str_map.h"
#include "msg/Messenger.h"
#include "msg/Message.h"
#include "common/config.h"
#define dout_subsys ceph_subsys_monc
+#undef dout_prefix
+#define dout_prefix _prefix(_dout, this)
+static ostream& _prefix(std::ostream *_dout, LogClient *logc) {
+ return *_dout << "log_client ";
+}
+
+static ostream& _prefix(std::ostream *_dout, LogChannel *lc) {
+ return *_dout << "log_channel(" << lc->get_log_channel() << ") ";
+}
+
+LogChannel::LogChannel(CephContext *cct, LogClient *lc, const string &channel)
+ : cct(cct), parent(lc), channel_lock("LogChannel::channel_lock"),
+ log_channel(channel), log_to_syslog(false), log_to_monitors(false)
+{
+}
+
+LogChannel::LogChannel(CephContext *cct, LogClient *lc,
+ const string &channel, const string &facility,
+ const string &prio)
+ : cct(cct), parent(lc), channel_lock("LogChannel::channel_lock"),
+ log_channel(channel), log_prio(prio), syslog_facility(facility),
+ log_to_syslog(false), log_to_monitors(false)
+{
+}
LogClient::LogClient(CephContext *cct, Messenger *m, MonMap *mm,
enum logclient_flag_t flags)
{
}
-LogClientTemp::LogClientTemp(clog_type type_, LogClient &parent_)
+LogClientTemp::LogClientTemp(clog_type type_, LogChannel &parent_)
: type(type_), parent(parent_)
{
}
parent.do_log(type, ss);
}
-void LogClient::do_log(clog_type prio, std::stringstream& ss)
+void LogChannel::do_log(clog_type prio, std::stringstream& ss)
{
while (!ss.eof()) {
string s;
}
}
-void LogClient::do_log(clog_type prio, const std::string& s)
+void LogChannel::do_log(clog_type prio, const std::string& s)
{
- Mutex::Locker l(log_lock);
+ Mutex::Locker l(channel_lock);
int lvl = (prio == CLOG_ERROR ? -1 : 0);
ldout(cct,lvl) << "log " << prio << " : " << s << dendl;
LogEntry e;
- e.who = messenger->get_myinst();
+ // who will be set when we queue the entry on LogClient
+ //e.who = messenger->get_myinst();
e.stamp = ceph_clock_now(cct);
- e.seq = ++last_log;
+ // seq will be set when we queue the entry on LogClient
+ // e.seq = ++last_log;
e.prio = prio;
e.msg = s;
+ e.channel = get_log_channel();
// log to syslog?
- if (cct->_conf->clog_to_syslog) {
- e.log_to_syslog(cct->_conf->clog_to_syslog_level,
- cct->_conf->clog_to_syslog_facility);
+ if (do_log_to_syslog()) {
+ ldout(cct,0) << __func__ << " log to syslog" << dendl;
+ e.log_to_syslog(get_log_prio(), get_syslog_facility());
}
// log to monitor?
- if (cct->_conf->clog_to_monitors) {
- log_queue.push_back(e);
-
- // if we are a monitor, queue for ourselves, synchronously
- if (is_mon) {
- assert(messenger->get_myname().is_mon());
- ldout(cct,10) << "send_log to self" << dendl;
- Message *log = _get_mon_log_message();
- messenger->get_loopback_connection()->send_message(log);
- }
+ if (log_to_monitors) {
+ parent->queue(e);
}
}
return log;
}
+void LogClient::_send_to_mon()
+{
+ assert(log_lock.is_locked());
+ assert(is_mon);
+ assert(messenger->get_myname().is_mon());
+ ldout(cct,10) << __func__ << "log to self" << dendl;
+ Message *log = _get_mon_log_message();
+ messenger->get_loopback_connection()->send_message(log);
+}
+
+version_t LogClient::queue(LogEntry &entry)
+{
+ Mutex::Locker l(log_lock);
+ entry.seq = ++last_log;
+ entry.who = messenger->get_myinst();
+ log_queue.push_back(entry);
+
+ if (is_mon) {
+ _send_to_mon();
+ }
+
+ return entry.seq;
+}
+
bool LogClient::handle_log_ack(MLogAck *m)
{
Mutex::Locker l(log_lock);
class Message;
struct Connection;
+class LogChannel;
+
class LogClientTemp
{
public:
- LogClientTemp(clog_type type_, LogClient &parent_);
+ LogClientTemp(clog_type type_, LogChannel &parent_);
LogClientTemp(const LogClientTemp &rhs);
~LogClientTemp();
private:
clog_type type;
- LogClient &parent;
+ LogChannel &parent;
stringstream ss;
};
-class LogClient
+/** Manage where we output to and at which priority
+ *
+ * Not to be confused with the LogClient, which is the almighty coordinator
+ * of channels. We just deal with the boring part of the logging: send to
+ * syslog, send to file, generate LogEntry and queue it for the LogClient.
+ *
+ * Past queueing the LogEntry, the LogChannel is done with the whole thing.
+ * LogClient will deal with sending and handling of LogEntries.
+ */
+class LogChannel
{
public:
- enum logclient_flag_t {
- NO_FLAGS = 0,
- FLAG_MON = 0x1,
- };
- LogClient(CephContext *cct, Messenger *m, MonMap *mm,
- enum logclient_flag_t flags);
-
- bool handle_log_ack(MLogAck *m);
+ LogChannel(CephContext *cct, LogClient *lc, const std::string &channel);
+ LogChannel(CephContext *cct, LogClient *lc,
+ const std::string &channel,
+ const std::string &facility,
+ const std::string &prio);
LogClientTemp debug() {
return LogClientTemp(CLOG_DEBUG, *this);
do_log(CLOG_SEC, s);
}
+ void set_log_to_monitors(bool v) {
+ log_to_monitors = v;
+ }
+ void set_log_to_syslog(bool v) {
+ log_to_syslog = v;
+ }
+ void set_log_channel(const std::string& v) {
+ log_channel = v;
+ }
+ void set_log_prio(const std::string& v) {
+ log_prio = v;
+ }
+ void set_syslog_facility(const std::string& v) {
+ syslog_facility = v;
+ }
+ std::string get_log_prio() { return log_prio; }
+ std::string get_log_channel() { return log_channel; }
+ std::string get_syslog_facility() { return syslog_facility; }
+ bool must_log_to_syslog() { return log_to_syslog; }
+ /**
+ * Do we want to log to syslog?
+ *
+ * @return true if log_to_syslog is true and both channel and prio
+ * are not empty; false otherwise.
+ */
+ bool do_log_to_syslog() {
+ return must_log_to_syslog() &&
+ !log_prio.empty() && !log_channel.empty();
+ }
+ bool must_log_to_monitors() { return log_to_monitors; }
+
+ typedef shared_ptr<LogChannel> Ref;
+
+private:
+ void do_log(clog_type prio, std::stringstream& ss);
+ void do_log(clog_type prio, const std::string& s);
+
+ CephContext *cct;
+ LogClient *parent;
+ Mutex channel_lock;
+ std::string log_channel;
+ std::string log_prio;
+ std::string syslog_facility;
+ bool log_to_syslog;
+ bool log_to_monitors;
+
+
+ friend class LogClientTemp;
+};
+
+typedef LogChannel::Ref LogChannelRef;
+
+class LogClient
+{
+public:
+ enum logclient_flag_t {
+ NO_FLAGS = 0,
+ FLAG_MON = 0x1,
+ };
+
+ LogClient(CephContext *cct, Messenger *m, MonMap *mm,
+ enum logclient_flag_t flags);
+ virtual ~LogClient() {
+ channels.clear();
+ }
+
+ bool handle_log_ack(MLogAck *m);
void reset_session();
Message *get_mon_log_message();
bool are_pending();
+ LogChannelRef create_channel() {
+ return create_channel(CLOG_CHANNEL_DEFAULT);
+ }
+
+ LogChannelRef create_channel(const std::string& name) {
+ LogChannelRef c;
+ if (channels.count(name))
+ c = channels[name];
+ else {
+ c = LogChannelRef(new LogChannel(cct, this, name));
+ channels[name] = c;
+ }
+ return c;
+ }
+
+ void destroy_channel(const std::string& name) {
+ if (channels.count(name))
+ channels.erase(name);
+ }
+
+ void shutdown() {
+ channels.clear();
+ }
+
+ version_t queue(LogEntry &entry);
+
private:
- void do_log(clog_type prio, std::stringstream& ss);
- void do_log(clog_type prio, const std::string& s);
Message *_get_mon_log_message();
+ void _send_to_mon();
CephContext *cct;
Messenger *messenger;
version_t last_log;
std::deque<LogEntry> log_queue;
- friend class LogClientTemp;
-};
+ std::map<std::string, LogChannelRef> channels;
+};
#endif