Skip to content

Text logger#1360

Open
figueroa1395 wants to merge 24 commits intomainfrom
feature/text-logger
Open

Text logger#1360
figueroa1395 wants to merge 24 commits intomainfrom
feature/text-logger

Conversation

@figueroa1395
Copy link
Copy Markdown
Member

This PR implements a text logger in the core with multithreaded capability. Unit tests are added.

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
@figueroa1395 figueroa1395 self-assigned this Apr 8, 2026
@figueroa1395 figueroa1395 added the feature New feature or request label Apr 8, 2026
@figueroa1395
Copy link
Copy Markdown
Member Author

Tests and probably implementation of the multithreaded text logger with flush handler are not yet in place, hence I put this as a draft. The rest can already be reviewed.

Any input is already appreciated, specially regarding the flush handler (which we don't have to expose yet, but I thought I'd be nice to add).

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
@figueroa1395
Copy link
Copy Markdown
Member Author

I have several TODOs that are meant for me - and the reviewers - to consider before merging. Most of the implementation should now work.

What's missing:

  • Fix the benchmark and run it with the new interface
  • Add an "end-to-end" test to proof the text logger actually logs everything we currently have in the main model.

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Comment thread power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp Outdated
Comment thread power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp Outdated
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
@figueroa1395
Copy link
Copy Markdown
Member Author

Benchmark works fine locally. @mgovers could you run it in windows and WLS from this branch locally to confirm please.

Only missing thing is to add the "end-to-end" test.

Btw, since the text logger adds a timestamp already, given the fact the we use an internal timer already (in current implementation), we would have a "double" timestamp. I went with this decision because I'm unsure if everything will go via the timer in the future, and the timer itself doesn't give us a timestamp like we would like it for the text logger. This is definitely something to look at but I consider it out of scope for when we want to polish the loggers. What do you think?

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
@figueroa1395 figueroa1395 marked this pull request as ready for review April 14, 2026 08:57
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
*/
BatchParameter calculate(Options const& options, MutableDataset const& result_data,
ConstDataset const& update_data) {
info_.clear();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please double-check whether the calculation info still works correctly

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This works, but now the user is responsible for cleaning between different calculations (if desired) if the same logger is used. Hence the benchmark has been adjusted to this.

@mgovers
Copy link
Copy Markdown
Member

mgovers commented Apr 15, 2026

Benchmark works fine locally. @mgovers could you run it in windows and WLS from this branch locally to confirm please.

Done. Both Windows and WSL pass

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Comment on lines +59 to +60
template <LazyLoggingFn Fn> void log(LogEvent tag, Fn&& fn) { log(tag, std::invoke(std::forward<Fn>(fn))); }
template <LazyLoggingFn Fn> void log(Fn&& fn) { log(LogEvent::unknown, std::invoke(std::forward<Fn>(fn))); }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you explain why you went for std::invoke? not against it, just interested

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No particular reason. Doing fn() should suffice here as I require it to be std::invocable, but I guess I went with the more "generic" option.

Comment on lines +62 to +63
!std::derived_from<std::remove_cvref_t<std::tuple_element_t<0, std::tuple<Args...>>>,
MultiThreadedLoggerImpl> &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

so what happens when it's of the form

MultiThreadedLoggerImpl other;
MultiThreadedLoggerImpl current{other, some_other_arg};

?
does it need to be explicitly deleted?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It doesn't need to be explicitly deleted because in MultiThreadedLoggerImpl current{other, some_other_arg} the first argument is also of type MultiThreadedLoggerImpl, which the concept forbids. This way, a MultiThreadedTextLogger recognizes a constructor with a FlushHandler present, but it prevents this constructor from triggering instead of the copy constructor during overload resolution. For instance, it allows:

MultiThreadedLoggerImpl logger{flush_handler};
MultiThreadedLoggerImpl copy{logger};

Comment on lines +68 to +75
using namespace std::chrono;
const auto now = system_clock::now();
const auto sec = floor<seconds>(now);
const auto ms = duration_cast<milliseconds>(now - sec).count();

// Z stands for UTC time zone
// format example: 2026-04-25 14:00:00.000Z
return std::format("{:%F %T}.{:03}Z", sec, ms);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isn't there a direct formatted std::chrono? or is that C++26?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the current way as far as I know (cfr. https://en.cppreference.com/w/cpp/chrono/system_clock/formatter.html).

if (&destination == this) {
return destination; // nothing to do
}
destination.data_ << data_.str();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

prevents unneeded copy

Suggested change
destination.data_ << data_.str();
destination.data_ << data_.view();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The copy is needed here. destination.data_ << data_.view(); would provide a string_view which can dangle.

For example, consider the user provided logger and a batch calculation with multithreading enabled. If we use a view here, once the child threads are done with the calculation, they merge_into the main logger and then they are deleted. That would be a problem.

Signed-off-by: Santiago Figueroa Manrique <figueroa1395@gmail.com>
Comment on lines +59 to +60
template <LazyLoggingFn Fn> void log(LogEvent tag, Fn&& fn) { log(tag, std::invoke(std::forward<Fn>(fn))); }
template <LazyLoggingFn Fn> void log(Fn&& fn) { log(LogEvent::unknown, std::invoke(std::forward<Fn>(fn))); }
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@mgovers @nitbharambe Should we already make this something like

Suggested change
template <LazyLoggingFn Fn> void log(LogEvent tag, Fn&& fn) { log(tag, std::invoke(std::forward<Fn>(fn))); }
template <LazyLoggingFn Fn> void log(Fn&& fn) { log(LogEvent::unknown, std::invoke(std::forward<Fn>(fn))); }
template <LazyLoggingFn Fn> void log(LogEvent tag, Fn&& fn, bool invoke = false ) {
if (!invoke) { return; }
log(tag, std::invoke(std::forward<Fn>(fn))); }
template <LazyLoggingFn Fn> void log(Fn&& fn, , bool invoke = false ) {
if (!invoke) { return; }
log(LogEvent::unknown, std::invoke(std::forward<Fn>(fn))); }

to make it truly lazy already?

@sonarqubecloud
Copy link
Copy Markdown

@figueroa1395 figueroa1395 enabled auto-merge April 17, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants