mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-06-01 01:54:00 +00:00
Compare commits
5 Commits
main-2.0
...
e251e7b2a3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e251e7b2a3 | ||
|
|
20742bb613 | ||
|
|
54a46458a3 | ||
|
|
29972f54d1 | ||
|
|
f35c563dac |
@@ -249,11 +249,6 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
|||||||
$<$<CONFIG:Debug>:DEBUG>
|
$<$<CONFIG:Debug>:DEBUG>
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(biergarten-pipeline PRIVATE
|
|
||||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/tooling/pipeline/src/=
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# 7. Runtime Assets
|
# 7. Runtime Assets
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_SOURCE_DIR}/locations.json
|
${CMAKE_SOURCE_DIR}/locations.json
|
||||||
@@ -265,4 +260,3 @@ add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
|||||||
${CMAKE_SOURCE_DIR}/prompts
|
${CMAKE_SOURCE_DIR}/prompts
|
||||||
${CMAKE_BINARY_DIR}/prompts
|
${CMAKE_BINARY_DIR}/prompts
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -51,12 +51,6 @@ class BiergartenPipelineOrchestrator {
|
|||||||
* 2. Resolve context for each city using the injected context service
|
* 2. Resolve context for each city using the injected context service
|
||||||
* 3. Generate brewery data for sampled cities
|
* 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
|
* @return true if successful, false if not
|
||||||
*/
|
*/
|
||||||
bool Run();
|
bool Run();
|
||||||
|
|||||||
@@ -43,35 +43,23 @@ enum class PipelinePhase {
|
|||||||
Teardown, ///< Finalization and cleanup.
|
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
|
* @struct LogEntry
|
||||||
* @brief Single structured log event.
|
* @brief Single structured log event.
|
||||||
*
|
*
|
||||||
* All fields are value types, which keeps transfer across the bounded channel
|
* All fields are value types, which keeps transfer across the bounded channel
|
||||||
* simple and avoids shared ownership.
|
* simple and avoids shared ownership.
|
||||||
*
|
|
||||||
* NOTE: timestamp, thread_id, and origin must be populated by ILogger::Log()
|
|
||||||
* before the entry is dispatched.
|
|
||||||
*/
|
*/
|
||||||
struct LogEntry {
|
struct LogEntry {
|
||||||
/// @brief Timestamp when the entry was created.
|
/// @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.
|
/// @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.
|
/// @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.
|
/// @brief Severity level of this entry.
|
||||||
|
|||||||
@@ -41,10 +41,9 @@ class LogProducer final : public ILogger {
|
|||||||
/**
|
/**
|
||||||
* @brief Queue a log message for asynchronous processing.
|
* @brief Queue a log message for asynchronous processing.
|
||||||
*
|
*
|
||||||
* Blocks while the channel applies backpressure. This blocking behavior
|
* Blocks while the channel applies backpressure.
|
||||||
* under heavy load is an accepted trade-off for simplicity.
|
|
||||||
*/
|
*/
|
||||||
void DoLog(LogEntry log_entry) override;
|
void Log(LogEntry log_entry) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BoundedChannel<LogEntry>& channel_;
|
BoundedChannel<LogEntry>& channel_;
|
||||||
|
|||||||
@@ -34,31 +34,9 @@ class ILogger {
|
|||||||
/**
|
/**
|
||||||
* @brief Submit a log message to the logging subsystem.
|
* @brief Submit a log message to the logging subsystem.
|
||||||
*
|
*
|
||||||
* @param payload User-provided log data (level, phase, message).
|
* @param log_entry Structured log entry data.
|
||||||
* @param origin Auto-captured source location of the call site.
|
|
||||||
*/
|
*/
|
||||||
void Log(LogDTO payload,
|
virtual void Log(LogEntry log_entry) = 0;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOGGER_H_
|
#endif // BIERGARTEN_PIPELINE_INCLUDES_SERVICES_LOGGING_LOGGER_H_
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
return usage_stream.str();
|
return usage_stream.str();
|
||||||
})();
|
})();
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Info,
|
logger->Log({.level = LogLevel::Info,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = title});
|
.message = title});
|
||||||
logger->Log(LogDTO{.level = LogLevel::Info,
|
logger->Log({.level = LogLevel::Info,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = usage});
|
.message = usage});
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
std::stringstream help_stream;
|
std::stringstream help_stream;
|
||||||
help_stream << "\n" << desc;
|
help_stream << "\n" << desc;
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Info,
|
logger->Log({.level = LogLevel::Info,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = help_stream.str()});
|
.message = help_stream.str()});
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
const std::string msg =
|
const std::string msg =
|
||||||
"Invalid arguments: --mocked and --model are mutually exclusive";
|
"Invalid arguments: --mocked and --model are mutually exclusive";
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Error,
|
logger->Log({.level = LogLevel::Error,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = msg});
|
.message = msg});
|
||||||
} else {
|
} else {
|
||||||
@@ -126,7 +126,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
const std::string msg =
|
const std::string msg =
|
||||||
"Invalid arguments: either --mocked or --model must be specified";
|
"Invalid arguments: either --mocked or --model must be specified";
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Error,
|
logger->Log({.level = LogLevel::Error,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = msg});
|
.message = msg});
|
||||||
} else {
|
} else {
|
||||||
@@ -170,7 +170,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
const std::string msg =
|
const std::string msg =
|
||||||
"Sampling parameters are ignored when using --mocked";
|
"Sampling parameters are ignored when using --mocked";
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Warn,
|
logger->Log({.level = LogLevel::Warn,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = msg});
|
.message = msg});
|
||||||
} else {
|
} else {
|
||||||
@@ -196,7 +196,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
std::string("Failed to parse command-line arguments: ") +
|
std::string("Failed to parse command-line arguments: ") +
|
||||||
exception.what();
|
exception.what();
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Error,
|
logger->Log({.level = LogLevel::Error,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = msg});
|
.message = msg});
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ std::optional<ApplicationOptions> ParseArguments(
|
|||||||
const std::string msg =
|
const std::string msg =
|
||||||
"Failed to parse command-line arguments: unknown error";
|
"Failed to parse command-line arguments: unknown error";
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger->Log(LogDTO{.level = LogLevel::Error,
|
logger->Log({.level = LogLevel::Error,
|
||||||
.phase = PipelinePhase::Startup,
|
.phase = PipelinePhase::Startup,
|
||||||
.message = msg});
|
.message = msg});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ int main(const int argc, char** argv) {
|
|||||||
return shutdown(EXIT_SUCCESS);
|
return shutdown(EXIT_SUCCESS);
|
||||||
|
|
||||||
} catch (const std::exception& exception) {
|
} catch (const std::exception& exception) {
|
||||||
const LogDTO log_entry{.level = LogLevel::Error,
|
const LogEntry log_entry{.level = LogLevel::Error,
|
||||||
.phase = PipelinePhase::Teardown,
|
.phase = PipelinePhase::Teardown,
|
||||||
.message = exception.what()};
|
.message = exception.what()};
|
||||||
if (log_producer) {
|
if (log_producer) {
|
||||||
|
|||||||
@@ -13,29 +13,6 @@
|
|||||||
#include "concurrency/bounded_channel.h"
|
#include "concurrency/bounded_channel.h"
|
||||||
#include "services/logging/log_entry.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)
|
LogDispatcher::LogDispatcher(BoundedChannel<LogEntry>& channel)
|
||||||
: channel_(channel) {}
|
: channel_(channel) {}
|
||||||
|
|
||||||
@@ -51,11 +28,7 @@ void LogDispatcher::Run() {
|
|||||||
|
|
||||||
const auto& log = entry.value();
|
const auto& log = entry.value();
|
||||||
|
|
||||||
logger->log(ToSpdlogLevel(log.level),
|
logger->log(ToSpdlogLevel(log.level), log.message);
|
||||||
"{:<20} │ thread: {:016x} │ [{}:{}] │ {}",
|
|
||||||
PipelinePhaseToString(log.phase),
|
|
||||||
std::hash<std::thread::id>{}(log.thread_id),
|
|
||||||
log.origin.file_name(), log.origin.line(), log.message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,4 @@
|
|||||||
LogProducer::LogProducer(BoundedChannel<LogEntry>& channel)
|
LogProducer::LogProducer(BoundedChannel<LogEntry>& channel)
|
||||||
: channel_(channel) {}
|
: channel_(channel) {}
|
||||||
|
|
||||||
void LogProducer::DoLog(LogEntry entry) { channel_.Send(std::move(entry)); }
|
void LogProducer::Log(LogEntry entry) { channel_.Send(std::move(entry)); }
|
||||||
|
|||||||
Reference in New Issue
Block a user