IAP GITLAB

Skip to content
Snippets Groups Projects
Commit e2c00791 authored by Remy Prechelt's avatar Remy Prechelt Committed by Ralf Ulrich
Browse files

Add new logging implementation using spdlog.

parent e7fbe127
No related branches found
No related tags found
2 merge requests!234WIP: Initial example of python as script language from C++,!223Complete rework of logging using spdlog
......@@ -8,12 +8,8 @@ target_link_libraries (particle_list_example CORSIKAparticles CORSIKAunits CORSI
CORSIKA_ADD_EXAMPLE (geometry_example)
target_link_libraries (geometry_example CORSIKAgeometry CORSIKAunits)
CORSIKA_ADD_EXAMPLE (logger_example)
target_link_libraries (logger_example CORSIKAunits CORSIKAlogging)
CORSIKA_ADD_EXAMPLE (stack_example)
target_link_libraries (stack_example SuperStupidStack CORSIKAunits
CORSIKAlogging)
target_link_libraries (stack_example SuperStupidStack CORSIKAunits)
# address sanitizer is making this example too slow, so we only do "undefined"
CORSIKA_ADD_EXAMPLE (boundary_example)
......
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#include <corsika/logging/Logger.h>
#include <boost/format.hpp>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
using namespace corsika::logging;
int main() {
{
cout << "writing to \"another.log\"" << endl;
ofstream logfile("another.log");
sink::SinkStream unbuffered_sink(logfile);
sink::BufferedSinkStream sink(logfile, sink::StdBuffer(10000));
Logger<MessageOn, sink::BufferedSinkStream> info("\033[32m", "info", sink);
Logger<MessageOn, sink::BufferedSinkStream> err("\033[31m", "error", sink);
// logger<ostream,messageconst,StdBuffer> info(std::cout, StdBuffer(10000));
/*
Logging& logs = Logging::GetInstance();
logs.AddLogger<>("info", info);
auto& log_1 = logs.GetLogger("info"); // no so useful, since type of log_1 is
std::any
*/
for (int i = 0; i < 10000; ++i) {
LOG(info, "irgendwas", " ", string("and more"), " ",
boost::format("error: %i message: %s. done."), i, "stupido");
LOG(err, "Fehler");
}
}
{
sink::NoSink off;
Logger<MessageOff> info("", "", off);
for (int i = 0; i < 10000; ++i) {
LOG(info, "irgendwas", string("and more"),
boost::format("error: %i message: %s. done."), i, "stupido", "a-number:", 8.99,
"ENDE");
}
}
return 0;
}
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#pragma once
namespace corsika::logging {
namespace sink {
/**
Output buffer template. NoBuffer does nothingk.
*/
/*
struct NoBuffer {
inline bool Test(const std::string&) const { return false; }
inline std::string GetString() const { return std::string(""); }
inline void Clear() {}
inline void Add(const std::string&) {}
};
*/
/**
Output buffer template. StdBuffer records fSize characters in
local memeory before passing it on to further output stages.
*/
struct StdBuffer {
StdBuffer(const int size)
: fSize(size) {}
inline bool Test(const std::string& s) {
return int(fBuffer.tellp()) + s.length() < fSize;
}
inline std::string GetString() const { return fBuffer.str(); }
inline void Clear() { fBuffer.str(""); }
inline void Add(const std::string& s) { fBuffer << s; }
private:
unsigned int fSize;
std::ostringstream fBuffer;
};
/**
Definition of Sink for log output.
*/
template <typename TStream, typename TBuffer = StdBuffer>
class BufferedSink {
public:
BufferedSink(TStream& out, TBuffer buffer = {})
: fOutput(out)
, fBuffer(std::move(buffer)) {}
void operator<<(const std::string& msg) {
if (!fBuffer.Test(msg)) {
fOutput << fBuffer.GetString();
fBuffer.Clear();
}
if (!fBuffer.Test(msg))
fOutput << msg;
else
fBuffer.Add(msg);
}
void Close() { fOutput << fBuffer.GetString(); }
private:
TStream& fOutput;
TBuffer fBuffer;
};
typedef BufferedSink<std::ostream, StdBuffer> BufferedSinkStream;
} // namespace sink
} // namespace corsika::logging
# create the library
add_library (CORSIKAlogging INTERFACE)
# namespace of library -> location of header files
......@@ -9,15 +10,10 @@ set (
# header files of this library
set (
CORSIKAlogging_HEADERS
Logger.h
Sink.h
MessageOn.h
MessageOff.h
NoSink.h
Sink.h
BufferedSink.h
Logging.h
)
# copy the headers into the namespace
CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAlogging
${CORSIKAlogging_NAMESPACE}
${CORSIKAlogging_HEADERS}
......@@ -32,6 +28,13 @@ target_include_directories (
C8::ext:boost
)
# and link against spdlog
target_link_libraries(
CORSIKAlogging
INTERFACE
spdlog::spdlog
)
# install library
install (
FILES ${CORSIKAlogging_HEADERS}
......@@ -41,10 +44,9 @@ install (
# ----------------
# code unit testing
#CORSIKA_ADD_TEST (testLogging)
#target_link_libraries (
# testLogging
# CORSIKAlogging
# CORSIKAtesting
# )
CORSIKA_ADD_TEST (testLogging)
target_link_libraries (
testLogging
CORSIKAlogging
CORSIKAtesting
)
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
/**
@File Logger.h
Everything around logfile generation and text output.
*/
#pragma once
#include <iosfwd>
#include <sstream>
#include <string>
#include <typeinfo>
#include <boost/format.hpp>
#include <corsika/logging/BufferedSink.h>
#include <corsika/logging/MessageOff.h>
#include <corsika/logging/MessageOn.h>
#include <corsika/logging/NoSink.h>
#include <corsika/logging/Sink.h>
namespace corsika::logging {
/**
@class Logger
Defines one stream to accept messages, and to wrote those into
TSink. The helper class MessageOn will convert input at
compile-time into message strings. The helper class MessageOff,
will just do nothing and will be optimized away at compile time.
*/
template <typename MSG = MessageOn, typename TSink = sink::NoSink>
class Logger : private MSG {
using MSG::Message;
public:
// Logger() : fName("") {}
Logger(const std::string color, const std::string name, TSink& sink)
: fSink(sink)
, fName(color + "[" + name + "]\033[39m ") {}
~Logger() { fSink.Close(); }
// Logger(const Logger&) = delete;
/**
Function to add string-concatenation of all inputs to output
sink.
*/
template <typename... Strings>
void Log(const Strings&... inputs) {
fSink << MSG::Message(inputs...);
}
const std::string& GetName() const { return fName; }
private:
TSink& fSink;
std::string fName;
};
} // namespace corsika::logging
/**
* @def LOG(...)
*
* This is the main interface to the logging facilities. If Logger
* object are defined (e.g. log1) use as
* @example LOG(log1, "var1=", variable1int, "var2=", variabl2double)
* for arbitrary long sequence
* of arguments. This may also include boost::format objects the
* output is concatenated, if log1 is switched off at compile time,
* the whole LOG command is optimized away by the compiler.
*/
#define LOG(__LOGGER, ...) \
__LOGGER.Log(__LOGGER.GetName(), __FILE__, ":", __LINE__, " (", __func__, ") -> ", \
__VA_ARGS__);
/*
* (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
*
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
/**
@File Logging.h
CORSIKA8 logging utilities.
*/
#pragma once
#include <spdlog/spdlog.h>
#include "spdlog/sinks/stdout_color_sinks.h"
namespace corsika::logging {
// bring spdlog into the corsika::logging namespace
using namespace spdlog;
/**
* Create a new C8-style logger.
*
* @param name The unique name of the logger.
* @param defaultlog If True, set this as the default logger.
* @returns The constructed and formatted logger.
*/
auto CreateLogger(std::string const& name, bool const defaultlog = false) {
// create the logger
auto logger = spdlog::stdout_color_mt(name);
// set the default C8 format
logger->set_pattern("[%n:%^%-8l%$] %v");
// if defaultlog is True, we set this as the default spdlog logger.
if (defaultlog) { spdlog::set_default_logger(logger); }
// and return the logger
return logger;
}
/**
* Get a reference to a named logger.
*
* If the logger does not exist, it is created.
*
* @param name The name of the logger to get.
*/
auto GetLogger(std::string const& name, bool const defaultlog = false) {
// attempt to get the logger from the registry
auto logger = spdlog::get(name);
// weg found the logger, so just return it
if (logger) {
return logger;
} else { // logger was not found so create it
return CreateLogger(name, defaultlog);
}
}
/**
* Set the default log level for all loggers.
*
* @param name The minimum log level required to print.
*
*/
auto SetDefaultLevel(level::level_enum minlevel) -> void {
spdlog::set_level(minlevel);
}
// create the default CORSIKA logger
inline auto corsika = GetLogger("corsika", true);
// define our macro-style loggers
#define C8LOG_DEBUG SPDLOG_DEBUG
#define C8LOG_INFO SPDLOG_INFO
#define C8LOG_WARN SPDLOG_WARN
#define C8LOG_ERROR SPDLOG_ERROR
#define C8LOG_CRITICAL SPDLOG_CRITICAL
// and the specific logger versions
#define C8LOG_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG
#define C8LOG_LOGGER_INFO SPDLOG_LOGGER_INFO
#define C8LOG_LOGGER_WARN SPDLOG_LOGGER_WARN
#define C8LOG_LOGGER_ERROR SPDLOG_LOGGER_ERROR
#define C8LOG_LOGGER_CRITICAL SPDLOG_LOGGER_CRITICAL
} // namespace corsika::logging
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#pragma once
namespace corsika::logging {
/**
Helper class to ignore all arguments to MessagesOn::Message and
always return empty string "".
*/
class MessageOff {
protected:
template <typename First, typename... Strings>
std::string Message(const First&, const Strings&...) {
return "";
}
};
} // namespace corsika::logging
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#pragma once
namespace corsika::logging {
/**
Helper class to convert all input arguments of MessageOn::Message
into string-concatenated version and return this as string.
*/
class MessageOn {
protected:
std::string Message() { return "\n"; }
template <typename First, typename... Strings>
std::string Message(const First& arg, const Strings&... rest) {
std::ostringstream ss;
ss << arg << Message(rest...);
return ss.str();
}
template <typename... Strings>
std::string Message(const int& arg, const Strings&... rest) {
return std::to_string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(const double& arg, const Strings&... rest) {
return std::to_string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(char const* arg, const Strings&... rest) {
return std::string(arg) + Message(rest...);
}
template <typename... Strings>
std::string Message(const std::string& arg, const Strings&... rest) {
return arg + Message(rest...);
}
// ----------------------
// boost format
template <typename... Strings>
std::string Message(const boost::format& fmt, const Strings&... rest) {
boost::format FMT(fmt);
return bformat(FMT, rest...);
}
template <typename Arg, typename... Strings>
std::string bformat(boost::format& fmt, const Arg& arg, const Strings&... rest) {
fmt % arg;
return bformat(fmt, rest...);
}
std::string bformat(boost::format& fmt) { return fmt.str() + "\n"; }
};
} // namespace corsika::logging
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#pragma once
namespace corsika::logging {
namespace sink {
struct NoSink {
inline void operator<<(const std::string&) {}
inline void Close() {}
};
} // namespace sink
} // namespace corsika::logging
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#pragma once
namespace corsika::logging {
/**
a sink for the logger must implement the two functions
operator<<(const std::string&)
and
Close()
See example: NoSink
*/
namespace sink {
/**
Definition of Sink for log output.
*/
template <typename TStream>
class Sink {
public:
Sink(TStream& out)
: fOutput(out) {}
void operator<<(const std::string& msg) { fOutput << msg; }
void Close() {}
private:
TStream& fOutput;
};
typedef Sink<std::ostream> SinkStream;
} // namespace sink
} // namespace corsika::logging
/*
* (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
* (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
*
* This software is distributed under the terms of the GNU General Public
* Licence version 3 (GPL Version 3). See file LICENSE for a full version of
* the license.
*/
#include <corsika/logging/Logger.h>
#include <corsika/logging/Logging.h>
#include <catch2/catch.hpp>
/*
this does nothing yet
*/
using namespace corsika;
TEST_CASE("Logging", "[Logging]") {
SECTION("sectionOne") {}
SECTION("top level functions using corsika logger") {
logging::info("This is an info message!");
logging::warn("This is a warning message!");
logging::debug("This is a debug message!");
logging::error("This is an error message!");
logging::critical("This is a critical error message!");
}
SECTION("create a specific logger") {
// create a logger manually
auto logger = logging::CreateLogger("loggerA");
// set a custom pattern for this logger
logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
// and make sure we can log with this created object
logger->info("This is an info message!");
logger->warn("This is a warning message!");
logger->debug("This is a debug message!");
logger->error("This is an error message!");
logger->critical("This is a critical error message!");
// get a reference to the logger using Get
auto other = logging::GetLogger("loggerA");
// and make sure we can use this other reference to log
other->info("This is an info message!");
other->warn("This is a warning message!");
other->debug("This is a debug message!");
other->error("This is an error message!");
other->critical("This is a critical error message!");
}
SECTION("get a new logger") {
// get a reference to an unknown logger
auto logger = logging::GetLogger("loggerB");
// and make sure we can log with this created object
logger->info("This is an info message!");
logger->warn("This is a warning message!");
logger->debug("This is a debug message!");
logger->error("This is an error message!");
logger->critical("This is a critical error message!");
}
SECTION("test log level") {
// set the default log level
logging::SetDefaultLevel(logging::level::critical);
// and make sure we can log with this created object
logging::info("This should NOT be printed!");
logging::warn("This should NOT be printed!");
logging::debug("This should NOT be printed!");
logging::error("This should NOT be printed!");
logging::critical("This SHOULD BE printed!!");
// and reset it for the next tests
logging::SetDefaultLevel(logging::level::debug);
}
SECTION("test macro style printing") {
// these print with the "corsika" logger
C8LOG_INFO("This is a macro info msg!");
C8LOG_DEBUG("This is a macro debug msg!");
C8LOG_ERROR("This is a macro error msg!");
C8LOG_CRITICAL("This is a macro critical msg!");
SECTION("sectionTwo") {}
// get a reference to an unknown logger
auto logger = logging::GetLogger("loggerD");
SECTION("sectionThree") {}
// these print with the "loggerD" logger
C8LOG_LOGGER_INFO(logger, "This is a macro info msg!");
C8LOG_LOGGER_WARN(logger, "This is a macro warn msg!");
}
}
......@@ -15,8 +15,10 @@ include(ExternalProject)
set (ThirdPartyChoiceValues "C8;SYSTEM" CACHE STRING
"List of possible values for the ThirdParty package choice")
mark_as_advanced (ThirdPartyChoiceValues)
##############################################################################
# Build spdlog
add_subdirectory(spdlog)
##############################################################################
# check for boost: either use C8 or system-level installation
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment