Какой смысл забивать?

Мне было интересно, какой смысл засорять? Насколько я могу судить, clog такой же, как cerr, но с буферизацией, поэтому он более эффективен. Обычно stderr - это то же самое, что и stdout, поэтому засорение - это то же самое, что и cout. Мне это кажется довольно неубедительным, поэтому я полагаю, что я неправильно это понимаю. Если я отправляю сообщения журнала в то же место, куда отправляются сообщения об ошибках (возможно, что-то в /var/log/messages), то, вероятно, я не слишком много пишу (так что при использовании забуференный серр). По моему опыту, я хочу, чтобы мои сообщения журнала были актуальными (не буферизованными), чтобы я мог помочь найти сбой (поэтому я не хочу использовать буферизованную засоренность). Видимо, я всегда должен использовать cerr.

Я хотел бы иметь возможность перенаправить засорение внутри моей программы. Было бы полезно перенаправить cerr, чтобы при вызове библиотечной процедуры я мог контролировать, куда идут cerr и clog. Могут ли некоторые компиляторы это поддерживать? Я только что проверил DJGPP и stdout определен как адрес структуры FILE, поэтому запрещается делать что-то вроде "stdout = freopen(...)".

  • Можно ли перенаправить clog, cerr, cout, stdin, stdout и / или stderr?
  • Единственная разница между буферизацией и засорением?
  • Как мне внедрить (или найти) более надежное средство ведения журнала (ссылки, пожалуйста)?

3 ответа

Решение

Можно ли перенаправить clog, cerr, cout, stdin, stdout и / или stderr?

Да. Вы хотите rdbuf функция.

ofstream ofs("logfile");
cout.rdbuf(ofs.rdbuf());
cout << "Goes to file." << endl;

Единственная разница между буферизацией и засорением?

Насколько я знаю, да.

Если вы находитесь в среде оболочки posix (я действительно думаю о bash), вы можете перенаправить любой файловый дескриптор на любой другой файловый дескриптор, поэтому для перенаправления вы можете просто:

$ myprogram 2>&5 

перенаправить stderr в файл, представленный как fd=5.

Изменить: если подумать, мне больше нравится ответ @Konrad Rudolph о перенаправлении. rdbuf() - более понятный и переносимый способ сделать это.

Что касается журналирования, ну... я начну с библиотеки Boost для всего, что есть в C++, которого нет в библиотеке std. Вот: Boost Logging v2

Редактировать: Boost Logging не является частью Boost Libraries; оно было рассмотрено, но не принято.

Изменить: 2 года спустя, в мае 2010 года, Boost принял библиотеку журналов, которая теперь называется Boost.Log.

Конечно, есть альтернативы:

  • Log4Cpp (API в стиле log4j для C++)
  • Log4Cxx (API в стиле log4j, спонсируемый Apache)
  • Pantheios (несуществующий? В прошлый раз, когда я пытался, я не мог заставить его основываться на недавнем компиляторе)
  • Google's GLog (шляпный совет @SuperElectric)

Там также Windows Event Logger.

И пара статей, которые могут быть полезны:

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

#include <fstream>
#include <iostream>

class redirecter
{
public:
    redirecter(std::ostream & dst, std::ostream & src)
        : src(src), sbuf(src.rdbuf(dst.rdbuf())) {}
    ~redirecter() { src.rdbuf(sbuf); }
private:
    std::ostream & src;
    std::streambuf * const sbuf;
};

void hello_world()
{
    std::cout << "Hello, world!\n";
}

int main()
{
    std::ofstream log("hello-world.log");
    redirecter redirect(log, std::cout);
    hello_world();
    return 0;
}

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

Перенаправления

Конрад Рудольф ответ хорош в отношении того, как перенаправить std::clog (std::wclog).

