Skip to content

Commit

Permalink
Fix up the log test and add extra functions to log level
Browse files Browse the repository at this point in the history
  • Loading branch information
TrentHouliston committed Aug 9, 2024
1 parent c959b09 commit 358bb52
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 14 deletions.
55 changes: 55 additions & 0 deletions src/LogLevel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2013 NUClear Contributors
*
* This file is part of the NUClear codebase.
* See https://github.com/Fastcode/NUClear for further info.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "LogLevel.hpp"

#include <ostream>

namespace NUClear {

std::string to_string(const LogLevel& level) {
switch (level) {
case LogLevel::TRACE: return "TRACE";
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARN: return "WARN";
case LogLevel::ERROR: return "ERROR";
case LogLevel::FATAL: return "FATAL";
default:
case LogLevel::UNKNOWN: return "UNKNOWN";
}
}

LogLevel from_string(const std::string& level) {
return level == "TRACE" ? LogLevel::TRACE
: level == "DEBUG" ? LogLevel::DEBUG
: level == "INFO" ? LogLevel::INFO
: level == "WARN" ? LogLevel::WARN
: level == "ERROR" ? LogLevel::ERROR
: level == "FATAL" ? LogLevel::FATAL
: LogLevel::UNKNOWN;
}

std::ostream& operator<<(std::ostream& os, const LogLevel& level) {
return os << to_string(level);
}

} // namespace NUClear
30 changes: 29 additions & 1 deletion src/LogLevel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef NUCLEAR_LOGLEVEL_HPP
#define NUCLEAR_LOGLEVEL_HPP

#include <ostream>

// Why do we need to include platform.hpp here?
// Because windows defines a bunch of things for legacy reasons, one of which is a #define for ERROR as blank
// Of course this causes a problem when we define our own token below as error as the preprocessor removes it
Expand Down Expand Up @@ -98,6 +99,33 @@ enum LogLevel : uint8_t {
FATAL
};

/**
* This function is used to convert a LogLevel into a string
*
* @param level the LogLevel to convert
*
* @return the string representation of the LogLevel
*/
std::string to_string(const LogLevel& level);

/**
* This function is used to convert a string into a LogLevel
*
* @param level the string to convert
*
* @return the LogLevel representation of the string
*/
LogLevel from_string(const std::string& level);

/**
* This function is used to convert a LogLevel into a string for printing.
*
* @param os the output stream to write to
* @param level the LogLevel to convert
* @return the output stream
*/
std::ostream& operator<<(std::ostream& os, const LogLevel& level);

} // namespace NUClear

#endif // NUCLEAR_LOGLEVEL_HPP
4 changes: 2 additions & 2 deletions src/PowerPlant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ void PowerPlant::submit(std::unique_ptr<threading::ReactionTask>&& task, const b
}
}

