Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Технология ведения лога (диагностика) 
:(
    Опции темы
archimed7592
Дата 26.11.2007, 18:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Архимед
****


Профиль
Группа: Завсегдатай
Сообщений: 2531
Регистрация: 12.6.2004
Где: Moscow

Репутация: 58
Всего: 93



Цитата(_Tanatos_ @  26.11.2007,  17:44 Найти цитируемый пост)
Попробовал - неработает!

Наверное это причуды твоего BCB smile.


--------------------
If you have an apple and I have an apple and we exchange apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these ideas, then each of us will have two ideas.
© George Bernard Shaw
PM Jabber   Вверх
Lazin
Дата 27.11.2007, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Цитата(_Tanatos_ @  26.11.2007,  17:44 Найти цитируемый пост)
Попробовал - неработает!
после неудачи порылся в инете на предмет использования, вот такой пример не работает
Что делаю не так?

Обрати внимание, на то, что std::uncaught_exception( ) срабатывает только если объект был создан внутри блока try, иначе он не вызывается. С Builderoм STL нормально работает...
PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 27.11.2007, 20:15 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата

Обрати внимание, на то, что std::uncaught_exception( ) срабатывает только если объект был создан внутри блока try, иначе он не вызывается. С Builderoм STL нормально работает...

Не знаю как нормально, но у меня не получилось добиться работы.

Добавлено через 13 минут и 38 секунд
Выкладываю на ваш суд черновой вариант классов

Начну с вспомогательных структур:
Код

//---------------------------------------------------------------------------
// Набор констант для использования с полем Type
typedef enum
{
    // Отрицательные значения зарезервированы, не использовать!
    lltBegin        = -1,
    lltComplete     = -2,

    lltDebug        = 0,
    lltInfo         = 1,
    lltNotice       = 2,
    lltError        = 3,
    lltCritical     = 4,
    lltAlert        = 5,
    lltEmergency    = 6,
}
TtLibLogType;
//---------------------------------------------------------------------------
class CtLibLogEngine;
class CtLibLogHelper;
//---------------------------------------------------------------------------
// Глобальный экземпляр класса
// TODO: в принципе надо заменить на указатель дабы
// для DLL можно было подключать логгер из основного процесса
extern CtLibLogEngine g_LogEngine;
//---------------------------------------------------------------------------
// Указатель на функцию-подписчик на событие добавления записи в журнал
typedef void (__closure *TtLibOnLogAdd)(CtLibLogEngine* LogEngine);
//---------------------------------------------------------------------------
// Массив подписчиков
typedef std::vector<TtLibOnLogAdd> TtLibLogSubscribers;
//---------------------------------------------------------------------------
// Эта структура описывает все данные одной записи
typedef struct
{
    unsigned long   m_RowID;    // Порядковый номер строки
    int             m_Level;    // Уровень вложенности
    signed char     m_LevelCh;  // Изменение уровня (пока не задействовано)
    int             m_ProcID;   // Идентификатор процесса
    int             m_UnitID;   // Идентификатор модуля
    signed char     m_Type;     // Тип сообщенияы
    int             m_Error;    // Код ошибки
    char*           m_Msg;      // Текст сообщения
    char*           m_Data;     // Дополнительные данные
    char*           m_Function; // Имя функции
    char*           m_File;     // Имя файла
    int             m_Line;     // Номер строки
    SYSTEMTIME      m_Time;     // Время события
}
TtLibLogRecord;
//---------------------------------------------------------------------------
// Структура для хранения условий обработки по модулям
typedef struct
{
    int     m_UnitID;   // Идентификатор модуля
    char    m_Min;      // Минимальное значение для m_Type
    char    m_Max;      // Максимальное значение для m_Type
}
TtLibLogCondition;
//---------------------------------------------------------------------------
// Массив условий
typedef std::vector<TtLibLogCondition> TtLibLogConditions;
//---------------------------------------------------------------------------


Далее привожу класс CtLibLogEngine.
Пока реализацию кинул в библиотечный файл, в будущем там останутся только inline функции
Код

class CtLibLogEngine
{
friend class CtLibLogHelper;
private:
    unsigned long       m_RowID;
    int                 m_Level;

    const char*         m_MsgBreakError;
    const char*         m_MsgBreakOk;
protected:
    TtLibLogRecord      m_Record;           // Данные сообщения
    TtLibLogConditions  m_Conditions;       // Условия фильтрации сообщений
    TtLibLogSubscribers m_Subscribers;      // Массив подписчиков на сообщения
public:
   ~CtLibLogEngine()
        {};
    CtLibLogEngine()
        : m_MsgBreakError(""),
          m_MsgBreakOk(""),
          m_RowID(0),
          m_Level(0),
          m_Record(TtLibLogRecord())
        {};

    void Initialize(const char* MsgBreakError, const char* MsgBreakOk)
    {
        m_MsgBreakError = MsgBreakError;
        m_MsgBreakOk = MsgBreakOk;
    }

    inline const TtLibLogRecord& getRecord() const
        { return m_Record; }

    void ConditionAdd(int UnitID, char Min, char Max)
        {
            m_Conditions.push_back();
            m_Conditions.back().m_UnitID = UnitID;
            m_Conditions.back().m_Min = Min;
            m_Conditions.back().m_Max = Max;
        }

    inline void SubscriberAdd(TtLibOnLogAdd OnLogAdd)
        {
            for(std::size_t i=0; i<m_Subscribers.size(); i++)
                if(m_Subscribers[i] == OnLogAdd)
                    return;
            m_Subscribers.push_back(OnLogAdd);
        }
    inline void SubscriberRemove(TtLibOnLogAdd OnLogAdd)
        {
            for(std::size_t i=0; i<m_Subscribers.size(); i++)
            {
                if(m_Subscribers[i] == OnLogAdd)
                {
                    for(std::size_t j=i; j<m_Subscribers.size()-1; j++)
                        m_Subscribers[j] = m_Subscribers[j+1];
                    m_Subscribers[m_Subscribers.size()-1] = NULL;
                    m_Subscribers.resize(m_Subscribers.size()-1);
                    return;
                }
            }
        };

    inline void RegisterMsg(int ProcID, int UnitID, char Type, int Error, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        {
            bool Ok = true;

            for(std::size_t i=0; i<m_Conditions.size(); i++)
                if(m_Conditions[i].m_UnitID)
                {
                    if(m_Conditions[i].m_Min<=Type && m_Conditions[i].m_Max>=Type)
                        { Ok = true; break; }
                    else
                        Ok = false;
                }

            m_RowID++;

            if(Ok)
            {
                m_Record.m_Level    = m_Level;
                m_Record.m_LevelCh  = 0;
                m_Record.m_RowID    = m_RowID;
                m_Record.m_ProcID   = ProcID;
                m_Record.m_UnitID   = UnitID;
                m_Record.m_Type     = Type;
                m_Record.m_Error    = Error;
                m_Record.m_Msg      = (char*) Msg;
                m_Record.m_Data     = (char*) Data;
                m_Record.m_Function = (char*) Function;
                m_Record.m_File     = (char*) File;
                m_Record.m_Line     = Line;
                GetLocalTime(&m_Record.m_Time);

                for(std::size_t i=0; i<m_Subscribers.size(); i++)
                    m_Subscribers[i](this);
            }
        }
};


Смотрим вспомогательный класс CtLibLogHelper
Код

class CtLibLogHelper
{
private:
    int             m_ProcID;       // Идентификатор процесса
    int             m_UnitID;       // Идентификатор модуля
    const char*     m_Function;     // Имя класса и функции
    const char*     m_File;         // Имя файла
    bool            m_Complete;     // Признак успешного завершения работы
protected:
public:
   ~CtLibLogHelper()
    {
        // Завершающая запись (номер строки неизвестен)
        if(m_Complete)
            g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, lltComplete, 0, g_LogEngine.m_MsgBreakOk, "", m_Function, m_File, 0);
        else
            g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, lltComplete, 0, g_LogEngine.m_MsgBreakError, "", m_Function, m_File, 0);
        g_LogEngine.m_Level--;
    }

    CtLibLogHelper(int ProcID, int UnitID, char Type, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        : m_Complete(false),
          m_ProcID(ProcID),
          m_UnitID(UnitID),
          m_Function(Function),
          m_File(File)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
        g_LogEngine.m_Level++;
    }
    CtLibLogHelper(char Type, const char* Msg, const char* Data, const char* Function, const char* File, int Line)
        : m_Complete(false),
          m_ProcID(0),
          m_UnitID(0),
          m_Function(Function),
          m_File(File)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
        g_LogEngine.m_Level++;
    }

    void Message(char Type, const char* Msg, const char* Data, int Line)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
    }

    void Error(char Type, int Error, const char* Msg, const char* Data, int Line)
    {
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, Error, Msg, Data, m_Function, m_File, Line);
    }

    void Complete(char Type, const char* Msg, const char* Data, int Line)
    {
        m_Complete = true;
        g_LogEngine.RegisterMsg(m_ProcID, m_UnitID, Type, 0, Msg, Data, m_Function, m_File, Line);
    }
};


А теперь пример использования:
Код

// Инициализация
    g_LogEngine.Initialize("...return (stack unwinding)", "...return (complete)");
// У меня эта функция отображает на экране лог в виде дерева
    g_LogEngine.SubscriberAdd(OnLogAdd);

// пример использования для записи в журнал
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    CtLibLogHelper LogHelper(lltBegin, "Процедура проверки вложенности...", ("Sender = " + Sender->ClassName()).c_str(), __FUNC__, __FILE__, __LINE__);
    LogHelper.Message(lltInfo, "Запускаем процедуру проверки", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №1", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №2", "", __LINE__);
    LogHelper.Message(lltInfo, "  Этап №3", "", __LINE__);
    LogHelper.Message(lltError, 12, "  Принудительный сбой в работе", "Здесь добавляем отладочную информацию.", __LINE__);
    LogHelper.Complete(lltInfo, "Процедура проверки выполнена!", "", __LINE__);
}

Тут даже без макросов все довольно компактно получается.
Имя функции и файла указываются только один раз.
PM MAIL   Вверх
_Tanatos_
Дата 27.11.2007, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Из текущих задач:
- сделать код безопасным для многопоточных приложений
- сделать глобальный объект указателем для поддержки DLL (при инициализации DLL надо соответственно инициировать указатель, а в основной программе соответственно создавать сам объект)
- Разработать класс (образец) подписчик для записи журнала в файл
- добавить раздельную нумерацию строк для потоков

Высказывайте свои пожелания, предложения и критику
PM MAIL   Вверх
_Tanatos_
Дата 27.11.2007, 21:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Примерно так выглядит класс записывающий данные в файл:

Код

class CtLibLogToFile
{
private:
    bool    m_Initialize;
    HANDLE  m_hFile;
protected:
public:
   ~CtLibLogToFile()
        {
            if(m_hFile)
                CloseHandle(m_hFile);
            m_hFile = NULL;
        }
    CtLibLogToFile()
        : m_Initialize(false),
          m_hFile(NULL)
        {};
    CtLibLogToFile(const char* FileName)
    {
        Initialize(FileName);
    };

    void Initialize(const char* FileName)
    {
        if(m_hFile==NULL && FileName!=NULL && m_Initialize==false)
        {
            m_hFile = CreateFile(FileName, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if(m_hFile != INVALID_HANDLE_VALUE)
                m_Initialize = true;
        }
        else
            // Повторная инициализация невозможна
            throw;
    };

    void OnLogAdd(CtLibLogEngine* LogEngine)
    {
        unsigned long   R;
        static char     Buffer[256];
        static char     Level[50] = "                                                         ";

        const TtLibLogRecord& Rec = LogEngine->getRecord();

        // Level
        WriteFile(m_hFile, Level, Rec.m_Level, &R, NULL);

        // №№ | Дата | Время
        sprintf(Buffer, "%010d  %04d//%02d//%02d %02d:%02d:%02d[%d]", Rec.m_RowID, Rec.m_Time.wYear, Rec.m_Time.wMonth, Rec.m_Time.wDay, Rec.m_Time.wHour, Rec.m_Time.wMinute, Rec.m_Time.wSecond, Rec.m_Time.wMilliseconds);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);
        // Код модуля | Код процесса/потока | Тип сообщения | Код ошибки |
        sprintf(Buffer, "  %04d %04d %03d %06d", Rec.m_UnitID, Rec.m_ProcID, Rec.m_Type, Rec.m_Error);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);

        // Текст сообщения | Данные | Файл | Строка |
        sprintf(Buffer, "  %s || %s || %s [%0d]", Rec.m_Msg, Rec.m_Data, Rec.m_File, Rec.m_Line);
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);

        sprintf(Buffer, "\n");
        WriteFile(m_hFile, Buffer, strlen(Buffer), &R, NULL);
    };
};


Пример использования
Код

// Глобально
CtLibLogToFile LogToFile;
...
// В рамках инициализации
    LogToFile.Initialize("D:\\Temp\\Log.txt"); // <===

    g_LogEngine = g_LogEngine->Initialize("...return (stack unwinding)", "...return (complete)");
    g_LogEngine->SubscriberAdd(OnLogAdd);
    g_LogEngine->SubscriberAdd(LogToFile.OnLogAdd); // <===


PM MAIL   Вверх
UnrealMan
Дата 28.11.2007, 00:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  27.11.2007,  20:15 Найти цитируемый пост)
Тут даже без макросов все довольно компактно получается.

Хреново без макросов получается. Во-первых, необходимость всюду прописывать __LINE__ - это ж... Кроме того, отладочная информация по логике вещей должна выводиться в лог только в отладочной версии программы. И желательно, чтобы в неотладочной версии параметры, представляющие собой отладочную информацию, зря не вычислялись. Указание типа лога через константу - IMHO, не самая лучшая идея.

Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко.
PM MAIL   Вверх
Lazin
Дата 28.11.2007, 09:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3820
Регистрация: 11.12.2006
Где: paranoid oil empi re

Репутация: 41
Всего: 154



Можно сделать так что-бы ф-я SubscriberAdd получала не указатель на ф-ю с модификатором __closure, а указатель на класс, а все "подписчики" имели-бы одинаковый интерфейс, это было-бы переносимо на другие компиляторы.
Цитата(_Tanatos_ @  27.11.2007,  21:07 Найти цитируемый пост)
Примерно так выглядит класс записывающий данные в файл:

Этот класс не учитывает одного простого факта - что возможен запуск нескольких копий одного приложения, в этом случае в файле будет мусор. Нужно синхронизировать доступ к файлу, например создать именованный мьютекс и захватывать его пере записью, и выводить в лог код процесса, что-бы можно было различать вывод разных процессов.
Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Указание типа лога через константу - IMHO, не самая лучшая идея.


Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко. 

Возможное решение этой проблеммы - использовать полиморфизм, передавать в объект logEngine объекты, содержащие нужную информацию. CtLibLogHelper может выступать в роли фабрики классов.

Добавлено через 2 минуты и 29 секунд
выглядеть это может так
Код

LogHelper.Message("blablabla");
LogHelper.AddValue("blablabla", val);
e.t.c.

PM MAIL Skype GTalk   Вверх
_Tanatos_
Дата 28.11.2007, 18:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Можно сделать так что-бы ф-я SubscriberAdd получала не указатель на ф-ю с модификатором __closure, а указатель на класс, а все "подписчики" имели-бы одинаковый интерфейс, это было-бы переносимо на другие компиляторы.


Принято:
Код

// Класс-подписчик на событие добавления записи в журнал
class CtLibLogSubscribe
{
public:
//    virtual ~CtLibLogSubscribe()=0;
    virtual void OnLogAdd(CtLibLogEngine* LogEngine)=0;
};

//---------------------------------------------------------------------------
typedef std::vector<CtLibLogSubscribe*> TtLibLogSubscribers;
//---------------------------------------------------------------------------

...

    inline void SubscriberAdd(CtLibLogSubscribe* Subscriber)
    inline void SubscriberRemove(CtLibLogSubscribe* Subscriber)

...

class CtLibLogToFile :
    public CtLibLogSubscribe
{
private:
...
}

В класс входит только одна функция, так как пока не вижу что еще может потребоваться.
Можно добавить туда же унифицированные функции настройки фильтра, аналогично классу Engine. Надо?

Добавлено через 4 минуты и 24 секунды
Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Цитата(_Tanatos_ @  27.11.2007,  21:07 )Примерно так выглядит класс записывающий данные в файл:Этот класс не учитывает одного простого факта - что возможен запуск нескольких копий одного приложения, в этом случае в файле будет мусор. Нужно синхронизировать доступ к файлу, например создать именованный мьютекс и захватывать его пере записью, и выводить в лог код процесса, что-бы можно было различать вывод разных процессов.


Не хочу чересчур усложнять класс, который должен работать максимально быстро.
ИМХО это обходится крайне просто, при инициализации лога добавьте к имени файла идентификатор процесса.
А если нужна паралельная запись в файл, то надо сделать отдельный класс для этой задачи
Таких классов-обработчиков может быть много. Например для накопления сообщений и отсылки их по почте и т.д.
В данном случае мой класс является лишь примером. Сначала закончу с ядром, а уж потом займусь переферией.

Добавлено через 8 минут и 54 секунды
Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Цитата(UnrealMan @  28.11.2007,  00:59 )Указание типа лога через константу - IMHO, не самая лучшая идея.

Совершенно непонятное заявление! Поясни, что тебе не нравится? Не нравятся константы, пишы числа, переменные, выражения, да что угодно - главное чтобы результат был signed char. А лучше всего напиши чем именно не нравится и для какой задачи тебе это не подходит.

P.S.
Не ленитесь подробней изложить свою мысль, ведь от этого напрямую зависит результат
PM MAIL   Вверх
_Tanatos_
Дата 28.11.2007, 19:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Цитата(UnrealMan @  28.11.2007,  00:59 Найти цитируемый пост)
Этот логгер неудобно будет использовать для вывода составных данных - когда строка формируется из подстрок, чисел и прочих данных с нужным форматированием. Придётся самому вычислять строку и передавать её логгеру - это как минимум громоздко. 

А как ты себе представляешь класс логгера должен форматировать твои данные? Мне кажется Вы на этот класс возлагаете не его задачи. И потом громоздкое и длинное сообщение будет неудобоваримым. А если речь идет о данных, то для них есть отдельное поле (см ниже). Как вспомогательный инструмент можно написать функцию аналогичную sprintf, но возвращающую указатель на готовую строку и вставлять эту конструкцию вместо сообщения:

LogHelper.Message(lltInfo, Format("  Этап №%d", Number), "", __LINE__);

Годится такой вариант?

Цитата(Lazin @  28.11.2007,  09:00 Найти цитируемый пост)
Возможное решение этой проблеммы - использовать полиморфизм, передавать в объект logEngine объекты, содержащие нужную информацию. CtLibLogHelper может выступать в роли фабрики классов.

Опять же непонятно зачем полиморфизм и причем тут фабрика классов? Приведенный тобой пример вполне подходит для формирования поля Data, если вызов функции AddValue будет приводить к добавлению строчки с именем переменной и ее значением. Если так, то согласен и постараюсь добавить несколько функций для работы с различными типами данных. Только проблема заключается в том, что надо будет так же добавлять функцию Post() в конце для определения момента когда сообщение полностью сформированно и его надо передать в класс Engine.

PM MAIL   Вверх
UnrealMan
Дата 28.11.2007, 21:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
А как ты себе представляешь класс логгера должен форматировать твои данные?

Так же, как это делают потоки типа fstream или хотя бы как fprintf.

Код
LOG_INFO("pass: ", std::setw(4), pass);
LOG_INFO_F("pass: %4.d", pass);


Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
Мне кажется Вы на этот класс возлагаете не его задачи. 

Это не первостепенная задача логгера, но тут можно провести аналогию с файловыми потоками и простым сишным выводом через fprintf. Для того, чтобы записать текст в файл, можно воспользоваться строковым потоком или sprintf и для результата вызвать write/fwrite. Но вряд ли кто-то захочет так делать, потому что проще не вводить лишние звенья, а сразу воспользоваться оператором << файлового потока или fprintf.

Цитата(_Tanatos_ @  28.11.2007,  19:15 Найти цитируемый пост)
Как вспомогательный инструмент можно написать функцию аналогичную sprintf, но возвращающую указатель на готовую строку и вставлять эту конструкцию вместо сообщения:

LogHelper.Message(lltInfo, Format("  Этап №%d", Number), "", __LINE__);

Годится такой вариант?

Это неудобный вариант.
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 12:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



В вашем примере указано только одно поле, а их как минимум два Сообщение и данные.
Такой вариант приемлим?
Код

LOG_INFO_MSG("Connect to %s", ServerName);
LOG_INFO_Data("Log: %4.d", login);
LOG_INFO_Data("pass: %4.d", pass);
LOG_WRITE;


P.S.
To All:
Спасибо всем, кто принимает участие в обсуждении!
Стараюсь учесть пожелания и предложения каждого
Сорри что изменения не так быстро воплощаются в код.

P.S.S.
Повторюсь, макросы пока не пишу так как они не сказываются на функциональности.
Понятное дело, что с ними короче и удобней, понятно что они будут и это даже не обсуждается.
Будут сделаны когда утоится состав и формат всех функций, так что не сердчайте на их отсутствие.
Но! Если здесь писать все в виде макросов, то совершенно непонятно получается что должно быть внутри.


Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 12:45
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Не хочется изобретать велосипед и раз уж речь зашла о форматировании, то хотелось бы использовать sprintf.
Только как передать переменное кол-во  аргументов дальше?

Код

void ExMsg(const char* Message, ...)
{
    ......
    sprintf(Temp, Message, /*Что здесь писать???*/);
    ......
}
    
Уважаемые гуру, нужен Ваш совет!
Писать множество отдельных функций для различных типов данных ИМХО нелогично (что делать если надо сразу два аргумента передать при форматировании одной строки?). 
PM MAIL   Вверх
UnrealMan
Дата 29.11.2007, 13:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  29.11.2007,  12:42 Найти цитируемый пост)
а их как минимум два Сообщение и данные

А какой резон отделять данные от сообщения?
PM MAIL   Вверх
UnrealMan
Дата 29.11.2007, 14:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 722
Регистрация: 30.3.2006

Репутация: 27
Всего: 32



Цитата(_Tanatos_ @  29.11.2007,  13:48 Найти цитируемый пост)
Только как передать переменное кол-во  аргументов дальше?

Код

#include <cstdarg>
#include <cstdio>

void Test(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    
    char tmp[0x1000];
    
    vsprintf(tmp, format, ap);
    va_end(ap);

    printf(tmp);
}

int main()
{
    Test("Text: %s%d%s", "str1 ", 123, " str2 ");
}


Добавлено через 3 минуты и 32 секунды
Есть одно маленькое "но": теперь тебе придётся помнить о возможности переполнения буфера, используемого для sprintf.

Это сообщение отредактировал(а) UnrealMan - 29.11.2007, 14:05
PM MAIL   Вверх
_Tanatos_
Дата 29.11.2007, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 20.11.2007

Репутация: нет
Всего: нет



Спасибо!

Сообщение от данных отделяю, для удобства.
Данных может быть много, но при беглом просмотре лога они лишь отвлекают глаз.
В сообщении содержится короткое сообщение по сути. В данных абсолютно все что угодно, что может быть полезным для отладки или даже можно написать подсказку для юзера smile, что-то вроде "Невозможно создать новый файл", "сначала закройте текущий файл!"

Вот пример использования:
Код

    LogHelper.ExMsg("Connect...");
    LogHelper.ExData("Address: %s\n", "address");
    LogHelper.ExData("Login: %s\n", "log");
    LogHelper.ExData("Password: %s\n", "pass123");
    LogHelper.ExPost(lltInfo, __LINE__);


У меня получилось немного иначе, так как при многократном вызове нужно сохранять предыдущую часть. Ну и требования по длинне сообщения смегчаются smile ограничена лишь максимальная длина сообщения для ОДНОЙ операции, но не общая
Так выглядит содержимое функции:
Код

    void ExMsg(const char* Message, ...)
    {
        int     Len = m_ExMessage ? strlen(m_ExMessage) : 0;
        char*   Temp = new char[Len+1024];
        va_list ap;

        if(m_ExMessage)
            strcpy(Temp, m_ExMessage);

        va_start(ap, Message);
        vsprintf(Temp+Len, Message, ap);
        va_end(ap);

        if(m_ExMessage)
        {
            delete m_ExMessage;
            m_ExMessage = NULL;
        }
        m_ExMessage = Temp;
    }


И парочку сервисных функций:
Код

    void ExPost(char Type, int Line)
    {
        g_LogEngine->RegisterMsg(m_ProcID, m_UnitID, Type, 0, m_ExMessage, m_ExData, m_Function, m_File, Line);
        ExReset();
    }
    void ExReset()
    {
        if(m_ExMessage)
        {
            delete m_ExMessage;
            m_ExMessage = NULL;
        }
        if(m_ExData)
        {
            delete m_ExData;
            m_ExData = NULL;
        }
    }

Естественно добавился вызов ExReset() в деструктор smile

Еще добавилось два конструктора:
Код

    CtLibLogHelper(int ProcID, int UnitID, char Type, const char* Function, const char* File, int Line);
    CtLibLogHelper(char Type, const char* Function, const char* File);

для инициализации класса без создания сообщения в конструкторе.

Это сообщение отредактировал(а) _Tanatos_ - 29.11.2007, 15:01
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1255 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.