5 Commits

Author SHA1 Message Date
Aaron Po
e251e7b2a3 Misc updates 2026-05-20 01:53:08 -04:00
Aaron Po
20742bb613 Update string concatenations to use std::format
add pretty print log
2026-05-20 00:46:57 -04:00
Aaron Po
54a46458a3 Integrate logging channel system
update logging to use logger channel

updates
2026-05-20 00:38:00 -04:00
Aaron Po
29972f54d1 Implement BoundedChannel and multithreaded logging infra 2026-05-20 00:36:44 -04:00
Aaron Po
f35c563dac Update class diagrams 2026-05-20 00:35:51 -04:00
10 changed files with 59 additions and 133 deletions

View File

@@ -249,11 +249,6 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:DEBUG>
)
target_compile_options(biergarten-pipeline PRIVATE
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/tooling/pipeline/src/=
)
# 7. Runtime Assets
configure_file(
${CMAKE_SOURCE_DIR}/locations.json
@@ -265,4 +260,3 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
${CMAKE_SOURCE_DIR}/prompts
${CMAKE_BINARY_DIR}/prompts
)

View File

@@ -51,12 +51,6 @@ class BiergartenPipelineOrchestrator {
* 2. Resolve context for each city using the injected context service
* 3. Generate brewery data for sampled cities
*
* @note STRUCTURAL CONCURRENCY REQUIREMENT:
* When transitioned to a multithreaded design, this method MUST structurally
* enforce that all deployed worker threads are joined before returning (e.g.
* by using std::jthread or a structured concurrency primitive). This ensures
* workers do not attempt to log to a closed channel during application teardown.
*
* @return true if successful, false if not
*/
bool Run();

View File

@@ -43,35 +43,23 @@ enum class PipelinePhase {
Teardown, ///< Finalization and cleanup.
};
/**
* @struct LogDTO
* @brief User-provided subset of log fields. Used to capture call-site info transparently.
*/
struct LogDTO {
LogLevel level;
PipelinePhase phase;
std::string message;
};
/**
* @struct LogEntry
* @brief Single structured log event.
*
* All fields are value types, which keeps transfer across the bounded channel
* simple and avoids shared ownership.
*
* NOTE: timestamp, thread_id, and origin must be populated by ILogger::Log()
* before the entry is dispatched.
*/
struct LogEntry {
/// @brief Timestamp when the entry was created.
std::chrono::system_clock::time_point timestamp{};
std::chrono::system_clock::time_point timestamp =
std::chrono::system_clock::now();
/// @brief Source location where the log call was made.
std::source_location origin{};
std::source_location origin = std::source_location::current();
/// @brief Thread responsible for emitting the log.
std::thread::id thread_id{};
std::thread::id thread_id = std::this_thread::get_id();
/// @brief Severity level of this entry.

View File

@@ -41,10 +41,9 @@ class LogProducer final : public ILogger {
/**
* @brief Queue a log message for asynchronous processing.
*
* Blocks while the channel applies backpressure. This blocking behavior
* under heavy load is an accepted trade-off for simplicity.
* Blocks while the channel applies backpressure.
*/
void DoLog(LogEntry log_entry) override;
void Log(LogEntry log_entry) override;
private:
BoundedChannel<LogEntry>& channel_;

View File

@@ -34,31 +34,9 @@ class ILogger {
/**
* @brief Submit a log message to the logging subsystem.
*
* @param payload User-provided log data (level, phase, message).
* @param origin Auto-captured source location of the call site.
* @param log_entry Structured log entry data.
*/
void Log(LogDTO payload,
std::source_location origin = std::source_location::current(),
std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now(),
std::thread::id thread_id = std::this_thread::get_id()) {
LogEntry entry;
entry.timestamp = timestamp;
entry.thread_id = thread_id;
entry.level = payload.level;
entry.phase = payload.phase;
entry.message = std::move(payload.message);
entry.origin = origin;
DoLog(std::move(entry));
}
protected:
/**
* @brief Underlying implementation to transport the log entry.
*
* Implementations must be thread-safe as DoLog can be called concurrently
* from multiple worker threads.
*/
virtual void DoLog(LogEntry log_entry) = 0;
virtual void Log(LogEntry log_entry) = 0;
};
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOGGER_H_

View File

@@ -71,10 +71,10 @@ std::optional<ApplicationOptions> ParseArguments(
return usage_stream.str();
})();
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Info,
logger->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = title});
logger->Log(LogDTO{.level = LogLevel::Info,
logger->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = usage});
}
@@ -90,7 +90,7 @@ std::optional<ApplicationOptions> ParseArguments(
std::stringstream help_stream;
help_stream << "\n" << desc;
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Info,
logger->Log({.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = help_stream.str()});
}
@@ -113,7 +113,7 @@ std::optional<ApplicationOptions> ParseArguments(
const std::string msg =
"Invalid arguments: --mocked and --model are mutually exclusive";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
logger->Log({.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
@@ -126,7 +126,7 @@ std::optional<ApplicationOptions> ParseArguments(
const std::string msg =
"Invalid arguments: either --mocked or --model must be specified";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
logger->Log({.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
@@ -170,7 +170,7 @@ std::optional<ApplicationOptions> ParseArguments(
const std::string msg =
"Sampling parameters are ignored when using --mocked";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Warn,
logger->Log({.level = LogLevel::Warn,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
@@ -196,7 +196,7 @@ std::optional<ApplicationOptions> ParseArguments(
std::string("Failed to parse command-line arguments: ") +
exception.what();
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
logger->Log({.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
}
@@ -205,7 +205,7 @@ std::optional<ApplicationOptions> ParseArguments(
const std::string msg =
"Failed to parse command-line arguments: unknown error";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
logger->Log({.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
}

View File

@@ -204,7 +204,7 @@ int main(const int argc, char** argv) {
return shutdown(EXIT_SUCCESS);
} catch (const std::exception& exception) {
const LogDTO log_entry{.level = LogLevel::Error,
const LogEntry log_entry{.level = LogLevel::Error,
.phase = PipelinePhase::Teardown,
.message = exception.what()};
if (log_producer) {

View File

@@ -13,29 +13,6 @@
#include "concurrency/bounded_channel.h"
#include "services/logging/log_entry.h"
namespace {
[[nodiscard]] constexpr std::string_view PipelinePhaseToString(
PipelinePhase phase) {
switch (phase) {
case PipelinePhase::Startup:
return "Startup";
case PipelinePhase::UserGeneration:
return "User Generation";
case PipelinePhase::BreweryAndBeerGeneration:
return "Brewery & Beer Gen";
case PipelinePhase::CheckinGeneration:
return "Checkin Gen";
case PipelinePhase::RatingGeneration:
return "Rating Gen";
case PipelinePhase::FollowGeneration:
return "Follow Gen";
case PipelinePhase::Teardown:
return "Teardown";
}
return "Unknown";
}
} // namespace
LogDispatcher::LogDispatcher(BoundedChannel<LogEntry>& channel)
: channel_(channel) {}
@@ -51,11 +28,7 @@ void LogDispatcher::Run() {
const auto& log = entry.value();
logger->log(ToSpdlogLevel(log.level),
"{:<20} │ thread: {:016x} │ [{}:{}] │ {}",
PipelinePhaseToString(log.phase),
std::hash<std::thread::id>{}(log.thread_id),
log.origin.file_name(), log.origin.line(), log.message);
logger->log(ToSpdlogLevel(log.level), log.message);
}
}

View File

@@ -16,4 +16,4 @@
LogProducer::LogProducer(BoundedChannel<LogEntry>& channel)
: channel_(channel) {}
void LogProducer::DoLog(LogEntry entry) { channel_.Send(std::move(entry)); }
void LogProducer::Log(LogEntry entry) { channel_.Send(std::move(entry)); }