void PowerPlant::log(const LogLevel& level, const std::string& message) {
void PowerPlant::log(const LogLevel& level, std::string message) {
// Get the current task
const auto* current_task = threading::ReactionTask::get_current_task();

// Direct emit the log message so that any direct loggers can use it
emit<dsl::word::emit::Direct>(std::make_unique<message::LogMessage>(
level,
current_task != nullptr ? current_task->parent->reactor.log_level : LogLevel::UNKNOWN,
message,
std::move(message),
current_task != nullptr ? current_task->stats : nullptr));
}
void PowerPlant::log(const LogLevel& level, std::stringstream& message) {
Expand Down
22 changes: 16 additions & 6 deletions src/PowerPlant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,21 +199,25 @@ class PowerPlant {
*/
template <enum LogLevel level, typename... Arguments>
void log(Arguments&&... args) {
log(level, std::forward<Arguments>(args)...);
}
template <typename... Arguments>
void log(const LogLevel& level, Arguments&&... args) {
std::stringstream ss;
log(level, ss, std::forward<Arguments>(args)...);
}
template <typename Last>
void log(const LogLevel& level, std::stringstream& ss, Last&& last) {
ss << std::forward<Last>(last);
log(level, ss);
}
template <typename First, typename... Arguments>
void log(const LogLevel& level, std::stringstream& ss, First&& first, Arguments&&... args) {
ss << std::forward<First>(first) << " ";
log(level, ss, std::forward<Arguments>(args)...);
}
template <typename Last>
void log(const LogLevel& level, std::stringstream& ss, Last&& last) {
ss << std::forward<Last>(last);
log(level, ss);
}
void log(const LogLevel& level, std::stringstream& message);
void log(const LogLevel& level, const std::string& message);
void log(const LogLevel& level, std::string message);

/**
* Emits data to the system and routes it to the other systems that use it.
Expand Down Expand Up @@ -348,6 +352,12 @@ void log(Arguments&&... args) {
PowerPlant::powerplant->log<level>(std::forward<Arguments>(args)...);
}
}
template <typename... Arguments>
void log(const LogLevel& level, Arguments&&... args) {
if (PowerPlant::powerplant != nullptr) {
PowerPlant::powerplant->log(level, std::forward<Arguments>(args)...);
}
}

} // namespace NUClear

Expand Down
17 changes: 17 additions & 0 deletions src/Reactor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,23 @@ class Reactor {
// If the log is above or equal to our log level
powerplant.log<level>(std::forward<Arguments>(args)...);
}

/**
* Log a message through NUClear's system.
*
* Logs a message through the system so the various log handlers can access it.
*
* @tparam Arguments The types of the arguments we are logging
*
* @param level The level to log at
* @param args The arguments we are logging
*/
template <typename... Arguments>
void log(const LogLevel& level, Arguments&&... args) const {

// If the log is above or equal to our log level
powerplant.log(level, std::forward<Arguments>(args)...);
}
};

} // namespace NUClear
Expand Down
10 changes: 5 additions & 5 deletions tests/tests/log/Log.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ TEST_CASE("Testing the Log<>() function", "[api][log]") {
// Test logs from reactions directly
for (const auto& log_level : levels) {
if (display_level <= log_level) {
const std::string expected = "Direct Reaction " + std::to_string(log_level);
const std::string expected = "Direct Reaction " + NUClear::to_string(log_level);
REQUIRE(messages[i].message == expected);
REQUIRE(messages[i].level == log_level);
REQUIRE(messages[i++].from_reaction);
Expand All @@ -189,7 +189,7 @@ TEST_CASE("Testing the Log<>() function", "[api][log]") {
// Test logs from reactions indirectly
for (const auto& log_level : levels) {
if (display_level <= log_level) {
const std::string expected = "Indirect Reaction " + std::to_string(log_level);
const std::string expected = "Indirect Reaction " + NUClear::to_string(log_level);
REQUIRE(messages[i].message == expected);
REQUIRE(messages[i].level == log_level);
REQUIRE(messages[i++].from_reaction);
Expand All @@ -198,7 +198,7 @@ TEST_CASE("Testing the Log<>() function", "[api][log]") {
// Test logs from free floating functions
for (const auto& log_level : levels) {
// No filter here, free floating prints everything
const std::string expected = "Non Reaction " + std::to_string(log_level);
const std::string expected = "Non Reaction " + NUClear::to_string(log_level);
REQUIRE(messages[i].message == expected);
REQUIRE(messages[i].level == log_level);
REQUIRE_FALSE(messages[i++].from_reaction);
Expand All @@ -207,7 +207,7 @@ TEST_CASE("Testing the Log<>() function", "[api][log]") {

// Test post-shutdown logs
{
const std::string expected = "Post Powerplant Shutdown " + std::to_string(NUClear::FATAL);
const std::string expected = "Post Powerplant Shutdown " + NUClear::to_string(NUClear::FATAL);
REQUIRE(messages[i].message == expected);
REQUIRE(messages[i].level == NUClear::FATAL);
REQUIRE(messages[i++].from_reaction);
Expand All @@ -219,7 +219,7 @@ TEST_CASE("Testing the Log<>() function", "[api][log]") {
// Test logs from free floating functions
for (const auto& log_level : levels) {
// No filter here, free floating prints everything
const std::string expected = "Non Reaction " + std::to_string(log_level);
const std::string expected = "Non Reaction " + NUClear::to_string(log_level);
REQUIRE(messages[i].message == expected);
REQUIRE(messages[i].level == log_level);
REQUIRE_FALSE(messages[i++].from_reaction);
Expand Down

0 comments on commit 358bb52

Please sign in to comment.