Зачем?
Логи обеспечивают контроль за выполнением программы. Пока вы пишете программы для себя, можно не уделять им внимания, но для производственного программирования важность логов трудно переоценить. Ваша программа отправила запрос стороннему API на основе действий пользователя. Полученный ответ привел к сбою. Кто виноват? Ответ вы найдете в логах. Если вы его туда записали.
Trace
Я всегда пишу лог трассировки. Он обеспечивает обзор процесса выполнения программы. При этом важно записывать сообщения обо всех этапах процесса, а не только сообщения об успешных действиях или произошедших ошибках. Предположим вы грузите файлы с FTP партнера, проверяя его каждые пять минут. При этом в лог пишете сообщения о загруженных файлах и ошибках. Открыв однажды лог вы замечаете, что там нет новых сообщений. В чем причина? Файлов для загрузки нет? А может программа зависла? Или планировщик перестал ее запускать? Поэтому пишите в лог все. Запустились -- записали. Отправили запрос -- записали. Получили ответ -- записали. Информации в логе может быть мало, но много -- никогда. С подробным логом приведенные вопросы не останутся без ответа.
Error
Удобно писать сообщения об ошибках в отдельный файл. Есть лог ошибок, значит они были, нет лога -- значит все спокойно. Я пишу сообщения об ошибках и в лог трассировки и в лог ошибок. При этом в лог трассировки идет только сообщение, а подробное описание со стеком в лог ошибки. В этом случае лог трассировки дает представление об истории возникновения ошибки и не засоряется тоннами информации о стеке. Если ваша библиотека для логирования позволяет, то можно в сообщение добавить идентификатор для связи записей в разных логах. Будет удобнее искать.
Детальный лог
Рассмотрим следующий сценарий. Пользователь заполняет форму, на основе которой создается запрос, отправляемый партнеру. В ответ партнер присылает информацию, которая обрабатывается и предоставляется пользователю. Помимо трассировки в этом случае рекомендую сформировать детальный лог содержащий информацию о том, что ввел пользователь, текст запроса партнеру, его ответ, обработанный ответ с информацией почему он обработан так, параметры обработки такие как времена отправки и получения, длительности выполнения запросов и обработки. Такие логи удобно размещать в отдельных файлах, именую их значениями ключевых параметров. Как правило таких файлов получается много, поэтому разносите их в папки по дате и времени. Удобный формат для таких файлов -- XML или JSON.
Что писать в лог
Время. Полностью и настолько точно, насколько сможете. Программные процессы быстротечны, поэтому доли секунды также важны, как и остальные части представления времени. Не опускайте дату -- лучше увидеть ее в штампе времени, чем разглядывать в имени файла. Месяцы, дни часы и секунды лучше выводить с ведущими нулями, а доли секунд -- с завершающими. Так столбец штампа времени будет иметь постоянную ширину, а это удобнее чем скачущие влево-вправо строки. Что же касается формата часов, то я предпочитаю 24 часовой формат. Просто потому, что не хочу каждый раз искать AM/PM и вспоминать, что там до полудня, а что после.
Тип сообщения. TRACE, INFO, DEBUG, ERROR и прочее. Без типа никак.
Сообщение. Описание того, что произошло. Формат произвольный, но возможно вы захотите использовать автоматическую обработку или разбор сообщений, тогда ваши задачи подскажут вам более формализованный формат. Я обычно заключаю значения ключевых параметров, выводимых в сообщение в квадратные скобки, так их проще выделять. Что же касается содержания сообщения, то правило тут одно -- оно должно отвечать на вопросы, а порождать их. Текст должен быть однозначным и содержать значение ключевых параметров контекста, в котором инициирована запись в лог.
Это были обязательные части записи в лог. Ниже приведены дополнительные.
Идентификатор записи. Если у вас одна запись пишется в несколько файлов, то этот идентификатор позволит вам их надежно сопоставить. Некоторые библиотеки для логирования предоставляют такую возможность в конфигурации. Если нет, то можно добавить такой идентификатор в сообщение.
Идентификатор потока. Полезен в многопоточных приложениях. Позволит вам отфильтровать сообщения по разным потокам.
Класс объекта, который породил запись в лог. Полезен для локализации проблем. Также бывает возможность вывода имени метода.
Формат записи
Я предпочитаю формат "одна запись -- одна строка". Многострочные записи сложно разбирать, особенно в случае переменной длины. Если еще и табуляцию использовать как разделитель, то в табличный процессор они грузятся без проблем. А там можно и поискать, и пофильтровать, и статистику собрать.
Размер и хранение
Большие файлы сложно читать. Редкая читалка прожует файл размером в 1ГБ. Да и вам, думаю, надоест колесо крутить, пока такой файл пролистаете. Ведь не всегда мы знаем, что ищем. Так что разумное ограничение: несколько МБ. Библиотеки для логирования, как правило, имеют средства для контроля размеров файлов и серийного именования.
Логов мало не бывает, поэтому общий размер может быть большим. У меня был случай, когда генерировалось до 1ГБ логов в день. Думаю это не предел. Поэтому важно иметь схему архивирования логов. В тот раз логи подрезались раз в неделю, содержимое архивировалось, а архивы хранились до года.