Какой смысл забивать?
Мне было интересно, какой смысл засорять? Насколько я могу судить, 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, неплох. Однако у меня есть три минуса:
- это требует, чтобы я полностью очистил все, если я хочу позвонить
fork();
как-то не переживаетfork();
вызовите правильно... (по крайней мере, в той версии, в которой я использовал поток) - файлы конфигурации (
.properties
) нелегко управлять в моей среде, где мне нравится, когда администраторы вносят изменения, не изменяя исходный - он использует 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.log
file Мне нужна блокировка, чтобы несколько писателей не искажали журналы между собой. Это не обязательно для[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 вручную
Я признаю, что это не для чрезвычайно критичного к производительности кода. Но кто все равно это пишет.