-
Notifications
You must be signed in to change notification settings - Fork 101
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
Logging #127
Logging #127
Conversation
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.
It would be good to cover the newly added API with tests.
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 high level concerns about the design, that I think we need to work on before I continue the review. In general, these are some red flags for me (code smells):
rcutils
should not need to depend on log4cxx or any third-party logging libraryrcutils
should never mention "ROS topics" or "rosout", implementation of a topic based logging output handler should, in my opinion, be completely outsidercutils
- supporting multiple output handlers could be done in
rcutils
, but could also be done outside of rcutils (unless I'm missing some conceptual issue with that) and I think we should aim for the latter without a decent design reason to do it locally - I'm not keen on the unmanaged buffer option for the
char_array
- I'm slight more ok with the new utility functions in
char_array
, but most could be replaced withif (size_i_need > char_array.capacity) {resize(char_array, size_i_need);} do_the_thing(char_array, ...);
Much of the logic and code changes look ok, so I think most of these design decision issues, if we decide to go another way, will be largely moving code around.
include/rcutils/logging.h
Outdated
*/ | ||
typedef void (* rcutils_logging_output_handler_t)( | ||
const rcutils_log_location_t *, // location | ||
int, // severity | ||
const char *, // name | ||
rcutils_time_point_value_t, // timestamp | ||
const char *, // format | ||
va_list * // args | ||
const char * // log_str |
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.
What's the logic behind this change? (sorry if you explained it somewhere else)
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 switched to this for two reasons. 1) I wanted to make sure the log message was consistent for every output handler. 2) I wanted to reduce the amount of redundancy in both the runtime execution of formatting logs and in the code. From what I saw, there wasn't an easy way to provide named key replacement during log formatting in libraries such as log4cxx. This would require external libraries written on top of those to either reimplement the code that replaces those keywords or restructure/move the existing code in rcutils that does that formatting/replacement so that it could be used by the external libraries.
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 I remember now why this is like this. The reason was so that that the output handler being used could decide how to do the formatting, specifically if they wanted to use a fixed sized string for the destination and truncate if too long (avoiding memory allocations) or if they wanted to use dynamic memory and something like snprintf
.
For passing to something like log4cxx
you can just vsnprintf
the format string and arg list into a log string right before passing it to log4cxx. That means redundant code (every output handler needs to do this), but it also means they have control over how the formatting happens.
Given this, I think we should roll back on this specific change. Sorry for being back and forth about it.
The current direct dependency is due to my misunderstanding of something someone told me. If I fix this then it should only require a group dependency. In the short term you could replace the log4cxx by adding a different external logger lib to the group and setting the environment variable when you build. A later improvement to this would be to make the library selectable at runtime similar to the rmw implementation, but this seemed like an improvement that could come later.
Yes, the rosout stuff was in here mostly as a place holder before I had started the feature work for the rosout topic. Now that I've started I can see that the rosout needs to be outside of rcutils. I was looking at moving it up into rcl, but I'm open to other suggestions of where it should live as well.
I don't have a strong opinion on where the output handlers should live. Adding them to rcutils was a convenient place to start. I'm perfectly happy to move them somewhere else. How would you feel about moving the two new output handlers to the
A lot of the functionality of the buffer used in the logging code to format messages seemed to be very close to the functionality needed by char_array, even going beyond to provide useful functionality not currently in the char_array. I'll admit that it does feel like overloading the functionality of char_array to provide the option for a buffer not managed by the char_array but it seemed like a useful way to move a lot of the buffer management code out of logging.c while keeping the functionality of a stack buffer for faster allocation. If you feel like it's a blocking change then I can see a few ways forward:
Yes, most of the new functions are mostly convenience functions to help clean up the code and abstract away the need for managing the size of your buffer from the business logic in your code.
Thanks for all your feedback. I agree that most of the design concerns you brought up seem like a matter of deciding where things should live and what makes sense for dependencies. I don't have very strong opinions on these, so let me know what specifically you'd like to address your concerns. |
1df0298
to
2b68388
Compare
I've fixed all of the requested formatting changes and ran ament_uncrustify on the repository. I think it picked up a few files weren't part of my change when it ran the uncrustify, but I included them because I figured they would break the uncrustify test if I reverted them. All unit tests were passing locally when I ran "colcon test" in a Docker Linux image. I also moved the the rosout, ext_lib, and multiplexing handlers out of rcutils and up to rcl as that is where they were being configured and added as the handler. Let me know if you would prefer for it to live someplace else entirely. There were several other questions/concerns that I answered in the comment above. Let me know what you still have concerns about after this change. |
This is a bit suspicious since the current master branch passed all linter tests just fine on all targeted platforms in our nightly CI. |
I noticed that the automated tests failed here as well due to a linter and uncrustify issue, even though they are all passing for me locally. Maybe I'm running an old version of ament? |
I've fixed the linter and uncrustify differences so that they pass the automated test here. Looks like most of the files that ament_uncrustify picked up outside my changes were actually in the rcl package not this one and I've removed those from the change. |
Sorry for the lag in feedback, I've been trying to get out of the way of one of our crystal milestones and it's been all consuming.
That's fine, however, I was expecting this group dependency and a future "hard dependency on the default external logger" to be in
Alternatively, it could be done at compile time, but still it would be figuring out which one in In the end, I think what I'm looking for is
I recognize that the
I think that's the right approach in order to keep
I like this approach the most, because it feels like to me we're trying to combine the equivalent of The other reason I'm wary of expanding features of
Well we can keep them for now, though I have a slight preference for Again, sorry for holding you up on feedback I'll try to be more responsive in the next few weeks. |
Not a problem. I've also been a bit slow to respond to the initial feedback due to some unforeseen travel. Hopefully I can stay focused on this now and get it closed out quickly.
I think we're converging on the same idea. In the latest update I've moved all the ext_lib dependencies out of
Alright, we can open this as an issue to migrate this into its own data structure. You're right that it is starting to treat char_array like a a String object and I like the idea of looking to explicitly create a String struct that can use an underlying char_array. |
I think that's perfectly fine, the runtime control of it is purely extra in my opinion.
Also, each time we extend these C data structures I feel more and more like wanting to bring in a C library that offers these, like glib or something like that but smaller and leaner. So that might be something for us to consider in the future rather than continuing to build our own wheels :) Again, for now, just plow ahead as-is. |
…h output handlers you want to use and setting the framework to allow for multiple output handlers in the logging code. cr https://code.amazon.com/reviews/CR-3734291
…Also automatically uncrustifying and fixing unit tests.
Rebased on top of master. |
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.
Honestly, I'm beginning to question the need for most of this pull request. Once you revert the change that the output handlers still take the format string and the va args, and move the multiplexing of the output handler to rcl, the rest of the changes just seem to be refactoring the way the log message is formatted for the default output handler.
I started a pull request to touch up some style issues and undo the formatting occurring before the output handler is called, but once I started to get deeper into it I wasn't sure anymore how this was qualitatively different from the existing code (it may be qualitatively better, but the end result still seems to be just formatting the components into the log message for printing).
I think we should quickly discuss it before merging. Perhaps I've missed something significant in the changes that justifies the code delta.
Either way, I think we can merge most of this pull request, but I want to understand more why the changes to the formatting code were needed.
For a concrete example: there used to be a set of if-else statements which decided how to handle each format token (e.g. converting timestamp into a nano seconds or floating point seconds) and now it's a dispatch system using a structure of function pointers associated with token strings. That might be better, but I guess it seems like a lateral move to me.
include/rcutils/logging.h
Outdated
*/ | ||
typedef void (* rcutils_logging_output_handler_t)( | ||
const rcutils_log_location_t *, // location | ||
int, // severity | ||
const char *, // name | ||
rcutils_time_point_value_t, // timestamp | ||
const char *, // format | ||
va_list * // args | ||
const char * // log_str |
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 I remember now why this is like this. The reason was so that that the output handler being used could decide how to do the formatting, specifically if they wanted to use a fixed sized string for the destination and truncate if too long (avoiding memory allocations) or if they wanted to use dynamic memory and something like snprintf
.
For passing to something like log4cxx
you can just vsnprintf
the format string and arg list into a log string right before passing it to log4cxx. That means redundant code (every output handler needs to do this), but it also means they have control over how the formatting happens.
Given this, I think we should roll back on this specific change. Sorry for being back and forth about it.
The changes to the formatting code were originally done to make the formatter more reusable for other log output functions to format with. When we shifted from passing around the vargs there wasn't really a need for this to be as reusable anymore. We kept these changes in the original PR because we thought they provided a slightly nicer abstraction of buffer management to string formatting that could be reusable elsewhere in the code. If this part of the PR is contentious then I'd rather just remove it at this point.
Yes, in this case it was probably a lateral move. It is arguably cleaner if you have a large number of tokens you want replaced or want the ability to register tokens outside this file. Since that doesn't seem to be the case it was a refactor that, in retrospect, wasn't needed.
There are two different formatting operations being applied here. The first is the vargs list that comes from the user's log and the second is the log formatting that's applied on top of it. To me, if these are meant to be modeled as just output sinks on top of the logger system that's implemented in rcutils then it didn't make sense to make every output reformat the user's string, even if it was as simple as calling vsnprintf, because you don't want inconsistencies in the log across different outputs. The second formatting for the log could make sense to be done by the output handler. Other logger systems do provide ways of having different logger formats for different output handlers. However, it didn't seem like the current code had been structured to allow for different formats on different outputs and so it seemed easier to just do it in one place. Additionally, the token replacement that is done with the log formatting is not universal across logging systems and there is additional logic to token replacement beyond what printf does. This is the code I really didn't want to start duplicating in multiple locations. I do see your point in allowing the user provided implementation to due more of the buffer management. Our disconnect may go back a little bit to the design of the logger interface being provided in rcutils. I was operating under the assumption that the output handlers should just be simple sinks for the logs so that the ROS system could have some control (such as enforcing the same formatting) over the various outputs. This is why I delegated more of the formatting work to the logger functions in rcutils. However, if the functions in rcutils are meant to just be a very simple facade over top of an entirely user provided logging system then, in my opinion, most of the logic currently in this file should be migrated out to its own implementation and this should be pared down to an actual facade that delegates all logging functionality (such as hierarchy management of severity levels) to the provided implementation. Let me know your thoughts. I'm happy to course correct here for this review, but I'd like to make sure I understand what your vision is for this logging to make sure I'm aligned. |
Ok, thanks for your feedback. To be clear I'm ok with the "lateral" code changes, I just wanted to make sure I wasn't missing another reason for them.
I agree with you about having consistent formatting across outputs for the user's log message, however, I think that the variations on how this formatting happens will be only intrinsically different, and that the output will be the same in all cases, except for perhaps truncation which may be unavoidable. For example in all these cases the output would be the same (again excluding truncation) but may be different w.r.t. dynamic memory:
That all being said, I think we can leave this as-is (formatting before passing to output handlers). I've been thinking about it more since last night and I think users that want to avoid memory allocation in these cases also want to avoid variadic arguments completely. So they'll probably do something like find-replace all use of variadic arguments and replace them with fixed operand formatting functions, AND then pass them to logging. In which case logging will not need vargs anyways.
I agree with you here that the "output format string" that we allow people to use (via the However, there are cases where applying this formatting first doesn't make sense, like with What I want to get away from is users having to specify something like the Put another way, I think it's important to use the same formatting logic and To that end I especially agree with:
So what I was imagining is something where these formatting functions were provided by
I think that is closer to what I imagined, such that the logging code in rcutils was:
What I think makes sense is this:
Does that sound ok? I started a pr for this pr which addressed the vargs stuff (which I'll drop now) and some other style stuff, so I finish that up and propose it. But I think we still need to stop formatting the output according to |
Thanks for the great explanations William. This all sounds good to me. I'll start work immediately to make the formatting functions public, add vargs back to the output handlers, and have each output handler in rcl call the formatting functions provided by rcutils. |
@nburek I was planning on leaving the vargs out of the output handler signatures actually (leaving it as-is and avoiding some further changes for this pr). Sorry if my comment was confusing. |
Specifically:
|
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.
Just one item to address, otherwise lgtm.
if (RCUTILS_RET_OK == status) { | ||
status = rcutils_logging_format_message( | ||
location, severity, name, timestamp, msg_array.buffer, &output_array); | ||
} |
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 we need an else case here where we at least either set the error state or use the SAFE_FWRITE
macro or fprintf(stderr, ...)
(as appears to be the style in this file) to let someone know that vsprintf
failed.
|
||
if (RCUTILS_RET_OK == status) { | ||
status = rcutils_logging_format_message( | ||
location, severity, name, timestamp, msg_array.buffer, &output_array); |
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.
Similarly, we should check status and notify someone somehow if status is no OK.
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.
lgtm
Signed-off-by: William Woodall <william@osrfoundation.org>
Connects to ros2/rcl#327
Summary
This change is part of a set of changes for new logging features. These changes include the following features:
rcutils
rcl
rclcpp
rc_logging_log4cxx
Remaining Feature Work
Links
https://discourse.ros.org/t/ros2-logging/6469
https://github.com/nburek/rc_logging_log4cxx
Pull requests for change
rclcpp: ros2/rclcpp#582
rcl: ros2/rcl#327
rcutils: #127