{"id":493005,"date":"2026-08-31T21:25:36","date_gmt":"2026-08-31T21:25:36","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=493005"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=493005","title":{"rendered":"\u0423\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 Spring Boot \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e AOP"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Spring Boot \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u043c \u044d\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439, \u0433\u0434\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0442\u0430\u043a \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.<\/p>\n<p>\u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0431\u0430\u0437\u0435 Spring AOP, \u043a\u043e\u0442\u043e\u0440\u043e\u0435:<\/p>\n<ul>\n<li>\n<p>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430\u0445<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0447\u0435\u0440\u0435\u0437 WebClient<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0433\u0438\u0431\u043a\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0443\u0440\u043e\u0432\u043d\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ul>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u0440\u0435\u0448\u0438\u043b\u0438 \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e<\/h3>\n<p>\u0412 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043d\u0430 \u0440\u0430\u043d\u043d\u0438\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0436\u0438\u043b\u043e \u0441\u0432\u043e\u0435\u0439 \u0436\u0438\u0437\u043d\u044c\u044e \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 &#8212; \u043a\u0442\u043e-\u0442\u043e \u043d\u0430\u0432\u0435\u0448\u0438\u0432\u0430\u043b \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043a\u0442\u043e-\u0442\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e <code>log.info()<\/code> \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430, \u043a\u0442\u043e-\u0442\u043e \u0437\u0430\u0431\u044b\u0432\u0430\u043b \u0437\u0430\u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0430\u0436\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0444\u043e\u0440\u043c\u0430\u0442 \u043b\u043e\u0433\u043e\u0432 \u0440\u0430\u0437\u043d\u044b\u0439, \u0433\u0434\u0435-\u0442\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0432\u0435\u0442\u044f\u0442\u0441\u044f \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u0431\u044b\u043b \u043f\u0440\u043e \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0430 \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0434\u0435 \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u0432\u044b\u043a\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0440\u0438 \u0442\u043e\u043c, \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u043a\u0430 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0430\u0437\u0430\u043d\u0430 \u043f\u043e \u0432\u0441\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044f\u043c<\/p>\n<p>\u041d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 HTTP \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0435<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/h3>\n<p>\u0421\u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u0432\u0430 \u0430\u0441\u043f\u0435\u043a\u0442\u0430 \u0438 \u043f\u0430\u0447\u043a\u0443 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439. \u041f\u0435\u0440\u0432\u044b\u0439 \u0430\u0441\u043f\u0435\u043a\u0442 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432, \u0432\u0442\u043e\u0440\u043e\u0439 &#8212; \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0441 WebClient. \u0414\u043b\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f \u043d\u0430 \u043f\u043e\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0430\u043c\u0430 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0430 \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c.<\/p>\n<p>\u041d\u0430\u0448\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432:<\/p>\n<h4>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432<\/h4>\n<p>\u0412\u0435\u0448\u0430\u0435\u0448\u044c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e \u043d\u0430 \u043c\u0435\u0442\u043e\u0434 &#8212; \u043e\u043d \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043e\u0442\u0432\u0435\u0442. \u041c\u043e\u0436\u043d\u043e \u0441\u043a\u0440\u044b\u0442\u044c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043f\u0430\u0440\u043e\u043b\u0438, \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u0444\u0430\u0439\u043b\u044b), \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438.<\/p>\n<pre><code class=\"java\">@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface ControllerMonitoring {    String value();    boolean ignoreRequestBody() default false;    String ignoreRequestReplacement() default \"ignored\";    boolean ignoreResponseBody() default false;    String ignoreResponseReplacement() default \"ignored\";    String[] headers() default {};}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"java\">@RestController@RequestMapping(\"\/api\/users\")public class UserController {    @PostMapping    @ControllerMonitoring(value = \"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\", headers = {\"X-Request-ID\", \"User-Agent\"})    public ResponseEntity&lt;UserResponse&gt; createUser(@RequestBody UserRequest request) {        return ResponseEntity.ok(response);    }    @PostMapping(\"\/login\")    @ControllerMonitoring(value = \"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f\", ignoreRequestBody = true)    public ResponseEntity&lt;TokenResponse&gt; login(@RequestBody LoginRequest request) {        return ResponseEntity.ok(token);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f <code>login<\/code> \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u043b\u043e\u0433 \u043d\u0435 \u043f\u043e\u043f\u0430\u0434\u0451\u0442, \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u201cignored\u201d. \u0414\u043b\u044f <code>createUser<\/code> \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0438\u0448\u0443\u0442\u0441\u044f \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 <code>X-Request-ID<\/code> \u0438 <code>User-Agent<\/code>.<\/p>\n<p>\u0421\u0430\u043c \u0430\u0441\u043f\u0435\u043a\u0442:<\/p>\n<pre><code class=\"java\">@Aspectpublic class ControllerMonitoringAspect {    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerMonitoringAspect.class);    @Pointcut(\"@annotation(ControllerMonitoring)\")    public void controllerMonitoring() {    }    @Around(\"controllerMonitoring()\")    public Object logMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();        String login = getLogin(); \/\/ \u0417\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438        request.setAttribute(\"startTime\", System.currentTimeMillis());        storeEventName(joinPoint);        logRequest(request, joinPoint, login);        Object result = joinPoint.proceed();        logResponse(request, result, joinPoint);        return result;    }    private void logRequest(HttpServletRequest request, ProceedingJoinPoint joinPoint, String login) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        ControllerMonitoring annotation = signature.getMethod().getAnnotation(ControllerMonitoring.class);        try {            Object body = annotation.ignoreRequestBody()                    ? annotation.ignoreRequestReplacement()                    : extractRequestBody(joinPoint);            Log log = Log.event(annotation.value() + \" (Request)\")                    .with(\"method\", request.getMethod())                    .with(\"path\", request.getRequestURI())                    .with(\"params\", request.getQueryString())                    .with(\"body\", JsonUtils.getJsonObject(body))                    .with(\"login\", login);            logHeaders(request, annotation, log);            LOGGER.info(log.toString());        } catch (Exception e) {            LOGGER.error(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\", e);        }    }    private void logHeaders(HttpServletRequest request, ControllerMonitoring annotation, Log log) {        String[] headersToLog = annotation.headers();        for (String headerName : headersToLog) {            String headerValue = request.getHeader(headerName);            if (headerValue != null) {                log.with(\"header.\" + headerName, headerValue);            } else {                LOGGER.warn(\"\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a '{}' \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\", headerName);            }        }    }    private void logResponse(HttpServletRequest request, Object result, ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        ControllerMonitoring annotation = signature.getMethod().getAnnotation(ControllerMonitoring.class);        try {            HttpStatus status = HttpStatus.OK;            Object body = result;            if (result instanceof ResponseEntity&lt;?&gt; response) {                status = HttpStatus.valueOf(response.getStatusCode().value());                body = annotation.ignoreResponseBody() ? annotation.ignoreResponseReplacement() : response.getBody();            }            long duration = System.currentTimeMillis() - (Long) request.getAttribute(\"startTime\");            LOGGER.info(Log.event(annotation.value() + \" (Response)\")                    .with(\"status\", status.value())                    .with(\"body\", JsonUtils.getJsonObject(body))                    .with(\"duration\", duration)                    .toString());        } catch (Exception e) {            LOGGER.error(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0442\u0432\u0435\u0442\u0430\", e);        }    }    private void storeEventName(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        LoggerUtils.addEventAttribute(signature.getMethod().getAnnotation(ControllerMonitoring.class).value());    }    private Object extractRequestBody(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Parameter[] parameters = signature.getMethod().getParameters();        for (int i = 0; i &lt; parameters.length; i++) {            if (parameters[i].isAnnotationPresent(RequestBody.class)) {                return joinPoint.getArgs()[i];            }        }        return null;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043b\u043e\u0433\u0430:<\/p>\n<p>Request \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f &#8212; \u0441 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c\u0438:<\/p>\n<pre><code class=\"json\">{  \"event\": \"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (Request)\",  \"method\": \"POST\",  \"path\": \"\/api\/users\",  \"params\": \"\",  \"body\": {    \"username\": \"user1\",    \"email\": \"example@example.com\"  },  \"login\": \"user1\",  \"header.X-Request-ID\": \"a1b2c3d4-e5f6-7890\",  \"header.User-Agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64)\"}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"json\">{  \"event\": \"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (Response)\",  \"status\": 200,  \"body\": {    \"id\": 12345,    \"username\": \"user1\",    \"email\": \"example@example.com\"  },  \"duration\": 345}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043b\u043e\u0433\u0438\u043d &#8212; \u0442\u0435\u043b\u043e \u0441\u043a\u0440\u044b\u0442\u043e:<\/p>\n<pre><code class=\"json\">{  \"event\": \"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f (Request)\",  \"method\": \"POST\",  \"path\": \"\/api\/users\/login\",  \"params\": \"\",  \"body\": \"ignored\",  \"login\": \"admin\"}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u043c\u0435\u043d\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 \u0432 HTTP case-insensitive, \u0438 <code>getHeader()<\/code> \u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430<\/p>\n<h4>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h4>\n<p>\u0412 \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 WebClient \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0432\u0438\u0434\u0435\u0442\u044c &#8212; \u043a\u0442\u043e, \u043a\u0443\u0434\u0430 \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043f\u043e\u0448\u0451\u043b.<\/p>\n<p>\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f:<\/p>\n<pre><code class=\"java\">@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface ClientRequestMonitoring {    String value() default \"\";}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043a\u043b\u0438\u0435\u043d\u0442\u0435:<\/p>\n<pre><code class=\"java\">@Componentpublic class UserServiceClient {    private final WebClient webClient;    @ClientRequestMonitoring(\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\")    public Mono&lt;UserInfo&gt; getUserInfo(String userId) {        return webClient.get()                .uri(\"\/users\/{id}\", userId)                .retrieve()                .bodyToMono(UserInfo.class);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u0441\u043f\u0435\u043a\u0442 \u0434\u043b\u044f \u043a\u0440\u0438\u0435\u043d\u0442\u043e\u0432:<\/p>\n<pre><code class=\"java\">@Aspectpublic class ClientRequestMonitoringAspect {    private static final Logger LOGGER = LoggerFactory.getLogger(ClientRequestMonitoringAspect.class);    @Pointcut(\"@annotation(ClientRequestMonitoring)\")    public void clientRequestMonitoring() {    }    @Around(\"clientRequestMonitoring()\")    public Object logMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {        storeEventInContext(joinPoint);        logRequest(joinPoint);        return joinPoint.proceed();    }    private void logRequest(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        Object[] args = joinPoint.getArgs();        try {            String event = getEventName(joinPoint);            Log log = Log.event(event + \" (Request)\");            if (args.length &gt; 0) {                Parameter[] parameters = method.getParameters();                for (int i = 0; i &lt; parameters.length; i++) {                    log.with(parameters[i].getName(), JsonUtils.getJsonObject(args[i]));                }            }            LOGGER.info(log.toString());        } catch (Exception e) {            LOGGER.error(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430\", e);        }    }    private String getEventName(JoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        String clientName = \"[\" + method.getDeclaringClass().getSimpleName() + \"] \";        return clientName + method.getAnnotation(ClientRequestMonitoring.class).value();    }    private void storeEventInContext(ProceedingJoinPoint joinPoint) {        String clientEvent = getEventName(joinPoint);        LoggerUtils.addClientEventAttribute(clientEvent);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043b\u043e\u0433\u0430:<\/p>\n<pre><code class=\"json\">{  \"event\": \"[UserServiceClient] \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u0430 (Request)\",  \"userId\": \"12345\"}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0442\u0432\u0435\u0442 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0447\u0435\u0440\u0435\u0437 <code>ExchangeFilterFunction<\/code> \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 WebClient, \u0430\u0441\u043f\u0435\u043a\u0442 \u0442\u0443\u0442 \u043d\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442.<\/p>\n<h4>\u041f\u0440\u043e\u043a\u0438\u0434\u044b\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0438\u0437 \u0430\u0441\u043f\u0435\u043a\u0442\u043e\u0432 \u0431\u044b\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0437\u0430 \u0438\u0445 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u043c\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 <code>@RestControllerAdvice<\/code>, \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439, \u0434\u0430 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0446\u0435\u043f\u043e\u0447\u043a\u0430\u0445), \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0442\u0438\u043b\u0438\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"java\">public class LoggerUtils {    public static final String CLIENT_EVENT = \"client_event\";    public static final String EVENT = \"event\";    public static void addClientEventAttribute(Object value) {        if (RequestContextHolder.getRequestAttributes() == null) {            return;        }        RequestContextHolder.currentRequestAttributes()                .setAttribute(CLIENT_EVENT, value, RequestAttributes.SCOPE_REQUEST);    }    public static String getClientEventAttribute() {        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();        if (requestAttributes != null) {            return (String) requestAttributes.getAttribute(CLIENT_EVENT, RequestAttributes.SCOPE_REQUEST);        } else {            return StringUtils.EMPTY;        }    }    public static void addEventAttribute(Object value) {        RequestContextHolder.currentRequestAttributes()                .setAttribute(EVENT, value, RequestAttributes.SCOPE_REQUEST);    }    public static String getEventAttribute() {        return (String) RequestContextHolder.currentRequestAttributes()                .getAttribute(EVENT, RequestAttributes.SCOPE_REQUEST);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c <code>ThreadLocalAccessor<\/code>, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043d\u0435 \u0442\u0435\u0440\u044f\u043b\u0441\u044f \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432.<\/p>\n<h4>\u041c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/h4>\n<p>\u041f\u043e\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435. \u0425\u043e\u0447\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043d\u0430 \u043f\u0440\u043e\u0434\u0435 \u043e\u043d\u0438 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043b\u0438\u0441\u044c, \u0430 \u043f\u0440\u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0435 \u0438\u043b\u0438 \u043d\u0430 \u0434\u0435\u0432\u0435 &#8212; \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\u0438\u0441\u044c. \u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0434\u0435\u043f\u043b\u043e\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u043b\u0438 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f:<\/p>\n<pre><code class=\"java\">@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface IgnoreLogging {    String replacement() default \"ignored\";    LogLevel showAtLevel() default LogLevel.DEBUG;    enum LogLevel {        TRACE, DEBUG, INFO, WARN, ERROR    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"java\">public class UserRequest {    private String username;    @IgnoreLogging    private String password;    @IgnoreLogging(replacement = \"****\")    private String creditCard;    @IgnoreLogging(showAtLevel = LogLevel.TRACE)    private String secretAnswer;}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 <code>INFO<\/code> \u0432\u0441\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f \u0441\u043a\u0440\u044b\u0442\u044b, \u043f\u043e\u0434\u043d\u044f\u043b\u0438 \u0434\u043e <code>DEBUG<\/code> \u043f\u0430\u0440\u043e\u043b\u044c \u0438 \u043d\u043e\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u044b \u0432\u0438\u0434\u043d\u044b, \u043f\u043e\u0434\u043d\u044f\u043b\u0438 \u0434\u043e <code>TRACE<\/code> \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u0441\u0451 \u0432\u0438\u0434\u043d\u043e. \u041c\u0435\u043d\u044f\u0435\u043c <code>application.yaml<\/code>\/\u2018application.properties\u2019 \u0438 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0440\u0435\u0434\u0435\u043f\u043b\u043e\u044f.<\/p>\n<p>\u041f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 Jackson <code>BeanSerializerModifier<\/code>:<\/p>\n<pre><code class=\"java\">public class IgnoreLoggingSerializerModifier extends BeanSerializerModifier {    private static final Logger logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);    @Override    public List&lt;BeanPropertyWriter&gt; changeProperties(            SerializationConfig config,            BeanDescription beanDesc,            List&lt;BeanPropertyWriter&gt; beanProperties) {        for (int i = 0; i &lt; beanProperties.size(); i++) {            BeanPropertyWriter writer = beanProperties.get(i);            IgnoreLogging annotation = writer.getAnnotation(IgnoreLogging.class);            if (annotation != null) {                beanProperties.set(i, new IgnoredValueWriter(writer, annotation));            }        }        return beanProperties;    }    private static class IgnoredValueWriter extends BeanPropertyWriter {        private final IgnoreLogging annotation;        public IgnoredValueWriter(BeanPropertyWriter base, IgnoreLogging annotation) {            super(base);            this.annotation = annotation;        }        @Override        public void serializeAsField(Object bean, JsonGenerator gen,                                     SerializerProvider prov) throws Exception {            if (shouldUnmask()) {                super.serializeAsField(bean, gen, prov);            } else {                gen.writeFieldName(_name);                gen.writeString(annotation.replacement());            }        }        private boolean shouldUnmask() {            IgnoreLogging.LogLevel showLevel = annotation.showAtLevel();            return switch (showLevel) {                case TRACE -&gt; logger.isTraceEnabled();                case DEBUG -&gt; logger.isDebugEnabled();                case INFO -&gt; logger.isInfoEnabled();                case WARN -&gt; logger.isWarnEnabled();                case ERROR -&gt; logger.isErrorEnabled();            };        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f: \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0441 <code>@IgnoreLogging<\/code> \u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0432\u043a\u043b\u044e\u0447\u0451\u043d \u043b\u0438 \u043f\u043e\u0440\u043e\u0433\u043e\u0432\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c. \u0412\u043a\u043b\u044e\u0447\u0451\u043d &#8212; \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043d\u0435\u0442 &#8212; \u043f\u0438\u0448\u0435\u043c <code>replacement<\/code>.<\/p>\n<p><code>ObjectMapper<\/code> \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439, \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043c\u0430\u043f\u043f\u0435\u0440 REST API \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0435\u043c. \u041c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043e \u043a \u043e\u0431\u0449\u0435\u043c\u0443 \u0443\u0440\u043e\u0432\u043d\u044e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (root logger), \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043b\u043e\u0433\u0433\u0435\u0440, \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0439 \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443, \u043d\u043e \u0442\u043e\u0433\u0434\u0430 \u0432 \u043f\u0440\u043e\u043f\u0435\u0440\u0442\u044f\u0445 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u0435\u043c\u0443.<\/p>\n<pre><code class=\"java\">public class JsonUtils {    private static final ObjectWriter objectWriter;    static {        ObjectMapper mapper = new ObjectMapper()                .registerModule(new JavaTimeModule())                .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);        SimpleModule ignoreLoggingModule = new SimpleModule(\"IgnoreLoggingModule\");        ignoreLoggingModule.setSerializerModifier(new IgnoreLoggingSerializerModifier());        mapper.registerModule(ignoreLoggingModule);        objectWriter = mapper.writerWithDefaultPrettyPrinter();    }    public static String getJsonObject(Object object) {        try {            return objectWriter.writeValueAsString(object);        } catch (JsonProcessingException e) {            return \"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 JSON: \" + e.getMessage();        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0434\u0443\u043b\u0435\u0439<\/h3>\n<p>\u0414\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 &#8212; \u043a\u043e\u043d\u0444\u0438\u0433 \u0438 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f \u0438\u043c\u043f\u043e\u0440\u0442:<\/p>\n<pre><code class=\"java\">public class ControllerMonitoringConfig {    @Bean    public ControllerMonitoringAspect controllerMonitoringAspect() {        return new ControllerMonitoringAspect();    }}@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(ControllerMonitoringConfig.class)public @interface EnableControllerMonitoring {}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0435\u0448\u0430\u0435\u043c \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u043d\u0430 \u043a\u043b\u0430\u0441\u0441, \u043f\u043e\u043c\u0435\u0447\u0435\u043d\u043d\u044b\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0435\u0439 <code>@SpringBootApplication<\/code> (\u0438\u043b\u0438 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441):<\/p>\n<pre><code class=\"java\">@SpringBootApplication@EnableControllerMonitoringpublic class Application {}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 &#8212; \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u043f\u043b\u044e\u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430:<\/p>\n<pre><code class=\"java\">public class ClientRequestMonitoringConfig {    @Bean    public ClientRequestMonitoringAspect clientRequestMonitoring() {        return new ClientRequestMonitoringAspect();    }    @PostConstruct    public void configureReactorContext() {        Hooks.enableAutomaticContextPropagation();        ContextRegistry.getInstance().registerThreadLocalAccessor(                LoggerUtils.CLIENT_EVENT,                LoggerUtils::getClientEventAttribute,                object -&gt; {},                () -&gt; {}        );    }}@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(ClientRequestMonitoringConfig.class)public @interface EnableClientRequestMonitoring {}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0411\u0435\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0435\u0430\u043a\u0442\u043e\u0440\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u0435\u0440\u044f\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0438 \u0432 \u043b\u043e\u0433\u0430\u0445 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432\u044b \u043d\u0435 \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0438\u043c\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f.<\/p>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h3>\n<p>\u0410\u0441\u043f\u0435\u043a\u0442\u044b \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u0434\u043d\u044f\u0442\u0438\u0435\u043c Spring Context:<\/p>\n<pre><code class=\"java\">@SpringBootTest@EnableAspectJAutoProxyclass ControllerMonitoringAspectTest {    @Test    void shouldMaskSensitiveData() {        UserRequest request = new UserRequest();        request.setPassword(\"secret123\");        String json = JsonUtils.getJsonObject(request);        assertThat(json).contains(\"\\\"password\\\":\\\"ignored\\\"\");        assertThat(json).doesNotContain(\"secret123\");    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b<\/h3>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043d\u043e \u0431\u0435\u0437 \u043d\u044e\u0430\u043d\u0441\u043e\u0432 \u043d\u0435 \u043e\u0431\u043e\u0448\u043b\u043e\u0441\u044c, \u0442\u0430\u043a\u0436\u0435 \u043e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e \u043e\u043d\u043e \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c, \u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438\u0448\u044c \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0441\u0432\u043e\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0422\u043e \u043e \u0447\u0435\u043c \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0438 \u043f\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c:<\/p>\n<h4>\u041f\u0435\u0440\u0444\u043e\u0440\u043c\u0430\u043d\u0441<\/h4>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 JSON. \u041d\u0430 \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430\u0445 \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u043e, \u043d\u043e \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u0436\u0451\u0441\u0442\u043a\u0438\u043c\u0438 SLA  \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430\u0447\u043d\u0451\u0442 \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f.<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439:<\/p>\n<pre><code class=\"java\">@Aspectpublic class AsyncControllerMonitoringAspect {    private final Executor logExecutor = Executors.newFixedThreadPool(POOL_SIZE);    @Around(\"controllerMonitoring()\")    public Object logMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {        CompletableFuture.runAsync(() -&gt; logRequest(...), logExecutor);        Object result = joinPoint.proceed();        CompletableFuture.runAsync(() -&gt; logResponse(...), logExecutor);        return result;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u043d\u0438\u0436\u0430\u0435\u0442\u0441\u044f, \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041d\u043e \u0435\u0441\u0442\u044c \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430: \u043f\u0440\u0438 \u0430\u0432\u0430\u0440\u0438\u0439\u043d\u043e\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u043f\u043e\u0442\u043e\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u043f\u0430\u0434\u0443\u0442. \u041f\u043b\u044e\u0441 \u0441 \u043e\u0442\u043b\u0430\u0434\u043a\u043e\u0439 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0449\u0438\u043d\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b. \u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 &lt; 100\u043c\u0441 \u0438\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430 \u0438\u043d\u0441\u0442\u0430\u043d\u0441.<\/p>\n<h4>\u0411\u043e\u043b\u044c\u0448\u0438\u0435 payload<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043d\u0430 10\u043a \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 <code>JsonUtils.getJsonObject()<\/code> \u0441\u0438\u043b\u044c\u043d\u043e \u043d\u0430\u0447\u043d\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u044b. \u041b\u043e\u0433\u0438\u0447\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0440\u0435\u0437\u0430\u043d\u0438\u0435: \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0432\u044b\u0435 N \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0430, \u043b\u0438\u0431\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u043f\u0440\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0430 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0443\u0440\u043e\u0432\u043d\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h4>\u0420\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u043e\u0442\u043e\u043a\u0438 (\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442)<\/h4>\n<p>\u0420\u0435\u0430\u043a\u0442\u043e\u0440 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u0442\u043e\u043a\u0438, \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f, \u0438 \u0432 \u043b\u043e\u0433\u0430\u0445 \u043e\u0442\u0432\u0435\u0442\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0438\u043c\u0435\u043d\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442\u043e\u0442\u0430. \u041b\u0435\u0447\u0438\u0442\u0441\u044f <code>Hooks.enableAutomaticContextPropagation()<\/code> \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0435\u0439 <code>ThreadLocalAccessor<\/code>. \u041f\u0440\u043e \u044d\u0442\u043e \u043b\u0435\u0433\u043a\u043e \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435.<\/p>\n<h4>\u0427\u0435\u043b\u043e\u0432\u0435\u0447\u0435\u0441\u043a\u0438\u0439 \u0444\u0430\u043a\u0442\u043e\u0440<\/h4>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u043d\u0430\u0434\u043e \u0430\u043d\u043d\u043e\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0443\u043a\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u0437\u0430\u0431\u044b\u0442\u044c \u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u043c\u043e\u043b\u0447\u0430 \u043d\u0435 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442\u0441\u044f. \u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e <code>@IgnoreLogging<\/code> \u043d\u0430\u0434\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u043e\u0432\u0435\u0441\u0438\u0442\u044c \u043d\u0430 \u043d\u043e\u0432\u043e\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435. IDE \u043d\u0435 \u043f\u043e\u0434\u0441\u043a\u0430\u0436\u0435\u0442, \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043d\u0435 \u0440\u0443\u0433\u043d\u0451\u0442\u0441\u044f. \u0420\u0435\u0448\u0430\u0435\u0442\u0441\u044f ArchUnit \u0442\u0435\u0441\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b <code>@RestController<\/code> \u0438\u043c\u0435\u044e\u0442 <code>@ControllerMonitoring<\/code>. \u041d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u043e\u043b\u0435\u0439 \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0442\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0434 \u0440\u0435\u0432\u044c\u044e.<\/p>\n<h4>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0438 Circuit Breaker<\/h4>\n<p>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043a\u043e\u0433\u0434\u0430 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0430\u043c\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439. \u041f\u0440\u0438 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0445 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430\u0445 \u043e\u0448\u0438\u0431\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u043b\u043e\u0433 (\u0434\u0438\u0441\u043a \u0443\u043f\u0451\u0440\u0441\u044f, \u0431\u0443\u0444\u0435\u0440 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0435\u043d) \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0432\u0435\u0441\u044c \u0441\u0435\u0440\u0432\u0438\u0441. \u0427\u0442\u043e\u0431\u044b \u0442\u0430\u043a\u043e\u0433\u043e \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c Circuit Breaker, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438 \u0441\u0435\u0440\u0438\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u0440\u0443\u0431\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"java\">@Componentpublic class LoggingCircuitBreaker {    private final AtomicInteger errorCount = new AtomicInteger(0);    private volatile boolean enabled = true;    public boolean canLog() {        return enabled &amp;&amp; errorCount.get() &lt; MAX_ERRORS;    }    public void recordError() {        if (errorCount.incrementAndGet() &gt;= MAX_ERRORS) {            enabled = false;        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043d\u0435 \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442, \u043a\u0430\u0441\u043a\u0430\u0434\u043d\u043e\u0433\u043e \u0441\u0431\u043e\u044f \u043d\u0435\u0442. \u041c\u0438\u043d\u0443\u0441 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c \u043e\u0442\u0432\u0430\u043b\u0438\u0432\u0448\u0438\u0435\u0441\u044f \u043b\u043e\u0433\u0438 \u0438 \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0432\u0430\u0436\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f.<\/p>\n<h3>\u0418\u0442\u043e\u0433\u043e<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u0432 \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445:<\/p>\n<ul>\n<li>\n<p>\u041b\u043e\u0433\u0438 \u0432\u043e \u0432\u0441\u0435\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 \u0432 \u0435\u0434\u0438\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435, \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u0440\u043e\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0432\u0445\u043e\u0434\u0430 \u0434\u043e \u0432\u044b\u0445\u043e\u0434\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u0430\u0440\u043e\u043b\u0438-\u0442\u043e\u043a\u0435\u043d\u044b \u043d\u0435 \u0441\u0432\u0435\u0442\u044f\u0442\u0441\u044f &#8212; \u043d\u0430 \u043f\u0440\u043e\u0434\u0435 <code>INFO<\/code>, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c &#8212; \u0441\u0442\u0430\u0432\u0438\u043c <code>DEBUG<\/code>, \u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 &#8212; \u043e\u0434\u043d\u0430 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f, \u043d\u0435 \u043d\u0430\u0434\u043e \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u043f\u0438\u043f\u0430\u0441\u0442\u0438\u0442\u044c<\/p>\n<\/li>\n<li>\n<p>\u0426\u0435\u043d\u0442\u0440\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 &#8212; \u043f\u0440\u0430\u0432\u0438\u043c \u0430\u0441\u043f\u0435\u043a\u0442, \u0438 \u0432\u0441\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u043f\u043e\u0434\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u044e\u0442<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0442\u0438\u043f\u043e\u0432\u043e\u0433\u043e \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 Spring Boot &#8212; \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u0441\u0447\u0435\u0442\u0443 &#8212; \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439, \u043d\u043e \u0442\u0430\u043c \u0441\u0432\u043e\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<hr\/>\n<h3>\u0421\u0441\u044b\u043b\u043a\u0438<\/h3>\n<ul>\n<li>\n<p><a href=\"https:\/\/docs.spring.io\/spring-framework\/docs\/current\/reference\/html\/core.html#aop\">Spring AOP Documentation<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/FasterXML\/jackson-docs\/wiki\/JacksonHowToCustomSerializers\">Jackson Customization<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/projectreactor.io\/docs\/core\/release\/reference\/#context\">Project Reactor Context Propagation<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1076956\/\">https:\/\/habr.com\/ru\/articles\/1076956\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Spring Boot \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0432\u0430\u0436\u043d\u0435\u0439\u0448\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u043c \u044d\u0442\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439, \u0433\u0434\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0442\u0430\u043a \u0438 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043a \u0432\u043d\u0435\u0448\u043d\u0438\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c.\u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 \u0431\u0430\u0437\u0435 Spring AOP, \u043a\u043e\u0442\u043e\u0440\u043e\u0435:\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430\u0445\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0447\u0435\u0440\u0435\u0437 WebClient\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0433\u0438\u0431\u043a\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0443\u0440\u043e\u0432\u043d\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\u041f\u043e\u0447\u0435\u043c\u0443 \u0440\u0435\u0448\u0438\u043b\u0438 \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e\u0412 \u043d\u0430\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043d\u0430 \u0440\u0430\u043d\u043d\u0438\u0445 \u044d\u0442\u0430\u043f\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0436\u0438\u043b\u043e \u0441\u0432\u043e\u0435\u0439 \u0436\u0438\u0437\u043d\u044c\u044e \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 &#8212; \u043a\u0442\u043e-\u0442\u043e \u043d\u0430\u0432\u0435\u0448\u0438\u0432\u0430\u043b \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043a\u0442\u043e-\u0442\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e log.info() \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430, \u043a\u0442\u043e-\u0442\u043e \u0437\u0430\u0431\u044b\u0432\u0430\u043b \u0437\u0430\u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0430\u0436\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0444\u043e\u0440\u043c\u0430\u0442 \u043b\u043e\u0433\u043e\u0432 \u0440\u0430\u0437\u043d\u044b\u0439, \u0433\u0434\u0435-\u0442\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0432\u0435\u0442\u044f\u0442\u0441\u044f \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u0443\u044e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u0431\u044b\u043b \u043f\u0440\u043e \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0430 \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u043e \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0434\u0435 \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u0432\u044b\u043a\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043e\u0432\u0438\u043d\u0443 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0440\u0438 \u0442\u043e\u043c, \u0447\u0442\u043e \u043b\u043e\u0433\u0438\u043a\u0430 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0430\u0437\u0430\u043d\u0430 \u043f\u043e \u0432\u0441\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044f\u043c\u041d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438 \u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442:\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 HTTP \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u041c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0442\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0435\u0427\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c\u0421\u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u0432\u0430 \u0430\u0441\u043f\u0435\u043a\u0442\u0430 \u0438 \u043f\u0430\u0447\u043a\u0443 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439. \u041f\u0435\u0440\u0432\u044b\u0439 \u0430\u0441\u043f\u0435\u043a\u0442 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442 \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432, \u0432\u0442\u043e\u0440\u043e\u0439 &#8212; \u043c\u0435\u0442\u043e\u0434\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0441 WebClient. \u0414\u043b\u044f \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0430\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f \u043d\u0430 \u043f\u043e\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0430\u043c\u0430 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442, \u043a\u043e\u0433\u0434\u0430 \u043c\u0430\u0441\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0430 \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c.\u041d\u0430\u0448\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432:\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432\u0412\u0435\u0448\u0430\u0435\u0448\u044c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e \u043d\u0430 \u043c\u0435\u0442\u043e\u0434 &#8212; \u043e\u043d \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043e\u0442\u0432\u0435\u0442. \u041c\u043e\u0436\u043d\u043e \u0441\u043a\u0440\u044b\u0442\u044c \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u043f\u0430\u0440\u043e\u043b\u0438, \u0442\u043e\u043a\u0435\u043d\u044b \u0438 \u0444\u0430\u0439\u043b\u044b), \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438.@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface ControllerMonitoring {    String value();    boolean ignoreRequestBody() default false;    String ignoreRequestReplacement() default &#171;ignored&#187;;    boolean ignoreResponseBody() default false;    String ignoreResponseReplacement() default &#171;ignored&#187;;    String[] headers() default {};}\u0412 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:@RestController@RequestMapping(&#171;\/api\/users&#187;)public class UserController {    @PostMapping    @ControllerMonitoring(value = &#171;\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f&#187;, headers = {&#171;X-Request-ID&#187;, &#171;User-Agent&#187;})    public ResponseEntity&lt;UserResponse&gt; createUser(@RequestBody UserRequest request) {        return ResponseEntity.ok(response);    }    @PostMapping(&#171;\/login&#187;)    @ControllerMonitoring(value = &#171;\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f&#187;, ignoreRequestBody = true)    public ResponseEntity&lt;TokenResponse&gt; login(@RequestBody LoginRequest request) {        return ResponseEntity.ok(token);    }}\u0414\u043b\u044f login \u0442\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432 \u043b\u043e\u0433 \u043d\u0435 \u043f\u043e\u043f\u0430\u0434\u0451\u0442, \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u201cignored\u201d. \u0414\u043b\u044f createUser \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0438\u0448\u0443\u0442\u0441\u044f \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 X-Request-ID \u0438 User-Agent.\u0421\u0430\u043c \u0430\u0441\u043f\u0435\u043a\u0442:@Aspectpublic class ControllerMonitoringAspect {    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerMonitoringAspect.class);    @Pointcut(&#171;@annotation(ControllerMonitoring)&#187;)    public void controllerMonitoring() {    }    @Around(&#171;controllerMonitoring()&#187;)    public Object logMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();        String login = getLogin(); \/\/ \u0417\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438        request.setAttribute(&#171;startTime&#187;, System.currentTimeMillis());        storeEventName(joinPoint);        logRequest(request, joinPoint, login);        Object result = joinPoint.proceed();        logResponse(request, result, joinPoint);        return result;    }    private void logRequest(HttpServletRequest request, ProceedingJoinPoint joinPoint, String login) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        ControllerMonitoring annotation = signature.getMethod().getAnnotation(ControllerMonitoring.class);        try {            Object body = annotation.ignoreRequestBody()                    ? annotation.ignoreRequestReplacement()                    : extractRequestBody(joinPoint);            Log log = Log.event(annotation.value() + &#187; (Request)&#187;)                    .with(&#171;method&#187;, request.getMethod())                    .with(&#171;path&#187;, request.getRequestURI())                    .with(&#171;params&#187;, request.getQueryString())                    .with(&#171;body&#187;, JsonUtils.getJsonObject(body))                    .with(&#171;login&#187;, login);            logHeaders(request, annotation, log);            LOGGER.info(log.toString());        } catch (Exception e) {            LOGGER.error(&#171;\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430&#187;, e);        }    }    private void logHeaders(HttpServletRequest request, ControllerMonitoring annotation, Log log) {        String[] headersToLog = annotation.headers();        for (String headerName : headersToLog) {            String headerValue = request.getHeader(headerName);            if (headerValue != null) {                log.with(&#171;header.&#187; + headerName, headerValue);            } else {                LOGGER.warn(&#171;\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a &#8216;{}&#8217; \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d&#187;, headerName);            }        }    }    private void logResponse(HttpServletRequest request, Object result, ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        ControllerMonitoring annotation = signature.getMethod().getAnnotation(ControllerMonitoring.class);        try {            HttpStatus status = HttpStatus.OK;            Object body = result;            if (result instanceof ResponseEntity&lt;?&gt; response) {                status = HttpStatus.valueOf(response.getStatusCode().value());                body = annotation.ignoreResponseBody() ? annotation.ignoreResponseReplacement() : response.getBody();            }            long duration = System.currentTimeMillis() &#8212; (Long) request.getAttribute(&#171;startTime&#187;);            LOGGER.info(Log.event(annotation.value() + &#187; (Response)&#187;)                    .with(&#171;status&#187;, status.value())                    .with(&#171;body&#187;, JsonUtils.getJsonObject(body))                    .with(&#171;duration&#187;, duration)                    .toString());        } catch (Exception e) {            LOGGER.error(&#171;\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0442\u0432\u0435\u0442\u0430&#187;, e);        }    }    private void storeEventName(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        LoggerUtils.addEventAttribute(signature.getMethod().getAnnotation(ControllerMonitoring.class).value());    }    private Object extractRequestBody(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Parameter[] parameters = signature.getMethod().getParameters();        for (int i = 0; i &lt; parameters.length; i++) {            if (parameters[i].isAnnotationPresent(RequestBody.class)) {                return joinPoint.getArgs()[i];            }        }        return null;    }}\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043b\u043e\u0433\u0430:Request \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f &#8212; \u0441 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c\u0438:{  &#171;event&#187;: &#171;\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (Request)&#187;,  &#171;method&#187;: &#171;POST&#187;,  &#171;path&#187;: &#171;\/api\/users&#187;,  &#171;params&#187;: &#171;&#187;,  &#171;body&#187;: {    &#171;username&#187;: &#171;user1&#187;,    &#171;email&#187;: &#171;example@example.com&#187;  },  &#171;login&#187;: &#171;user1&#187;,  &#171;header.X-Request-ID&#187;: &#171;a1b2c3d4-e5f6-7890&#187;,  &#171;header.User-Agent&#187;: &#171;Mozilla\/5.0 (Windows NT 10.0; Win64; x64)&#187;}\u041e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435:{  &#171;event&#187;: &#171;\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (Response)&#187;,  &#171;status&#187;: 200,  &#171;body&#187;: {    &#171;id&#187;: 12345,    &#171;username&#187;: &#171;user1&#187;,    &#171;email&#187;: &#171;example@example.com&#187;  },  &#171;duration&#187;: 345}\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043b\u043e\u0433\u0438\u043d &#8212; \u0442\u0435\u043b\u043e \u0441\u043a\u0440\u044b\u0442\u043e:{  &#171;event&#187;: &#171;\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f (Request)&#187;,  &#171;method&#187;: &#171;POST&#187;,  &#171;path&#187;: &#171;\/api\/users\/login&#187;,  &#171;params&#187;: &#171;&#187;,  &#171;body&#187;: &#171;ignored&#187;,  &#171;login&#187;: &#171;admin&#187;}\u0418\u043c\u0435\u043d\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 \u0432 HTTP case-insensitive, \u0438 getHeader() \u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432\u0412 \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445 WebClient \u0434\u0451\u0440\u0433\u0430\u0435\u0442 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0438 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0432\u0438\u0434\u0435\u0442\u044c &#8212; \u043a\u0442\u043e, \u043a\u0443\u0434\u0430 \u0438 \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043f\u043e\u0448\u0451\u043b.\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f:@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface ClientRequestMonitoring {    String value() default &#171;&#187;;}\u0412 \u043a\u043b\u0438\u0435\u043d\u0442\u0435:@Componentpublic class UserServiceClient {    private final WebClient webClient;    @ClientRequestMonitoring(&#171;\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435&#187;)    public Mono&lt;UserInfo&gt; getUserInfo(String userId) {        return webClient.get()                .uri(&#171;\/users\/{id}&#187;, userId)                .retrieve()                .bodyToMono(UserInfo.class);    }}\u0410\u0441\u043f\u0435\u043a\u0442 \u0434\u043b\u044f \u043a\u0440\u0438\u0435\u043d\u0442\u043e\u0432:@Aspectpublic class ClientRequestMonitoringAspect {    private static final Logger LOGGER = LoggerFactory.getLogger(ClientRequestMonitoringAspect.class);    @Pointcut(&#171;@annotation(ClientRequestMonitoring)&#187;)    public void clientRequestMonitoring() {    }    @Around(&#171;clientRequestMonitoring()&#187;)    public Object logMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {        storeEventInContext(joinPoint);        logRequest(joinPoint);        return joinPoint.proceed();    }    private void logRequest(ProceedingJoinPoint joinPoint) {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = signature.getMethod();        Object[] args = joinPoint.getArgs();        try {            String event = getEventName(joinPoint);            Log log = Log.event(event + &#187; (Request)&#187;);            if (args.length &gt; 0) {                Parameter[] parameters = method.getParameters();                for (int i = 0; i &lt; parameters.length; i++) {                    log.with(parameters[i].getName(), JsonUtils.getJsonObject(args[i]));                }            }            LOGGER.info(log.toString());        } catch (Exception e) {            LOGGER.error(&#171;\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430&#187;, e);        }    }    private String&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-493005","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/493005","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=493005"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/493005\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=493005"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=493005"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=493005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}