-
Notifications
You must be signed in to change notification settings - Fork 23
RSDK-5986: Resource-level logging for the C++ SDK #383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: Ethan <[email protected]>
Co-authored-by: Ethan <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome! Any plans on adding to the simple or complex module examples to show how to use the new VIAM_LOG
?
const auto response = impl_->stub_->Log(ctx, req, &resp); | ||
if (is_error_response(response)) { | ||
// Manually override to force this to get logged to console so we don't set off an infinite | ||
// loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A classic; had to do something like this in go, too, I think.
Per @benjirewis suggestion just added some logging to the simple and complex module examples, although now I'm wondering if I should just replace all the use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was interested so had a quick look through. Just some high level thoughts and comments. No need to wait on me to move forward with this.
#include <boost/log/attributes/clock.hpp> | ||
#include <boost/log/expressions/keyword.hpp> | ||
#include <boost/log/sinks/sync_frontend.hpp> | ||
#include <boost/log/sinks/text_ostream_backend.hpp> | ||
#include <boost/log/sources/severity_channel_logger.hpp> | ||
#include <boost/log/utility/manipulators/add_value.hpp> | ||
#include <boost/utility/string_view.hpp> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the long term plan still include going boost free in the API? Should there be a level of indirection about what logging backend is in use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes definitely. The situation with Boost.Log's stream operator-based approach makes this difficult but I suspect it is still doable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which is to say hopefully in a subsequent boost-insulating PR
src/viam/sdk/log/logging.hpp
Outdated
#define VIAM_LOG_IMPL(lg, level) \ | ||
BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level) \ | ||
<< ::boost::log::add_value(::viam::sdk::attr_file_type{}, \ | ||
::viam::sdk::log_detail::trim_filename(__FILE__)) \ | ||
<< ::boost::log::add_value(::viam::sdk::attr_line_type{}, __LINE__) | ||
|
||
/// @brief Log macro for general SDK logs. | ||
/// @ingroup Log | ||
/// | ||
/// Use this macro to generate log messages pertaining to the SDK at large. | ||
#define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::LogManager::get().global_logger(), level) | ||
|
||
/// @brief Log macro for resource-level logs. | ||
/// @ingroup Log | ||
/// | ||
/// This macro can only be called from the definition of a member function of a class inheriting | ||
/// @ref Resource. It will log messages to the log source of that specific resource, allowing | ||
/// resource-level log filtering. | ||
#define VIAM_RESOURCE_LOG(level) VIAM_LOG_IMPL(this->logger_, level) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these macros form part of the library API? Or, if we had the machinery (prelude/postlude headers with push/pop, etc.) would they not be visible to consumers of the library? I think they probably are intended for user consumption (the former case), for instance in the case of writing a module.
I wonder then if they should be in a dedicated header, logging_macros.hpp
or similar, so that the Viam C++ SDK can prevent them from leaking into client code by way of its own usage of the logging macros in headers, but consumers can opt in to visibility by consuming the header directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say they are part of the API yes. Pursuant to eventually insulating Boost entirely (touching on another of your comments) I'm inclined to present the macros to users as a black box and not have them call any Boost.Log functions on their own.
I'm aware of other logging libraries that are more amenable to non-macro use but unfortunately the operator<<
approach of this one makes it fairly unusable for practical purposes without a logging macro.
|
||
} // namespace log_detail | ||
|
||
BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_channel, "Channel", std::string); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some worries about what the implications are for when we want to do symbol visibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was bugging me a bit as well. There is a slight subtlety that BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE
just defines something like struct attr_channel_type
whereas BOOST_LOG_ATTRIBUTE_KEYWORD
defines struct attr_channel_type
as well as const attr_channel_type attr_channel
. We are using the former--is that still an issue?
In any case I'm going to hack away and see if I can't figure out a way to hide those because I don't like exposing them in a header either.
src/viam/sdk/log/logging.hpp
Outdated
"TimeStamp", | ||
boost::log::attributes::local_clock::value_type); | ||
|
||
#define VIAM_LOG_IMPL(lg, level) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe VIAM_SDK_LOG...
to keep it VIAM_SDK
as the macro prefix and viam::sdk
as the namespace prefix.
/// @brief Severity levels for the logger. | ||
/// @ingroup Log | ||
enum class log_level : std::int8_t { | ||
trace = -2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think elsewhere for enum constants we do k_x
:
k_int8 = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true although I wonder about this from a usability perspective, it feels weird for users to have to type VIAM_LOG(k_warn)
rather than VIAM_LOG(warn)
... maybe this should be an exception to the rule? I've seen other libraries do macros-per-severity (eg VIAM_LOG_INFO
) but this starts to get combinatorial since we already have the general- and resource-flavored log macros
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm willing for it to be an exception. Maybe throw a comment on there indicating that it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple small questions on the code itself! Also:
- I think there was something that was cribbed from SO? It'd be nice to make sure still that do at least one of a) document where it came from, or b) unit test it
- I think we should update the
README
to indicate how users should (or should not) be interacting with boost log directly when using the SDK. - The format of the server side logs looks slightly off to me insofar as the properly formatted server side log doesn't seem to include a file or line number? Unless I'm missing something.
Otherwise this looks good to me!
std::cout << argv[0] << ": The path `" << tflite_module_path.c_str() | ||
<< "` provided for `--tflite-module-path` is not an existing regular file" | ||
<< std::endl; | ||
VIAM_SDK_LOG(error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(q) just wondering why we no longer output argv[0]
here and below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so argv[0]
is executable name, and every single log message in the old code was prepending exe name, which I get the desire for but isn't really a pattern that we'd used anywhere else. This was the inspiration for providing the option to change the general channel/module name printed with all log messages, which is used on line 96 to basically set the global channel name to argv[0]
|
||
return EXIT_SUCCESS; | ||
} | ||
} catch (const std::exception& ex) { | ||
std::cout << argv[0] << ": " | ||
<< "Failed: a std::exception was thrown: `" << ex.what() << "``" << std::endl; | ||
std::cerr << argv[0] << ": " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(q) why are we using cerr
here instead of VIAM_SDK_LOG(error)
? I'd expect that we'd catch errors around the logger itself when we create it so we probably would still be able to use the logger even if we caught an exception here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since the entire main
is in a function try-catch block, the Instance
is out of scope within this catch
block. It's possible (though unlikely) that the exception could even have originated from the Instance
, so we can't be assured that it exists and logging is set up
@@ -757,10 +757,10 @@ int serve(const std::string& socket_path) try { | |||
|
|||
return EXIT_SUCCESS; | |||
} catch (const std::exception& ex) { | |||
std::cout << "ERROR: A std::exception was thrown from `serve`: " << ex.what() << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same q as above, why are we not using the logger here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same answer as above :)
This PR introduces resource-level logging to the C++ SDK. Building off the work in #374, the
Instance
class now initializes the logging machinery and handles bookkeeping for resource-level logging.We are still using Boost.Log for the logging but there is a pretty significant overhaul which makes use of some more advanced features of the library. The distinction between Boost.Log's concept of source and sink is helpful. There is a single logging source in the SDK, namely the source of message generated by user code (or the SDK) using the
VIAM_LOG
orVIAM_RESOURCE_LOG
macros. Precisely one of two sinks may be active.RobotClient
.Starting up a
ModuleService
will automatically switch from 1. to 2.Currently there is a lot of header/ABI leakage of Boost.Log components. Unfortunately this may be nontrivial to work around, given that Boost.Log uses ostream-style streaming of log messages, but I do think it is doable. That said, we currently have other Boost components in headers/APIs, so I think this is lower priority for now.
Possible bikeshed: the class currently called
Logger
might more accurately be called something likeLogManager
, given that it manages the SDK's log source and handles setup of the logging core and the console sink.The unit tests now feature tests of console logging at the SDK level and the resource level, with tests of global filtering and resource-level filtering as well. Note that these are only console logging tests, but reviewers please advise if it may be possible to connect a
RobotClient
toMockRobot
or similar in the tests, and override the gRPCLog
method so we can inspect logs getting sent via the non-console log sink.On recommendation from @stuqdog here are some sample log outputs:
Client-side logging, general SDK:
Client-side, resource-level:
Sample RDK output:
edit: fixed a bug where RDK was not logging file/line, now looks like