mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-28 14:23:33 +08:00
Logger implementation
This commit is contained in:
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <uavcan/time.hpp>
|
||||||
|
#include <uavcan/debug.hpp>
|
||||||
|
#include <uavcan/protocol/debug/LogMessage.hpp>
|
||||||
|
#include <uavcan/marshal/char_array_formatter.hpp>
|
||||||
|
#include <uavcan/node/publisher.hpp>
|
||||||
|
|
||||||
|
#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
|
||||||
|
# error UAVCAN_CPP_VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace uavcan
|
||||||
|
{
|
||||||
|
|
||||||
|
class ILogSink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ILogSink() { }
|
||||||
|
virtual void log(const protocol::debug::LogMessage& message) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename StorageType<typename protocol::debug::LogLevel::FieldTypes::value>::Type LogLevel;
|
||||||
|
|
||||||
|
static const LogLevel LevelAboveAll = (protocol::debug::LogLevel::FieldTypes::value::BitLen << 1) - 1;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum { DefaultTxTimeoutMs = 2000 };
|
||||||
|
|
||||||
|
Publisher<protocol::debug::LogMessage> logmsg_pub_;
|
||||||
|
protocol::debug::LogMessage msg_buf_;
|
||||||
|
LogLevel level_;
|
||||||
|
ILogSink* external_sink_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Logger(INode& node)
|
||||||
|
: logmsg_pub_(node)
|
||||||
|
, external_sink_(NULL)
|
||||||
|
{
|
||||||
|
level_ = protocol::debug::LogLevel::ERROR;
|
||||||
|
setTxTimeout(MonotonicDuration::fromMSec(DefaultTxTimeoutMs));
|
||||||
|
assert(getTxTimeout() == MonotonicDuration::fromMSec(DefaultTxTimeoutMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
int log(const protocol::debug::LogMessage& message);
|
||||||
|
|
||||||
|
LogLevel getLevel() const { return level_; }
|
||||||
|
void setLevel(LogLevel level) { level_ = level; }
|
||||||
|
|
||||||
|
ILogSink* getExternalSink() const { return external_sink_; }
|
||||||
|
void setExternalSink(ILogSink* sink) { external_sink_ = sink; }
|
||||||
|
|
||||||
|
MonotonicDuration getTxTimeout() const { return logmsg_pub_.getTxTimeout(); }
|
||||||
|
void setTxTimeout(MonotonicDuration val) { logmsg_pub_.setTxTimeout(val); }
|
||||||
|
|
||||||
|
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
int log(LogLevel level, const char* source, const char* format, Args... args)
|
||||||
|
{
|
||||||
|
if (level >= level_)
|
||||||
|
{
|
||||||
|
msg_buf_.level.value = level;
|
||||||
|
msg_buf_.source = source;
|
||||||
|
msg_buf_.text.clear();
|
||||||
|
CharArrayFormatter<typename protocol::debug::LogMessage::FieldTypes::text> formatter(msg_buf_.text);
|
||||||
|
formatter.write(format, args...);
|
||||||
|
return log(msg_buf_);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
int logDebug(const char* source, const char* format, Args... args)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::DEBUG, source, format, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
int logInfo(const char* source, const char* format, Args... args)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::INFO, source, format, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
int logWarning(const char* source, const char* format, Args... args)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::WARNING, source, format, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
int logError(const char* source, const char* format, Args... args)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::ERROR, source, format, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int log(LogLevel level, const char* source, const char* text)
|
||||||
|
{
|
||||||
|
if (level >= level_)
|
||||||
|
{
|
||||||
|
msg_buf_.level.value = level;
|
||||||
|
msg_buf_.source = source;
|
||||||
|
msg_buf_.text = text;
|
||||||
|
return log(msg_buf_);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int logDebug(const char* source, const char* text)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::DEBUG, source, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int logInfo(const char* source, const char* text)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::INFO, source, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int logWarning(const char* source, const char* text)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::WARNING, source, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int logError(const char* source, const char* text)
|
||||||
|
{
|
||||||
|
return log(protocol::debug::LogLevel::ERROR, source, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <uavcan/protocol/logger.hpp>
|
||||||
|
|
||||||
|
namespace uavcan
|
||||||
|
{
|
||||||
|
|
||||||
|
const Logger::LogLevel Logger::LevelAboveAll;
|
||||||
|
|
||||||
|
int Logger::log(const protocol::debug::LogMessage& message)
|
||||||
|
{
|
||||||
|
if (message.level.value >= level_)
|
||||||
|
{
|
||||||
|
const int res = logmsg_pub_.broadcast(message);
|
||||||
|
if (external_sink_)
|
||||||
|
{
|
||||||
|
external_sink_->log(message);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <uavcan/protocol/logger.hpp>
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct LogSink : public uavcan::ILogSink
|
||||||
|
{
|
||||||
|
std::queue<uavcan::protocol::debug::LogMessage> msgs;
|
||||||
|
|
||||||
|
void log(const uavcan::protocol::debug::LogMessage& message)
|
||||||
|
{
|
||||||
|
msgs.push(message);
|
||||||
|
std::cout << message << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
uavcan::protocol::debug::LogMessage pop()
|
||||||
|
{
|
||||||
|
if (msgs.empty())
|
||||||
|
{
|
||||||
|
std::cout << "LogSink is empty" << std::endl;
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
const uavcan::protocol::debug::LogMessage m = msgs.front();
|
||||||
|
msgs.pop();
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool popMatchByLevelAndText(int level, const std::string& source, const std::string& text)
|
||||||
|
{
|
||||||
|
const uavcan::protocol::debug::LogMessage m = pop();
|
||||||
|
return
|
||||||
|
level == m.level.value &&
|
||||||
|
source == m.source &&
|
||||||
|
text == m.text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST(Logger, Basic)
|
||||||
|
{
|
||||||
|
InterlinkedTestNodes nodes;
|
||||||
|
|
||||||
|
uavcan::Logger logger(nodes.a);
|
||||||
|
|
||||||
|
ASSERT_EQ(uavcan::protocol::debug::LogLevel::ERROR, logger.getLevel());
|
||||||
|
|
||||||
|
LogSink sink;
|
||||||
|
|
||||||
|
// Will fail - types are not registered
|
||||||
|
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||||
|
ASSERT_GT(0, logger.logError("foo", "Error (fail - type is not registered)"));
|
||||||
|
ASSERT_EQ(0, logger.logDebug("foo", "Debug (ignored - low logging level)"));
|
||||||
|
|
||||||
|
ASSERT_FALSE(logger.getExternalSink());
|
||||||
|
logger.setExternalSink(&sink);
|
||||||
|
ASSERT_EQ(&sink, logger.getExternalSink());
|
||||||
|
|
||||||
|
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||||
|
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::debug::LogMessage> _reg1;
|
||||||
|
|
||||||
|
SubscriberWithCollector<uavcan::protocol::debug::LogMessage> log_sub(nodes.b);
|
||||||
|
ASSERT_LE(0, log_sub.start());
|
||||||
|
|
||||||
|
ASSERT_EQ(0, logger.logDebug("foo", "Debug (ignored due to low logging level)"));
|
||||||
|
ASSERT_LE(0, logger.logError("foo", "Error"));
|
||||||
|
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2));
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::ERROR);
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->source, "foo");
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->text, "Error");
|
||||||
|
|
||||||
|
logger.setLevel(uavcan::protocol::debug::LogLevel::DEBUG);
|
||||||
|
ASSERT_LE(0, logger.logWarning("foo", "Warning"));
|
||||||
|
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2));
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::WARNING);
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->source, "foo");
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->text, "Warning");
|
||||||
|
|
||||||
|
ASSERT_LE(0, logger.logInfo("foo", "Info"));
|
||||||
|
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2));
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::INFO);
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->source, "foo");
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->text, "Info");
|
||||||
|
|
||||||
|
ASSERT_LE(0, logger.logDebug("foo", "Debug"));
|
||||||
|
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2));
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::DEBUG);
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->source, "foo");
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->text, "Debug");
|
||||||
|
|
||||||
|
ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::ERROR, "foo", "Error"));
|
||||||
|
ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::WARNING, "foo", "Warning"));
|
||||||
|
ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::INFO, "foo", "Info"));
|
||||||
|
ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::DEBUG, "foo", "Debug"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
|
||||||
|
# error UAVCAN_CPP_VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||||
|
|
||||||
|
TEST(Logger, Cpp11Formatting)
|
||||||
|
{
|
||||||
|
InterlinkedTestNodes nodes;
|
||||||
|
|
||||||
|
uavcan::Logger logger(nodes.a);
|
||||||
|
logger.setLevel(uavcan::protocol::debug::LogLevel::DEBUG);
|
||||||
|
|
||||||
|
SubscriberWithCollector<uavcan::protocol::debug::LogMessage> log_sub(nodes.b);
|
||||||
|
ASSERT_LE(0, log_sub.start());
|
||||||
|
|
||||||
|
ASSERT_LE(0, logger.logWarning("foo", "char='%*', %* is %*", '$', "double", 12.34));
|
||||||
|
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2));
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::WARNING);
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->source, "foo");
|
||||||
|
ASSERT_EQ(log_sub.collector.msg->text, "char='$', double is 12.340000");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user