slf4j-spring-boot-starter
Введение
Компонент, который упрощает работу с логированием, используя аннотации. Помогает избежать необходимости писать код для логирования в разных местах. Также позволяет определять местоположение кода.
Архитектура программного обеспечения
Зависит от spring-boot-starter-aop.
Принцип работы
AOP + Reflect.
Область применения
Любые методы, вызываемые из spring.
Текущая версия 1.4.10.
Инструкция по установке
mvn clean install
Использование
<dependency>
<groupId>wiki.xsx</groupId>
<artifactId>slf4j-spring-boot-starter</artifactId>
<version>1.4.10</version>
</dependency>
yml:
logging:
level:
wiki.xsx.core: 对应级别
# 日志组件配置(按需配置)
slf4j:
# 全局综合日志级别
global-log-level: debug
# 全局综合日志代码定位
global-log-position: unknown
# 全局综合日志格式化
global-log-formatter: wiki.xsx.core.log.DefaultLogFormatter
# 全局综合日志回调
global-log-callback: wiki.xsx.core.log.VoidLogCallback
# 全局参数日志级别
global-param-log-level: debug
# 全局参数日志代码定位
global-param-log-position: unknown
# 全局参数日志格式化
global-param-log-formatter: wiki.xsx.core.log.DefaultParamLogFormatter
# 全局参数日志回调
global-param-log-callback: wiki.xsx.core.log.VoidLogCallback
# 全局结果日志级别
global-result-log-level: debug
# 全局结果日志代码定位
global-result-log-position: unknown
# 全局结果日志格式化
global-result-log-formatter: wiki.xsx.core.log.DefaultResultLogFormatter
# 全局结果日志回调
global-result-log-callback: wiki.xsx.core.log.VoidLogCallback
# 全局异常日志回调
global-throwing-log-callback: wiki.xsx.core.log.VoidLogCallback
properties:
logging.level.wiki.xsx.core=对应级别
# 日志组件配置(按需配置)
# 全局综合日志级别
logging.slf4j.global-log-level=debug
# 全局综合日志代码定位
logging.slf4j.global-log-position=unknown
# 全局综合日志格式化
logging.slf4j.global-log-formatter=wiki.xsx.core.log.DefaultLogFormatter
# 全局综合日志回调
logging.slf4j.global-log-callback=wiki.xsx.core.log.VoidLogCallback
# 全局参数日志级别
logging.slf4j.global-param-log-level=debug
# 全局参数日志代码定位
logging.slf4j.global-param-log-position=unknown
# 全局参数日志格式化
logging.slf4j.global-param-log-formatter=wiki.xsx.core.log.DefaultParamLogFormatter
# 全局参数日志回调
logging.slf4j.global-param-log-callback=wiki.xsx.core.log.VoidLogCallback
# 全局结果日志级别
logging.slf4j.global-result-log-level=debug
# 全局结果日志代码定位
logging.slf4j.global-result-log-position=unknown
# 全局结果日志格式化
logging.slf4j.global-result-log-formatter=wiki.xsx.core.log.DefaultResultLogFormatter
# 全局结果日志回调
logging.slf4j.global-result-log-callback=wiki.xsx.core.log.VoidLogCallback
# 全局异常日志回调
logging.slf4j.global-throwing-log-callback=wiki.xsx.core.log.VoidLogCallback
@RestController
public class TestParamLogController {
@ParamLog(value = "ParamLog-test1")
@GetMapping("/paramLogTest1")
public Map<String, Object> logTest1(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
return result;
}
@ParamLog(value = "ParamLog-test2", paramFilter = {"request"})
@GetMapping("/paramLogTest2")
public Map<String, Object> logTest2(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
return result;
}
@ParamLog(value = "ParamLog-test3", paramFilter = {"request"}, callback = LogTestCallback.class)
@GetMapping("/paramLogTest3")
public Map<String, Object> logTest3(HttpServletRequest request, List<Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
``` 1. **Аннотация @ResultLog**:
* Пример использования аннотации для маркировки методов контроллера:
* *@RestController* — аннотация, которая помечает класс как контроллер;
* *public class TestResultLogController* — класс контроллера;
* *value = «ResultLog-test1»* — значение ключа аннотации, которое будет использоваться при логировании;
* *@GetMapping(«/resultLogTest1»)* — метод контроллера, который принимает запросы на получение данных с адреса /resultLogTest1;
* *Map<String, Object> logTest1(HttpServletRequest request, Map<String, Object> param)* — метод, в котором происходит обработка запроса и формирование ответа;
* *return result;* — возврат сформированного ответа.
* Второй метод контроллера:
* *@ResultLog(value = «ResultLog-test2», callback = LogTestCallback.class)* — использование аннотации с указанием значения ключа и класса, реализующего интерфейс *LogCallback*.
2. **Интерфейс LogCallback**:
* *public interface LogCallback* — интерфейс, который определяет метод *callback* для обработки логов;
* *void callback(Annotation annotation, MethodInfo methodInfo, Map<String, Object> paramMap, Object result)* — метод интерфейса, который получает информацию о методе, параметрах и результате выполнения метода и выводит сообщение в лог.
3. **Тестирование**:
* В данном разделе представлен тестовый класс *TestResultLogControllerTest*, который проверяет работу методов контроллера *logTest1* и *logTest2*.
4. **Пример вывода логов**:
В этом разделе представлены примеры сообщений из лога, которые содержат информацию о вызовах методов *logTest1*, *logTest2* и *logTest3*.
5. **@ResultLog** — пример использования:
* Представлен пример использования аннотации *@ResultLog* для методов контроллера.
Это перевод исходного текста. Если у вас есть дополнительные вопросы или уточнения, пожалуйста, дайте мне знать. **Перевод текста на русский язык:**
#####
4. Эффект печати журнала:
2020-01-16 22:09:15.873 DEBUG 9280 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestResultLogController.logTest1(TestResultLogController.java:23)], название бизнес-операции: [ResultLog-test1], результат возврата: [{msg=success, data={key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}, code=200}] 2020-01-16 22:09:15.874 DEBUG 9280 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestResultLogController.logTest2(TestResultLogController.java:33)], название бизнес-операции: [ResultLog-test2], результат возврата: [{msg=success, data=[test-list, {key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}], code=200}] 2020-01-16 22:09:15.874 INFO 9280 --- [ main] wiki.xsx.log.controller.LogTestCallback : Метод wiki.xsx.log.controller.TestResultLogController.logTest2 успешно выполнил функцию обратного вызова
##### Пример аннотации @ThrowingLog:
1. Маркировка аннотацией журнала:
```java
@RestController
public class TestThrowingLogController {
@ThrowingLog(value = "ThrowingLog-test1")
@GetMapping("/throwingLogTest1")
public Map<String, Object> logTest1(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", 1/0);
return result;
}
@ThrowingLog(value = "ThrowingLog-test2", callback = LogTestCallback.class)
@GetMapping("/throwingLogTest2")
public Map<String, Object> logTest2(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", 1/0);
return result;
}
}
// Добавление в контейнер IOC, иначе вызов завершится неудачно
@Component
@Slf4j
public class LogTestCallback implements LogCallback {
@Override
public void callback(Annotation annotation, MethodInfo methodInfo, Map<String, Object> paramMap, Object result) {
log.info(methodInfo.getClassAllName()+"."+methodInfo.getMethodName()+" метод обратного вызова успешно выполнен");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestThrowingLogControllerTest {
@Autowired
private TestThrowingLogController testThrowingLogController;
@Test
public void test() {
List<Object> list = new ArrayList<>(2);
list.add("test-list");
Map<String, Object> paramMap = new LinkedHashMap<>(5);
paramMap.put("key1", "hello-world");
paramMap.put("key2", 11);
paramMap.put("key3", 11.11D);
paramMap.put("key4", new String[]{"hello", "world"});
paramMap.put("key5", new Number[]{111, 112, 113});
list.add(paramMap);
try {
this.testThrowingLogController.logTest1(null, paramMap);
}catch (Exception e) {
}
try {
this.testThrowingLogController.logTest2(null, paramMap);
}catch (Exception e) {
}
}
}
2020-01-16 22:13:58.226 ERROR 22184 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestThrowingLogController.logTest1], название бизнес-операции: [ThrowingLog-test1], информация об ошибке:
java.lang.ArithmeticException: / by zero
...
2020-01-16 22:13:58.227 ERROR 22184 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestThrowingLogController.logTest2], название бизнес-операции: [ThrowingLog-test2], информация об ошибке:
java.lang.ArithmeticException: / by zero
...
2020-01-16 22:13:58.228 INFO 22184 --- [ main] wiki.xsx.log.controller.LogTestCallback : Метод wiki.xsx.log.controller.TestThrowingLogController.logTest2 успешно выполнил функцию обратного вызова
``` **Аннотация @Log: пример использования**
1. **Аннотации для маркировки журнала:**
```java
@RestController
public class TestLogController {
@Log(value = "Log-test1")
@GetMapping("/logTest1")
public Map<String, Object> logTest1(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
return result;
}
@Log(value = "Log-test2", paramFilter = {"request"})
@GetMapping("/logTest2")
public Map<String, Object> logTest2(HttpServletRequest request, Map<String, Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
return result;
}
@Log(value = "Log-test3", paramFilter = {"request"}, callback = LogTestCallback.class)
@GetMapping("/logTest3")
public Map<String, Object> logTest3(HttpServletRequest request, List<Object> param) {
Map<String, Object> result = new HashMap<>(3);
result.put("code", 200);
result.put("msg", "success");
result.put("data", param);
return result;
}
}
// 加入IOC容器,否则 вызов не удастся
@Component
@Slf4j
public class LogTestCallback implements LogCallback {
@Override
public void callback(Annotation annotation, MethodInfo methodInfo, Map<String, Object> paramMap, Object result) {
log.info(methodInfo.getClassAllName() + "." + methodInfo.getMethodName() + "метод обратного вызова успешно выполнен");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestLogControllerTest {
@Autowired
private TestLogController testLogController;
@Test
public void test() {
List<Object> list = new ArrayList<>(2);
list.add("test-list");
Map<String, Object> paramMap = new LinkedHashMap<>(5);
paramMap.put("key1", "hello-world");
paramMap.put("key2", 11);
paramMap.put("key3", 11.11D);
paramMap.put("key4", new String[]{"hello", "world"});
paramMap.put("key5", new Number[]{111, 112, 113});
list.add(paramMap);
this.testLogController.logTest1(null, paramMap);
this.testLogController.logTest2(null, paramMap);
this.testLogController.logTest3(null, list);
}
}
2020-01-16 22:16:25.736 DEBUG 8304 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestLogController.logTest1 (TestLogController.java:23)], бизнес-имя: [Log-test1], получение параметров: [{request=null, param={key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}}], возврат результата: [{msg=success, data={key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}, code=200}], затраченное время: 50 мс
2020-01-16 22:16:25.744 DEBUG 8304 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestLogController.logTest2 (TestLogController.java:33)], бизнес-имя: [Log-test2], получение параметров: [{param={key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}}], возврат результата: [{msg=success, data={key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}, code=200}], затраченное время: 30 мс
2020-01-16 22:16:25.744 DEBUG 8304 --- [ main] wiki.xsx.core.log.LogProcessor : Вызов метода: [wiki.xsx.log.controller.TestLogController.logTest3 (TestLogController.java:43)], бизнес-имя: [Log-test3], получение параметров: [{параметр=[test-list, {key1=hello-world, key2=11, key3=11.11, key4=[hello, world], key5=[111, 112, 113]}]]}, возврат результата: [{msg=успех, данные=[test-список, {key1=привет-мир, key2=11, key3=11.11, key4=[привет, мир], key5=[111, 112, 113]}], код=200}], затраченное время: 40 мс
2020-01-16 22:16:25.744 INFO 8304 --- [ main] wiki.xsx.log.controller.LogTestCallback : Метод wiki.xsx.log.controller.TestLogController.logTest3 успешно выполнил обратный вызов
``` **Позиция**: код-позиционирование, по умолчанию DEFAULT.
**paramFilter**: фильтр параметров по имени, по умолчанию {}.
**formatter**: форматирование, для разных типов журналов используются разные реализации (реализация соответствует интерфейсу и должна быть помещена в IOC-контейнер).
**callback**: лог-обратный вызов, по умолчанию VoidLogCallback.class (реализует интерфейс wiki.xsx.core.log.LogCallback и должен быть помещён в IOC-контейнер).
#### Журналы
1. **DEBUG (по умолчанию)**: уровень отладки.
2. **INFO**: информационный уровень.
3. **WARN**: уровень предупреждения.
4. **ERROR**: уровень ошибки.
#### Описание интерфейса комплексного форматирования журналов
```java
public interface LogFormatter {
/**
* Форматирование
* @param log объект журнала
* @param level уровень журнала
* @param busName название бизнес-процесса
* @param methodInfo информация о методе
* @param args список параметров
* @param filterParamNames список фильтруемых параметров
* @param result результат
*/
void format(
Logger log,
Level level,
String busName,
MethodInfo methodInfo,
Object[] args,
String[] filterParamNames,
Object result
);
}
public interface ParamLogFormatter {
/**
* Форматирование
* @param log объект журнала
* @param level уровень журнала
* @param busName название бизнес-процесса
* @param methodInfo информация о методе
* @param args список параметров
* @param filterParamNames список фильтруемых параметров
*/
void format(
Logger log,
Level level,
String busName,
MethodInfo methodInfo,
Object[] args,
String[] filterParamNames
);
}
public interface ResultLogFormatter {
/**
* Форматирование
* @param log объект журнала
* @param level уровень журнала
* @param busName название бизнес-процесса
* @param methodInfo информация о методе
* @param result результат
*/
void format(
Logger log,
Level level,
String busName,
MethodInfo methodInfo,
Object result
);
}
public interface LogCallback {
/**
* Метод обратного вызова
* @param annotation используемая аннотация
* @param methodInfo информация о методе
* @param paramMap словарь параметров
* @param result результат вызова метода
*/
void callback(
Annotation annotation,
MethodInfo methodInfo,
Map<String, Object> paramMap,
Object result
);
}
Если уровень журнала — DEBUG, то код-позиционирование включается по умолчанию для удобства отладки.
Для других уровней код-позиционирование отключается по умолчанию, чтобы уменьшить ненужные расходы. При необходимости его можно включить вручную (position=Position.ENABLED).
CPU | AMD Athlon(tm) II X4 640 Processor(3000 Mhz) |
Память | 8.00 GB (1333 MHz) |
Жёсткий диск | Apacer A S510S 128GB SATA Disk Device |
Инструмент тестирования | Apache JMeter 5.1.1 |
Способ тестирования | HTTP-запрос, тест на печать журнала, цикл 5 раз, берётся последний результат |
С включённой функцией позиционирования кода:
Тип журнала | Количество параллельных запросов | Среднее время одного запроса (мс) | Пропускная способность (запросов в секунду) |
---|---|---|---|
@ParamLog | 1000 | 136 | 484 |
@ResultLog | 1000 | 86 | 417 |
@Log | 1000 | 29 | 425 |
С отключённой функцией позиционирования кода:
Тип журнала | Количество параллельных запросов | Среднее время одного запроса (мс) | Пропускная способность (запросов в секунду) |
---|---|---|---|
@ParamLog | 1000 | 274 | 491 |
@ResultLog | 1000 | 66 | 519 |
@Log | 1000 | 108 | 483 |
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )