IAP GITLAB

Skip to content
Snippets Groups Projects
Commit f07df1c0 authored by Ralf Ulrich's avatar Ralf Ulrich
Browse files

Merge branch 'rprechelt-logging-update' into 'master'

Complete rework of logging using spdlog

See merge request !223
parents 38b77021 8224f2b0
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
Pipeline #1923 passed
Showing
with 318 additions and 409 deletions
......@@ -2,3 +2,7 @@
path = Data
url = ../../AirShowerPhysics/corsika-data
branch = master
[submodule "ThirdParty/spdlog"]
path = ThirdParty/spdlog
url = https://github.com/gabime/spdlog.git
shallow = true
......@@ -79,6 +79,9 @@ set (CMAKE_SHARED_LINKER_FLAGS_COVERAGE "--coverage")
# clang produces a lot of unecessary warnings without this:
add_compile_options ("$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wno-nonportable-include-path>")
# set a flag to inform code that we are in debug mode
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
# COAST - interface, this requires CORSIKA7 to be installed first
# COAST will allow you to program code in CORSIKA8 and execute it inside CORSIKA7
if (WITH_COAST)
......
......@@ -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.
*
* See testLogging.cc for a complete set of examples for
* how the logging functions should be used.
*/
#pragma once
// Configure some behaviour of sdlog.
// This must be done before spdlog is included.
// use the coarse system clock. This is *much* faster
// but introduces a real time error of O(10 ms).
#define SPDLOG_CLOCK_COARSE
// do not create a default logger (we provide our own "corsika" logger)
#define SPDLOG_DISABLE_DEFAULT_LOGGER
// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where
// printing function names in trace statements. This is much
// nicer than __FUNCTION__ under GCC/clang.
#define SPDLOG_FUNCTION __PRETTY_FUNCTION__
// if this is a Debug build, include debug messages in objects
#ifdef DEBUG
// trace is the highest level of logging (ALL messages will be printed)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#else // otherwise, remove everything but "critical" messages
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_CRITICAL
#endif
#include <spdlog/spdlog.h>
#include "spdlog/sinks/stdout_color_sinks.h"
namespace corsika::logging {
// bring spdlog into the corsika::logging namespace
using namespace spdlog;
/*
* The default pattern for CORSIKA8 loggers.
*/
const std::string default_pattern{"[%n:%^%-8l%$] %v"};
/**
* Create a new C8-style logger.
*
* Use this if you are explicitly (and can guarantee) that you
* are creating a logger for the first time. It is recommended
* that for regular usage, the `GetLogger` function is used instead
* as that will also create the logger if it has not yet been created.
*
* Calling `CreateLogger` twice to create the same logger will
* result in an spdlog duplicate exception.
*
* @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
// this is currently a colorized multi-threading safe logger
auto logger = spdlog::stdout_color_mt(name);
// set the default C8 format
logger->set_pattern(default_pattern);
// 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 smart pointer to an existing logger.
*
* This should be the default method for code to obtain a
* logger. If the logger *does not* exist, it is *created* and
* returned to the caller.
*
* This should be preferred over `CreateLogger`.
*
* @param name The name of the logger to get.
* @param defaultlog If True, make this the default logger.
* @returns The constructed and formatted logger.
*/
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);
}
}
/**
* The default "corsika" logger.
*/
inline auto corsika = GetLogger("corsika", true);
/**
* Set the default log level for all *newly* created loggers.
*
* @param name The minimum log level required to print.
*
*/
auto SetDefaultLevel(level::level_enum const minlevel) -> void {
spdlog::set_level(minlevel);
}
/**
* Set the log level for the "corsika" logger.
*
* @param name The minimum log level required to print.
*
*/
auto SetLevel(level::level_enum const minlevel) -> void {
corsika->set_level(minlevel);
}
/**
* Set the log level for a specific logger.
*
* @param logger The logger to set the level of.
* @param name The minimum log level required to print.
*
*/
template <typename TLogger>
auto SetLevel(TLogger& logger, level::level_enum const minlevel) -> void {
logger->set_level(minlevel);
}
/**
* Add the source (filename, line no) info to the logger.
*
* @param logger The logger to set the level of.
*
*/
template <typename TLogger>
auto AddSourceInfo(TLogger& logger) -> void {
logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v");
}
/**
* Reset the logging pattern to the default.
*
* @param logger The logger to set the level of.
*
*/
template <typename TLogger>
auto ResetPattern(TLogger& logger) -> void {
logger->set_pattern(default_pattern);
}
// define our macro-style loggers
#define C8LOG_TRACE SPDLOG_TRACE
#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_TRACE SPDLOG_LOGGER_TRACE
#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!!");
// get a reference to an unknown logger
auto logger = logging::GetLogger("loggerD");
// now set the default log level for this logger
logging::SetLevel(logger, logging::level::critical);
// now try the various logging functions
logger->info("This should NOT be printed!");
logger->warn("This should NOT be printed!");
logger->debug("This should NOT be printed!");
logger->error("This should NOT be printed!");
logger->critical("This SHOULD BE printed!!");
// and reset it for the next tests
logging::SetDefaultLevel(logging::level::debug);
logging::SetLevel(logging::level::debug);
}
SECTION("test macro style logging") {
// these print with the "corsika" logger
C8LOG_INFO("test macro style logging");
C8LOG_DEBUG("test macro style logging");
C8LOG_ERROR("test macro style logging");
C8LOG_CRITICAL("test macro style logging");
// get a reference to an unknown logger
auto logger = logging::GetLogger("loggerE");
// add the filename, source information to this logger
logging::AddSourceInfo(logger);
// these print with the "loggerE" logger
C8LOG_LOGGER_INFO(logger, "test macro style logging");
C8LOG_LOGGER_WARN(logger, "test macro style logging");
SECTION("sectionTwo") {}
// reset the logging pattern
logging::ResetPattern(logger);
SECTION("sectionThree") {}
// these trace macros should not print file, function, and line
C8LOG_LOGGER_TRACE(logger, "test macro style logging:");
}
}
......@@ -11,8 +11,6 @@
#include <corsika/process/stack_inspector/StackInspector.h>
#include <corsika/units/PhysicalUnits.h>
#include <corsika/logging/Logger.h>
#include <corsika/setup/SetupTrajectory.h>
#include <chrono>
......
set (
SETUP_HEADERS
SetupStack.h
SetupLogger.h
SetupEnvironment.h
SetupTrajectory.h
)
......
/*
* (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 {}
......@@ -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
......
Subproject commit 58875bdcd790dd2f5cb8d95ef1da7970de0a4530
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