logging — C ++ стиль Logger, который поддерживает макрос __LINE__ и другие

Я хочу сделать Logger, который можно использовать как std::cout, но я хочу записать некоторые дополнительные данные, такие как дата, время, __LINE__, __func__, а также __FILE__ который должен быть сохранен в файл автоматически.

пример

ToolLogger log;
log << "some data" << std::endl;

Ожидаемый результат

[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data

Неадекватное решение

Для этого я должен поставить макросы как __LINE__ прямо в строке, где я вызываю мой регистратор, иначе макросы не будут работать правильно. Я обнаружил, что могу заменить std::endl с моим макросом, который будет делать эту черную магию следующим образом:

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
<< ((ToolLogger::line = __LINE__) ? "" : "") \
<< ((ToolLogger::function = __func__).empty() ? "" : "") \
<< std::endl

Макрос logendl использует статические переменные из моего ToolLogger класс для сохранения значений __LINE__, __func__ а также __FILE__ нужно позже. Так что на самом деле использование логгера будет выглядеть так:

ToolLogger log;
log << "some data" << logendl;

В классе я должен перегружать operator<< чтобы заставить это работать, и мне нужно два из них. Один для принятия нормальных значений, таких как std::string или же int, а другой взять std::endl Манипулятор. Вот самые важные вещи из моего класса:

class ToolLogger
{
public:

// standard operator<< //
template<typename T>
ToolLogger& operator<< (const T& str)
{
out << str;
return *this;
}

// operator<< for taking the std::endl manipulator //
typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
typedef CoutType& (*StandardEndLine)(CoutType&);
ToolLogger& operator<<(StandardEndLine manip)
{
// save fileName, line and function to the file //
// and all what is already in stringstream //
// clear stringstream //
return *this;
}

static string fileName;
static int line;
static string function;

private:

ofstream file;
std::stringstream out;
};

string ToolLogger::fileName;
int ToolLogger::line;
string ToolLogger::function;

проблема

Проблема в этом решении заключается в том, что я могу использовать свой регистратор двумя способами:

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // compiles -> wrong /

Так что на самом деле мне нужно удалить operator<< из моего класса, который берет std::endl манипулятор, и решить его по-другому, но как это сделать? Я думал об изменении std::endl в logendl макрос в другой пользовательский манипулятор, а затем этот пользовательский манипулятор будет делать работу, которая на самом деле делает operator<<, но я понятия не имею, как это сделать. Я ищу другое решение, какие-либо предложения?

2

Решение

Вот что я делаю. Это как бы обходит ваш вопрос. То есть это избавляет от необходимости определять endl, Что я делаю, это выделить Logger класс (который просто берет строки и выводит их туда, куда вам нужно, чтобы пойти) из LogMessage класс, который создает сообщение.

Преимущества:

  • Каждый класс сам по себе довольно прост.

  • Очень простые макросы. Я не определяю макрос ниже, но это достаточно легко сделать.

  • Нет необходимости определять endl, Сообщение заканчивается точкой с запятой, когда класс LogMessage разрушает

Дайте мне знать, что вы думаете:

#include <iostream>
#include <sstream>
#include <string>

// logger class
// this is not complete, it exists just to illustrate the LogIt function
class Logger
{
public:
void LogIt(const std::string & s)
{
std::cout << s << std::endl;
}
};

// builds a logging message; outputs it in the destructor
class LogMessage
{
public:
// constructor
// takes identifying info of message.  You can add log level if needed
LogMessage(const char * file, const char * function, int line)
{
os << file << ": " << function << '('  << line << ") ";
}

// output operator
template<typename T>
LogMessage & operator<<(const T & t)
{
os << t;
return *this;
}

// output message to Logger
~LogMessage()
{
Logger logger; // get logger here (perhaps it's a singleton?)
logger.LogIt(os.str());
}
private:
std::ostringstream os;
};

int main()
{
// example usage
// typically this is invoked via a simple macro to reduce typing of the LogMessage constructor
LogMessage(__FILE__, __func__, __LINE__) << "this is an int " << 5;
}
2

Другие решения

Вы могли бы иметь LoggerAt класс с LoggerAt(const char*filename, int lineno) конструктор (возможно, подкласс std::ostringstreamи т. д.), затем определите

#define LOG(Out) do {LoggerAt(__FILE__,__LINE__) \
<< Out << std::endl; }while(0)

В некоторых моих проектах на C ++ я написал:

void mom_inform_at(const char*fil, int lin, std::ostringstream& out)
{ out.flush();
std::clog << fil << ":" << lin
<< " INFORM: " << out.str() << std::endl ;
}

#define MOM_INFORM_AT(Fil,Lin,Output) do {      \
std::ostringstream out_##Lin;               \
out_##Lin << mom_outlog << Output ;       \
mom_inform_at(Fil,Lin,out_##Lin);         \
} while(0)

#define MOM_INFORM_AT_BIS(Fil,Lin,Output) \
MOM_INFORM_AT(Fil,Lin,Output)

#define MOM_INFORM(Out)                         \
MOM_INFORM_AT_BIS(__FILE__,__LINE__,Out)

И используя что-то вроде MOM_INFORM("x=" << " point:" << pt); где вы могли бы представить себе обычный Point pt; пример с соответствующим std::ostream& operator << (std::ostream&out, const Point&point) функция.

Обратите внимание, что использовать удобно __FILE__ а также __LINE__ тебе лучше использовать макросы.

1

Я решил свою проблему. Другие ответы, размещенные здесь, могут быть лучше основных, но я хотел использовать logger простым способом, как в C ++ std::cout используется. Также моё решение может быть неоптимальным и может привести к другим проблемам, но оно отвечает моим требованиям.

Я добавил кастом std::ostream

class CustomOstream : public std::ostream
{
public:

static CustomOstream& endl( CustomOstream& out )
{
return out;
}
};

и изменил макрос, чтобы использовать endl функция от CustomOstream

#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
<< ((ToolLogger::line = __LINE__) ? "" : "") \
<< ((ToolLogger::function = __func__).empty() ? "" : "") \
<< ToolLogger::CustomOstream::endl

Так же operator<< из основного класса был изменен

ToolLogger& operator<< (CustomOstream& (*f)(CustomOstream&))
{
// do something //
return *this;
}

Теперь регистратор можно использовать так, как я хотел

log << "some data" << logendl;   // correct //
log << "some data" << std::endl; // won't compile -> correct //
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector