Логгинг
От: Константин Ленин Россия  
Дата: 29.03.06 16:06
Оценка:
Решил тут логгинг переделать.Старый был неудобен и основывался на макросах.

Кое-что получилось . Хочу послушать мнения по поводу пригодности.


#pragma once
#include <Windows.h>
#include <atlstr.h>
#include <iosfwd>
#include <string>
#include <memory>

namespace Logging
{
    enum LogLevel
    {
        LogAll = 0,
        LogInfo,
        LogWarning,
        LogError,
        LogNo
    };

    std::ostream& operator<<( std::ostream& stream, LogLevel level )
    {
        LPCTSTR lpszPrefix = NULL;

        switch( level )
        {
            case LogAll:
                        lpszPrefix = _T("Debug: ");
                        break;

            case LogInfo:
                        lpszPrefix = _T("Info: ");
                        break;

            case LogWarning:
                        lpszPrefix = _T("Warning: ");
                        break;

            case LogError:
                        lpszPrefix = _T("Error: ");
                        break;

            default:
                return stream;
        }

        return stream<<lpszPrefix;
    }

    class ILogger
    {
    protected:

        virtual void WriteEntry_( LogLevel level, LPCTSTR lpszMessage ) = 0;

        ~ILogger(){}

    public:

        void WriteEntry(LogLevel level, LPCTSTR lpszMessage )
        {
            WriteEntry_(level, lpszMessage);
        }
    };    
    
    class LogEntry
    {
    protected:

        typedef ATL::CString String;

        ILogger*                pLogger_;
        std::auto_ptr<String>    pBuffer_;
        LogLevel                Level_;

    protected:

        LogEntry( LogLevel level, ILogger* pLogger )
            : Level_(level), pLogger_(pLogger), pBuffer_( new String() )
        {}        

        LogEntry& operator =( const LogEntry& );        

        template <typename T>
        void AppendRecord( const T& record )
        {
            pBuffer_->Append(record);
            pBuffer_->Append(_T(". "));
        }

    public:

        LogEntry( LogEntry& entry )
        {
            Level_ = entry.Level_;
            pLogger_ = entry.pLogger_;
            pBuffer_ = entry.pBuffer_;
        }

        virtual ~LogEntry()
        {
            try
            {
                if( !pBuffer_->IsEmpty() ) pLogger_->WriteEntry( Level_, (*pBuffer_) );
            }
            catch(...){}
        }

        static LogEntry Instance( LogLevel level, ILogger* pLogger )
        {
            return LogEntry( level, pLogger );
        }

        LogEntry operator <<( int value )
        {
            String tmp;
            tmp.Format("%d", value);
            AppendRecord(tmp);
            return *this;
        }

        LogEntry operator <<( HRESULT value )
        {
            String tmp;
            tmp.Format("Error 0x%08x", value);
            AppendRecord(tmp);
            return *this;
        }

        template <typename T>
        LogEntry operator <<( const T& value )
        {            
            AppendRecord(value);
            return *this;
        }
    };    

    class ConsoleLogger : public ILogger
    {
    protected:

        void WriteEntry_( LogLevel level, LPCTSTR lpszMessage )
        {
//            if( level > LogInfo )
            {
                std::cout<<level<<lpszMessage<<endl;
            }            
        };

    public:

        static ConsoleLogger& Instance()
        {
            static ConsoleLogger logger;
            return logger;
        }        
        
        static LogEntry log( LogLevel level )
        {
            return LogEntry::Instance(level, &ConsoleLogger::Instance());
        }
    };

    template <typename T>
    LogEntry operator <<( LogLevel level, const T& value )
    {
        return LogEntry::Instance(level, &ConsoleLogger::Instance())<<value;
    }

    int main()
    {
        LogWarning<<_T("wow")<<10<<E_FAIL<<-10000;//красиво, но не позволяет выбирать тип логгера
        ConsoleLogger::log(LogInfo)<<_T("xaxaxaxxaxaxxax..")<<_T("suxx...");//некрасиво, но позволяет...
    }
}


Набросал за час, так что пинать не сильно
Из достоинств — неограниченное добавление записей в строку, можно прикрутить манипуляторы.
Идея взята здесь
Автор: Шахтер
Дата: 16.02.06



03.04.06 20:29: Перенесено из 'C/C++'
Estuve en Granada y me acorde' de ti
Re: Логгинг
От: MaximE Великобритания  
Дата: 29.03.06 18:23
Оценка:
Читай обсуждение http://thread.gmane.org/gmane.comp.lib.boost.devel/134074

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 2.0
Re[2]: Логгинг
От: Константин Ленин Россия  
Дата: 30.03.06 08:02
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Читай обсуждение http://thread.gmane.org/gmane.comp.lib.boost.devel/134074


ME>--

ME>Maxim Yegorushkin

Читать не стал, времени нет. А вот исходники глянул, но за 5 минут разобраться не удалось. Общее впечатление — больно громоздкая.
Estuve en Granada y me acorde' de ti
Re: Логгинг
От: srggal Украина  
Дата: 30.03.06 08:13
Оценка:
Здравствуйте, Константин Ленин, Вы писали:

Сюда Re: Я индус?
Автор: Шахтер
Дата: 16.02.06
смотрели ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Логгинг
От: Константин Ленин Россия  
Дата: 30.03.06 09:01
Оценка: :)
Здравствуйте, srggal, Вы писали:

S>Здравствуйте, Константин Ленин, Вы писали:


S>Сюда Re: Я индус?
Автор: Шахтер
Дата: 16.02.06
смотрели ?

а вы конец моего сообщения смотрели?
Estuve en Granada y me acorde' de ti
Re[3]: Логгинг
От: srggal Украина  
Дата: 30.03.06 09:14
Оценка:
Здравствуйте, Константин Ленин, Вы писали:

КЛ>Здравствуйте, srggal, Вы писали:


S>>Здравствуйте, Константин Ленин, Вы писали:


S>>Сюда Re: Я индус?
Автор: Шахтер
Дата: 16.02.06
смотрели ?

КЛ>а вы конец моего сообщения смотрели?

Дык в том треде , я вскользь писал про накрутку разных policy, и как пример — упоминал MT policy

Как это будет выглядеть в Вашем случае ?
И насколько это будет удобно ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Логгинг
От: Аноним  
Дата: 30.03.06 09:20
Оценка:
Насчет выбора типа логгера, можна это переложить на сам класс логгера, то есть:

Log<<"LogType1:"<<p1<<p2;
Log<<"LogType2:"<<p1;


Правда придется немножко попарсить строки, для определения того является ли это сообщением лога или указывает на тип лога, но более удобно...
Re[4]: Логгинг
От: Константин Ленин Россия  
Дата: 30.03.06 11:03
Оценка:
Здравствуйте, srggal, Вы писали:

В первую очередь меня интересует LogEntry и принцып ее реализации, а не конкретные имплементации ILogger.

S>Здравствуйте, Константин Ленин, Вы писали:


КЛ>>Здравствуйте, srggal, Вы писали:


S>>>Здравствуйте, Константин Ленин, Вы писали:


S>>>Сюда Re: Я индус?
Автор: Шахтер
Дата: 16.02.06
смотрели ?

КЛ>>а вы конец моего сообщения смотрели?

S>Дык в том треде , я вскользь писал про накрутку разных policy, и как пример — упоминал MT policy


Полиси могут накручиваться как на имплементации ILogger, так и на LogEntry. В частности, MT policy это полиси конкретного ILogger, к LogEntry она не должна иметь никакого отношения. Это я к тому, что я говорил выше о приоритетах интересов.


S>Как это будет выглядеть в Вашем случае ?


Об этом я еще не думал. Да и выглядеть должно как "обычно".

S>И насколько это будет удобно ?

Использовать разные полиси? Не знаю. Как видите, точное указание типа логгера(и его стратегий) требует небольшого синтаксического оверхэда.

Че-то мало оценок... Либо в лом читать, либо это уже где-то есть.
Estuve en Granada y me acorde' de ti
Re[5]: Логгинг
От: rg45 СССР  
Дата: 30.03.06 11:13
Оценка:
"Константин Ленин" <44487@users.rsdn.ru> wrote in message news:1813474@news.rsdn.ru...
><skipped>
> Че-то мало оценок... Либо в лом читать, либо это уже где-то есть.

Вечная нехватка времени. Пример достаточно объемистый, а оценивать не разобравшись не хочется.
Posted via RSDN NNTP Server 2.0
--
https://ezstat.ru/26n775.gif Оппозиция собиралась не для того, чтоб мешки ворочать.
Re[5]: Логгинг
От: srggal Украина  
Дата: 30.03.06 12:16
Оценка:
Здравствуйте, Константин Ленин, Вы писали:


S>>Дык в том треде , я вскользь писал про накрутку разных policy, и как пример — упоминал MT policy


КЛ>Полиси могут накручиваться как на имплементации ILogger, так и на LogEntry. В частности, MT policy это полиси конкретного ILogger, к LogEntry она не должна иметь никакого отношения. Это я к тому, что я говорил выше о приоритетах интересов.


Отчасти Вы правы, но это комбинаторный взрыв.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[6]: Логгинг
От: Константин Л. Россия  
Дата: 30.03.06 14:53
Оценка:
Здравствуйте, srggal, Вы писали:

S>Здравствуйте, Константин Ленин, Вы писали:



S>>>Дык в том треде , я вскользь писал про накрутку разных policy, и как пример — упоминал MT policy


КЛ>>Полиси могут накручиваться как на имплементации ILogger, так и на LogEntry. В частности, MT policy это полиси конкретного ILogger, к LogEntry она не должна иметь никакого отношения. Это я к тому, что я говорил выше о приоритетах интересов.


S>Отчасти Вы правы, но это комбинаторный взрыв.




Ну шаблонные классы с несколькими параметрами тоже комбинаторный взрыв. Ну кто-нить хоть код посмотрит?
Estuve en Granada y me acorde' de ti
Re: Логгинг
От: Chipsеt Россия http://merlinko.com
Дата: 30.03.06 21:14
Оценка:
Здравствуйте, Константин Ленин, Вы писали:

КЛ>Решил тут логгинг переделать.Старый был неудобен и основывался на макросах.


КЛ>Кое-что получилось . Хочу послушать мнения по поводу пригодности.


КЛ>Набросал за час, так что пинать не сильно

КЛ>Из достоинств — неограниченное добавление записей в строку, можно прикрутить манипуляторы.
КЛ>Идея взята здесь
Автор: Шахтер
Дата: 16.02.06


Я кстати в той теме всё-же остановился на макросах.
Плюс: позволяет автоматически залоггировать прототип функции из которой был произведен логгинг. А так-же файл, номер строки и т.д.и т.п
http://fool.exler.ru/sm/val.gif"Всё что не убивает нас, делает нас сильнее..."
Re: Логгинг
От: remark Россия http://www.1024cores.net/
Дата: 02.04.06 01:02
Оценка:
Здравствуйте, Константин Ленин, Вы писали:

КЛ>Решил тут логгинг переделать.Старый был неудобен и основывался на макросах.


КЛ>Кое-что получилось . Хочу послушать мнения по поводу пригодности.



По поводу потокового синтаксиса — согласен полностью.

В конце каждого вывода хотелось бы видеть автоматическое добавление перевода строки. По крайней мере я привык к такой фиче — не жаловался ни разу.

Не согласен по поводу:


        template <typename T>
        void AppendRecord( const T& record )
        {
            pBuffer_->Append(record);
            pBuffer_->Append(_T(". "));
        }

        template <typename T>
        LogEntry operator <<( const T& value )
        {            
            AppendRecord(value);
            return *this;
        }


Не понятно как расширять логирование для пользовательских типов?


Зачем в LogEntry секция protected? И зачем в нём динамическое создание pBuffer_?


Полностью не согласен по поводу:

ConsoleLogger::log(LogInfo)<<_T("xaxaxaxxaxaxxax..")<<_T("suxx...");



Пользовательский код, который логирует, абсолютно не должен знать о логгерах, и уж точно не должен выбирать тип конкретного логгера. Это можно делать только централизованно на уровне приложения. Имхо.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Логгинг
От: _ace_ Россия acefsm.livejournal.com
Дата: 02.04.06 17:35
Оценка:
http://logging.apache.org/log4cxx/
Re: Логгинг
От: Кодт Россия  
Дата: 02.04.06 22:09
Оценка: 1 (1)
Здравствуйте, Константин Ленин, Вы писали:

КЛ>        LogEntry operator <<( int value )
КЛ>        {
КЛ>            String tmp;
КЛ>            tmp.Format("%d", value);
КЛ>            AppendRecord(tmp);
КЛ>            return *this;
КЛ>        }

КЛ>        LogEntry operator <<( HRESULT value )
КЛ>        {
КЛ>            String tmp;
КЛ>            tmp.Format("Error 0x%08x", value);
КЛ>            AppendRecord(tmp);
КЛ>            return *this;
КЛ>        }

КЛ>        template <typename T>
КЛ>        LogEntry operator <<( const T& value )
КЛ>        {            
КЛ>            AppendRecord(value);
КЛ>            return *this;
КЛ>        }

Учитывая, что HRESULT — это unsigned int, пользователя могут ждать приключения.
Выход — завести обёртку
struct HResult
{
  HRESULT hr;
  explicit HResult(HRESULT h) : hr(h) {}
  String verbose() const;
  // За словесными описаниями можно слазить в ::FormatMessage(),
  // а чтобы не делать это слишком часто - используй кэш
  // И не забывай, что есть коды не только ошибок, но и удач - S_OK, S_FALSE и другие, которые SUCCEEDED(hr)
};

и уже написать перегрузку operator<< для этого типа.

Кстати говоря, лучше, чтобы перегрузки были для чего-то одного: либо operator<<, либо AppendRecord.
И ещё кстати, если operator<< перегружен — то сделать его свободной функцией, чтобы пользователи могли добавлять свои сигнатуры.
А может быть, неплохо разделить все три составляющих:
— синтаксический сахар (operator <<)
— перевод типа в строку (скажем, функция String verbose(T) — перегруженная по-всякому или шаблонная)
— добавление строки в лог (метод LogEntry.AppendRecord(String))
Тогда оператор будет выглядеть вот так:
template<class T> LogEntry& operator << (LogEntry& log, T const& t)
{ log.AppendRecord(verbose(t)); return log; }
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[2]: Логгинг
От: Константин Л. Россия  
Дата: 03.04.06 08:53
Оценка:
Здравствуйте, _ace_, Вы писали:


__>http://logging.apache.org/log4cxx/


Спасибо за ссылку, но что-то типа этого у меня и было. Хотелось избавиться от вызова именно методов с фиксированным кол-ом параметров, и, следовательно, с конкатенацией строк.
Estuve en Granada y me acorde' de ti
Re[2]: Логгинг
От: Константин Л. Россия  
Дата: 03.04.06 08:56
Оценка:
Здравствуйте, Кодт, Вы писали:

Я привел пару операторов << только в качестве примера и они не претендуют на завершенность.
За HRESULT спасибо, я пока не концентрировался на конкретных типах.
Estuve en Granada y me acorde' de ti
Re[2]: Логгинг
От: Константин Л. Россия  
Дата: 03.04.06 09:11
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Константин Ленин, Вы писали:


КЛ>>Решил тут логгинг переделать.Старый был неудобен и основывался на макросах.


КЛ>>Кое-что получилось . Хочу послушать мнения по поводу пригодности.



R>По поводу потокового синтаксиса — согласен полностью.


R>В конце каждого вывода хотелось бы видеть автоматическое добавление перевода строки. По крайней мере я привык к такой фиче — не жаловался ни разу.


Это может делать как LogEntry, так и конкретный ILogger::WriteEntry

R>Не согласен по поводу:



R>
R>        template <typename T>
R>        void AppendRecord( const T& record )
R>        {
R>            pBuffer_->Append(record);
R>            pBuffer_->Append(_T(". "));
R>        }

R>        template <typename T>
R>        LogEntry operator <<( const T& value )
R>        {            
R>            AppendRecord(value);
R>            return *this;
R>        }
R>


R>Не понятно как расширять логирование для пользовательских типов?


Я об этом пока не сильно думал

R>Зачем в LogEntry секция protected? И зачем в нём динамическое создание pBuffer_?

хз)
Маленькая оптимизация. Вместо копирования содержимого строки в LogEntry::LogEntry( LogEntry& ) я копирую только указатель. Если ты заметил, на каждый вызов LogEntry::operator<< создается новый объект LogEntry. Т.е на



LogWarning<<_T("")<<_T("");


мы получаем 5 временных объектов LogEntry и 4 вызова LogEntry::LogEntry( LogEntry& ) с 4мя копированиями строки. ИМХО копирование указателя на строку лучше чем копирование строки.


R>Полностью не согласен по поводу:


R>
R>ConsoleLogger::log(LogInfo)<<_T("xaxaxaxxaxaxxax..")<<_T("suxx...");
R>



R>Пользовательский код, который логирует, абсолютно не должен знать о логгерах, и уж точно не должен выбирать тип конкретного логгера. Это можно делать только централизованно на уровне приложения. Имхо.


ИМХО, не всегда. Иногда требуется что-то записать в EventLog, когда в обычных случаях пишем в файл, например.


R>
Estuve en Granada y me acorde' de ti
Re[3]: Логгинг
От: remark Россия http://www.1024cores.net/
Дата: 03.04.06 11:06
Оценка: +1
Здравствуйте, Константин Л., Вы писали:


R>>Пользовательский код, который логирует, абсолютно не должен знать о логгерах, и уж точно не должен выбирать тип конкретного логгера. Это можно делать только централизованно на уровне приложения. Имхо.


КЛ>ИМХО, не всегда. Иногда требуется что-то записать в EventLog, когда в обычных случаях пишем в файл, например.



Нет, так у тебя ничего не получиться.
Тогда такой вид вывода в лог должен быть выделен в отдельную перпендикулярное множество абстракций.
Т.е. надо сделать некое множество {CommonLog, UserEventLog, ...}.
Весь код пишет некий CommonLog, а некоторые записи идут в UserEventLog. Но при этом приложение всё равно должно централизовано управлять, что CommonLog сейчас идёт в файл, а UserEventLog сейчас идёт в EventLog Windows или он может идти в тот же файл, что и CommonLog, если другово не требуется.

При этом всегда можно в одном месте настроить, что CommonLog мы например хотим изменить с консоли в файл, т.к. приложение перестало быть консольным. А UserEventLog теперь надо поменять на что-то другое, т.к. приложение стало работать не под Windows.
Если же ты будешь по всему коду упоминать имена конкретных логгеров, то тем самым ты "убьёшь" код.

А в некоторых ситуациях это будет просто невозможно. Ты сейчас думаешь только о коде проекта. Представь такую ситуацию. У тебя есть один проект, который пишет лог в файл. И второй, который пишет лог на консоль. Тебе надо написать некий общий код, который ты хочешь использовать в обеих проектах. Куда ты будешь выводить лог? В FileLog? В ConsoleLog? Единственный возможный вариант в CommonLog! А каждый проект уже сам разберётся куда конкретно это вывести.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Логгинг
От: Константин Л. Россия  
Дата: 03.04.06 17:33
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Константин Ленин, Вы писали:


К>
КЛ>>        LogEntry operator <<( int value )
КЛ>>        {
КЛ>>            String tmp;
КЛ>>            tmp.Format("%d", value);
КЛ>>            AppendRecord(tmp);
КЛ>>            return *this;
КЛ>>        }

КЛ>>        LogEntry operator <<( HRESULT value )
КЛ>>        {
КЛ>>            String tmp;
КЛ>>            tmp.Format("Error 0x%08x", value);
КЛ>>            AppendRecord(tmp);
КЛ>>            return *this;
КЛ>>        }

КЛ>>        template <typename T>
КЛ>>        LogEntry operator <<( const T& value )
КЛ>>        {            
КЛ>>            AppendRecord(value);
КЛ>>            return *this;
КЛ>>        }
К>

К>Учитывая, что HRESULT — это unsigned int, пользователя могут ждать приключения.
К>Выход — завести обёртку

HRESULT — это long
Estuve en Granada y me acorde' de ti
Re[3]: Логгинг
От: Left2 Украина  
Дата: 04.04.06 08:19
Оценка:
__>>http://logging.apache.org/log4cxx/

КЛ>Спасибо за ссылку, но что-то типа этого у меня и было. Хотелось избавиться от вызова именно методов с фиксированным кол-ом параметров, и, следовательно, с конкатенацией строк.


В log4cxx вывод идёт через std::strstream — никаких методов с фиксированным кол-вом параметров.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Логгинг
От: Константин Л. Россия  
Дата: 06.04.06 16:04
Оценка:
Здравствуйте, Константин Ленин, Вы писали:

Вот окончательная версия


namespace Logging
{
    typedef ATL::CString String;

    enum LogLevel
    {
        LogDebug = 0,
        LogInfo,
        LogWarning,
        LogError,
        LogNo
    };

    LPCTSTR LogLevelToString( LogLevel level );

    class ILogger
    {
    protected:

        virtual void WriteEntry_( LogLevel level, LPCTSTR message ) = 0;

        ~ILogger(){}

    public:

        void WriteEntry(LogLevel level, LPCTSTR message );        
    };        

    class LogEntry
    {
    private:        

        ILogger*                pLogger_;
        std::auto_ptr<String>    pBuffer_;
        LogLevel                Level_;                        

        LogEntry& operator =( const LogEntry& );        

    public:

        LogEntry::LogEntry( LogLevel level, ILogger* pLogger )
             : Level_( level ),
               pLogger_( pLogger ),
               pBuffer_( new String() )
            {}
        
        LogEntry::LogEntry( LogEntry& entry )
        : Level_( entry.Level_ ),
            pLogger_( entry.pLogger_ ),
            pBuffer_( entry.pBuffer_ )
            {    
                  entry.pLogger_ = NULL;        
            }

                LogEntry::~LogEntry()
            { 
           try
           {
            if( pBuffer_.get() && !pBuffer_->IsEmpty() && pLogger_ ) pLogger_->WriteEntry( Level_, (*pBuffer_) );
           }
           catch(...){}
            }        

        LogEntry LogEntry::print( LPCTSTR value )
            {             
            if( value && _tcslen(value) ) (*pBuffer_) += value;
            return *this;
                }
    };            

    template <typename Logger>
    LogEntry log( LogLevel level )
    {
        return LogEntry(level, &Logger::Instance() );
    }

    class HR
    {
    public:

        HRESULT HR_;

        explicit HR( HRESULT hr )
            : HR_(hr)
        {}        
    };    

    LogEntry operator <<( LogEntry& entry, LPCTSTR value );    
    LogEntry operator <<( LogEntry& entry, int value );    
    LogEntry operator <<( LogEntry& entry, unsigned int value );    
    LogEntry operator <<( LogEntry& entry, long value );    
    LogEntry operator <<( LogEntry& entry, unsigned long value );    
    LogEntry operator <<( LogEntry& entry, const HR& hr );    
    LogEntry operator <<( LogEntry& entry, LogEntry (*manip)( LogEntry& ) );    

    //manipulators

    LogEntry dot( LogEntry& entry );    
    LogEntry colon( LogEntry& entry );    
    LogEntry endl( LogEntry& entry );
    LogEntry comma( LogEntry& entry );    

    //endof manipulators    

    template <typename MTPolicy>
    class FileLogger : public Logging::ILogger, protected MTPolicy
    {
    private:

        class AutoLock
        {
            const FileLogger* pLogger_;

        public:

            AutoLock( const FileLogger* pLogger )
                : pLogger_( pLogger )
            {
                pLogger->Lock();
            }

            ~AutoLock()
            {
                pLogger_->Unlock();
                pLogger_ = NULL;
            }
        };

    private:

        FileLogger& operator =( const FileLogger& );
        FileLogger( const FileLogger& );

        ~FileLogger();

    public:

        FileLogger();
        
        static FileLogger& Instance()
        {
            static FileLogger fileLogger;
            return fileLogger;
        }


    private:
        

        //ILogger implementation
        void WriteEntry_( LogLevel level, LPCTSTR message );
    };

    class MultiThreadedPolicy;
    typedef FileLogger<MultiThreadedPolicy> DefaultLogger;

    template <typename T>
    LogEntry operator <<( LogLevel level, const T& value )
    {
        return LogEntry(level, &DefaultLogger::Instance() ) << value;
    }
    
    class MethodTracer
    {
    public:

        MethodTracer::MethodTracer(LPCTSTR location, HRESULT* pHr)
        : pHr_(pHr),
          Location_(location)
            {
            LogInfo<<_T("Enter              -> ") << Location_<< endl;
             }

            MethodTracer::~MethodTracer()
             {
            try
            {
            LogInfo<< _T("Return( ") << HR( pHr_ != NULL? *pHr_ : S_OK ) << _T(" ) <- ") << Location_ << endl;
            }
                catch( ... ){}
             }
    private:    

        String            Location_;
        HRESULT*        pHr_;    
    };

    //MT policies

    class SingleThreadedPolicy
    {
    protected:

        SingleThreadedPolicy(){}    

        void Lock(){}
        void Unlock(){}
    };

    class MultiThreadedPolicy
    {
        mutable ATL::CComAutoCriticalSection Mutex_;

    protected:        

        MultiThreadedPolicy(){}

        void Lock() const
        {
            Mutex_.Lock();
        }
        void Unlock() const
        {
            Mutex_.Unlock();
        }
    };
}
Estuve en Granada y me acorde' de ti
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.