-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Added the ability to redirect logging to a callable. #6244
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: master
Are you sure you want to change the base?
Conversation
a544354
to
9eb28fc
Compare
@mvieth do you have any idea to why there is linker errors with pcl::octree:OctreeKey::maxDepth ? |
Must have something to do with the usage of |
Ahh, its in that print statement it fails... hmm, still odd though. |
6d1d0a4
to
44320b0
Compare
Maybe pcl::console::LogRecorder should be a member of the Logger, to avoid creating a stringstream for each stream command - would that make sense? |
05b6bfa
to
a376418
Compare
Nice for me, even if I not like too much the following: pcl::console::Logger::getInstance().setCallback(...) because of the Moreover, I think that the previous callback needs to be returned from the |
Somehow the callback needs to be saved - and having just a static global variable to a std::function, didn't seem right either. At least it was the best solution I found, also browsing other logging frameworks. |
return instance; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// |
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.
template <typename Functor>
void pcl::console::Logger::setCallback(Functor&& callback){
getInstance().setCallback(std::move(callback));
}
DISCLAIMER: draft, untested code.
@larshg wouldn't this work? Would the template be a problem there?
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 want a free function like:
template <typename Functor>
void
setCallback(Functor&& callback)
{
Logger::getInstance().setCallback(std::move(callback));
}
so you can call:
pcl::console::setCallback(...);
Yes, thats possible.
Moreover, why generic template "functor" instead of |
Else I couldn't get it to work with lambdas - but maybe I did it wrong 😄 |
To restore normal logging again, you can just set the callback to a nullptr. |
I had a quick look and it looks promising to me, thanks for your 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 assume we are targeting PCL 1.16.0, due to likely ABI changes?
Is there a way to easily reset to the default behaviour, after calling setCallback
? Edit: I just read your other comment: set callback to a nullptr. Please document this in the code somewhere.
} | ||
|
||
LogRecorder& | ||
operator<<(std::ostream& (std::ostream&)) |
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 one looks strange, what is it for?
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 was for handling std::endl if I remember correctly - that apparently need this operator.
if (pcl::io::loadPCDFile (pcd_filename, input_pointcloud2)) | ||
{ | ||
PCL_ERROR ("ERROR: Could not read input point cloud %s.\n", pcd_filename.c_str ()); | ||
PCL_ERROR ("ERROR: Could not read input point cloud %s.\n", pcd_filename); |
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 .c_str()
still work? But is not necessary any more?
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.
Seems it still is necessary. See https://godbolt.org/z/zaca3aGK3 - MSVC output correctly, but GCC output random stuff.
|
||
/* \brief maximum depth that can be addressed */ | ||
static const unsigned char maxDepth = | ||
static_cast<unsigned char>(sizeof(uindex_t) * 8); |
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 be possible to keep this and deprecate it? Same for max_sample_checks_
.
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 guess so - not sure if it will do linking errors downstream though.
common/include/pcl/console/print.h
Outdated
{ \ | ||
pcl::console::LogRecorder rec; \ | ||
rec << ARGS; \ | ||
pcl::console::LogRecord entry{pcl::console::LEVEL, rec.to_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.
Could there be a way to tell the stringstream of LogRecorder to directly operate on the message string of the LogRecord? Otherwise there are multiple allocations and copies here, I think.
4248763
to
5384843
Compare
…t logging to console
448c188
to
746959d
Compare
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.
Pull Request Overview
This PR adds the ability to redirect PCL logging to a callable function, which addresses the need for programmatic access to logging output rather than just console printing. This change allows users to capture and handle log messages in custom ways.
- Introduces a callback-based logging system with LogRecord structure
- Refactors existing print functions to use templates and std::string instead of C-style variadic arguments
- Replaces deprecated static member variables with static getter functions
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
File | Description |
---|---|
common/include/pcl/console/print.h | Adds Logger class with callback support and converts print functions to templates |
common/src/print.cpp | Implements Logger singleton and callback-based printing logic |
test/common/test_console.cpp | New test file for validating logging functionality and callback behavior |
test/common/CMakeLists.txt | Adds test registration for console testing |
sample_consensus/include/pcl/sample_consensus/sac_model.h | Replaces deprecated static member with getter function |
octree/include/pcl/octree/octree_key.h | Replaces deprecated static member with getter function |
octree/include/pcl/octree/impl/octree_pointcloud.hpp | Updates to use new getter function |
octree/include/pcl/octree/impl/octree_base.hpp | Updates to use new getter function and adds missing includes |
octree/include/pcl/octree/impl/octree2buf_base.hpp | Updates to use new getter function |
octree/src/octree_inst.cpp | Adds missing include for octree_key.h |
octree/include/pcl/octree/octree_impl.h | Removes redundant include |
tools/train_linemod_template.cpp | Updates print calls to use std::string instead of .c_str() |
examples/segmentation/example_lccp_segmentation.cpp | Simplifies logging by removing stringstream usage |
examples/segmentation/example_cpc_segmentation.cpp | Simplifies logging by removing stringstream usage |
*/ | ||
template <typename... Args> | ||
std::string | ||
to_string(const std::string fmt_str, ...) |
Copilot
AI
Aug 9, 2025
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 variadic template function to_string
declares variadic arguments with ...
but doesn't use them in the parameter list. This should be to_string(const std::string& fmt_str, Args&&... args)
to properly accept the template arguments.
Copilot uses AI. Check for mistakes.
va_list ap; | ||
while (true) { | ||
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */ | ||
std::strcpy(&formatted[0], fmt_str.c_str()); |
Copilot
AI
Aug 9, 2025
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 call to std::strcpy
is copying the format string over the allocated buffer, but then vsnprintf
is called with the same format string. This strcpy call is unnecessary and potentially dangerous as it doesn't respect buffer bounds. Remove this line.
std::strcpy(&formatted[0], fmt_str.c_str()); | |
// Removed unnecessary and potentially dangerous strcpy |
Copilot uses AI. Check for mistakes.
// Get a second point which is different than the first | ||
samples.resize (getSampleSize ()); | ||
for (unsigned int iter = 0; iter < max_sample_checks_; ++iter) | ||
for (unsigned int iter = 0; iter < getMaxSampleSize(); ++iter) |
Copilot
AI
Aug 9, 2025
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 loop condition has changed from max_sample_checks_
to getMaxSampleSize()
. However, getMaxSampleSize()
returns the maximum sample checks (1000), while the function name suggests it returns a sample size. This creates confusion and potential logical errors. The function should be named getMaxSampleChecks()
to match its purpose.
Copilot uses AI. Check for mistakes.
pcl::console::setVerbosityLevel(pcl::console::L_VERBOSE); | ||
|
||
PCL_ALWAYS_STREAM("Always_stream\n"); | ||
PCL_ERROR_STREAM("Error_stream\n") |
Copilot
AI
Aug 9, 2025
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.
Missing semicolon at the end of the statement.
PCL_ERROR_STREAM("Error_stream\n") | |
PCL_ERROR_STREAM("Error_stream\n"); |
Copilot uses AI. Check for mistakes.
|
||
class LogRecorder { | ||
public: | ||
LogRecorder(std::string& string) : strstream(string) { } |
Copilot
AI
Aug 9, 2025
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 constructor is trying to initialize strstream
with a std::string&
, but strstream
is declared as std::stringstream
. This will cause a compilation error. The constructor should be LogRecorder(std::string& string)
and the string should be stored as a reference to be updated later, or the design should be changed.
LogRecorder(std::string& string) : strstream(string) { } | |
LogRecorder(std::string& string) : target_string(string) { } | |
~LogRecorder() { target_string = strstream.str(); } |
Copilot uses AI. Check for mistakes.
I'm in deep water here, but it seems to work :)
Please come with suggestions, before spent too much time in the wrong way.
Could close #5589