11#include < viam/sdk/common/logger.hpp>
22
3+ #include < atomic>
34#include < iostream>
45
6+ #include < boost/date_time/posix_time/posix_time_types.hpp>
7+ #include < boost/log/attributes/mutable_constant.hpp>
58#include < boost/log/core.hpp>
9+ #include < boost/log/expressions.hpp>
10+ #include < boost/log/sources/severity_logger.hpp>
11+ #include < boost/log/support/date_time.hpp>
612#include < boost/log/trivial.hpp>
13+ #include < boost/log/utility/setup/common_attributes.hpp>
14+ #include < boost/log/utility/setup/console.hpp>
15+ #include < boost/log/utility/setup/file.hpp>
716
817namespace viam {
918namespace sdk {
1019
1120namespace logging = boost::log;
12- using ll = Logger::log_level;
21+ namespace attrs = boost::log::attributes;
22+ namespace expr = boost::log::expressions;
23+ namespace keywords = boost::log::keywords;
24+
25+ using ll = log_level;
1326using bll = logging::trivial::severity_level;
1427
1528struct Logger ::impl {
@@ -22,16 +35,67 @@ struct Logger::impl {
2235 ll level_;
2336};
2437
25- std::shared_ptr<logging::sources::severity_logger<bll>> Logger::underlying () {
26- return impl_->logger_ ;
38+ // Convert file path to only the filename
39+ std::string path_to_filename (const std::string& path) {
40+ return path.substr (path.find_last_of (" /\\ " ) + 1 );
41+ }
42+
43+ namespace {
44+ std::atomic<bool > inited = false ;
45+ }
46+
47+ void init_attributes () {
48+ logging::core::get ()->add_thread_attribute (" File" , attrs::mutable_constant<std::string>(" " ));
49+ logging::core::get ()->add_thread_attribute (" LogLevel" ,
50+ attrs::mutable_constant<std::string>(" " ));
51+ logging::core::get ()->add_thread_attribute (" LoggerName" ,
52+ attrs::mutable_constant<std::string>(" " ));
53+ logging::core::get ()->add_thread_attribute (" Line" , attrs::mutable_constant<int >(0 ));
54+ }
55+
56+ void init_logging (std::ostream& strm) {
57+ if (inited) {
58+ return ;
59+ }
60+ inited = true ;
61+
62+ init_attributes ();
63+
64+ // remove existing stdout/stderr logging since we're sending logs to RDK
65+ boost::log::core::get ()->remove_all_sinks ();
66+
67+ logging::add_console_log (strm,
68+ boost::parameter::keyword<keywords::tag::format>::get () =
69+ (expr::stream << ' [' << expr::attr<std::string>(" File" ) << ' :'
70+ << expr::attr<int >(" Line" ) << " ] " ));
71+ logging::add_common_attributes ();
72+ }
73+
74+ void init_logging () {
75+ if (inited) {
76+ return ;
77+ }
78+ inited = true ;
79+
80+ init_attributes ();
81+
82+ logging::add_console_log (
83+ std::clog,
84+ boost::parameter::keyword<keywords::tag::format>::get () =
85+ (expr::stream << expr::format_date_time<boost::posix_time::ptime>(
86+ " TimeStamp" , " %Y-%m-%d_%H:%M:%S.%f" )
87+ << " : <" << boost::log::trivial::severity << " > " << ' ['
88+ << expr::attr<std::string>(" File" ) << ' :' << expr::attr<int >(" Line" )
89+ << " ] " << expr::smessage));
90+ logging::add_common_attributes ();
2791}
2892
2993Logger::Logger (std::string name)
3094 : name_(std::move(name)), level_(log_level::info), impl_(std::make_unique<impl>()) {};
3195Logger::Logger (std::string name, log_level level)
3296 : name_(std::move(name)), level_(level), impl_(std::make_unique<impl>(level)) {};
3397
34- logging::trivial::severity_level _log_level_to_severity_level (Logger:: log_level level) {
98+ logging::trivial::severity_level _log_level_to_severity_level (log_level level) {
3599 switch (level) {
36100 case ll::error: {
37101 return bll::error;
@@ -55,29 +119,63 @@ logging::trivial::severity_level _log_level_to_severity_level(Logger::log_level
55119 }
56120}
57121
58- void Logger::log (const std::string& log, log_level level) const {
59- if (impl_->level_ > level) {
60- return ;
122+ std::string level_to_string (log_level level) {
123+ switch (level) {
124+ case ll::error: {
125+ return " error" ;
126+ }
127+ case ll::warning: {
128+ return " warning" ;
129+ }
130+ case ll::debug: {
131+ return " debug" ;
132+ }
133+ case ll::fatal: {
134+ return " fatal" ;
135+ }
136+ case ll::trace: {
137+ return " trace" ;
138+ }
139+ case ll::info: {
140+ return " info" ;
141+ }
61142 }
62- BOOST_LOG_SEV (*(impl_->logger_ ), _log_level_to_severity_level (level)) << log;
63- }
64- void Logger::debug (const std::string& log) const {
65- this ->log (log, ll::debug);
66- }
67- void Logger::error (const std::string& log) const {
68- this ->log (log, ll::error);
69- }
70- void Logger::fatal (const std::string& log) const {
71- this ->log (log, ll::fatal);
72- }
73- void Logger::info (const std::string& log) const {
74- this ->log (log, ll::info);
75143}
76- void Logger::trace (const std::string& log) const {
77- this ->log (log, ll::trace);
144+
145+ template <typename ValueType>
146+ ValueType set_get_attrib (const char * name, ValueType value) {
147+ auto attr = boost::log::attribute_cast<boost::log::attributes::mutable_constant<ValueType>>(
148+ boost::log::core::get ()->get_thread_attributes ()[name]);
149+ attr.set (value);
150+ return attr.get ();
78151}
79- void Logger::warning (const std::string& log) const {
80- this ->log (log, ll::warning);
152+
153+ std::string Logger::log_delineator () {
154+ // a bit hacky, the problem is we need some way of including log level and logger name
155+ // information that can be passed to a `robot_client.log(...)` call. Storing this information
156+ // as a boost keyword attribute (the way we do for `File` and `Line`) isn't sufficient because,
157+ // while there's always a file and line no that's being called from, there isn't always a named
158+ // logger or an established log level being used (e.g., a client could be calling a `BOOST_LOG`
159+ // macro that doesn't support severity level directly, without using one of our named loggers).
160+ // In such a case, we'd be passing along whatever logger name and level were previously set,
161+ // which isn't great. So, we use the delineator as a sentinel string to split up our actual log
162+ // message, our level, and our logger name that is qualified enough to (hopefully) never see
163+ // collisions.
164+ static std::string delineator (" __VIAM_IMPL_LOG_DELINEATOR__ " );
165+ return delineator;
166+ } // namespace sdk
167+
168+ void Logger::log (const std::string& msg, log_level level, const char * filename, int line_no) const {
169+ std::cout << " in the implementation log call!\n " << std::flush;
170+ // in case logging hasn't been initialized, let's set it up.
171+ init_logging ();
172+ set_get_attrib (" LogLevel" , level_to_string (level));
173+ set_get_attrib (" LoggerName" , name_);
174+ BOOST_LOG_STREAM_WITH_PARAMS (
175+ *(impl_->logger_ ),
176+ (set_get_attrib (" File" , viam::sdk::path_to_filename (filename)))(set_get_attrib (
177+ " Line" , line_no))(boost::log::keywords::severity = _log_level_to_severity_level (level)))
178+ << msg;
81179}
82180
83181Logger::~Logger () = default ;
0 commit comments