Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sql auditing #1370

Closed
wants to merge 3 commits into from
Closed

Conversation

juandent
Copy link
Contributor

First implementation of sql_auditing -- testing use of it

@fnc12 fnc12 self-requested a review December 22, 2024 12:57
@fnc12
Copy link
Owner

fnc12 commented Dec 22, 2024

architecture of current implementation of sql auditing can't be combined with the project's architecture which (projects) have logs system like boost::log or custom written ones. How this approach is going to wok in projects which use boost::log for example?

dev/sql_auditing.h Outdated Show resolved Hide resolved
std::string destination_file = "sql_auditor.txt";
friend class sql_auditor;
sql_auditor_settings() {}
inline static sql_auditor_settings& settings() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the app has >1 storages and author wants to write logs to different files for different storages?

Copy link
Contributor Author

@juandent juandent Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the destination file could be part of the storage_base object but then we could not log the initial instructions (i.e. make_storage) -- what if we want to see the SQL emitted by make_storage()? What do you think? is storage_base the place to put the sql_auditor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since the call to sql_auditor::log() occurs in util.h, how do we know which storage it belongs to? I am talking about e.g. void perform_exec()??? we only know the sqlite3* but that does not map to storage so its no use

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could intercept on_open to register every connection used by a storage_base?

Copy link
Contributor Author

@juandent juandent Dec 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and create a map of sqlite3* pointers and storage_base* pointers!!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think? is storage_base the place to put the sql_auditor?

I don't think we need sql_auditor at all but it is up to you

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

class sql_auditor_settings {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

different classes better be put in different files like so: one file for one class/enum/struct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok - one question about namespace: what to use here? namespace sqlite_orm or global namespace?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use global namespace in the lib cause it may lead to collisions in the user code

@@ -0,0 +1,86 @@
#pragma once
#include <fstream>
#include "error_code.h"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put "" include after <> include like it is done everywhere within this repo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

dev/sql_auditing.h Show resolved Hide resolved
#include "error_code.h"
#include <chrono>
#include <ctime> // For std::localtime and std::tm
#include <iomanip> // For std::put_time
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please skip For, just write expressions for which this include is required how it is done everywhere within this repo

open();
}

inline void sql_auditor_settings::set_destination_file(const std::string& filename) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one can refactor argument to accepts std::string by value not by reference and to move it. It can skip one extra copying of std::string

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};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if this exception is fired, caught, the app is executing further and calls sql_auditor::log?

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())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the user of sqlite_orm wants to use sql auditor but doesn't want @ symbol to be put in the logs?

@@ -14,6 +14,14 @@ TEST_CASE("Prepared select") {

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

#define JD_AUDITING_SETTINGS
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the purpose of lines 18 and 19? you define a value and next you check whether it is defined (and it is definitely defined)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its the way I mark tentative code...
experimental code...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is useless cause it never goes in branch, it goes the same way in all the conditions so if you drop it nothing will change

Copy link
Owner

@fnc12 fnc12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

architecture of current implementation of sql auditing can't be combined with the project's architecture which (projects) have logs system like boost::log or custom written ones. How this approach is going to wok in projects which use boost::log for example?

@@ -14,6 +14,14 @@ TEST_CASE("Prepared select") {

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

#define JD_AUDITING_SETTINGS
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is useless cause it never goes in branch, it goes the same way in all the conditions so if you drop it nothing will change

class storage_base;
}
}
using sqlite_orm::internal::storage_base;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this using is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using is used here because log_settings and log_admin are declared in the glocal namespace

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use global namespace in the lib code

status = stats;
global_settings = glob_settings;
}
log_settings() {}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need default constructor here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need a default constructor for log_settings because we provide a default constructor for log_admin!!

std::shared_ptr<std::ofstream> log_file;
std::string log_file_name;
std::string time_format;
enum status_code { FAIL, GOOD } status = status_code::FAIL;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid using non-class enums cause old enums add their constants into global scope

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

};

inline std::shared_ptr<std::ofstream> get_file(std::string log_file_name) {
static std::map<std::string, std::shared_ptr<std::ofstream>> files;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to keep file streams in a static map? better avoid static vars at all cause it is not thread safe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__the other alternative would be a global object;....

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use global/static objects cause it it not thread safe and break the architecture. Global object is anti-pattern

static std::map<std::string, std::shared_ptr<std::ofstream>> files;
auto it = files.find(log_file_name);
if(it == files.end()) {
std::shared_ptr<std::ofstream> pointer_to_shared{new std::ofstream()};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use std::make_shared instead of new. Avoid using new and delete if you can

@@ -766,6 +766,9 @@ namespace sqlite_orm {
if(this->on_open) {
this->on_open(db);
}
// JD added
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment is useless cause git blame has info about authors of every line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@@ -766,6 +766,9 @@ namespace sqlite_orm {
if(this->on_open) {
this->on_open(db);
}
// JD added
log_admin::admin().register_db(this, db);
// JD end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here: this comment is useless cause git blame has info about authors of every line

Copy link
Contributor Author

@juandent juandent Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@juandent
Copy link
Contributor Author

juandent commented Jan 5, 2025 via email

@fnc12
Copy link
Owner

fnc12 commented Jan 5, 2025

Why do they break the architecture?

Cause global object (singleton) is anti-pattern. If we can avoid it we have to avoid it

@juandent
Copy link
Contributor Author

juandent commented Jan 5, 2025 via email

@juandent
Copy link
Contributor Author

juandent commented Jan 5, 2025 via email

@fnc12
Copy link
Owner

fnc12 commented Jan 5, 2025

Which is the global/static object you refer to?

The one defined as static in a function and returns as a ref

But I am using singleton pattern

This comment doesn't make any sense

@fnc12
Copy link
Owner

fnc12 commented Jan 5, 2025

@juandent you are still ignoring this comment

architecture of current implementation of sql auditing can't be combined with the project's architecture which (projects) have logs system like boost::log or custom written ones. How this approach is going to wok in projects which use boost::log for example?

It is the main issue with this PR. No reason to spend time on talking about singleton and other stuff until this one is solved

@juandent
Copy link
Contributor Author

juandent commented Jan 5, 2025 via email

@fnc12
Copy link
Owner

fnc12 commented Jan 5, 2025

I am following Dimitri Nesteruk’ “Design patterns in Modern C++” and Fedor Pikus’ “Hands-on design patterns with C++” both of which implement a safe way for singletons.

Good for you. Still it is not acceptable for sqlite_orm cause singleton is anti-pattern

@juandent
Copy link
Contributor Author

juandent commented Jan 5, 2025 via email

@fnc12
Copy link
Owner

fnc12 commented Jan 5, 2025

This is no way to react!
“Good for you” you say! Can you be more sarcastic?

I am not sarcastic at all. Please don't ignore my other comments

@fnc12
Copy link
Owner

fnc12 commented Jan 8, 2025

Discussed in DM. Closing

@fnc12 fnc12 closed this Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants