Skip to content
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

Merged
merged 1 commit into from
Jun 5, 2017
Merged

add logging API and macros #23

merged 1 commit into from
Jun 5, 2017

Conversation

dirk-thomas
Copy link
Member

This is the patch moved from ros2/rcl#138.

I will have to remove the usage of time and with that the THROTTLE macros since that is a concept introduced in rcl and not available here. That will imply that the macros containing THROTTLE can only be implemented in rcl 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 use RCUTILS_... 馃槖

@dirk-thomas dirk-thomas added the in progress Actively being worked on (Kanban column) label May 31, 2017
@dirk-thomas dirk-thomas self-assigned this May 31, 2017
@wjwwood
Copy link
Member

wjwwood commented May 31, 2017

You could consider moving the "time source" struct, the system time source, and the steady time source to rcutils as well, leaving only the ROS time source in rcl.

@wjwwood
Copy link
Member

wjwwood commented May 31, 2017

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 uint64_t. Then you could provide integration with the rcl_time_source_t in the rcl package.

@dirk-thomas
Copy link
Member Author

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 uint64_t. Then you could provide integration with the rcl_time_source_t in the rcl package.

This is pretty different to how get_now currently behaves. I don't think I will perform such a refactoring just for the logging API. This should be considered separately.

The following PR moves the time stuff from rcl to rcutils: #25. I will rebase this once that PR has been merged.

@wjwwood
Copy link
Member

wjwwood commented Jun 1, 2017

This is pretty different to how get_now currently behaves. I don't think I will perform such a refactoring just for the logging API. This should be considered separately.

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.

@wjwwood wjwwood mentioned this pull request Jun 1, 2017
@dirk-thomas
Copy link
Member Author

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.

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?

@wjwwood
Copy link
Member

wjwwood commented Jun 1, 2017

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.

@dirk-thomas
Copy link
Member Author

dirk-thomas commented Jun 2, 2017

Rebased and updated:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

Ready for review.

@dirk-thomas dirk-thomas added in review Waiting for review (Kanban column) and removed in progress Actively being worked on (Kanban column) labels Jun 2, 2017
@clalancette
Copy link
Contributor

I'll take a look.

@clalancette clalancette self-requested a review June 2, 2017 20:23
Copy link
Contributor

@clalancette clalancette left a 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;
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Member

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).

Copy link
Member Author

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)
{
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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 } \
}
///@@}

Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

///@@{
#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]@
Copy link
Contributor

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.

Copy link
Member Author

@dirk-thomas dirk-thomas Jun 2, 2017

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.

Copy link
Contributor

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.

Copy link
Member Author

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;

Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine.

@dirk-thomas dirk-thomas merged commit b5a7a12 into master Jun 5, 2017
@dirk-thomas dirk-thomas deleted the logging branch June 5, 2017 18:20
@dirk-thomas dirk-thomas removed the in review Waiting for review (Kanban column) label Jun 5, 2017
@wjwwood
Copy link
Member

wjwwood commented Jun 6, 2017

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,
Copy link
Member

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 ?

Copy link
Member Author

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).

Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants