mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-17 09:47:23 +00:00
Add multithreaded logging infrastructure for preparation for future designs (#225)
* Update class diagrams * Implement BoundedChannel and multithreaded logging infra * Integrate logging channel system * Update string concatenations to use std::format * Add pretty print log
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file biergarten_pipeline_orchestrator/biergarten_pipeline_orchestrator.cc
|
||||
* @brief BiergartenDataGenerator constructor implementation.
|
||||
*/
|
||||
|
||||
#include "biergarten_pipeline_orchestrator.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
BiergartenPipelineOrchestrator::BiergartenPipelineOrchestrator(
|
||||
std::shared_ptr<ILogger> logger,
|
||||
std::unique_ptr<IEnrichmentService> context_service,
|
||||
std::unique_ptr<DataGenerator> generator,
|
||||
std::unique_ptr<IExportService> exporter,
|
||||
const ApplicationOptions &app_options)
|
||||
: logger_(std::move(logger)),
|
||||
context_service_(std::move(context_service)),
|
||||
generator_(std::move(generator)),
|
||||
exporter_(std::move(exporter)),
|
||||
application_options_(app_options) {}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file biergarten_pipeline_orchestrator/generate_breweries.cc
|
||||
* @brief BiergartenDataGenerator::GenerateBreweries() implementation.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
|
||||
#include "biergarten_pipeline_orchestrator.h"
|
||||
#include "services/logging/logger.h"
|
||||
|
||||
void BiergartenPipelineOrchestrator::GenerateBreweries(
|
||||
std::span<const EnrichedCity> cities) {
|
||||
logger_->Log({.level = LogLevel::Info,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message = "=== SAMPLE BREWERY GENERATION ==="});
|
||||
|
||||
generated_breweries_.clear();
|
||||
size_t skipped_count = 0;
|
||||
size_t export_failed_count = 0;
|
||||
|
||||
for (const auto& [location, region_context] : cities) {
|
||||
try {
|
||||
const BreweryResult brewery =
|
||||
generator_->GenerateBrewery(location, region_context);
|
||||
|
||||
const GeneratedBrewery gen{.location = location, .brewery = brewery};
|
||||
|
||||
generated_breweries_.push_back(gen);
|
||||
|
||||
try {
|
||||
exporter_->ProcessRecord(gen);
|
||||
} catch (const std::exception& export_exception) {
|
||||
++export_failed_count;
|
||||
|
||||
logger_->Log(
|
||||
{.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message =
|
||||
std::format("[Pipeline] Generated brewery for '{}' ({}) but SQLite export failed: {}",
|
||||
location.city, location.country, export_exception.what())});
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
++skipped_count;
|
||||
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message = std::format("[Pipeline] Skipping city '{}' ({}): brewery generation failed: {}",
|
||||
location.city, location.country, e.what())});
|
||||
}
|
||||
}
|
||||
|
||||
if (skipped_count > 0) {
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::BreweryAndBeerGeneration,
|
||||
.message = std::format(
|
||||
"[Pipeline] Skipped {} city/cities due to generation errors",
|
||||
skipped_count)});
|
||||
}
|
||||
|
||||
if (export_failed_count > 0) {
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::Teardown,
|
||||
.message = std::format(
|
||||
"[Pipeline] Failed to export {} generated brewery/breweries to SQLite",
|
||||
export_failed_count)});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @file biergarten_pipeline_orchestrator/log_results.cc
|
||||
* @brief BiergartenDataGenerator::LogResults() implementation.
|
||||
*/
|
||||
|
||||
#include <boost/json/array.hpp>
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
|
||||
#include "../../includes/json_handling/pretty_print.h"
|
||||
#include "biergarten_pipeline_orchestrator.h"
|
||||
#include "services/logging/logger.h"
|
||||
void BiergartenPipelineOrchestrator::LogResults() const {
|
||||
boost::json::array output;
|
||||
|
||||
for (const auto& [location, brewery] : generated_breweries_) {
|
||||
output.push_back(boost::json::object{
|
||||
{"name_en", brewery.name_en},
|
||||
{"description_en", brewery.description_en},
|
||||
{"name_local", brewery.name_local},
|
||||
{"description_local", brewery.description_local},
|
||||
{"location", boost::json::object{
|
||||
{"city", location.city},
|
||||
{"country", location.country},
|
||||
{"state_province", location.state_province},
|
||||
{"iso3166_2", location.iso3166_2},
|
||||
{"latitude", location.latitude},
|
||||
{"longitude", location.longitude},
|
||||
}}});
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
PrettyPrint(oss, output);
|
||||
logger_->Log({.level = LogLevel::Info,
|
||||
.phase = PipelinePhase::Teardown,
|
||||
.message = oss.str()});
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file biergarten_pipeline_orchestrator/query_cities_with_countries.cc
|
||||
* @brief BiergartenDataGenerator::QueryCitiesWithCountries() implementation.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
|
||||
#include "biergarten_pipeline_orchestrator.h"
|
||||
#include "json_handling/json_loader.h"
|
||||
#include "services/logging/logger.h"
|
||||
|
||||
std::vector<Location>
|
||||
BiergartenPipelineOrchestrator::QueryCitiesWithCountries() {
|
||||
logger_->Log({.level = LogLevel::Info,
|
||||
.phase = PipelinePhase::Startup,
|
||||
.message = "=== GEOGRAPHIC DATA OVERVIEW ==="});
|
||||
|
||||
const std::filesystem::path locations_path = "locations.json";
|
||||
|
||||
auto all_locations = JsonLoader::LoadLocations(locations_path, logger_);
|
||||
|
||||
const size_t sample_count = std::min(
|
||||
static_cast<size_t>(application_options_.pipeline.location_count),
|
||||
all_locations.size());
|
||||
|
||||
const auto sample_count_signed =
|
||||
static_cast<std::iter_difference_t<decltype(all_locations.cbegin())>>(
|
||||
sample_count);
|
||||
|
||||
std::vector<Location> sampled_locations;
|
||||
sampled_locations.reserve(sample_count);
|
||||
|
||||
std::random_device random_generator;
|
||||
std::ranges::sample(all_locations, std::back_inserter(sampled_locations),
|
||||
sample_count_signed, random_generator);
|
||||
|
||||
logger_->Log({.level = LogLevel::Info,
|
||||
.phase = PipelinePhase::Startup,
|
||||
.message = std::format(" Locations available: {}",
|
||||
all_locations.size())});
|
||||
logger_->Log({.level = LogLevel::Info,
|
||||
.phase = PipelinePhase::Startup,
|
||||
.message = std::format(" Sampled locations: {}",
|
||||
sampled_locations.size())});
|
||||
return sampled_locations;
|
||||
}
|
||||
63
tooling/pipeline/src/biergarten_pipeline_orchestrator/run.cc
Normal file
63
tooling/pipeline/src/biergarten_pipeline_orchestrator/run.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file biergarten_pipeline_orchestrator/run.cc
|
||||
* @brief BiergartenDataGenerator::Run() implementation.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <utility>
|
||||
|
||||
#include "biergarten_pipeline_orchestrator.h"
|
||||
#include "services/logging/logger.h"
|
||||
|
||||
bool BiergartenPipelineOrchestrator::Run() {
|
||||
try {
|
||||
exporter_->Initialize();
|
||||
|
||||
std::vector<Location> cities = QueryCitiesWithCountries();
|
||||
std::vector<EnrichedCity> enriched;
|
||||
enriched.reserve(cities.size());
|
||||
|
||||
size_t skipped_count = 0;
|
||||
for (auto& city : cities) {
|
||||
try {
|
||||
std::string region_context = context_service_->GetLocationContext(city);
|
||||
// logger_->Log(LogLevel::Debug, PipelinePhase::UserGeneration,
|
||||
// "[Pipeline] Context for '" + city.city + "' (" +
|
||||
// city.iso3166_2 + ") gathered:\n" + region_context);
|
||||
|
||||
enriched.push_back(
|
||||
EnrichedCity{.location = std::move(city),
|
||||
.region_context = std::move(region_context)});
|
||||
} catch (const std::exception& exception) {
|
||||
++skipped_count;
|
||||
logger_->Log(
|
||||
{.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::UserGeneration,
|
||||
.message = std::format(
|
||||
"[Pipeline] Skipping city '{}' ({}): context lookup failed: {}",
|
||||
city.city, city.country, exception.what())});
|
||||
}
|
||||
}
|
||||
|
||||
if (skipped_count > 0) {
|
||||
logger_->Log({.level = LogLevel::Warn,
|
||||
.phase = PipelinePhase::UserGeneration,
|
||||
.message = std::format(
|
||||
"[Pipeline] Skipped {} city/cities due to context lookup errors",
|
||||
skipped_count)});
|
||||
}
|
||||
|
||||
this->GenerateBreweries(enriched);
|
||||
exporter_->Finalize();
|
||||
this->LogResults();
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
logger_->Log(
|
||||
{.level = LogLevel::Error,
|
||||
.phase = PipelinePhase::Teardown,
|
||||
.message =
|
||||
std::format("Pipeline execution failed with error: {}", e.what())});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user