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

Reply via email to