Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Added logging library.
  • Loading branch information
Klaim committed Nov 12, 2012
commit e214cf64998496561bf25609fb1996e87b9c7f41
67 changes: 67 additions & 0 deletions include/network/logging/logging.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef NETWORK_LOGGING_HPP_20121112
#define NETWORK_LOGGING_HPP_20121112

#include <sstream>
#include <functional>

namespace network { namespace logging {

class log_record;

//using log_record_handler = std::function< void (const std::string&) >; // use this when VS can compile it...
typedef std::function< void (const log_record&) > log_record_handler;

void set_log_record_handler( log_record_handler handler );
void log( const log_record& message );

namespace handler
{
log_record_handler get_std_log_handler();
log_record_handler get_default_log_handler();
}

/** Helper to build a log record as a stream. */
class log_record
{
public:
log_record(){} // = default;
Copy link
Member

Choose a reason for hiding this comment

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

Just use =default now that we're C++11 only anyway.

Copy link
Author

Choose a reason for hiding this comment

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

I agree, I just cannot compile this at this time as I'm using VS2012 and don't have easy access to other compiler for some time. I'll do it anyway and try to compile it on my server.


// Implicit construction from string
log_record( const std::string& message )
{
write( message );
}

~log_record()
{
log( *this );
}

template< typename TypeOfSomething >
log_record& write( const TypeOfSomething& something ) // THINK: use universal references?
Copy link
Member

Choose a reason for hiding this comment

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

Yes, use TypeOfSomething&& here.

Copy link
Author

Choose a reason for hiding this comment

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

Ok.

{
m_text_stream << something;
return *this;
}

std::string full_message() const { return m_text_stream.str(); }

private:
// disable copy
log_record( const log_record& ); // = delete;
log_record& operator=( const log_record& ); // = delete;
Copy link
Member

Choose a reason for hiding this comment

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

Just use =delete as we're C++11 only anyway.


std::ostringstream m_text_stream; // stream in which we build the message

};

template< typename TypeOfSomething >
inline log_record& operator<<( log_record& log, const TypeOfSomething& something ) // THINK: use universal references?
Copy link
Member

Choose a reason for hiding this comment

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

Yes use const TypeOfSomething&& here.

Copy link
Author

Choose a reason for hiding this comment

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

You mean non-const then? It's not universal reference if it's const if I understand correctly.

Copy link
Member

Choose a reason for hiding this comment

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

Yes.

{
return log.write( something );
}

}}


#endif /* end of include guard: NETWORK_LOGGING_HPP_20121112 */
26 changes: 26 additions & 0 deletions libs/network/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU)
endif()
endif()

if( CPP-NETLIB_ALWAYS_LOGGING )
add_definitions( /D NETWORK_ENABLE_LOGGING )
endif()

if( NOT CPP-NETLIB_DISABLE_LOGGING )
set( CPP-NETLIB_LOGGING_SRCS
logging/logging.cpp
)
add_library(cppnetlib-logging ${CPP-NETLIB_LOGGING_SRCS})
foreach (src_file ${CPP-NETLIB_LOGGING_SRCS})
if (${CMAKE_CXX_COMPILER_ID} MATCHES GNU)
set_source_files_properties(${src_file}
PROPERTIES COMPILE_FLAGS ${CPP-NETLIB_CXXFLAGS})
endif()
endforeach(src_file)

# this library name is defined only if we created the target
# if not then it will be empty
set( CPP-NETLIB_LOGGING_LIB cppnetlib-logging )

endif()

set(CPP-NETLIB_URI_SRCS uri/uri.cpp uri/schemes.cpp uri/normalize.cpp)
add_library(cppnetlib-uri ${CPP-NETLIB_URI_SRCS})
foreach (src_file ${CPP-NETLIB_URI_SRCS})
Expand Down Expand Up @@ -100,6 +122,7 @@ set(CPP-NETLIB_HTTP_SERVER_SRCS
add_library(cppnetlib-http-server ${CPP-NETLIB_HTTP_SERVER_SRCS})
add_dependencies(cppnetlib-http-server
${Boost_LIBRARIES}
${CPP-NETLIB_LOGGING_LIB}
cppnetlib-constants
cppnetlib-uri
cppnetlib-message
Expand All @@ -111,6 +134,7 @@ add_dependencies(cppnetlib-http-server
)
target_link_libraries(cppnetlib-http-server
${Boost_LIBRARIES}
${CPP-NETLIB_LOGGING_LIB}
cppnetlib-constants
cppnetlib-uri
cppnetlib-message
Expand Down Expand Up @@ -151,6 +175,7 @@ set(CPP-NETLIB_HTTP_CLIENT_SRCS
add_library(cppnetlib-http-client ${CPP-NETLIB_HTTP_CLIENT_SRCS})
add_dependencies(cppnetlib-http-client
${Boost_LIBRARIES}
${CPP-NETLIB_LOGGING_LIB}
cppnetlib-constants
cppnetlib-uri
cppnetlib-message
Expand All @@ -162,6 +187,7 @@ add_dependencies(cppnetlib-http-client
)
target_link_libraries(cppnetlib-http-client
${Boost_LIBRARIES}
${CPP-NETLIB_LOGGING_LIB}
cppnetlib-constants
cppnetlib-uri
cppnetlib-message
Expand Down
52 changes: 52 additions & 0 deletions libs/network/src/logging/logging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

#ifdef NETWORK_NO_LIB
#undef NETWORK_NO_LIB
#endif

#include <iostream>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/shared_lock_guard.hpp>
Copy link
Member

Choose a reason for hiding this comment

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

Have you looked at <thread>? You don't need to change this now but I am in the process of moving to std::thread and std::mutex everywhere instead.

Copy link
Author

Choose a reason for hiding this comment

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

You mean at standard mutexes? The problem is that there is no shared mutex (multiple readers, single writer) in the standard... or I could replace with std::mutex but then the reading lock would be expensive.

Copy link
Member

Choose a reason for hiding this comment

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

Why would you need a reader? This is a logging sink right, why do you need to synchronize on reads?

Copy link
Author

Choose a reason for hiding this comment

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

This mutex is used only to lock on changing the log handler. The log handler itself should have it's own mechanism to handle thread-safe logging (as boost::log do for example). So here we shouldn't lock (I think) when only calling the handler, just let it manage thread safety. The implementation will be able to make the good choices for efficiency itself, like using some kind of concurrent log record stack (one of the configuration of boost::log does that for example). I think it's a problem that should be solved by the logging implementation, not by the log user, that is cppnetlib.
Am I more clear about the intention for this mutex?

Copy link
Member

Choose a reason for hiding this comment

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

I see. So you're trying to make readers wait on writers if there are any. That sounds reasonable.

However, why not just use a shared_ptr and copy that around instead? That way you don't have to lock when you're changing the shared_ptr as .reset() is guaranteed to be race-free.

Copy link
Author

Choose a reason for hiding this comment

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

That's right! I didn't think about this way of doing. The shared_ptr would lock on assignation/reset. However, am I correct in thinking that if I test the shared_ptr then dereference it, I have no guarantee that I'm not derefenrencing nullptr? I mean, the only correct thread-safe way I see it working is if I copy the shared_ptr in an other one, which would be almost the same as locking it (and releasing it)? (on the performance side I mean)
What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, make a copy then use the copy of the shared_ptr. That's how shared_ptr was designed to be used IIUC. 😃

Compare the cost of this with the reader-writer lock. I don't think you're going to see much of a difference as both of them will be incrementing an atomic count anyway. 😀

#include <network/logging/logging.hpp>

namespace network { namespace logging {

namespace handler
{
namespace
{
void std_log_handler( const log_record& log )
{
std::cerr << log.full_message() << std::endl;
}
}

log_record_handler get_std_log_handler() { return &std_log_handler; }
log_record_handler get_default_log_handler() { return &std_log_handler; }
}


namespace
{
// the log handler have to manage itself the thread safety on call
log_record_handler current_log_record_handler = handler::std_log_handler;
boost::upgrade_mutex mutex_log_handler; // we still need to not change the log handler concurrently
Copy link
Member

Choose a reason for hiding this comment

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

Why is this not a static?

Copy link
Author

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but objects in anonymous namespace are implicitly static (and their symbol isn't exposed). Am I wrong?

Copy link
Author

Choose a reason for hiding this comment

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

I can't cite the standard at the moment, but this suggest what I believe: http://stackoverflow.com/questions/8549659/c-namespace-and-static-variables

Copy link
Member

Choose a reason for hiding this comment

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

They're not implicitly static, they're implicitly with internal linkage. Marking this static ensures that it's initialised as a static object and if it's a POD then it's static value-initialized, meaning no runtime overhead. Also its lifetime is ensured to be valid before main() is called and until std::at_exit is called (and when other threads exit).

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I'll add static. I'll need to check in the standard to clarify my knowledge about this later.

}


void set_log_record_handler( log_record_handler handler )
{
boost::lock_guard<boost::upgrade_mutex> write_lock( mutex_log_handler );
current_log_record_handler = handler;
}

void log( const log_record& log )
{
boost::shared_lock<boost::upgrade_mutex> read_lock( mutex_log_handler );
if( current_log_record_handler )
{
current_log_record_handler( log );
}
}


}}