Hi everyone,
here are the latest results of my efforts to provide H2 with a faster
logging system.
any comments welcomed.
Jeremy
/*
* proposal for a new Object and Logger design
*
* features :
* - pretty fast logging system using a ring buffer
* - log string formating using snprintf
* - uses no slow std::string or QString but simple char[]
* - H2_OBJECT macro used to initialize Object derived classes, usefull to set futur capabilities
* - reference to the logger set as a class variable within Object sub classes
*/
#include <pthread.h>
/****************************************************************************************************************/
#include <cstring>
#include <ostream>
#include <sstream>
/* Mutex protected RingBuffer */
class MRingBuffer {
public:
~MRingBuffer();
MRingBuffer( unsigned n_buf, unsigned b_len );
unsigned write( const char* msg, const unsigned len );
std::ostringstream& flush( std::ostringstream &out );
private:
unsigned __n_buf;
unsigned __b_len;
unsigned __read_idx;
unsigned __write_idx;
char** __rr_buffer;
pthread_mutex_t __mutex;
/* inlined methods */
void lock() { pthread_mutex_lock( &__mutex ); }
void unlock() { pthread_mutex_unlock( &__mutex ); }
bool is_empty() { return __read_idx==__write_idx; }
char* read_buffer() { return __rr_buffer[__read_idx]; }
char* write_buffer() { return __rr_buffer[__write_idx]; }
void inc_read_idx( ) { __read_idx = (__read_idx+1)%__n_buf; }
void inc_write_idx( ) { __write_idx = (__write_idx+1)%__n_buf; }
};
MRingBuffer::MRingBuffer( unsigned n_buf, unsigned b_len ) {
__n_buf = n_buf;
__b_len = b_len;
__rr_buffer = new char*[__n_buf];
if(__rr_buffer==NULL) { throw 0; }
for(int i=0;i<__b_len;i++) {
__rr_buffer[i] = new char[__b_len];
if(__rr_buffer[i]==NULL) { throw 0; }
}
__write_idx = __read_idx = 0;
pthread_mutex_init( &__mutex, NULL );
}
MRingBuffer::~MRingBuffer() {
for(int i=0;i<__b_len;i++) delete [] __rr_buffer[i];
delete [] __rr_buffer;
}
unsigned MRingBuffer::write( const char* msg, const unsigned len ) {
int ret=0;
unsigned l = ( len > __b_len ? __b_len : len );
lock();
memcpy( write_buffer(), msg, l );
//write_buffer()[l-1]='\0';
inc_write_idx();
if(is_empty()) inc_read_idx();
unlock();
return l;
}
std::ostringstream& MRingBuffer::flush( std::ostringstream &out ) {
lock();
while(!is_empty()) {
out << read_buffer();
inc_read_idx();
}
unlock();
return out;
}
/****************************************************************************************************************/
#include <cstdarg>
#include <cstdio>
class Logger {
public:
typedef enum _log_level {
None = 0,
Error = 1,
Warning = 2,
Info = 4,
Debug = 8,
AELockTracing = 0x10
} log_level_t;
~Logger();
Logger( unsigned level, unsigned n_buf, unsigned b_len );
void set_log_level(unsigned lev) { __log_level = lev; }
unsigned get_log_level() { return __log_level; }
int log( log_level_t lvl, const char* msg, ... );
private:
bool __running;
unsigned __log_level; // A bitmask of log_level_t
MRingBuffer *buffer;
pthread_t loggerThread;
static const char* __prefixes[];
static const char* __suffix;
friend void* loggerThread_func(void* param);
};
void* loggerThread_func(void* param);
#define LOG_PREFIX_LEN 9
const char* Logger::__prefixes[] = { "\033[0m ", "\033[31m(E) ", "\033[36m(W) ", "\033[32m(I) ", "\033[35m(D) ", "\033[0m " };
#define LOG_SUFFIX_LEN 5
const char* Logger::__suffix = "\033[0m\n";
Logger::Logger( unsigned level, unsigned n_buf, unsigned b_len ) : __running(true), __log_level(level) {
buffer = new MRingBuffer( n_buf, b_len );
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_create( &loggerThread, &attr, loggerThread_func, this );
}
Logger::~Logger(){
__running = false;
pthread_join( loggerThread, NULL );
}
#define LOG_BUFFER_LEN 50
int Logger::log( log_level_t lvl, const char* msg, ... ) {
if(!(lvl&__log_level)) return 0;
char tmp[LOG_BUFFER_LEN];
/* prefix */
int i;
switch(lvl) {
case Logger::None: i = 0; break;
case Logger::Error: i = 1; break;
case Logger::Warning: i = 2; break;
case Logger::Info: i = 3; break;
case Logger::Debug: i = 4; break;
default: i = 0; break;
}
memcpy( tmp, __prefixes[i], LOG_PREFIX_LEN );
i = LOG_PREFIX_LEN;
/* msg */
va_list args;
va_start( args, msg );
i += vsnprintf( &tmp[i], LOG_BUFFER_LEN-i, msg, args );
va_end(args);
/* suffix */
if(LOG_BUFFER_LEN-i<(LOG_SUFFIX_LEN+1)) i = LOG_BUFFER_LEN-(LOG_SUFFIX_LEN+1);
i += snprintf(&tmp[i],LOG_SUFFIX_LEN+1,"%s\n",__suffix);
/* log */
return buffer->write( tmp, i );
}
#include <iostream>
#include <sstream>
void* loggerThread_func(void* param) {
if ( param == NULL ) {
std::cerr << "Logger instance missing" << std::endl;
return NULL;
}
Logger *logger = ( Logger* )param;
MRingBuffer *buffer = logger->buffer;
while ( logger->__running ) {
usleep( 1000 );
std::ostringstream o;
std::cerr << buffer->flush( o ).str();
}
}
/****************************************************************************************************************/
#include <map>
#include <cassert>
#include <iostream>
#include <sstream>
#include <cstdlib>
using namespace std;
class Object {
public:
typedef map<const char*, int> object_count_t;
typedef map<const Object*, const char*> object_ref_t;
Object( const char* class_name ) { add_object( this, class_name ); }
Object( const Object& obj ) { add_object( this, obj.class_name() ); }
~Object() { del_object( this ); }
virtual const char* class_name() const = 0;
static void write_objects_map(ostream &out );
static void write_objects_map_to_cerr() { Object::write_objects_map( cerr ); }
static void bootstrap( Logger *logger ) {
__logger = logger;
pthread_mutex_init( &__mutex, NULL );
}
private:
static void del_object( const Object* obj );
static void add_object( const Object* obj, const char* class_name );
static object_count_t __objects_count;
static object_ref_t __objects_refs;
static pthread_mutex_t __mutex;
protected:
static Logger* __logger;
};
/* Object class instance */
pthread_mutex_t Object::__mutex;
Logger* Object::__logger = 0;
Object::object_count_t Object::__objects_count;
Object::object_ref_t Object::__objects_refs;
void Object::write_objects_map(ostream &out ) {
int n = 0;
ostringstream o;
pthread_mutex_lock( &__mutex );
object_count_t::iterator it = __objects_count.begin();
while ( it != __objects_count.end() ) {
o << "\t[ " << (*it).first << " ]\t" << (*it).second << endl;
n+=(*it).second;
it++;
}
pthread_mutex_unlock( &__mutex );
out << endl << "Objects map : " << endl << o.str() << "Total : " << n << " objects." << endl << endl;
return;
}
void Object::add_object( const Object* obj, const char* class_name ) {
pthread_mutex_lock( &__mutex );
if( __objects_count.size()==0) atexit( Object::write_objects_map_to_cerr );
__objects_count[ class_name ]++;
__objects_refs[obj ] = class_name;
pthread_mutex_unlock( &__mutex );
}
void Object::del_object( const Object* obj ) {
object_ref_t::iterator it_ref = __objects_refs.find( obj );
if ( it_ref==__objects_refs.end() ) {
cerr << "delete an unreferenced object " << obj << endl;
return;
}
object_count_t::iterator it_count = __objects_count.find( (*it_ref).second );
if ( it_count==__objects_count.end() ) {
cerr << "delete a referenced but not counted object " << obj << " " << (*it_ref).second << endl;
return;
}
pthread_mutex_lock( &__mutex );
assert(__objects_count[ (*it_count).first ]>0);
__objects_refs.erase( it_ref );
__objects_count[ (*it_count).first ]--;
pthread_mutex_unlock( &__mutex );
}
/* Object instance logging macros */
#define LOG_DEBUG( ... ) __logger->log( Logger::Debug, __VA_ARGS__ );
#define LOG_INFO( ... ) __logger->log( Logger::Info, __VA_ARGS__ );
#define LOG_WARN( ... ) __logger->log( Logger::Warning, __VA_ARGS__ );
#define LOG_ERROR( ... ) __logger->log( Logger::Error, __VA_ARGS__ );
/* Object inherited class declaration macro */
#define H2_OBJECT() \
public: virtual const char* class_name() const { return __class_name; } \
private: static const char* __class_name; \
/****************************************************************************************************************/
class Dev1 : public Object {
H2_OBJECT()
public:
Dev1() :Object(__class_name) {
LOG_DEBUG( msg_create, class_name() )
}
~Dev1() {
LOG_WARN( msg_destroy, class_name() )
}
int get_int() { return 123; }
float get_float() { return 123.3222; }
void say_hello( char* name ) {
LOG_INFO( "Hello %s from %s->%s",name, class_name(),__FUNCTION__ )
}
static const char *msg_create;
static const char *msg_destroy;
};
const char* Dev1::__class_name="Dev1";
const char* Dev1::msg_create = "%s Object Creation";
const char* Dev1::msg_destroy = "%s Object Destruction";
/****************************************************************************************************************/
class Dev2 : public Object {
H2_OBJECT()
public:
Dev2() :Object(__class_name){ }
void Destroy() { }
};
const char* Dev2::__class_name="Dev2";
/****************************************************************************************************************/
int run() {
Dev1 a = Dev1(); Dev1 b = Dev1(); Dev2 c = Dev2(); Dev2 d = Dev2(); Dev2 e = Dev2( d );
a.say_hello( (char*)"Jeremy" );
Object* o = &a;
a.say_hello( (char*)"Corina" );
Object::write_objects_map( cerr );
a.say_hello( (char*)"Roisin" );
}
int main () {
Logger *l = new Logger( Logger::Warning|Logger::Debug|Logger::Info, 7, LOG_BUFFER_LEN );
Object::bootstrap( l );
run();
usleep(1000);
}
------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev
_______________________________________________
Hydrogen-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/hydrogen-devel