-
Notifications
You must be signed in to change notification settings - Fork 4
Feature/add logger #40
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
WalkthroughAdds a new Logger subsystem (header + implementation) integrated into the build and extends TStringBase with new append/index operators and a larger initial buffer; includes helper logging macros, multi-stream support, and minor copyFrom refactor. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Caller
participant Macros as Logging Macro (file/line)
participant Logger as Logger (singleton)
participant Streams as LogStream(s)
Caller->>Macros: log_xxx(domain, "msg")
Macros->>Logger: log(level, domain, msg, file, line)
alt Logger verbosity allows
Logger->>Logger: format(level, domain, msg, optional trace, datetime?)
loop for each active stream
Logger->>Streams: write(formattedMessage)
end
Streams-->>Caller: (side-effect: output)
else suppressed
Logger-->>Caller: no-op
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration 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.
Actionable comments posted: 10
🧹 Nitpick comments (3)
code/Common/Logger.cpp (2)
256-270
: Implement getDateTime functionalityThe
getDateTime
method is stubbed out and always returns "none", making the timestamp feature non-functional.Would you like me to implement a proper datetime formatting function using standard C++ chrono library? This would provide actual timestamps for log messages when
PrintMode::WithDateTime
is used.
48-55
: Optimize stripFilename for cross-platform compatibilityThe current implementation only handles forward slashes and could be more efficient.
Consider using
std::filesystem
for better cross-platform support:-static ::std::string stripFilename(const ::std::string &filename) { - if (filename.empty()) { - return filename; - } - - ::std::string::size_type pos = filename.find_last_of("/"); - if (pos == ::std::string::npos) { - return filename; - } - const ::std::string strippedName = filename.substr(pos + 1, filename.size() - pos - 1); - - return strippedName; -} +static ::std::string stripFilename(const ::std::string &filename) { + if (filename.empty()) { + return filename; + } + // Handle both forward and backward slashes + ::std::string::size_type pos = filename.find_last_of("/\\"); + if (pos == ::std::string::npos) { + return filename; + } + return filename.substr(pos + 1); +}include/cppcore/Common/Logger.h (1)
207-252
: Fix documentation inconsistenciesThe documentation for the logging macros references
osre_trace
,osre_debug
, etc., but the actual macro names arelog_trace
,log_debug
, etc.Update the documentation to match the actual macro names:
-/// @fn osre_trace +/// @fn log_traceAnd fix the typo "writhe" → "write" in lines 234, 242, and 250.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
CMakeLists.txt
(1 hunks)code/Common/Logger.cpp
(1 hunks)include/cppcore/Common/Logger.h
(1 hunks)include/cppcore/Common/TStringBase.h
(3 hunks)
🔇 Additional comments (2)
CMakeLists.txt (1)
78-79
: Build configuration looks good!The Logger header and source files are correctly added to the
cppcore_common_src
group, maintaining consistency with the project structure.include/cppcore/Common/Logger.h (1)
143-143
: Update default parameter after fixing enum typoAfter fixing the
WhithoutDateTime
typo, update this line as well.
inline void TStringBase<T>::copyFrom(TStringBase<T> &base, const T *ptr, size_t size) { | ||
if (ptr != nullptr) { | ||
T *targetPtr = base.mBuffer; | ||
if (size > 0) { | ||
if (size > base.mCapacity) { | ||
if (base.mStringBuffer != nullptr) { | ||
delete [] base.mStringBuffer; | ||
} | ||
base.mStringBuffer = new T[size]; | ||
base.mCapacity = size; | ||
targetPtr = base.mStringBuffer; | ||
} | ||
memcpy(targetPtr, ptr, size * sizeof(T)); | ||
base.mSize = size; | ||
if (ptr == nullptr) { | ||
return; | ||
} | ||
|
||
T *targetPtr = base.mBuffer; | ||
if (size == 0) { | ||
return; | ||
} | ||
|
||
if (size > base.mCapacity) { | ||
if (base.mStringBuffer != nullptr) { | ||
delete [] base.mStringBuffer; | ||
} | ||
base.mStringBuffer = new T[size]; | ||
base.mCapacity = size; | ||
targetPtr = base.mStringBuffer; | ||
} | ||
memcpy(targetPtr, ptr, size * sizeof(T)); | ||
base.mSize = size; | ||
} |
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.
🛠️ Refactor suggestion
copyFrom doesn't ensure null termination
The copyFrom
method uses memcpy
but doesn't ensure null termination for C-style strings, which could lead to issues when using c_str()
.
Add null termination after copying:
memcpy(targetPtr, ptr, size * sizeof(T));
base.mSize = size;
+ // Ensure null termination
+ if (base.mStringBuffer != nullptr) {
+ base.mStringBuffer[size] = '\0';
+ } else if (size < InitSize) {
+ base.mBuffer[size] = '\0';
+ }
Also, when allocating mStringBuffer
, allocate size + 1
to accommodate the null terminator:
- base.mStringBuffer = new T[size];
+ base.mStringBuffer = new T[size + 1];
- base.mCapacity = size;
+ base.mCapacity = size + 1;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
inline void TStringBase<T>::copyFrom(TStringBase<T> &base, const T *ptr, size_t size) { | |
if (ptr != nullptr) { | |
T *targetPtr = base.mBuffer; | |
if (size > 0) { | |
if (size > base.mCapacity) { | |
if (base.mStringBuffer != nullptr) { | |
delete [] base.mStringBuffer; | |
} | |
base.mStringBuffer = new T[size]; | |
base.mCapacity = size; | |
targetPtr = base.mStringBuffer; | |
} | |
memcpy(targetPtr, ptr, size * sizeof(T)); | |
base.mSize = size; | |
if (ptr == nullptr) { | |
return; | |
} | |
T *targetPtr = base.mBuffer; | |
if (size == 0) { | |
return; | |
} | |
if (size > base.mCapacity) { | |
if (base.mStringBuffer != nullptr) { | |
delete [] base.mStringBuffer; | |
} | |
base.mStringBuffer = new T[size]; | |
base.mCapacity = size; | |
targetPtr = base.mStringBuffer; | |
} | |
memcpy(targetPtr, ptr, size * sizeof(T)); | |
base.mSize = size; | |
} | |
inline void TStringBase<T>::copyFrom(TStringBase<T> &base, const T *ptr, size_t size) { | |
if (ptr == nullptr) { | |
return; | |
} | |
T *targetPtr = base.mBuffer; | |
if (size == 0) { | |
return; | |
} | |
if (size > base.mCapacity) { | |
if (base.mStringBuffer != nullptr) { | |
delete [] base.mStringBuffer; | |
} | |
// Allocate an extra slot for the null terminator | |
base.mStringBuffer = new T[size + 1]; | |
base.mCapacity = size + 1; | |
targetPtr = base.mStringBuffer; | |
} | |
memcpy(targetPtr, ptr, size * sizeof(T)); | |
base.mSize = size; | |
// Ensure null termination | |
if (base.mStringBuffer != nullptr) { | |
base.mStringBuffer[size] = '\0'; | |
} else if (size < InitSize) { | |
base.mBuffer[size] = '\0'; | |
} | |
} |
🤖 Prompt for AI Agents
In include/cppcore/Common/TStringBase.h lines 162 to 182, the copyFrom method
copies data using memcpy but does not ensure null termination for C-style
strings, which can cause issues with c_str(). Fix this by allocating
mStringBuffer with size + 1 when resizing, and after copying, explicitly set the
character at index size to the null terminator to guarantee proper string
termination.
template <class T> | ||
inline TStringBase<T> &TStringBase<T>::operator += (const TStringBase<T> &rhs) { | ||
if (rhs.isEmpty()) { | ||
return *this; | ||
} | ||
|
||
copyFrom(*this, rhs.c_str(), rhs.size()); | ||
|
||
return *this; | ||
} |
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.
operator+= replaces content instead of appending
The operator+=
implementation uses copyFrom
which replaces the entire string content rather than appending to it. This is incorrect behavior for a concatenation operator.
The operator should append the content, not replace it. Consider implementing proper string concatenation logic that:
- Calculates the new total size
- Reallocates if necessary
- Copies the existing content and appends the new content
- Updates the size accordingly
🤖 Prompt for AI Agents
In include/cppcore/Common/TStringBase.h around lines 203 to 212, the operator+=
incorrectly replaces the string content instead of appending. To fix this,
modify the operator to calculate the new total size by adding the current size
and rhs size, reallocate the internal buffer if needed to accommodate the new
size, copy the existing content followed by the rhs content into the buffer, and
update the size member to reflect the concatenated string length before
returning *this.
template <class T> | ||
inline T TStringBase<T>::operator[](size_t index) const { | ||
return mBuffer[index]; | ||
} |
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.
Add bounds checking to operator[]
The index operator doesn't validate the index against the string size, which could lead to out-of-bounds access.
Add bounds checking:
template <class T>
inline T TStringBase<T>::operator[](size_t index) const {
+ if (index >= mSize) {
+ // Handle error - throw exception or return default value
+ throw std::out_of_range("Index out of bounds");
+ }
- return mBuffer[index];
+ return c_str()[index];
}
Also note that the current implementation always returns from mBuffer
, but it should use c_str()
to handle cases where mStringBuffer
is in use.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
template <class T> | |
inline T TStringBase<T>::operator[](size_t index) const { | |
return mBuffer[index]; | |
} | |
template <class T> | |
inline T TStringBase<T>::operator[](size_t index) const { | |
if (index >= mSize) { | |
// Handle error - throw exception or return default value | |
throw std::out_of_range("Index out of bounds"); | |
} | |
return c_str()[index]; | |
} |
🤖 Prompt for AI Agents
In include/cppcore/Common/TStringBase.h around lines 220 to 223, the operator[]
lacks bounds checking and always accesses mBuffer directly, which risks
out-of-bounds errors and ignores cases where mStringBuffer is used. Modify
operator[] to first check if the index is within the valid range of the string
size, throwing an exception or handling the error if not. Then, access the
character via c_str() instead of mBuffer to correctly handle both buffer storage
scenarios.
Fix review findings
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.
Actionable comments posted: 1
♻️ Duplicate comments (2)
code/Common/Logger.cpp (2)
156-160
: Refactor fragile indentation logic.The hardcoded character positions (6, 7) for detecting indentation markers remain fragile and should be refactored as previously suggested.
184-188
: Refactor fragile indentation logic.The hardcoded character positions (6, 7) for detecting indentation markers remain fragile and should be refactored as previously suggested.
🧹 Nitpick comments (4)
code/Common/Logger.cpp (4)
83-106
: Consider thread safety for the singleton pattern.The singleton implementation is correct for single-threaded use. For multi-threaded environments, consider using
std::once_flag
or similar mechanisms to ensure thread-safe initialization.
138-148
: Simplify the verbosity condition.The condition for
info()
can be simplified since it covers most verbosity modes except potentially a hypothetical "Silent" mode.Consider simplifying to:
-if (getVerboseMode() == VerboseMode::Normal || getVerboseMode() == VerboseMode::Verbose || getVerboseMode() == VerboseMode::Debug || getVerboseMode() == VerboseMode::Trace) { +if (getVerboseMode() != VerboseMode::Silent) { // assuming Silent mode existsOr use a numeric comparison if verbosity levels are ordered.
231-236
: Consider ownership semantics for log streams.The
unregisterLogStream
method deletes the stream, which assumes the Logger owns all registered streams. This could cause issues if streams are shared or managed externally. Consider documenting this ownership model or providing separate methods for owned vs. unowned streams.
272-312
: Consider refactoring to reduce code duplication.All helper print functions follow the same pattern. Consider extracting the common logic into a template or helper function to reduce duplication.
Example approach:
template<typename LogMethod> void printHelper(LogMethod method, const String &domain, const String &file, int line, const String &msg) { String message = msg; addTraceInfo(file, line, message); (Logger::getInstance()->*method)(domain, message); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
code/Common/Logger.cpp
(1 hunks)include/cppcore/Common/Logger.h
(1 hunks)include/cppcore/Common/TStringBase.h
(3 hunks)
🪛 GitHub Actions: CMake
code/Common/Logger.cpp
[warning] 253-253: Compiler warning: unused variable ‘Space’ [-Wunused-variable]
🚧 Files skipped from review as they are similar to previous changes (2)
- include/cppcore/Common/TStringBase.h
- include/cppcore/Common/Logger.h
🧰 Additional context used
🪛 GitHub Actions: CMake
code/Common/Logger.cpp
[warning] 253-253: Compiler warning: unused variable ‘Space’ [-Wunused-variable]
🔇 Additional comments (7)
code/Common/Logger.cpp (7)
1-30
: LGTM!The license header and includes are properly structured and appropriate for the logger implementation.
32-69
: Well-implemented helper functions.The static helper functions are correctly implemented with proper error handling:
appendDomain
safely handles empty domainsstripFilename
properly handles edge casesaddTraceInfo
conditionally adds trace information based on verbosity mode
71-82
: LGTM!The AbstractLogStream methods are correctly implemented for managing stream activation state.
108-114
: LGTM!Simple and correct getter/setter implementation for verbosity mode.
239-250
: LGTM!Constructor and destructor are properly implemented, with consistent ownership semantics for managing log streams.
268-270
: LGTM!Simple and correct implementation of standard output stream.
314-315
: LGTM!Proper namespace closing.
|
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.
Actionable comments posted: 1
♻️ Duplicate comments (1)
code/Common/Logger.cpp (1)
180-184
: Refactor fragile marker detection; also fix off-by-oneHardcoded indices (6,7) assume all prefixes are exactly six characters and break easily if formats change. Prior feedback already flagged this. Please centralize the prefix length and compare substrings.
- if (msg.size() > 8) { - if (msg[6] == '=' && msg[7] == '>') { - mIntention += 2; - } - } + // Example: make detection resilient + static constexpr size_t kSeverityPrefixLen = 6; + if (msg.size() >= kSeverityPrefixLen + 2) { + const char* s = msg.c_str() + kSeverityPrefixLen; + if (s[0] == '=' && s[1] == '>') { + mIntention += 2; + } + }Optionally, consider detecting markers at the start of the message portion via a small helper that doesn’t rely on magic numbers. This would also let you freely normalize prefixes (e.g., include a space after “Fatal:”/“Trace:”).
🧹 Nitpick comments (7)
code/Common/Logger.cpp (7)
41-53
: Handle Windows paths in stripFilename (support both '/' and '')Current implementation only looks for '/', so Windows-style paths won't be stripped. Also, the final substr can be simplified.
- static ::std::string stripFilename(const ::std::string &filename) { + static ::std::string stripFilename(const ::std::string &filename) { if (filename.empty()) { return filename; } - ::std::string::size_type pos = filename.find_last_of("/"); + ::std::string::size_type pos = filename.find_last_of("/\\"); if (pos == ::std::string::npos) { return filename; } - const ::std::string strippedName = filename.substr(pos + 1, filename.size() - pos - 1); - - return strippedName; + return filename.substr(pos + 1); }
172-178
: Respect stream activation and remove trailing space before newline
- Only write to active streams.
- " \n" appends a trailing blank before newline; it’s unnecessary.
- logMsg += String(" \n", 2); + logMsg += String("\n", 1); for (size_t i = 0; i < mLogStreams.size(); ++i) { AbstractLogStream *stream = mLogStreams[i]; - if (stream != nullptr) { + if (stream != nullptr && stream->isActive()) { stream->write(logMsg); } }
222-233
: Avoid iterator invalidation/logic pitfalls in unregister; clarify ownership
- After remove(i) the array shrinks; continuing the loop is unnecessary and error-prone if duplicates aren’t expected.
- API appears to transfer ownership to Logger (it deletes streams on unregister and in destructor). Please document this in the header to prevent double-deletes by callers.
for (size_t i = 0; i < mLogStreams.size(); ++i) { if (mLogStreams[i] == logStream) { delete mLogStreams[i]; mLogStreams.remove(i); + break; } }
Would you confirm that ownership is intended to be transferred to Logger on register? If not, we should remove the delete here and only remove the pointer.
245-259
: Implement getDateTime using std::chrono instead of returning "none"Returning a placeholder breaks the WithDateTime mode. A small, portable implementation avoids the commented custom DateTime dependency.
String Logger::getDateTime() { - //static const uint32_t Space = 2; - /* DateTime currentDateTime = DateTime::getCurrentUTCTime(); - std::stringstream stream; - stream.fill('0'); - stream << std::setw(Space) << currentDateTime.getCurrentDay() << "." - << std::setw(Space) << currentDateTime.getCurrentMonth() << "." - << std::setw(Space * 2) << currentDateTime.getCurrentYear() << " " - << std::setw(Space) << currentDateTime.getCurrentHour() << ":" - << std::setw(Space) << currentDateTime.getCurrentMinute() << ":" - << std::setw(Space) << currentDateTime.getCurrentSeconds(); - */ - String todo("none", 4); - return todo; + using clock = std::chrono::system_clock; + std::time_t t = clock::to_time_t(clock::now()); + std::tm tm{}; +#if defined(_WIN32) + gmtime_s(&tm, &t); +#else + gmtime_r(&t, &tm); +#endif + std::ostringstream stream; + stream.fill('0'); + stream << std::setw(2) << tm.tm_mday << "." + << std::setw(2) << (tm.tm_mon + 1) << "." + << std::setw(4) << (tm.tm_year + 1900) << " " + << std::setw(2) << tm.tm_hour << ":" + << std::setw(2) << tm.tm_min << ":" + << std::setw(2) << tm.tm_sec; + const std::string s = stream.str(); + return String(s.c_str(), s.size()); }Additional includes required at top of file:
#include <chrono> #include <ctime>
114-123
: Reduce duplication in severity handlers (trace/debug/info) and normalize prefixesThe three methods share the same pattern. Consider extracting a small helper that builds the “prefix + message + domain” and calls print(), which will also make it easier to standardize spacing after the colon across all severities.
Example idea:
- String makeLog(const char* prefix, const String& msg, const String& domain);
- Then trace/debug/info call that helper with "Trace:", "Dbg: ", "Info: " etc.
This will also simplify future changes to the indentation marker detection.
Also applies to: 125-134, 136-147
149-171
: Consider thread-safety for multi-threaded loggingprint() reads/writes shared state (mIntention, mLogStreams). If Logger is used from multiple threads, races are possible. A single mutex protecting indentation updates and stream iteration would be sufficient.
If thread-safety is a requirement, I can propose a minimal locking strategy (lock in print/register/unregister) that does not leak into the public API.
91-98
: Minor: avoid unnecessary static_cast in getInstance()create()’s return value can simply be ignored; the cast adds noise.
- if (nullptr == sLogger) { - static_cast<void>(create()); - } + if (nullptr == sLogger) { + create(); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
code/Common/Logger.cpp
(1 hunks)include/cppcore/Common/Logger.h
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- include/cppcore/Common/Logger.h
🧰 Additional context used
🧬 Code graph analysis (1)
code/Common/Logger.cpp (1)
include/cppcore/Common/Logger.h (3)
mVerboseMode
(192-194)getVerboseMode
(123-186)mIntention
(193-193)
🔇 Additional comments (1)
code/Common/Logger.cpp (1)
69-79
: LGTM on stream activation methodsactivate/deactivate/isActive are straightforward and correct.
if (msg.size() > 8 && msg[6] == '<' && msg[7] == '=') { | ||
mIntention -= 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.
Fix underflow and boundary bugs in indentation handling
- mIntention is uint32_t and can underflow on “<=” messages, leading to massive indentation and potential performance issues.
- Condition should be size >= 8 to safely access [6] and [7]; size > 8 misses minimal markers like exactly "=>"/"<=".
- if (msg.size() > 8 && msg[6] == '<' && msg[7] == '=') {
- mIntention -= 2;
- }
+ if (msg.size() >= 8 && msg[6] == '<' && msg[7] == '=') {
+ if (mIntention >= 2) {
+ mIntention -= 2;
+ } else {
+ mIntention = 0;
+ }
+ }
String logMsg;
if (0 != mIntention) {
for (uint32_t i = 0; i < mIntention; i++) {
logMsg += ' ';
}
}
Also applies to: 160-163
🤖 Prompt for AI Agents
In code/Common/Logger.cpp around lines 154-156 (and similarly 160-163), the
indentation adjustment reads characters at indices 6 and 7 but uses msg.size() >
8 which incorrectly excludes messages of length 8 and risks out-of-bounds
access; also mIntention is uint32_t and decrementing by 2 can underflow. Change
the size check to msg.size() >= 8 to ensure indices 6 and 7 exist, and guard the
decrement by testing that mIntention >= 2 (or use saturated subtraction) before
subtracting 2 to prevent underflow; apply the same fixes to the other block at
160-163.
Summary by CodeRabbit
New Features
Improvements