diff --git a/include/ntcore_c.h b/include/ntcore_c.h index c9e35bea8c..6a6493ea9f 100644 --- a/include/ntcore_c.h +++ b/include/ntcore_c.h @@ -35,6 +35,19 @@ enum NT_EntryFlags { NT_PERSISTENT = 0x01 }; +/** NetworkTables logging levels. */ +enum NT_LogLevel { + NT_LOG_CRITICAL = 50, + NT_LOG_ERROR = 40, + NT_LOG_WARNING = 30, + NT_LOG_INFO = 20, + NT_LOG_DEBUG = 10, + NT_LOG_DEBUG1 = 9, + NT_LOG_DEBUG2 = 8, + NT_LOG_DEBUG3 = 7, + NT_LOG_DEBUG4 = 6 +}; + /* * Structures */ @@ -309,6 +322,11 @@ void NT_DisposeConnectionInfoArray(struct NT_ConnectionInfo *arr, size_t count); /* timestamp */ unsigned long long NT_Now(void); +/* logging */ +typedef void (*NT_LogFunc)(unsigned int level, const char *file, + unsigned int line, const char *msg); +void NT_SetLogger(NT_LogFunc func, unsigned int min_level); + #ifdef __cplusplus } #endif diff --git a/include/ntcore_cpp.h b/include/ntcore_cpp.h index 32648ee6b0..0ca56025ef 100644 --- a/include/ntcore_cpp.h +++ b/include/ntcore_cpp.h @@ -220,6 +220,11 @@ const char* LoadPersistent( /* timestamp */ unsigned long long Now(); +/* logging */ +typedef std::function LogFunc; +void SetLogger(LogFunc func, unsigned int min_level); + } // namespace nt #endif /* NTCORE_CPP_H_ */ diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 4bd9d0877e..ca330483e6 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -12,17 +12,10 @@ #include "tcpsockets/TCPAcceptor.h" #include "tcpsockets/TCPConnector.h" +#include "Log.h" using namespace nt; -inline void DEBUG(const char* str, ...) { - va_list args; - va_start(args, str); - vfprintf(stderr, str, args); - fputc('\n', stderr); - va_end(args); -} - ATOMIC_STATIC_INIT(Dispatcher) Dispatcher::Dispatcher() @@ -302,7 +295,7 @@ bool Dispatcher::ClientHandshake( if (msg->Is(Message::kServerHelloDone)) break; if (!msg->Is(Message::kEntryAssign)) { // unexpected message - DEBUG("client: received message (%d) other than entry assignment during initial handshake", msg->type()); + DEBUG("client: received message (" << msg->type() << ") other than entry assignment during initial handshake"); return false; } incoming.emplace_back(std::move(msg)); @@ -388,7 +381,7 @@ bool Dispatcher::ServerHandshake( if (msg->Is(Message::kClientHelloDone)) break; if (!msg->Is(Message::kEntryAssign)) { // unexpected message - DEBUG("server: received message (%d) other than entry assignment during initial handshake", msg->type()); + DEBUG("server: received message (" << msg->type() << ") other than entry assignment during initial handshake"); return false; } incoming.push_back(msg); diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 0000000000..a7daa4de4b --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,16 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "Log.h" + +using namespace nt; + +ATOMIC_STATIC_INIT(Logger) + +Logger::Logger() : m_min_level(100) {} + +Logger::~Logger() {} diff --git a/src/Log.h b/src/Log.h new file mode 100644 index 0000000000..95d04fee55 --- /dev/null +++ b/src/Log.h @@ -0,0 +1,83 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2015. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#ifndef NT_LOG_H_ +#define NT_LOG_H_ + +#include +#include +#include + +#include "atomic_static.h" +#include "ntcore_c.h" + +namespace nt { + +class Logger { + public: + static Logger& GetInstance() { + ATOMIC_STATIC(Logger, instance); + return instance; + } + ~Logger(); + + typedef std::function LogFunc; + + void SetLogger(LogFunc func) { m_func = func; } + + void set_min_level(unsigned int level) { m_min_level = level; } + unsigned int min_level() const { return m_min_level; } + + void Log(unsigned int level, const char* file, unsigned int line, + const char* msg) { + if (!m_func || level < m_min_level) return; + m_func(level, file, line, msg); + } + + bool HasLogger() const { return m_func != nullptr; } + + private: + Logger(); + + LogFunc m_func; + unsigned int m_min_level; + + ATOMIC_STATIC_DECL(Logger) +}; + +#define LOG(level, x) \ + do { \ + Logger& logger = Logger::GetInstance(); \ + if (logger.min_level() <= level && logger.HasLogger()) { \ + std::ostringstream oss; \ + oss << x; \ + logger.Log(level, __FILE__, __LINE__, oss.str().c_str()); \ + } \ + } while (0) + +#define ERROR(x) LOG(NT_LOG_ERROR, x) +#define WARNING(x) LOG(NT_LOG_WARNING, x) +#define INFO(x) LOG(NT_LOG_INFO, x) + +#ifdef NDEBUG +#define DEBUG(x) +#define DEBUG1(x) +#define DEBUG2(x) +#define DEBUG3(x) +#define DEBUG4(x) +#else +#define DEBUG(x) LOG(NT_LOG_DEBUG, x) +#define DEBUG1(x) LOG(NT_LOG_DEBUG1, x) +#define DEBUG2(x) LOG(NT_LOG_DEBUG2, x) +#define DEBUG3(x) LOG(NT_LOG_DEBUG3, x) +#define DEBUG4(x) LOG(NT_LOG_DEBUG4, x) +#endif + +} // namespace nt + +#endif // NT_LOG_H_ diff --git a/src/NetworkConnection.cpp b/src/NetworkConnection.cpp index f7885e2ab6..cab818952a 100644 --- a/src/NetworkConnection.cpp +++ b/src/NetworkConnection.cpp @@ -8,20 +8,13 @@ #include "NetworkConnection.h" #include "tcpsockets/TCPStream.h" +#include "Log.h" #include "raw_socket_istream.h" #include "WireDecoder.h" #include "WireEncoder.h" using namespace nt; -inline void DEBUG(const char* str, ...) { - va_list args; - va_start(args, str); - vfprintf(stderr, str, args); - fputc('\n', stderr); - va_end(args); -} - NetworkConnection::NetworkConnection(std::unique_ptr stream, HandshakeFunc handshake, Message::GetEntryTypeFunc get_entry_type, @@ -81,8 +74,8 @@ void NetworkConnection::ReadThreadMain() { [&] { decoder.set_proto_rev(m_proto_rev); auto msg = Message::Read(decoder, m_get_entry_type); - if (!msg) - DEBUG("error reading in handshake: %s", decoder.error()); + if (!msg && decoder.error()) + DEBUG("error reading in handshake: " << decoder.error()); return msg; }, [&](llvm::ArrayRef> msgs) { @@ -116,11 +109,11 @@ void NetworkConnection::WriteThreadMain() { while (m_active) { auto msgs = m_outgoing.pop(); - DEBUG("write thread woke up"); + DEBUG4("write thread woke up"); if (msgs.empty()) break; encoder.set_proto_rev(m_proto_rev); encoder.Reset(); - DEBUG("sending %d messages", msgs.size()); + DEBUG4("sending " << msgs.size() << " messages"); for (auto& msg : msgs) { if (msg) msg->Write(encoder); } @@ -128,7 +121,7 @@ void NetworkConnection::WriteThreadMain() { if (!m_stream) break; if (encoder.size() == 0) continue; if (m_stream->send(encoder.data(), encoder.size(), &err) == 0) break; - DEBUG("sent %d bytes", encoder.size()); + DEBUG4("sent " << encoder.size() << " bytes"); } m_state = static_cast(kDead); m_active = false; diff --git a/src/Storage.cpp b/src/Storage.cpp index 3cebf5a749..cf7fc862aa 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -12,11 +12,10 @@ #include "llvm/StringExtras.h" #include "Base64.h" +#include "Log.h" using namespace nt; -#define DEBUG(str) puts(str) - ATOMIC_STATIC_INIT(Storage) Storage::Storage() {} diff --git a/src/ntcore_c.cpp b/src/ntcore_c.cpp index 472950d863..425818a124 100644 --- a/src/ntcore_c.cpp +++ b/src/ntcore_c.cpp @@ -288,6 +288,10 @@ const char *NT_LoadPersistent(const char *filename, * Utility Functions */ +void NT_SetLogger(NT_LogFunc func, unsigned int min_level) { + nt::SetLogger(func, min_level); +} + void NT_DisposeValue(NT_Value *value) { switch (value->type) { case NT_UNASSIGNED: diff --git a/src/ntcore_cpp.cpp b/src/ntcore_cpp.cpp index 988b42f65c..c8e1291dd3 100644 --- a/src/ntcore_cpp.cpp +++ b/src/ntcore_cpp.cpp @@ -13,6 +13,7 @@ #include #include "Dispatcher.h" +#include "Log.h" #include "Storage.h" namespace nt { @@ -173,4 +174,10 @@ const char* LoadPersistent( return nullptr; } +void SetLogger(LogFunc func, unsigned int min_level) { + Logger& logger = Logger::GetInstance(); + logger.SetLogger(func); + logger.set_min_level(min_level); +} + } // namespace nt