Немного теории
Думаю, все те кто программирует обязательно да что-нибудь логирует. Почти любое логирование полезно как для самого разработчика, так и для конечных пользователей приложения. В частности программирования на Java используется такая библиотека как log4j. Однако в плане настроек логирования с этой библиотекой не все так гладко. Не, ну конечно кто любит форматы конфигурационных файлов отличающихся от стандартных, то ладно. По-умолчанию, библиотека использует настройки из файла log4j.properties
Вид настроек примерно может выглядеть так:
log4j.rootLogger=DEBUG, consoleAppender, fileAppender
log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender=org.apache.log4j.RollingFileAppender
log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern=[%t] %-5p %c %x - %m%n
log4j.appender.fileAppender.File=demoApplication.log
А может выглядеть как в xml-формате:
<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
<Properties>
<Property name="name1">value</property>
<Property name="name2" value="value2"/>
</Properties>
<Filter type="type" ... />
<Appenders>
<Appender type="type" name="name">
<Filter type="type" ... />
</Appender>
...
</Appenders>
<Loggers>
<Logger name="name1">
<Filter type="type" ... />
</Logger>
...
<Root level="level">
<AppenderRef ref="name"/>
</Root>
</Loggers>
</Configuration>
А также в JSON-формате:
{ "configuration": { "status": "error", "name": "RoutingTest",
"packages": "org.apache.logging.log4j.test",
"properties": {
"property": { "name": "filename",
"value" : "target/rolling1/rollingtest-$${sd:type}.log" }
},
"ThresholdFilter": { "level": "debug" },
"appenders": {
"Console": { "name": "STDOUT",
"PatternLayout": { "pattern": "%m%n" },
"ThresholdFilter": { "level": "debug" }
},
"Routing": { "name": "Routing",
"Routes": { "pattern": "$${sd:type}",
"Route": [
{
"RollingFile": {
"name": "Rolling-${sd:type}", "fileName": "${filename}",
"filePattern": "target/rolling1/test1-${sd:type}.%i.log.gz",
"PatternLayout": {"pattern": "%d %p %c{1.} [%t] %m%n"},
"SizeBasedTriggeringPolicy": { "size": "500" }
}
},
{ "AppenderRef": "STDOUT", "key": "Audit"}
]
}
}
},
"loggers": {
"logger": { "name": "EventLogger", "level": "info", "additivity": "false",
"AppenderRef": { "ref": "Routing" }},
"root": { "level": "error", "AppenderRef": { "ref": "STDOUT" }}
}
}
}
Каждый такой формат представляет из себя:
Громоздкость
Увеличение вероятности появления ошибки при конфигурировании администратором (и разработчиками)
Сложность чтения
Давно принятый формат для конфигураций все же лежит в Linux (и не только). Вид его выглядит примерно так:
[section]
key1=value1
key2=value2
#какой-то комментарий
[section2]
key1=value1
key2=value2
Преимущества такого вида:
Аккуратность
Легкочитаемость
Практика
Теперь перейдем к собственно к коду для тех кто все же предпочитает конфигурационные файлы для своих программ иметь в стиле nix
Маны на сам Logger - https://logging.apache.org/log4j/2.x/
Первым делом, в своем пакете объявляете все необходимое для работы с log4j:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.lang.Integer;
import java.lang.Boolean;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
Потому как у меня это является отдельным классом, в котором приложение при запуске подсасывает все конфигурационные параметры, то соответственно создаете отдельный класс в пакете:
public class customLogger {
static public class lg{
static public Logger log;
}
private String loggingLevel;
private String loggingSize_log;
private String loggingDirectory;
private String loggingPattern;
private int loggingRotation;
private String parseConfigFile(String section, String param) throws Exception{
String value = ""; //Значение для вывода
final String contentFile = new String(Files.readAllBytes(Paths.get("/<... путь до конфига>/config.conf")), StandardCharsets.UTF_8); //файл с конфигурацией
String regex = "\\["+section+"\\].*?[^#]"+param+"\\=(.*?)(\\n|$)"; //формируем правило для регулярки
final Pattern patte = Pattern.compile(regex, Pattern.DOTALL | Pattern.MULTILINE);
final Matcher match = patte.matcher(contentFile);
match.find(); //инициализируем поиск
value = match.group(1);
return value;
}
public void init() throws Exception{
loggingLevel = this.parseConfigFile("logging", "level");
loggingDirectory = this.parseConfigFile("logging", "directory");
loggingSize_log = this.parseConfigFile("logging", "size_log");
loggingPattern = this.parseConfigFile("logging", "pattern");
loggingRotation = Integer.parseInt(this.parseConfigFile("logging", "rotation"));
Level lvl = Level.INFO; //задаем уровень логирования, подтягиваем из файла конфигурации
switch(loggingLevel) {
case "info":
lvl = Level.INFO;
break;
case "standard":
lvl = Level.INFO;
break;
case "trace":
lvl = Level.TRACE;
break;
case "debug":
lvl = Level.DEBUG;
break;
case "all":
lvl = Level.ALL;
break;
case "none":
lvl = Level.OFF;
break;
default:
lvl = Level.ALL;
break;
}
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.ERROR); //уровень логирования самого Logger при инициализации
builder.setConfigurationName("RollingBuilder"); //название конфигуратора логирования
builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL).addAttribute("level", lvl)); //Пороговые фильтры
AppenderComponentBuilder file = builder.newAppender("Stdout", "CONSOLE").addAttribute("target",
ConsoleAppender.Target.SYSTEM_OUT);
file.add(builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
builder.add(file);
LayoutComponentBuilder standard = builder.newLayout("PatternLayout").addAttribute("pattern", "%d %-5level: %msg%n%throwable");
file.add(standard); //формат логирования (Дата, Уровень логирования, Передаваемое сообщение в логер)
ComponentBuilder triggeringPolicy = builder.newComponent("Policies");
triggeringPolicy.addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?"));//периодичность создания нового лога
triggeringPolicy.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", loggingSize_log));//размер лога при котором будет создан новый лог
file = builder.newAppender("rolling", "RollingFile"); //инициализация блока для настройки записи в файл
file.addAttribute("fileName", loggingDirectory); //директория+имя файла, желательное полное имя
file.addAttribute("filePattern", loggingRotation); //директория+имя файла при ротации
file.add(standard);
file.addComponent(triggeringPolicy);
builder.add(file);
//Добавление логгеров, по которым будет происходить логирование.
builder.add(builder.newLogger("PFSLogger", lvl).add(builder.newAppenderRef("rolling")).addAttribute("additivity", false));
//Настройка рут логера, в случае если сообщения не будут определены через newLogger
builder.add(builder.newRootLogger(lvl).add(builder.newAppenderRef("rolling")));
Configurator.initialize(builder.build());//Инициализируем весь соборанный конфиг
//Получаем имя логгер, позже созданного (имя getLogger должно быть таким же как в newLogger, иначе будет происходить по newRootLogger, со своим уровнем логирования)
lg.log = LogManager.getLogger("PFSLogger");
lg.log.info("Запускается Logger...");
}
}
Далее, чтобы инициализировать логгер, нужно в главном пакете вызвать метод
/////Инициализируем логгер
customLogger log = new customLogger();
log.init();
В итоге, лог будет создан по директории, указанный в строке
file.addAttribute("fileName", loggingDirectory); //директория+имя файла, желательное полное имя