В других ответах рассказывается о различных возможностях, таких как использование перенаправления командной строки, например 2>output.log. В Unix вы также можете создать файл и добавить другой вывод к своим командам с помощью чего-то вроде3>output.log. Затем в вашей программе вы должны использоватьfdномер 3 для печати журналов. Вы можете продолжить печать наstdout а также stderrкак обычно. Visual Studio IDE имеет аналогичную функцию с ихCDebug команда, которая отправляет свой вывод в окно вывода IDE.

stderr такой же как stdout?

Обычно это правда, но в Unix вы можете настроить stderr к /dev/console что означает, что он переходит к другому tty(он же терминал). В наши дни он используется редко. У меня так было на IRIX. Я бы открыл отдельное X-Window и увидел в нем ошибки.

Также многие люди отправляют сообщения об ошибках на /dev/null. В командной строке вы пишете:

command ...args... 2>/dev/null

системный журнал

Одна вещь не упомянута, в Unix у вас также есть syslog().

Новейшие версии под Linux (и, вероятно, Mac OS/X) делают намного больше, чем раньше. В частности, он может использовать идентификатор и некоторые другие параметры для перенаправления журналов в конкретный файл (т.е.mail.log). Механизм системного журнала может использоваться между компьютерами, поэтому журналы с компьютера A могут быть отправлены на компьютер B. И, конечно, вы можете фильтровать журналы различными способами, особенно по степени серьезности.

В syslog() также очень прост в использовании:

syslog(LOG_ERR, "message #%d", count++);

Он предлагает 8 уровней (или серьезности), формат а-ля printf(), и список аргументов для формата.

Программно вы можете настроить несколько вещей, если сначала вызовете openlog()функция. Вы должны позвонить в него перед первым звонком вsyslog().

Как упоминалось в unixman83, вместо этого вы можете использовать макрос. Таким образом, вы можете включать некоторые параметры в свои сообщения, не повторяя их снова и снова. Может быть, что-то вроде этого (см. Variadic Macro):

// (not tested... requires msg to be a string literal)
#define LOG(lvl, msg, ...) \
     syslog(lvl, msg " (in " __FILE__ ":%d)", __VA_ARGS__, __LINE__)

Вы также можете найти __func__ полезно.

Перенаправление, фильтрация и т. Д. Выполняется путем создания файлов конфигурации. Вот пример из моего проекта Snapchat:

mail.err /var/log/mail/mail.err
mail.* /var/log/mail/mail.log
& stop

Я устанавливаю файл под /etc/rsyslog.d/ и запустите:

invoke-rc.d rsyslog restart

так что syslog сервер обрабатывает эти изменения и сохраняет все журналы, относящиеся к почте, в эти папки.

Примечание: мне также нужно создать /var/log/mail папку и файлы внутри папки, чтобы убедиться, что все работает правильно (потому что в противном случае почтовый демон может не иметь достаточных разрешений).

snaplogger (маленький штекер)

Я использовал log4cplus, который, начиная с версии 1.2.x, неплох. Однако у меня есть три минуса:

  1. это требует, чтобы я полностью очистил все, если я хочу позвонить fork(); как-то не переживает fork(); вызовите правильно... (по крайней мере, в той версии, в которой я использовал поток)
  2. файлы конфигурации (.properties) нелегко управлять в моей среде, где мне нравится, когда администраторы вносят изменения, не изменяя исходный
  3. он использует C++03, и сейчас мы находимся в 2019 году... Я бы хотел иметь хотя бы C++11

Из-за этого, и особенно из-за пункта (1), я написал свою собственную версию под названием snaplogger. Однако это не совсем отдельный проект. Я использую много других проектов из среды snapcpp (гораздо проще просто получить snapcpp и запуститьbin/build-snapскрипт или просто загрузите двоичные файлы с панели запуска.)

Преимущество использования регистратора, такого как snaplogger или log4cplus, заключается в том, что вы обычно можете определить любое количество пунктов назначения и многие другие параметры (например, уровень серьезности, предлагаемый syslog()). Log4cplus может отправлять свои выходные данные во многие разные места: файлы, системный журнал, систему журналов MS-Windows, консоль, сервер и т. Д. Проверьте приложения в этих двух проектах, чтобы получить представление о списке возможностей. Интересным фактором здесь является то, что любой журнал можно отправить по всем адресатам. Полезно иметь файл с именем all.log, куда все ваши службы отправляют свои журналы. Это позволяет понять определенные ошибки, что было бы не так просто с отдельными файлами журнала при параллельном запуске множества служб.

Вот простой пример в файле конфигурации снаплоггера:

[all]
type=file
lock=true
filename=/var/log/snapwebsites/all.log

[file]
lock=false
filename=/var/log/snapwebsites/firewall.log

Обратите внимание, что для all.logfile Мне нужна блокировка, чтобы несколько писателей не искажали журналы между собой. Это не обязательно для[file] раздел, потому что у меня есть только один процесс (без потоков) для этого.

Оба предлагают вам возможность добавлять свои собственные приложения. Так, например, если у вас есть приложение Qt с окном вывода, вы можете написать приложение для отправки выводаSNAP_LOG_ERROR() звонки в это окно.

snaplogger также предлагает вам способ расширить поддержку переменных в сообщениях (также называемых форматом). Например, я могу вставить дату, используя ${date}переменная. Затем я могу настроить его с помощью параметра. Чтобы вывести только год, я использую${date:year}. Эта поддержка переменных параметров также является расширяемой.

snaploggerможет фильтровать вывод по серьезности (например, syslog), по регулярному выражению и по компонентам. У нас естьnormal и secure компонент, по умолчанию normal. Я хочу, чтобы журналы отправлялись наsecureкомпонент для записи в защищенные файлы. Это означает, что подкаталог более защищен, чем обычные журналы, которые может просматривать большинство администраторов. Когда я запускаю свои HTTP-службы, иногда я отправляю информацию, например, последние 3 цифры кредитной карты. Я предпочитаю хранить их в безопасном журнале. Это также могут быть ошибки, связанные с паролем. На самом деле все, что я считаю угрозой безопасности в журнале. Опять же, компоненты являются расширяемыми, так что вы можете иметь свои собственные.

Одна маленькая деталь о классе перенаправителя. Его нужно уничтожить как следует, и только один раз. Деструктор гарантирует, что это произойдет, если функция, в которой он объявлен, действительно возвращается, а сам объект никогда не копируется.

Чтобы гарантировать невозможность копирования, укажите операторы частного копирования и присваивания:

class redirecter
{
public:
    redirecter(std::ostream & src, std::ostream & dst)
        : src_(src), sbuf(src.rdbuf(dst.rdbuf())) {}
    ~redirecter() { src.rdbuf(sbuf); }
private:
    std::ostream & src_;
    std::streambuf * const sbuf_;
    // Prevent copying.                        
    redirecter( const redirecter& );
    redirecter& operator=( const redirecter& );
};

Я использую эту технику, перенаправляя std::clog в файл журнала в main(). Чтобы гарантировать, что main () действительно вернется, я помещаю внутренности main () в блок try/catch. Затем в другом месте моей программы, где я мог бы вызвать exit(), вместо этого я генерирую исключение. Это возвращает управление в main (), которая затем может выполнить оператор return.

Basic Logger

#define myerr(e) {CriticalSectionLocker crit; std::cerr << e << std::endl;}

Используется в качестве myerr("ERR: " << message); или же myerr("WARN: " << message << code << etc);

Это очень эффективно.

Затем сделайте:

./programname.exe 2> ./stderr.log
perl parsestderr.pl stderr.log

или просто разобрать stderr.log вручную

Я признаю, что это не для чрезвычайно критичного к производительности кода. Но кто все равно это пишет.

Другие вопросы по тегам