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

Implement session::get_last_query_context() #1164

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/api/client.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# API Reference

The core client interface is a set of classes and free functions declared in the `soci.h` header file.
All names are dbeclared in the `soci` namespace.
All names are declared in the `soci` namespace.

There are also additional names declared in the `soci::details` namespace, but they are not supposed to be directly used by the users of the library and are therefore not documented here.
When such types are used in the declarations that are part of the "public" interface, they are replaced by "IT", which means "internal type".
Expand Down Expand Up @@ -76,7 +76,11 @@ public:
void set_log_stream(std::ostream * s);
std::ostream * get_log_stream() const;

void log_query(std::string const & query);
void clear_query_parameters();
void add_query_parameter(std::string name, std::string value);
std::string get_last_query() const;
std::string get_last_query_context() const;

void uppercase_column_names(bool forceToUpper);

Expand Down
19 changes: 18 additions & 1 deletion docs/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The following members of the `session` class support the basic logging functiona
* `void set_log_stream(std::ostream * s);`
* `std::ostream * get_log_stream() const;`
* `std::string get_last_query() const;`
* `std::string get_last_query_context() const;`

The first two functions allow to set the user-provided output stream object for logging.
The `NULL` value, which is the default, means that there is no logging.
Expand All @@ -26,7 +27,23 @@ An example use might be:
Each statement logs its query string before the preparation step (whether explicit or implicit) and therefore logging is effective whether the query succeeds or not.
Note that each prepared query is logged only once, independent on how many times it is executed.

The `get_last_query` function allows to retrieve the last used query.
The `get_last_query` function allows to retrieve the last used query. The associated `get_last_query_context` function allows to obtain a string
representation of the bound values (if any) used in the last query. That is, while `get_last_query` might return something like
`INSERT INTO dummy (val) VALUES (:val)`, `get_last_query_context` will return something like `:val=42` and together they can be used to get a detailed
understanding of what happened in the last query. Cached parameters are cleared at the beginning of each query and are **not** persisted across
multiple queries.

Logging of query parameters is **enabled by default** but can at any time be adjusted to your needs by using the `set_query_context_logging_mode`
function of the `session` object. For instance
```cpp
sql.set_query_context_logging_mode(log_context::on_error);
```
Possible values are
* `log_context::always` - Always cache the bound parameters of queries (the default)
* `log_context::never` - Never cache bound parameters. This also ensures that bound parameters are not part of exception messages. It is therefore
suitable for queries that bind sensitive information that must not be leaked.
* `log_context::on_error` - Only caches bound parameters in case the query encounters an error. This is intended for cases in which you don't want to
have the overhead of caching parameters during regular operations but still want this extra information in case of errors.

## Flexible logging using custom loggers

Expand Down
13 changes: 13 additions & 0 deletions include/private/soci-compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@
# define SOCI_GCC_WARNING_RESTORE(x)
#endif

// SOCI_MSVC_WARNING_{SUPPRESS,RESTORE} macros are similar but for MSVC (they
// work for all the supported versions).
#if defined(_MSC_VER)
# define SOCI_MSVC_WARNING_SUPPRESS(x) \
__pragma(warning(push)) \
__pragma(warning(disable:x))
# define SOCI_MSVC_WARNING_RESTORE(x) \
__pragma(warning(pop))
#else
# define SOCI_MSVC_WARNING_SUPPRESS(x)
# define SOCI_MSVC_WARNING_RESTORE(x)
#endif

// CHECK_CXX_STD(version) evaluates to 1 if the C++ version is at least the
// version specified.
#if defined(_MSVC_LANG)
Expand Down
22 changes: 22 additions & 0 deletions include/soci/log-context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef SOCI_LOG_CONTEXT_H_INCLUDED
#define SOCI_LOG_CONTEXT_H_INCLUDED

namespace soci
{

enum class log_context
{
never,
always,
on_error,
};

} // namespace soci

#endif // SOCI_LOG_CONTEXT_H_INCLUDED
31 changes: 30 additions & 1 deletion include/soci/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,21 @@
#include "soci/soci-platform.h"

#include <ostream>
#include <vector>

