<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Victor Matheus da Silva</title>
    <description>The latest articles on DEV Community by Victor Matheus da Silva (@victormsti).</description>
    <link>https://dev.to/victormsti</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858133%2Fc6c90ffb-9767-4f4f-bd64-ea67bb6ab924.png</url>
      <title>DEV Community: Victor Matheus da Silva</title>
      <link>https://dev.to/victormsti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/victormsti"/>
    <language>en</language>
    <item>
      <title>Padronizando Logs de uma aplicação Java usando Spring AOP</title>
      <dc:creator>Victor Matheus da Silva</dc:creator>
      <pubDate>Thu, 05 May 2022 19:08:07 +0000</pubDate>
      <link>https://dev.to/victormsti/padronizando-logs-de-uma-aplicacao-java-usando-spring-aop-12ic</link>
      <guid>https://dev.to/victormsti/padronizando-logs-de-uma-aplicacao-java-usando-spring-aop-12ic</guid>
      <description>&lt;p&gt;Em aplicações corporativas, é muito comum colocarmos logs nos métodos que criamos para indicar, por exemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Os parâmetros de entrada&lt;/li&gt;
&lt;li&gt;O tempo de execução&lt;/li&gt;
&lt;li&gt;O retorno do método&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Acontece que isso, se feito de forma manual, se torna muito &lt;strong&gt;repetitivo e propenso a erros&lt;/strong&gt;. O dev pode facilmente esquecer de incluir algum desses logs, além de aumentar as linhas de código e assim diminuir a legibilidade.&lt;/p&gt;

&lt;p&gt;Como exemplo, para fins didáticos, iremos demonstrar uma API simples que retorna uma mensagem juntamente com o nome que foi passado como parâmetro.&lt;/p&gt;

&lt;p&gt;Observe o código abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RequestMapping("/api/v1")
@Slf4j
public class SpringAopExampleController {

    private final SpringAopExampleService service;

    @Autowired
    public SpringAopExampleController(SpringAopExampleService service) {
        this.service = service;
    }

    @GetMapping("/messages/users/{username}")
    public ResponseEntity&amp;lt;String&amp;gt; getMessage(@PathVariable String username){
        long startTime = System.currentTimeMillis();
        log.info("m=getMessage; c=SpringAopExampleController; username={}", username);

        String messageToReturn = service.getMessage(username);

        long endTime = System.currentTimeMillis();
        log.info("received message: {}; response time in milliseconds: {}", messageToReturn, (endTime - startTime));
        return ResponseEntity.ok(messageToReturn);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
@Slf4j
public class SpringAopExampleService {

    public String getMessage(String username){
        long startTime = System.currentTimeMillis();
        log.info("m=getMessage; c=SpringAopExampleService; username={}", username);

        String generatedMessage = "Hello World " + username + " from Spring AOP!";

        long endTime = System.currentTimeMillis();
        log.info("received message: {}; response time in milliseconds: {}", generatedMessage, (endTime - startTime));
        return generatedMessage;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo segue o log de saída da execução da API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2022-05-05 14:49:02.583  INFO 21319 --- [nio-8080-exec-2] c.e.s.a.c.SpringAopExampleController     : m=getMessage; c=SpringAopExampleController; username=Victor
2022-05-05 14:49:02.583  INFO 21319 --- [nio-8080-exec-2] c.e.s.a.service.SpringAopExampleService  : m=getMessage; c=SpringAopExampleService; username=Victor
2022-05-05 14:49:02.584  INFO 21319 --- [nio-8080-exec-2] c.e.s.a.service.SpringAopExampleService  : received message: Hello World Victor from Spring AOP!; response time in milliseconds: 1
2022-05-05 14:49:02.584  INFO 21319 --- [nio-8080-exec-2] c.e.s.a.c.SpringAopExampleController     : received message: Hello World Victor from Spring AOP!; response time in milliseconds: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perceba como a leitura do código fica difícil&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Primeiro capturamos o início do fluxo;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Depois logamos o nome do método, nome da classe e os parâmetros;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chamamos o Service para armazenar a mensagem que será retornada;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capturamos o final do fluxo;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logamos o retorno, juntamente com o tempo de execução em milisegundos;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O mesmo se repete para a Classe de serviço.&lt;/p&gt;

&lt;p&gt;Todas essas informações são importantes, mas repetir esse processo para todos os métodos é inviável.&lt;/p&gt;

&lt;p&gt;Como solução desse problema, podemos usar o &lt;strong&gt;Spring AOP&lt;/strong&gt;. Com ele, podemos automatizar procedimentos repetitivos que geralmente codificamos manualmente;&lt;/p&gt;

&lt;p&gt;Vantagens do seu uso nessa abordagem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Evita redundância de código: esse processo de log descrito anteriormente não fica mais sendo manualmente repetido em todos os metodos e sim em um classe específica do Spring para tal uso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reaproveitamento de códgo: a classe a qual concentra a estrutura padrão dos logs pode ser reutilizada em outros projetos;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Facilidade de manutenção: ao invés de alterar um padrão de log em todos os métodos, a manutenção ou melhoria será feita apenas uma vez e será refletida em todo o sistema;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Podemos ter uma classe como a exemplificada abaixo para centralizar esse tipo de log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Aspect
@Component
@Slf4j
public class SpringAopInterceptor   {

    @Around("execution(* com.example.spring.aop..*.*(..))")
    public Object executionTime(ProceedingJoinPoint point) throws Throwable {
        if(hasParams(point)) {
            log.info("opening method", kv("method", point.getSignature().getName()),
                    kv("class",point.getSignature().getDeclaringTypeName()), "params",
                    kv("method-params", getParamsAndValuesFromMethod(point)));
        }
        else{
            log.info("opening method", kv("method", point.getSignature().getName()),
                    kv("class",point.getSignature().getDeclaringTypeName()));
        }

        long startTime = System.currentTimeMillis();

        Object object = point.proceed();

        long endTime = System.currentTimeMillis();
        log.info("closing method", kv("method", point.getSignature().getName()),
                kv("class",point.getSignature().getDeclaringTypeName()),
                kv("execution-time in milliseconds", endTime-startTime));
        return object;
    }

        @AfterReturning(pointcut = "execution(* com.example.spring.aop..*.*(..))", returning = "result")
    public void logAfter(JoinPoint point, Object result) {
        if(result!=null){
            log.info("method-response", kv("method", point.getSignature().getName()),
                    kv("class", point.getSignature().getDeclaringTypeName()),
                    kv("response", result));
        }
    }

    private Boolean hasParams(ProceedingJoinPoint point){
        if(point.getArgs()!= null &amp;amp;&amp;amp; point.getArgs().length != 0){
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            Map&amp;lt;String, Object&amp;gt; paramsAndValues = new HashMap&amp;lt;&amp;gt;();
            String[] parameterNames = methodSignature.getParameterNames();
            return parameterNames != null;
        }
        else return false;

    }

    private Map&amp;lt;String, Object&amp;gt; getParamsAndValuesFromMethod(ProceedingJoinPoint point){
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Map&amp;lt;String, Object&amp;gt; paramsAndValues = new HashMap&amp;lt;&amp;gt;();
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = point.getArgs();

        for(int i=0; i&amp;lt;args.length; i++){
            paramsAndValues.put(parameterNames[i], args[i]);
        }

        return paramsAndValues;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nessa estrutura, estamos usando o &lt;strong&gt;Logback&lt;/strong&gt; e o &lt;strong&gt;Logstash&lt;/strong&gt;. Então é necessário criar o arquivo &lt;strong&gt;logback.xml&lt;/strong&gt; e colocá-lo dentro da pasta &lt;strong&gt;resources&lt;/strong&gt; da sua aplicação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OBS:&lt;/strong&gt; os logs passarão a ficar no formato JSON. Irei explicar com detalhes no próximo artigo. Por hora, basta copiar esse arquivo xml e incluir as dependências. Você pode consultar tudo que precisa no &lt;strong&gt;código fonte&lt;/strong&gt; que irei disponibilizar no final do artigo. &lt;/p&gt;

&lt;p&gt;Observe como ficaram os logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"@timestamp":"2022-05-05T15:09:52.346-03:00","@version":"1","message":"opening method","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.controller.SpringAopExampleController","method-params":{"username":"Victor"}}
{"@timestamp":"2022-05-05T15:09:52.389-03:00","@version":"1","message":"opening method","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.service.SpringAopExampleService","method-params":{"username":"Victor"}}
{"@timestamp":"2022-05-05T15:09:52.404-03:00","@version":"1","message":"method-response","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.service.SpringAopExampleService","response":"Hello World Victor from Spring AOP!"}
{"@timestamp":"2022-05-05T15:09:52.404-03:00","@version":"1","message":"closing method","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.service.SpringAopExampleService","execution-time in milliseconds":15}
{"@timestamp":"2022-05-05T15:09:52.413-03:00","@version":"1","message":"method-response","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.controller.SpringAopExampleController","response":{"headers":{},"body":"Hello World Victor from Spring AOP!","statusCode":"OK","statusCodeValue":200}}
{"@timestamp":"2022-05-05T15:09:52.473-03:00","@version":"1","message":"closing method","logger_name":"com.example.spring.aop.config.SpringAopInterceptor","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"method":"getMessage","class":"com.example.spring.aop.controller.SpringAopExampleController","execution-time in milliseconds":101}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, temos uma classe centralizada focada em logar nossa aplicação, refletindo para todos os métodos dentro do package especificado. Perceba também que temos muito mais riqueza de detalhes.&lt;/p&gt;

&lt;p&gt;Agora veja como as classes de Controller e Service ficaram muito mais simples &lt;strong&gt;sem aquela repetição de código manual&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/messages/users/{username}")
    public ResponseEntity&amp;lt;String&amp;gt; getMessage(@PathVariable String username){
        return ResponseEntity.ok(service.getMessage(username));
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class SpringAopExampleService {

    public String getMessage(String username){
        return "Hello World " + username + " from Spring AOP!";
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em um próximo artigo, irei explicar sobre como deixar os logs da aplicação em formato de JSON, o qual ajuda bastante no throubleshooting e até na criação de dashboards e monitoramentos.&lt;/p&gt;

&lt;p&gt;Espero que tenha contribuído em algo e até mais!&lt;/p&gt;

&lt;p&gt;Código fonte do projeto disponível no meu Github:&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
