-
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
add logging API and macros #23
Conversation
You could consider moving the "time source" struct, the system time source, and the steady time source to |
Another option would be to make the throttle macros just take a function pointer to a "get now" function that returned something like nanoseconds as |
This is pretty different to how The following PR moves the time stuff from |
I wasn't suggesting you refactor the existing get_now. I was suggesting you make the time related logging macros require a function of that signature. It would then be up to the caller to provide a function that matches it. That would decouple those logging macros from the time source concept. You could still provide convenience macros that used a static global "system" and/or "steady" time source for the user so they don't have to provide such a function. |
That would imply that the "get now" function pointer can't report errors anymore, correct? What happens if the specific rcl time source returns an error code? How does the wrapping function handle that? |
How does the logging macro report errors right now? I guess the function could do the same. Or the get_now function could be more sophisticated, with a signature like: bool (* get_now)(uint64_t * time_out, const char * error_out) Or similar. |
I'll take a look. |
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 nits, questions, and some things that might be nice cleanups, but looks pretty good overall to me.
|
||
rcl_logging_output_handler_t g_rcl_logging_output_handler = NULL; | ||
|
||
int g_rcl_logging_severity_threshold = 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.
static for all of these?
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.
They are currently defined as extern
(https://github.com/ros2/rcutils/pull/23/files#diff-f1d39d4e841937fde043a8e49b7e6c2aR87) to be globally accessible.
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 seems to me that all of these are either implementation details (initialized), or already have getters and setters (output_handler, threshold), so we could get rid of the extern on all of them and make them static 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.
I don't see a reason to hide them. In ROS 1 all of these global variables are exposed - usually because at some point that was requested.
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.
Usually I just want to keep implementations as hidden as possible; it makes it easier to change later if we want. That being said, I don't feel that strongly about this particular one, so I'll leave it up to you.
extern "C" | ||
{ | ||
#endif | ||
|
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.
Is wrapping the entire C file in "extern "C"" really required? I think that would only make a difference if we tried to compile the C files with a C++ compiler, not a C compiler.
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 don't know if this is the reason, but if you compile this with a c++ compiler by accident then the extern C prevents name mangling, which also prevent implicit overloading (if you change parameters in the header but forget to change the source file).
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.
We are doing the same in various other files.
void rcl_logging_console_output_handler( | ||
rcl_log_location_t * location, | ||
int severity, const char * name, const char * format, va_list * args) | ||
{ |
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.
Would it make sense to make severity an "enum RCUTILS_LOG_SEVERITY" instead of an int (here and elsewhere)? It won't make a huge difference to the code, but would make it clear to anyone implementing their own output handler what the types they can expect are.
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 rather not restrict the valid severity levels. Currently the numbers are consecutive but I would suggest to diverge from that in the future and leave room for "in between" levels for users to define.
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.
OK, that makes sense.
*/ | ||
# define RCUTILS_UNLIKELY(x) (x) | ||
#endif | ||
|
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 seems to me that it would be better to move all of the RCUTILS LIKELY/UNLIKELY stuff into a separate header file. It is not logging specific, it will de-clutter logging_macros.h a bit, and will probably be useful to other users at some point.
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 rather keep it close to the code where it is being used atm. If it turns out that it is useful elsewhere it can be refactored to a common location.
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.
Sure.
#define RCUTILS_LOG_CONDITION_THROTTLE_AFTER } \ | ||
} | ||
///@@} | ||
|
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.
Actually, it also might be nice to move all of the above macros into a separate header file as well. That way, when users of the logging API just want to know what they can call, they just have the generated list of macros (below). The implementation details (all of the stuff above) would be hidden unless they go looking for it. What do you think?
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 am not sure what you mean with "hidden unless they go looking for it". The doxygen documentation will contain the API of the generated code.
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 mostly talking about having a "logging_macros.h" and a "logging_macros-impl.h", or something like that. But thinking about it further, since this is a generated file most people will probably be just looking at the documentation anyway, so this is fine as it is.
resource/logging_macros.h.em
Outdated
///@@{ | ||
#if (RCUTILS_LOG_MIN_SEVERITY > RCUTILS_LOG_SEVERITY_@(severity)) | ||
// empty logging macros for severity @(severity) when being disabled at compile time | ||
@[for suffix in feature_combinations]@ |
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.
Could we indent this for loop (and everything else inside of the "for severity in severities" loop)? It would make it a bit easier to read.
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 no, since the goal is that the generated code is correctly indented which would not be the case if indentation is added based on the template logic. The template parts (if
, else
) etc. use indentation to make the nesting more obvious.
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.
Yeah, understood about the generated code. I was talking about the template parts in particular; the line "@[for suffix in feature_combinations]@" seems like it should be indented, since it is inside of the severity loop above.
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 added spaces for the two nested @[ for
loops.
\ | ||
if (RCUTILS_LIKELY(__rcutils_logging_condition)) { \ | ||
__rcutils_logging_last_logged = __rcutils_logging_now; | ||
|
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 question more than a review comment. I see here where we store the duration, and determine the last_logged time. But I don't see where we conditionally print if it has been at least duration. How does that work?
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 few lines above the variable __rcutils_logging_condition
contains the condition. The actual if
is directly above this comment.
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.
Oh, I see now. Because this is the "before" condition, when we generate the print line we are still inside of the if
condition. That detail is a bit subtle; it might be worthwhile to put a comment explaining that.
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.
The doc block mentions how the "before" and "after" condition macros are being used: https://github.com/ros2/rcutils/pull/23/files#diff-16a8c3749d32285b558137f65c5c60d2R81
Please feel free to suggest a specific improvement what you would like to see in the docs.
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 tried to come up with something, but I couldn't really improve it very much. So never mind this :).
g_log_calls = 0; | ||
rcl_log(&location, RCUTILS_LOG_SEVERITY_DEBUG, "name1", "message %d", 11); | ||
EXPECT_EQ(g_log_calls, 1u); | ||
EXPECT_TRUE(g_last_log_event.location != NULL); |
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.
You could use ASSERT_TRUE here and get rid of the if condition 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.
Since I want the following checks to be performed independently of the result of this check I think this should be EXPECT
.
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 fine.
This added a flakey test (only on macOS so far): #27 Notifying @dirk-thomas and @clalancette according to https://github.com/ros2/ros2/wiki/Build-Cop-and-Build-Farmer-Guide#new-failure-actions (Important Failures). |
fprintf(stream, "[%s] [%s]: %s\n", severity_string, name, message_buffer); | ||
} else { | ||
fprintf( | ||
stream, "[%s] [%s]: %s (%s() at %s:%zu)\n", severity_string, name, message_buffer, |
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 assumes that we pass a name. So, for example:
RCUTILS_LOG_INFO("foo")
results in:
[INFO] []: foo ...
@dirk-thomas was that the original intent ?
Slightly related, is it expected that by default function_name, file_name and line_number are printed ?
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 assumes that we pass a name
Yes, if desired the code could leave the brackets around empty name out.
is it expected that by default function_name, file_name and line_number are printed
Yes, for now that was the intent. In the future this should be configurable (as many other things).
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.
ok thanks for clarifying
That's something to keep in mind given that the executables tested using regex/string matching on console output can NOT use these macros as is. We would need to add more complex regex logic for the console output to be matched properly
This is the patch moved from ros2/rcl#138.
I will have to remove the usage of
time
and with that theTHROTTLE
macros since that is a concept introduced inrcl
and not available here. That will imply that the macros containingTHROTTLE
can only be implemented inrcl
and will therefore require a different#include
directive.I assume the throttle specific macros then use the
RCL_
prefix while all the macros from this package useRCUTILS_
... 馃槖