-
Notifications
You must be signed in to change notification settings - Fork 42
Clean-up main model: Add logger interface #1093
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: main
Are you sure you want to change the base?
Conversation
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
…feature/logger-interface Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
…feature/logger-interface Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
void log(LogEvent tag, double value) override { log_impl(tag, value); } | ||
void log(LogEvent tag, Idx value) override { log_impl(tag, static_cast<double>(value)); } |
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.
Should we add a super generic log(LogEvent tag)
without value to just log the event taking place?
void log(LogEvent tag, double value) override { log_impl(tag, value); } | |
void log(LogEvent tag, Idx value) override { log_impl(tag, static_cast<double>(value)); } | |
void log(LogEvent tag, double value) override { log_impl(tag, value); } | |
void log(LogEvent tag, Idx value) override { log_impl(tag, static_cast<double>(value)); } | |
void log(LogEvent tag) override { log_impl(tag); } |
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.
apologies. i did add this but apparently didn't commit. reopening
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.
OHHH i see, i did add it to the logger interface but not here.
What should the report look like? integer counter (similar to how maximum_num_iterations
is a double but represents a float)?
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.
thinking about it, it is tricky because it depends on the event whether it should time as 0
, or keep track of a counter... maybe it's good to add it when the first event comes into play that should just be an event counter
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 imagine it makes sense that we would sometimes simply want to know that things have happened, without needing to know how long the duration of each thing. For timers it makes perfect sense to associate a value to tell the duration.
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 do think it would be useful. i just don't know how to report.
let's say: we have the following code
CalculationInfo info;
try {
Timer timer{info, LogEvent::some_event_timer}; // current way
CodeHit{info, LogEvent::some_event_started}; // new event
do_something();
CodeHit{info, LogEvent::some_event_hit}; // other new event
CodeHit{info, LogEvent::some_event_hit}; // other new event
} except (...) {
CodeHit{info, LogEvent::some_event_threw_exception}; // another event
}
// what should the output look like here?
auto const report = info.report();
// example output:
// std::map<LogEvent, double>{
// {some_event_timer: 0.2},
// {some_event_started: 1.0}, // 1.0 because it is hit once
// {some_event_hit: 2.0} // 2.0 because it was hit twice
// }
// now let's consider accidentally using a timed log event
CodeHit{info, LogEvent::some_event_timer};
// what should the output look like here?
auto const report = info.report();
// example output
// std::map<LogEvent, double>{
// {some_event_timer, 1.2}, // CodeHit accidentally adds 1 to timer
// {some_event_started, 1.0},
// {some_event_hit, 2.0}
// }
under the hood, what we could do is split up the report into separate maps, e.g.
struct Timing {
double timing{};
};
struct MaximizedInteger {
Idx count{};
};
struct HitCount {
Idx count{};
};
// member variables; maybe combine into one but maybe has performance hit
std::map<LogEvent, Timing> timings;
std::map<LogEvent, MaximizedInteger> num_iter;
std::map<LogEvent, HitCount> hit_count; // or maybe just std::vector<LogEvent>
using Metric = std::variant<Timing, MaximizedInteger, HitCount>;
std::map<LogEvent, Metric> report(); // combines the 3 maps and throws error if there
// are events logged in 2 different maps
feels like overengineering in the short term, though...
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
Signed-off-by: Martijn Govers <Martijn.Govers@Alliander.com>
|
@@ -43,8 +43,26 @@ enum class LogEvent : int16_t { | |||
max_num_iter = 2248, // TODO(mgovers): find other error code | |||
}; | |||
|
|||
struct Logger { |
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.
An interface is a good idea and definitely be incorporated in any design we agree on.
logger->log(tag, values...); | ||
} | ||
} | ||
capturing::into_the_void(std::forward<T>(values)...); |
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.
values can be any count of arguments and maybe not all get used in logging and hence unused ones get sent to void?
std::unique_ptr<Logger> clone() const override { return std::make_unique<NoLogger>(); } | ||
}; | ||
|
||
class LogDispatcher final : public LogDispatch { |
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.
Although Dispatcher is currently not agreed on, I think this is a good experiment that we should refer back to in future when we include new logs.
~LogDispatcher() override = default; | ||
|
||
private: | ||
std::vector<Logger*> loggers_; |
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.
If multithreading related logic for log was to be moved to logger, it can be:
std::vector<std::vector<Logger*>> loggers_;
And there would be a separate aggregation function.
inline Logger& merge_into(Logger& destination, CalculationInfo const& source) { | ||
for (const auto& [tag, value] : source.report()) { | ||
destination.log(tag, value); |
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.
Maybe this merge_into can call a different function merge_thread_into. And we can then have a maximum action for all rather than accumulate
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 base_log needs to be added and not maximized, but some similar logic can be a way to not have sum of threads as output of benchmark
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 believe the merge_into
is a nice example of something where it depends on the event type whether to add or maximize. e.g., the total amount of time spent within a thread is indeed something that could use the
However, that should be a multi-threaded logger decision (e.g., it can take 1 event of a specific event type and convert it into 2 events, one that uses
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.
implementation-wise, that could look like something along the lines of
class MultiThreadedCalculationInfo final : public Logger {
private:
using SubThreadLogger = CalculationInfo; // probably needs to be a wrapper class with mutex and/or thread id knowledge
using MainThreadLogger = CalculationInfo;
public:
template <typename... Args>
void log(Args&&... args)
requires requires(SubThreadLogger logger) {
{ logger.log(args) };
}
{
// assert it is the main thread and/or add a mutex
main_log.log(std::forward<Args>(args));
}
void sync() {
// assert it is the main thread and/or add a mutex
for (auto& thread_log : thread_logs) {
if (thread_log != nullptr) {
sync_thread(*thread_log);
}
}
}
void report() const {
return main_log.report();
}
private:
MainThreadLogger main_log;
std::vector<std::unique_ptr<SubThreadLogger>> thread_logs;
void sync_thread(SubThreadLogger thread_log) {
// guard with mutex
for (auto& [tag, value] : thread_log.report) {
switch(tag) {
case total_time_in_thread:
log(maximum_time_in_thread, value);
log(tag, value);
break;
default:
log(tag, value);
break;
}
}
}
};
NOTE: this is an experiment. While it can be merged as-is, the thought process behind it is more important than the actual code changes