14 <<
"t=" << std::fixed << std::setprecision(1) <<
event.timestamp()
15 <<
" [" <<
event.source_id() <<
"] "
33 : filename_(filename) {
34 file_.open(filename, std::ios::out | std::ios::trunc);
35 if (!
file_.is_open()) {
36 throw std::runtime_error(
"Failed to open log file: " + filename);
46 std::string escape_json(
const std::string& s) {
50 case '"': o <<
"\\\"";
break;
51 case '\\': o <<
"\\\\";
break;
52 case '\b': o <<
"\\b";
break;
53 case '\f': o <<
"\\f";
break;
54 case '\n': o <<
"\\n";
break;
55 case '\r': o <<
"\\r";
break;
56 case '\t': o <<
"\\t";
break;
58 if (
'\x00' <= c && c <=
'\x1f') {
59 o <<
"\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c);
70 if (!
file_.is_open())
return;
74 <<
"\"timestamp\":" << std::fixed << std::setprecision(6) <<
event.timestamp() <<
","
76 <<
"\"type\":\"" <<
event.type_name() <<
"\","
77 <<
"\"source_id\":\"" << escape_json(event.
source_id()) <<
"\"";
80 if (
auto* income =
dynamic_cast<const IncomeEvent*
>(&event)) {
81 file_ <<
",\"amount\":" << income->amount()
82 <<
",\"category\":\"" << escape_json(income->category()) <<
"\""
83 <<
",\"target_account\":\"" << escape_json(income->target_account()) <<
"\"";
84 }
else if (
auto* expense =
dynamic_cast<const ExpenseEvent*
>(&event)) {
85 file_ <<
",\"amount\":" << expense->amount()
86 <<
",\"category\":\"" << escape_json(expense->category()) <<
"\""
87 <<
",\"target_account\":\"" << escape_json(expense->target_account()) <<
"\"";
88 }
else if (
auto* asset =
dynamic_cast<const AssetEvent*
>(&event)) {
89 file_ <<
",\"asset_id\":\"" << escape_json(asset->asset_id()) <<
"\""
90 <<
",\"value\":" << asset->value()
91 <<
",\"delta\":" << asset->delta();
92 }
else if (
auto* liability =
dynamic_cast<const LiabilityEvent*
>(&event)) {
93 file_ <<
",\"liability_id\":\"" << escape_json(liability->liability_id()) <<
"\""
94 <<
",\"value\":" << liability->value()
95 <<
",\"delta\":" << liability->delta();
96 }
else if (
auto* account =
dynamic_cast<const AccountEvent*
>(&event)) {
97 file_ <<
",\"account_id\":\"" << escape_json(account->account_id()) <<
"\""
98 <<
",\"balance\":" << account->balance()
99 <<
",\"delta\":" << account->delta()
100 <<
",\"reason\":\"" << escape_json(account->reason()) <<
"\"";
101 }
else if (
auto* transfer =
dynamic_cast<const TransferEvent*
>(&event)) {
102 file_ <<
",\"from_account\":\"" << escape_json(transfer->from_account()) <<
"\""
103 <<
",\"to_account\":\"" << escape_json(transfer->to_account()) <<
"\""
104 <<
",\"amount\":" << transfer->amount()
105 <<
",\"reason\":\"" << escape_json(transfer->reason()) <<
"\"";
112 if (
file_.is_open()) {
118 if (
file_.is_open()) {
Event emitted when an account balance changes.
Event emitted when asset value changes.
void flush() override
Flush any buffered output.
void write(const Event &event, LogLevel level) override
Write an event to the output.
void close() override
Close the writer and release resources.
Base class for all typed events in the simulation.
const std::string & source_id() const
Source model ID that generated this event.
Event emitted when an expense occurs.
Event emitted when income is received.
void close() override
Close the writer and release resources.
void write(const Event &event, LogLevel level) override
Write an event to the output.
JsonWriter(const std::string &filename)
void flush() override
Flush any buffered output.
Event emitted when liability value changes.
Event emitted when funds are transferred between accounts.
const char * log_level_to_string(LogLevel level)
Convert LogLevel to string representation.
LogLevel
Log severity levels for filtering.