-#pragma once
-
-#include <type_traits>
-
-#include <boost/range/irange.hpp>
-
-#include "linq.h"
-
-// Macro that defines & and &= for an enum class. Add more when needed.
-
-#define ENUM_ENABLE_BITWISE(enum_class) \
- static enum_class operator&(enum_class lhs, enum_class rhs) \
- { \
- return static_cast<enum_class>( \
- static_cast<std::underlying_type<enum_class>::type>(lhs) \
- & static_cast<std::underlying_type<enum_class>::type>(rhs)); \
- }; \
- static enum_class& operator&=(enum_class& lhs, enum_class rhs) \
- { \
- lhs = lhs & rhs; \
- return lhs; \
- }; \
+#pragma once\r
+\r
+#include <type_traits>\r
+\r
+#include <boost/range/irange.hpp>\r
+\r
+#include "linq.h"\r
+\r
+// Macro that defines & and &= for an enum class. Add more when needed.\r
+\r
+#define ENUM_ENABLE_BITWISE(enum_class) \\r
+ static enum_class operator&(enum_class lhs, enum_class rhs) \\r
+ { \\r
+ return static_cast<enum_class>( \\r
+ static_cast<std::underlying_type<enum_class>::type>(lhs) \\r
+ & static_cast<std::underlying_type<enum_class>::type>(rhs)); \\r
+ }; \\r
+ static enum_class& operator&=(enum_class& lhs, enum_class rhs) \\r
+ { \\r
+ lhs = lhs & rhs; \\r
+ return lhs; \\r
+ }; \\r
static enum_class operator | (enum_class lhs, enum_class rhs) \\r
{ \\r
return static_cast<enum_class>( \\r
static_cast<std::underlying_type<enum_class>::type>(lhs) \\r
| static_cast<std::underlying_type<enum_class>::type>(rhs)); \\r
}; \\r
- static enum_class& operator|=(enum_class& lhs, enum_class rhs) \
- { \
- lhs = lhs | rhs; \
- return lhs; \
- }; \
+ static enum_class& operator|=(enum_class& lhs, enum_class rhs) \\r
+ { \\r
+ lhs = lhs | rhs; \\r
+ return lhs; \\r
+ }; \\r
static enum_class operator ^ (enum_class lhs, enum_class rhs) \\r
{ \\r
return static_cast<enum_class>( \\r
static_cast<std::underlying_type<enum_class>::type>(lhs) \\r
^ static_cast<std::underlying_type<enum_class>::type>(rhs)); \\r
}; \\r
- static enum_class& operator^=(enum_class& lhs, enum_class rhs) \
- { \
- lhs = lhs ^ rhs; \
- return lhs; \
- };
-
-namespace caspar {
-
-// For enum classes starting at 0 and without any gaps with a terminating count constant.
-template <typename E>
-const std::vector<E>& enum_constants()
-{
- typedef typename std::underlying_type<E>::type integer;
-
- static const auto ints = boost::irange(static_cast<integer>(0), static_cast<integer>(E::count));
- static const auto result = cpplinq::from(ints.begin(), ints.end())
- //.cast<E>()
- .select([](int i) { return static_cast<E>(i); })
- .to_vector();
-
- return result;
-}
-
-}
+ static enum_class& operator^=(enum_class& lhs, enum_class rhs) \\r
+ { \\r
+ lhs = lhs ^ rhs; \\r
+ return lhs; \\r
+ }; \\r
+ static enum_class operator~ (enum_class e) \\r
+ { \\r
+ return static_cast<enum_class>( \\r
+ ~static_cast<std::underlying_type<enum_class>::type>(e)); \\r
+ };\r
+\r
+namespace caspar {\r
+\r
+// For enum classes starting at 0 and without any gaps with a terminating count constant.\r
+template <typename E>\r
+const std::vector<E>& enum_constants()\r
+{\r
+ typedef typename std::underlying_type<E>::type integer;\r
+\r
+ static const auto ints = boost::irange(static_cast<integer>(0), static_cast<integer>(E::count));\r
+ static const auto result = cpplinq::from(ints.begin(), ints.end())\r
+ //.cast<E>()\r
+ .select([](int i) { return static_cast<E>(i); })\r
+ .to_vector();\r
+\r
+ return result;\r
+}\r
+\r
+}\r
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/ptree.hpp>
+#include <boost/thread/mutex.hpp>
#include <tbb/atomic.h>
auto stream_sink = boost::make_shared<stream_sink_type>(stream_backend);
// Never log calltrace to console. The terminal is too slow, so the log queue will build up faster than consumed.
- stream_sink->set_filter(category != log_category::call);
+ stream_sink->set_filter(category != log_category::calltrace);
bool print_all_characters = false;
stream_sink->set_formatter(boost::bind(&my_formatter<boost::log::wformatting_ostream>, print_all_characters, _1, _2));
bool print_all_characters = true;
sink->set_formatter(boost::bind(&my_formatter<boost::log::formatting_ostream>, print_all_characters, _1, _2));
+ sink->set_filter(category != log_category::calltrace);
boost::log::core::get()->add_sink(sink);
});
}
+boost::mutex& get_filter_mutex()
+{
+ static boost::mutex instance;
+
+ return instance;
+}
+
+boost::log::trivial::severity_level& get_level()
+{
+ static boost::log::trivial::severity_level instance;
+
+ return instance;
+}
+
+log_category& get_disabled_categories()
+{
+ static log_category instance = log_category::calltrace;
+
+ return instance;
+}
+
+void set_log_filter()
+{
+ auto severity_filter = boost::log::trivial::severity >= get_level();
+ auto disabled_categories = get_disabled_categories();
+
+ boost::log::core::get()->set_filter([=](const boost::log::attribute_value_set& attributes)
+ {
+ return severity_filter(attributes)
+ && static_cast<int>(disabled_categories & attributes["Channel"].extract<log_category>().get()) == 0;
+ });
+}
+
void set_log_level(const std::wstring& lvl)
{
+ boost::lock_guard<boost::mutex> lock(get_filter_mutex());
+
if (boost::iequals(lvl, L"trace"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::trace);
+ get_level() = boost::log::trivial::trace;
else if (boost::iequals(lvl, L"debug"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::debug);
+ get_level() = boost::log::trivial::debug;
else if (boost::iequals(lvl, L"info"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::info);
+ get_level() = boost::log::trivial::info;
else if (boost::iequals(lvl, L"warning"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::warning);
+ get_level() = boost::log::trivial::warning;
else if (boost::iequals(lvl, L"error"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::error);
+ get_level() = boost::log::trivial::error;
else if (boost::iequals(lvl, L"fatal"))
- boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::fatal);
+ get_level() = boost::log::trivial::fatal;
+
+ set_log_filter();
+}
+
+void set_log_category(const std::wstring& cat, bool enabled)
+{
+ log_category category_to_set;
+
+ if (boost::iequals(cat, L"calltrace"))
+ category_to_set = log_category::calltrace;
+ else if (boost::iequals(cat, L"communication"))
+ category_to_set = log_category::communication;
+ else
+ return; // Ignore
+
+ boost::lock_guard<boost::mutex> lock(get_filter_mutex());
+ auto& disabled_categories = get_disabled_categories();
+
+ if (enabled)
+ disabled_categories &= ~category_to_set;
+ else
+ disabled_categories |= category_to_set;
+
+ set_log_filter();
}
void print_child(
#include "os/stack_trace.h"
#include "utf.h"
#include "thread_info.h"
+#include "enum_class.h"
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
enum class log_category
{
- normal,
- call,
- communication
+ normal = 1,
+ calltrace = 2,
+ communication = 4
};
+ENUM_ENABLE_BITWISE(log_category)
BOOST_LOG_ATTRIBUTE_KEYWORD(category, "Channel", ::caspar::log::log_category)
typedef boost::log::sources::wseverity_channel_logger_mt<boost::log::trivial::severity_level, log_category> caspar_logger;
#define CASPAR_LOG(lvl)\
BOOST_LOG_CHANNEL_SEV(::caspar::log::logger::get(), ::caspar::log::log_category::normal, ::boost::log::trivial::lvl)
#define CASPAR_LOG_CALL(lvl)\
- BOOST_LOG_CHANNEL_SEV(::caspar::log::logger::get(), ::caspar::log::log_category::call, ::boost::log::trivial::lvl)
+ BOOST_LOG_CHANNEL_SEV(::caspar::log::logger::get(), ::caspar::log::log_category::calltrace, ::boost::log::trivial::lvl)
#define CASPAR_LOG_COMMUNICATION(lvl)\
BOOST_LOG_CHANNEL_SEV(::caspar::log::logger::get(), ::caspar::log::log_category::communication, ::boost::log::trivial::lvl)
catch(...){}
void set_log_level(const std::wstring& lvl);
+void set_log_category(const std::wstring& cat, bool enabled);
void print_child(
boost::log::trivial::severity_level level,
moveable = 4,\r
resizable = 8,\r
rasterized = 16,\r
- cornerpin = 32,\r
- all = 63\r
+ cornerpin = 32//,\r
+ //all = 63\r
};\r
ENUM_ENABLE_BITWISE(layer_tag);\r
\r
-inline layer_tag operator ~ (layer_tag rhs)\r
+/*inline layer_tag operator ~ (layer_tag rhs)\r
{\r
return (layer_tag)(static_cast<int>(layer_tag::all) ^ static_cast<int>(rhs));\r
-}\r
+}*/\r
\r
layer_tag string_to_layer_tags(const std::wstring& str);\r
\r
return L"202 LOG OK\r\n";
}
+void log_category_describer(core::help_sink& sink, const core::help_repository& repo)
+{
+ sink.short_description(L"Enable/disable a logging category in the server.");
+ sink.syntax(L"LOG CATEGORY [category:calltrace,communication] [enable:0,1]");
+ sink.para()->text(L"Enables or disables the specified logging category.");
+ sink.para()->text(L"Examples:");
+ sink.example(L">> LOG CATEGORY calltrace 1", L"to enable call trace");
+ sink.example(L">> LOG CATEGORY calltrace 0", L"to disable call trace");
+}
+
+std::wstring log_category_command(command_context& ctx)
+{
+ log::set_log_category(ctx.parameters.at(0), ctx.parameters.at(1) == L"1");
+
+ return L"202 LOG OK\r\n";
+}
+
void set_describer(core::help_sink& sink, const core::help_repository& repo)
{
sink.short_description(L"Change the value of a channel variable.");
repo.register_channel_command( L"Basic Commands", L"REMOVE", remove_describer, remove_command, 0);
repo.register_channel_command( L"Basic Commands", L"PRINT", print_describer, print_command, 0);
repo.register_command( L"Basic Commands", L"LOG LEVEL", log_level_describer, log_level_command, 1);
+ repo.register_command( L"Basic Commands", L"LOG CATEGORY", log_category_describer, log_category_command, 2);
repo.register_channel_command( L"Basic Commands", L"SET", set_describer, set_command, 2);
repo.register_command( L"Basic Commands", L"LOCK", lock_describer, lock_command, 2);
\r
<!--\r
<log-level> info [trace|debug|info|warning|error|fatal]</log-level>\r
+<log-categories> communication [calltrace|communication|calltrace,communication]</log-categories>\r
<force-deinterlace> true [true|false]</force-deinterlacing>\r
<channel-grid> false [true|false]</channel-grid>\r
<mixer>\r
#include <boost/thread.hpp>
#include <boost/thread/future.hpp>
#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
#include <tbb/atomic.h>
#include <future>
+#include <set>
#include <csignal>
env::configure(config_file_name);
log::set_log_level(env::properties().get(L"configuration.log-level", L"info"));
+ auto log_categories_str = env::properties().get(L"configuration.log-categories", L"communication");
+ std::set<std::wstring> log_categories;
+ boost::split(log_categories, log_categories_str, boost::is_any_of(L", "));
+ for (auto& log_category : { L"calltrace", L"communication" })
+ log::set_log_category(log_category, log_categories.find(log_category) != log_categories.end());
if (env::properties().get(L"configuration.debugging.remote", false))
wait_for_remote_debugging();
// Start logging to file.
- log::add_file_sink(env::log_folder() + L"caspar", caspar::log::category != caspar::log::log_category::call);
- log::add_file_sink(env::log_folder() + L"calltrace", caspar::log::category == caspar::log::log_category::call);
+ log::add_file_sink(env::log_folder() + L"caspar", caspar::log::category != caspar::log::log_category::calltrace);
+ log::add_file_sink(env::log_folder() + L"calltrace", caspar::log::category == caspar::log::log_category::calltrace);
std::wcout << L"Logging [info] or higher severity to " << env::log_folder() << std::endl << std::endl;
// Setup console window.