Skip to content

Commit

Permalink
first implementation of sql_auditing
Browse files Browse the repository at this point in the history
  • Loading branch information
juandent committed Dec 20, 2024
1 parent 4d4a76c commit f32b3e0
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 2 deletions.
1 change: 1 addition & 0 deletions dev/error_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace sqlite_orm {
index_is_out_of_bounds,
value_is_null,
no_tables_specified,
failure_to_init_logfile,
};
}

Expand Down
86 changes: 86 additions & 0 deletions dev/sql_auditing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma once
#include <fstream>
#include "error_code.h"
#include <chrono>
#include <ctime> // For std::localtime and std::tm
#include <iomanip> // For std::put_time
#include <string>

enum class auditing_behavior : signed char { OFF = 0, ON = 1 };

class sql_auditor_settings;

class sql_auditor {

std::ofstream log_file;
friend class sql_auditor_settings;
sql_auditor();
void open();
inline static sql_auditor& auditor() {
static sql_auditor auditor{};
return auditor;
}

public:
static void log(const std::string& message);
};

class sql_auditor_settings {
std::string destination_file = "sql_auditor.txt";
friend class sql_auditor;
sql_auditor_settings() {}
inline static sql_auditor_settings& settings() {
static sql_auditor_settings sql_settings;
return sql_settings;
}
auditing_behavior behavior = auditing_behavior::ON;
std::string format_str = "%Y-%m-%d %H:%M:%S";

public:
static void set_destination_file(const std::string& filename);
static void set_behavior(auditing_behavior behavior) {
settings().behavior = behavior;
}
static void set_format(const std::string& format_str) {
settings().format_str = format_str;
}
};

inline sql_auditor::sql_auditor() {
open();
}

inline void sql_auditor_settings::set_destination_file(const std::string& filename) {
settings().destination_file = filename;
sql_auditor::auditor().open();
}

inline void sql_auditor::open() {
using namespace std;
if(!log_file.is_open()) {
log_file.open(sql_auditor_settings::settings().destination_file, ios::trunc | ios::out);
if(!log_file.good()) {
throw std::system_error{sqlite_orm::orm_error_code::failure_to_init_logfile};
}
}
}

inline void sql_auditor::log(const std::string& message) {
// guard and exit if off
if(sql_auditor_settings::settings().behavior == auditing_behavior::OFF)
return;

// would use format if C++ 20
auto now = std::chrono::system_clock::now();

std::time_t now_time = std::chrono::system_clock::to_time_t(now);

// Convert to local time (std::tm structure)
// WARNING: localtime is not thread safe!
std::tm local_time = *std::localtime(&now_time);

// Print the local time in a human-readable format
auditor().log_file << "@: " << std::put_time(&local_time, sql_auditor_settings::settings().format_str.c_str())
<< " = ";
auditor().log_file << message << std::endl;
}
6 changes: 5 additions & 1 deletion dev/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#include <utility> // std::move

#include "error_code.h"
// Before clang-format 17

#include "sql_auditing.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -62,6 +63,7 @@ namespace sqlite_orm {
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
return stmt;
}

Expand All @@ -70,6 +72,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand All @@ -80,6 +83,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand Down
93 changes: 92 additions & 1 deletion include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2863,6 +2863,7 @@ namespace sqlite_orm {
index_is_out_of_bounds,
value_is_null,
no_tables_specified,
failure_to_init_logfile,
};
}

Expand Down Expand Up @@ -13447,7 +13448,94 @@ namespace sqlite_orm {

// #include "error_code.h"

// Before clang-format 17
// #include "sql_auditing.h"

#include <fstream>
// #include "error_code.h"

#include <chrono>
#include <ctime> // For std::localtime and std::tm
#include <iomanip> // For std::put_time
#include <string>

enum class auditing_behavior : signed char { OFF = 0, ON = 1 };

class sql_auditor_settings;

class sql_auditor {

std::ofstream log_file;
friend class sql_auditor_settings;
sql_auditor();
void open();
inline static sql_auditor& auditor() {
static sql_auditor auditor{};
return auditor;
}

public:
static void log(const std::string& message);
};

class sql_auditor_settings {
std::string destination_file = "sql_auditor.txt";
friend class sql_auditor;
sql_auditor_settings() {}
inline static sql_auditor_settings& settings() {
static sql_auditor_settings sql_settings;
return sql_settings;
}
auditing_behavior behavior = auditing_behavior::ON;
std::string format_str = "%Y-%m-%d %H:%M:%S";

public:
static void set_destination_file(const std::string& filename);
static void set_behavior(auditing_behavior behavior) {
settings().behavior = behavior;
}
static void set_format(const std::string& format_str) {
settings().format_str = format_str;
}
};

inline sql_auditor::sql_auditor() {
open();
}

inline void sql_auditor_settings::set_destination_file(const std::string& filename) {
settings().destination_file = filename;
sql_auditor::auditor().open();
}

inline void sql_auditor::open() {
using namespace std;
if(!log_file.is_open()) {
log_file.open(sql_auditor_settings::settings().destination_file, ios::trunc | ios::out);
if(!log_file.good()) {
throw std::system_error{sqlite_orm::orm_error_code::failure_to_init_logfile};
}
}
}

inline void sql_auditor::log(const std::string& message) {
// guard and exit if off
if(sql_auditor_settings::settings().behavior == auditing_behavior::OFF)
return;

// would use format if C++ 20
auto now = std::chrono::system_clock::now();

std::time_t now_time = std::chrono::system_clock::to_time_t(now);

// Convert to local time (std::tm structure)
// WARNING: localtime is not thread safe!
std::tm local_time = *std::localtime(&now_time);

// Print the local time in a human-readable format
auditor().log_file << "@: " << std::put_time(&local_time, sql_auditor_settings::settings().format_str.c_str())
<< " = ";
auditor().log_file << message << std::endl;
}

namespace sqlite_orm {

Expand Down Expand Up @@ -13504,6 +13592,7 @@ namespace sqlite_orm {
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
return stmt;
}

Expand All @@ -13512,6 +13601,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand All @@ -13522,6 +13612,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand Down
8 changes: 8 additions & 0 deletions tests/prepared_statement_tests/select.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ TEST_CASE("Prepared select") {

auto filename = "prepared.sqlite";
remove(filename);

#define JD_AUDITING_SETTINGS
#ifdef JD_AUDITING_SETTINGS
sql_auditor_settings::set_destination_file("log.txt");
sql_auditor_settings::set_behavior(auditing_behavior::ON);
sql_auditor_settings::set_format("%H:%M:%S");
#endif

auto storage = make_storage(filename,
make_index("user_id_index", &User::id),
make_table("users",
Expand Down

0 comments on commit f32b3e0

Please sign in to comment.