namespace soci
{


struct SOCI_DECL query_parameter
{
explicit query_parameter(std::string name = {}, std::string value = {})
: name(std::move(name)), value(std::move(value)) {}

std::string name;
std::string value;
};

// Allows to customize the logging of database operations performed by SOCI.
//
// To do it, derive your own class from logger_impl and override its pure
Expand All @@ -28,7 +39,13 @@ class SOCI_DECL logger_impl
virtual ~logger_impl();

// Called to indicate that a new query is about to be executed.
virtual void start_query(std::string const & query) = 0;
virtual void start_query(std::string const & query);

// Called to log a parameter that is bound to the currently active query
virtual void add_query_parameter(std::string name, std::string value);

// Clears all currently logged query parameters
virtual void clear_query_parameters();

logger_impl * clone() const;

Expand All @@ -38,6 +55,10 @@ class SOCI_DECL logger_impl
virtual void set_stream(std::ostream * s);
virtual std::ostream * get_stream() const;
virtual std::string get_last_query() const;
virtual std::string get_last_query_context() const;

protected:
std::vector<query_parameter> queryParams_;

private:
// Override to return a new heap-allocated copy of this object.
Expand Down Expand Up @@ -68,10 +89,18 @@ class SOCI_DECL logger

void start_query(std::string const & query) { m_impl->start_query(query); }

virtual void add_query_parameter(std::string name, std::string value)
{
m_impl->add_query_parameter(std::move(name), std::move(value));
}

virtual void clear_query_parameters() { m_impl->clear_query_parameters(); }

// Methods used for the implementation of session basic logging support.
void set_stream(std::ostream * s) { m_impl->set_stream(s); }
std::ostream * get_stream() const { return m_impl->get_stream(); }
std::string get_last_query() const { return m_impl->get_last_query(); }
std::string get_last_query_context() const { return m_impl->get_last_query_context(); }

private:
logger_impl * m_impl;
Expand Down
8 changes: 8 additions & 0 deletions include/soci/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "soci/query_transformation.h"
#include "soci/connection-parameters.h"
#include "soci/logger.h"
#include "soci/log-context.h"

// std
#include <cstddef>
Expand Down Expand Up @@ -107,8 +108,14 @@ class SOCI_DECL session
void set_log_stream(std::ostream * s);
std::ostream * get_log_stream() const;

void set_query_context_logging_mode(log_context ctx);
log_context get_query_context_logging_mode() const;

void log_query(std::string const & query);
void clear_query_parameters();
void add_query_parameter(std::string name, std::string value);
std::string get_last_query() const;
std::string get_last_query_context() const;

void set_got_data(bool gotData);
bool got_data() const;
Expand Down Expand Up @@ -223,6 +230,7 @@ class SOCI_DECL session
std::ostringstream query_stream_;
std::unique_ptr<details::query_transformation_function> query_transformation_;

log_context query_ctx_logging_mode_ = log_context::always;
logger logger_;

connection_parameters lastConnectParameters_;
Expand Down
3 changes: 3 additions & 0 deletions include/soci/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ class SOCI_DECL statement_impl

bool alreadyDescribed_;

// Unconditionally call session::add_query_parameter() for all parameters.
void do_add_query_parameters();

std::size_t intos_size();
std::size_t uses_size();
void pre_exec(int num);
Expand Down
37 changes: 37 additions & 0 deletions src/core/logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ void throw_not_supported()
} // namespace anonymous


void logger_impl::start_query(const std::string &)
{
clear_query_parameters();
}

void logger_impl::add_query_parameter(std::string name, std::string value)
{
queryParams_.emplace_back(std::move(name), std::move(value));
}

void logger_impl::clear_query_parameters()
{
queryParams_.clear();
}

logger_impl * logger_impl::clone() const
{
logger_impl * const impl = do_clone();
Expand Down Expand Up @@ -57,6 +72,28 @@ std::string logger_impl::get_last_query() const
SOCI_DUMMY_RETURN(std::string());
}

std::string logger_impl::get_last_query_context() const
{
std::string context;

bool first = true;
for (const query_parameter &param : queryParams_)
{
if (first)
{
first = false;
}
else
{
context += ", ";
}

context += ":" + param.name + "=" + param.value;
}

return context;
}

logger::logger(logger_impl * impl)
: m_impl(impl)
{
Expand Down
49 changes: 49 additions & 0 deletions src/core/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "soci/connection-pool.h"
#include "soci/soci-backend.h"
#include "soci/query_transformation.h"
#include "soci/log-context.h"

using namespace soci;
using namespace soci::details;
Expand Down Expand Up @@ -47,6 +48,8 @@ class standard_logger_impl : public logger_impl

virtual void start_query(std::string const & query)
{
logger_impl::start_query(query);

if (logStream_ != NULL)
{
*logStream_ << query << '\n';
Expand Down Expand Up @@ -465,6 +468,16 @@ std::ostream * session::get_log_stream() const
}
}

void session::set_query_context_logging_mode(log_context ctx)
{
query_ctx_logging_mode_ = ctx;
}

log_context session::get_query_context_logging_mode() const
{
return query_ctx_logging_mode_;
}

void session::log_query(std::string const & query)
{
if (isFromPool_)
Expand All @@ -477,6 +490,30 @@ void session::log_query(std::string const & query)
}
}

void session::clear_query_parameters()
{
if (isFromPool_)
{
pool_->at(poolPosition_).clear_query_parameters();
}
else
{
logger_.clear_query_parameters();
}
}

void session::add_query_parameter(std::string name, std::string value)
{
if (isFromPool_)
{
pool_->at(poolPosition_).add_query_parameter(std::move(name), std::move(value));
}
else
{
logger_.add_query_parameter(std::move(name), std::move(value));
}
}

std::string session::get_last_query() const
{
if (isFromPool_)
Expand All @@ -489,6 +526,18 @@ std::string session::get_last_query() const
}
}

std::string session::get_last_query_context() const
{
if (isFromPool_)
{
return pool_->at(poolPosition_).get_last_query_context();
}
else
{
return logger_.get_last_query_context();
}
}

void session::set_got_data(bool gotData)
{
if (isFromPool_)
Expand Down
Loading