Логирование в log4j подразумевает что любой LoggingEvent может быть преобразован в сериализованую форму. Способ преобразования определяется механизмом Layout. Так, например, PatternLayout с шаблоном "%d{ISO8601} %-5p [%c{1}] %m%n" и выводит строки похожие на Dec 14, 2011 12:16:39 PM org.apache.catalina.loader.WebappClassLoader loadClass INFO: Illegal access: this web application instance has been stopped already. а SimpleLayout - строки, похожие, на "INFO - Illegal access: this web application instance has been stopped already". В стандартной поставке идут Layouts - HTMLLayout, (Enhanced)PatternLayout, SimpleLayout, которые не подходят для автоматического разбора. Также в поставке в новых версиях идет XMLLayout, который, логирует в псевдо-xml(в well-formed xml должен быть только 1 корневой элемент, а XMLLayout просто пишет xml-элемент на каждый LoggingEvent, без вывода корневого элемента), в котором все данные представлены в удобном для разбора формате.
<log4j:event logger="RESOURCE_ANOMALY" timestamp="1325028851405" level="WARN" thread="main"> <log4j:message><![CDATA[Cannot close opened file]]></log4j:message> <log4j:throwable><![CDATA[java.lang.IOException: Disk Quota Exceeded at MainClazz.generateLogMessage(MainClazz.java:43) at MainClazz.main(MainClazz.java:30) ]]></log4j:throwable> <log4j:properties> <log4j:data name="ADDRESS" value="127.0.0.1"/> <log4j:data name="UserAgent" value="Wget/3.0.1"/> </log4j:properties> </log4j:event>
XML не является удобным для поиска форматом(о господи, неужели мы будем парсить гигабайтные XML при каждом поиске? no way.), и писать в файл я его бы не стал. Скорее XML является хорошим промежуточным форматом. Такой event можно записать в индексированую БД. Писать в БД можно как с каждого логгирующего агента посредством собственного Appender, так и с центрального сервера. Во втором случае мы получаем некоторые преимущества - возможность централизовать конфигурацию логгирования, сокртыие используемого движка хранения. К движку данных не предъявляется больших требований, всего скорее, пойдет любой с возможностью полнотекстового поиска - clientserver SQL БД, файловая SQL БД, NoSQL(riak, voldemort? там все впорядке с FTS?) и даже просто FTS движок, такой как lucene(и его производные) или sphinx. Необходимо учитвать ожидаемые нагрузки а так же требования к сохранности данных(отдельное упоминание о том, что, например соответствие PCI/DSS или Sarbanes-Oxley act требует наличие logging and log retention policy). Все написанное далее учитывает следующие нагрузки и условия логирования - 11(расширяемость до 70) логгирующих агентов, 200МБ сырых логов в день с каждого агента, пики до 100Кб/с логов с агента в течении секунды, сохранность данных по крайней мере - 30 дней, обязательное асинхронное логирования(нельзя себе позволить приложению подтормаживать из-за latency записи логов), отсутствие особых требований к сохранности логов при внештатных ситуациях(вне /scope/ ситуации недоступности сервера логирования, однако надо предусмотреть возможные события), отсутствие необходимости realtime поиска(приемлим delay между логированием события у клиента и индексированием в 10м).
Последний фактор(отсутствие необходимости в realtime поиске) делает интересным вариантом логирование в mongodb, который по умолчанию настроен на асинхронную запись(без fsync на диск). Учитвая паттерны поиска по логам, интересным вариантом являются FTS движки. Если FTS движок позволяет не просто индексировать данные и искать по ним(возвращать id записей), но и хранить сами кортежи индексируемых данных, то можно обойтись одним только FTS движком.
Учитывая паттерны ведения логов необходимо также предусмотреть возможность сопроставления записям в логах нуля и более файлов. Так, например, внешний SOAP, XML-RPC, REST, или просто HTTP POST интерфейсы, обладают компактной формой записи входящего запроса. Такие файлы неплохо аттачить к записи в логах. Сама функциональность логирования файлов является ортогональной логированию диагностических записей, поэтому, я бы вынес ее их скоупа *этой* системы. Сейчас я вижу механизм аттача(колэйшн, collation) файлов к записи логов таким(это псевдокод, не ожидайте, что он синтаксически корректен):
class MindlessClient{ ... if (request.invalid()) { String requestLoggedUUID = FileLogger.persistFile(request.getOutputStream()); String configurationFileUUID = FileLogger.persistFile(Configuration.toString()); LOG.error("Request invalid. UUIDs request: "+requestLoggedUUID +", configuration: "+configurationFileUUID ); // no guarantees, that files are successfully saved. we don't bother, anyway } ... } class FileLogger{ Thread processorLog = new Thread(new Runnable(){ @override public void run() { while(true) { try{ Pair<Object,String> p = queue.take(); File f = new File("/path/to/shared/nfs/"+p.getSecond()); IOUtils.write(f,p.getFirst()); // blocking, cleans up } catch(InterruptedException ie) {/* disregard that*/} catch(IOException ioe) {LOGGER.error(e,e);} } } }); static{ processorLog.start(); } BlockingQueue<Pair<Object,String>> filesToPersist=new LinkedBlockingQueue<Pair<Object,String>>(); /* dont block, return always instantly*/ public static String persistFile(String/* or InputStream */ content){ String filename="FILE"+UUID.randomUUID(); // Should generate something like FILE{0aa1ff-08fa-1112ac-77341a} which is distinguishable, unique reference to file in log. filesToPersist.add(Pair.of(content,filename)); return filename; }; public static FileWriteStatus getFileStatus(String uuid) { //check queues, check file existence on fs, return status: WAITING, WRITTEN, MISSING } }
Т.е механизмы логирования и коллэйшна файлов разделены и ведутся по-разному, единственное их место соприкосновения - текст сообщения, в котором существуют референсы на UUIDы файлов, хранящихся на NFS диске. Если надо найти по UUID содержимое файла - это тривиально, если наоборот(найти сообщение по UUID) - то делается FTS по тексту сообщения.