среда, 20 января 2010 г.

Debug database errors

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

Так исторически сложилось, что c базой данных мне приходится работать через OLE DB. Для получения текстовых сообщений об ошибках взаимодействия с БД можно использовать замечательную функцию AtlTraceErrorRecords. Эта функция выводит подробную информацию об ошибке на нужном языке, но работает только в отладочной сборке и выводит сообщения не в наш лог, что, очевидно, не очень удобно. Доработать эту функцию несложно, т.к. она предоставляется в исходных кодах в файле atldbcli.h. После доработки выглядит она следующим образом:
namespace DBUtils {

CString TraceErrorRecords(HRESULT hrErr) const
{
    CDBErrorInfo ErrorInfo;
    ULONG   cRecords;
    HRESULT   hr;
    ULONG   i;
    CComBSTR  bstrDesc, bstrHelpFile, bstrSource;
    GUID   guid;
    DWORD   dwHelpContext;
    WCHAR   wszGuid[40];
    CString   msg;

    // If the user passed in an HRESULT then trace it
    if (hrErr != S_OK)
        SystemLog<LOG_DEBUG > (L"OLE DB Error Record dump for hr = 0x%x") % hrErr;

    LCID lcLocale = GetSystemDefaultLCID();

    hr = ErrorInfo.GetErrorRecords(&cRecords);
    if (FAILED(hr) && ErrorInfo.m_spErrorInfo == NULL) {
        SystemLog<LOG_DEBUG>(L"No OLE DB Error Information found: hr = 0x%x") % hr;
    }
    else {
        for (i = 0; i < cRecords; i++) {
            hr = ErrorInfo.GetAllErrorInfo(i, lcLocale, &bstrDesc, &bstrSource, &guid,
                &dwHelpContext, &bstrHelpFile);
            if (FAILED(hr)) {
                SystemLog<LOG_DEBUG>(L"OLE DB Error Record dump retrieval failed: hr = 0x%x") % hr;
                return msg;
            }
            StringFromGUID2(guid, wszGuid, sizeof(wszGuid) / sizeof(WCHAR));
            CString tmpMsg;
            tmpMsg.Format(_T("Row #: %4d Source: \"%s\" Description: \"%s\" Help File: \"%s\" Help Context: %4d GUID: %s\n"),
                i, OLE2T(bstrSource), OLE2T(bstrDesc), OLE2T(bstrHelpFile), dwHelpContext, OLE2T(wszGuid));
            SystemLog<LOG_DEBUG>(tmpMsg);
            bstrSource.Empty();
            bstrDesc.Empty();
            bstrHelpFile.Empty();

            msg += tmpMsg;
        }
        SystemLog<LOG_DEBUG>(L"OLE DB Error Record dump end");
    }

    return msg;
}

}//namespace DBUtils
Для сохранения в файл здесь используется уже описанная ранее функция SystemLog. В результате в файл сохраняются сообщения, которые легко понять даже пользователю. Результат функции также можно использовать в окне Task Dialog в разделе с дополнительной технической информацией.