Skip to content

Commit ed5d0c2

Browse files
committed
Adds client IP address logging
Implements logging of the client IP address for requests and exceptions. This change introduces a utility class to retrieve the client IP address, considering headers like "CF-Connecting-IP" and "X-Forwarded-For". The IP address is then included in log messages for requests, responses, and exceptions, aiding in debugging and monitoring.
1 parent 80d8a65 commit ed5d0c2

File tree

7 files changed

+143
-38
lines changed

7 files changed

+143
-38
lines changed

src/main/java/com/dmware/api_onibusbh/config/RequestLoggingFilter.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.dmware.api_onibusbh.config;
22

3+
import com.dmware.api_onibusbh.utils.ClientIpUtils;
34
import jakarta.servlet.FilterChain;
45
import jakarta.servlet.ServletException;
56
import jakarta.servlet.http.HttpServletRequest;
@@ -35,10 +36,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
3536
MDC.put(TRANSACTION_ID_KEY, transactionId);
3637

3738
try {
39+
String clientIp = ClientIpUtils.getClientIp(request);
3840
logger.info("Requisição recebida: {} {}", request.getMethod(), request.getRequestURI(),
3941
kv("method", request.getMethod()),
4042
kv("uri", request.getRequestURI()),
41-
kv("client_ip", request.getRemoteAddr()),
43+
kv("client_ip", clientIp),
4244
kv("user_agent", request.getHeader("User-Agent"))
4345
);
4446

@@ -52,7 +54,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
5254
response.getStatus(),
5355
duration,
5456
kv("status_code", response.getStatus()),
55-
kv("duration_ms", duration)
57+
kv("duration_ms", duration),
58+
kv("client_ip", clientIp)
5659
);
5760

5861
} finally {
Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.dmware.api_onibusbh.infra;
22

33
import com.dmware.api_onibusbh.exceptions.*;
4+
import com.dmware.api_onibusbh.utils.ClientIpUtils;
5+
import jakarta.servlet.http.HttpServletRequest;
46
import org.slf4j.Logger;
57
import org.slf4j.LoggerFactory;
68
import org.springframework.http.HttpStatus;
@@ -21,66 +23,129 @@ public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
2123
private static final Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);
2224

2325
@ExceptionHandler(LinhasNotFoundException.class)
24-
public ResponseEntity<ErrorResponse> linhasNotFoundException(LinhasNotFoundException ex) {
25-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
26+
public ResponseEntity<ErrorResponse> linhasNotFoundException(LinhasNotFoundException ex, HttpServletRequest request) {
27+
logger.warn("Recurso não encontrado",
28+
kv("exception", ex.getClass().getSimpleName()),
29+
kv("status", HttpStatus.NOT_FOUND),
30+
kv("detail", ex.getMessage()),
31+
kv("path", request.getRequestURI()),
32+
kv("method", request.getMethod()),
33+
kv("client_ip", ClientIpUtils.getClientIp(request)));
2634
return ErrorResponse.of("Não foi encontrada nenhuma linha, por favor tente novamente", HttpStatus.NOT_FOUND);
2735
}
2836

2937
@ExceptionHandler(CoordenadasNotFoundException.class)
30-
public ResponseEntity<ErrorResponse> coordenadasNotFoundException(CoordenadasNotFoundException ex) {
31-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
38+
public ResponseEntity<ErrorResponse> coordenadasNotFoundException(CoordenadasNotFoundException ex, HttpServletRequest request) {
39+
logger.warn("Recurso não encontrado",
40+
kv("exception", ex.getClass().getSimpleName()),
41+
kv("status", HttpStatus.NOT_FOUND),
42+
kv("detail", ex.getMessage()),
43+
kv("path", request.getRequestURI()),
44+
kv("method", request.getMethod()),
45+
kv("client_ip", ClientIpUtils.getClientIp(request)));
3246
return ErrorResponse.of("Não foi encontrada nenhuma coordenada, por favor tente novamente",
3347
HttpStatus.NOT_FOUND);
3448
}
3549

3650
@ExceptionHandler(DicionarioNotFoundException.class)
37-
public ResponseEntity<ErrorResponse> dicionarioNotFoundException(DicionarioNotFoundException ex) {
38-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
51+
public ResponseEntity<ErrorResponse> dicionarioNotFoundException(DicionarioNotFoundException ex, HttpServletRequest request) {
52+
logger.warn("Recurso não encontrado",
53+
kv("exception", ex.getClass().getSimpleName()),
54+
kv("status", HttpStatus.NOT_FOUND),
55+
kv("detail", ex.getMessage()),
56+
kv("path", request.getRequestURI()),
57+
kv("method", request.getMethod()),
58+
kv("client_ip", ClientIpUtils.getClientIp(request)));
3959
return ErrorResponse.of("Não foi encontrado o dicionário para os dados, por favor tente novamente",
4060
HttpStatus.NOT_FOUND);
4161
}
4262

4363
@ExceptionHandler(LinhaNotFoundException.class)
44-
public ResponseEntity<ErrorResponse> linhaNotFoundException(LinhaNotFoundException ex) {
45-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
64+
public ResponseEntity<ErrorResponse> linhaNotFoundException(LinhaNotFoundException ex, HttpServletRequest request) {
65+
logger.warn("Recurso não encontrado",
66+
kv("exception", ex.getClass().getSimpleName()),
67+
kv("status", HttpStatus.NOT_FOUND),
68+
kv("detail", ex.getMessage()),
69+
kv("path", request.getRequestURI()),
70+
kv("method", request.getMethod()),
71+
kv("client_ip", ClientIpUtils.getClientIp(request)));
4672
return ErrorResponse.of("Não foi encontrada nenhuma linha, por favor tente novamente", HttpStatus.NOT_FOUND);
4773
}
4874

4975
@ExceptionHandler(ValidJsonException.class)
50-
public ResponseEntity<ErrorResponse> validJsonException(ValidJsonException ex) {
51-
logger.warn("Erro de validação JSON", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.BAD_REQUEST), kv("mensagem_erro", ex.getMessage()));
76+
public ResponseEntity<ErrorResponse> validJsonException(ValidJsonException ex, HttpServletRequest request) {
77+
logger.warn("Erro de validação JSON",
78+
kv("exception", ex.getClass().getSimpleName()),
79+
kv("status", HttpStatus.BAD_REQUEST),
80+
kv("detail", ex.getMessage()),
81+
kv("path", request.getRequestURI()),
82+
kv("method", request.getMethod()),
83+
kv("client_ip", ClientIpUtils.getClientIp(request)));
5284
return ErrorResponse.of(ex.getMessage(), HttpStatus.BAD_REQUEST);
5385
}
5486

5587
@ExceptionHandler(RuntimeException.class)
56-
public ResponseEntity<ErrorResponse> runtimeException(RuntimeException ex) {
57-
logger.error("Erro interno não tratado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.INTERNAL_SERVER_ERROR), ex);
88+
public ResponseEntity<ErrorResponse> runtimeException(RuntimeException ex, HttpServletRequest request) {
89+
logger.error("Erro interno não tratado",
90+
kv("exception", ex.getClass().getSimpleName()),
91+
kv("status", HttpStatus.INTERNAL_SERVER_ERROR),
92+
kv("detail", ex.getMessage()),
93+
kv("path", request.getRequestURI()),
94+
kv("method", request.getMethod()),
95+
kv("client_ip", ClientIpUtils.getClientIp(request)),
96+
ex);
5897
return ErrorResponse.of("Ocorreu um erro interno, por favor tente novamente", HttpStatus.INTERNAL_SERVER_ERROR);
5998
}
6099

61100
@ExceptionHandler(IOException.class)
62-
public ResponseEntity<ErrorResponse> ioException(IOException ex) {
63-
logger.error("Erro interno não tratado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.INTERNAL_SERVER_ERROR), ex);
101+
public ResponseEntity<ErrorResponse> ioException(IOException ex, HttpServletRequest request) {
102+
logger.error("Erro interno não tratado",
103+
kv("exception", ex.getClass().getSimpleName()),
104+
kv("status", HttpStatus.INTERNAL_SERVER_ERROR),
105+
kv("detail", ex.getMessage()),
106+
kv("path", request.getRequestURI()),
107+
kv("method", request.getMethod()),
108+
kv("client_ip", ClientIpUtils.getClientIp(request)),
109+
ex);
64110
return ErrorResponse.of("Ocorreu um erro interno, por favor tente novamente", HttpStatus.INTERNAL_SERVER_ERROR);
65111
}
66112

67113
@ExceptionHandler(NoResourceFoundException.class)
68-
public ResponseEntity<ErrorResponse> noResourceFoundException(NoResourceFoundException ex) {
69-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
114+
public ResponseEntity<ErrorResponse> noResourceFoundException(NoResourceFoundException ex, HttpServletRequest request) {
115+
logger.warn("Recurso não encontrado",
116+
kv("exception", ex.getClass().getSimpleName()),
117+
kv("status", HttpStatus.NOT_FOUND),
118+
kv("detail", ex.getMessage()),
119+
kv("path", request.getRequestURI()),
120+
kv("method", request.getMethod()),
121+
kv("client_ip", ClientIpUtils.getClientIp(request)));
70122
return ErrorResponse.of("Verifique a rota digitada ou os dados enviados", HttpStatus.NOT_FOUND);
71123
}
72124

73125
@ExceptionHandler(NoHandlerFoundException.class)
74-
public ResponseEntity<ErrorResponse> noHandlerFoundException(NoHandlerFoundException ex) {
75-
logger.warn("Recurso não encontrado", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.NOT_FOUND));
126+
public ResponseEntity<ErrorResponse> noHandlerFoundException(NoHandlerFoundException ex, HttpServletRequest request) {
127+
logger.warn("Recurso não encontrado",
128+
kv("exception", ex.getClass().getSimpleName()),
129+
kv("status", HttpStatus.NOT_FOUND),
130+
kv("detail", ex.getMessage()),
131+
kv("path", request.getRequestURI()),
132+
kv("method", request.getMethod()),
133+
kv("client_ip", ClientIpUtils.getClientIp(request)));
76134
return ErrorResponse.of("Verifique a rota digitada ou os dados enviados", HttpStatus.NOT_FOUND);
77135
}
78136

79137
@ExceptionHandler(RateLimitExceededException.class)
80-
public ResponseEntity<ErrorResponse> rateLimitExceededException(RateLimitExceededException ex) {
81-
logger.warn("Rate limit excedido", kv("exception", ex.getClass().getSimpleName()), kv("status", HttpStatus.TOO_MANY_REQUESTS), kv("retry_after", ex.getRetryAfterSeconds()));
138+
public ResponseEntity<ErrorResponse> rateLimitExceededException(RateLimitExceededException ex, HttpServletRequest request) {
139+
logger.warn("Rate limit excedido",
140+
kv("exception", ex.getClass().getSimpleName()),
141+
kv("status", HttpStatus.TOO_MANY_REQUESTS),
142+
kv("retry_after", ex.getRetryAfterSeconds()),
143+
kv("path", request.getRequestURI()),
144+
kv("method", request.getMethod()),
145+
kv("client_ip", ClientIpUtils.getClientIp(request)));
82146
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
83147
.header("X-Rate-Limit-Retry-After-Seconds", String.valueOf(ex.getRetryAfterSeconds()))
84148
.body(new ErrorResponse(java.time.LocalDateTime.now(), ex.getMessage(), HttpStatus.TOO_MANY_REQUESTS));
85149
}
150+
86151
}

src/main/java/com/dmware/api_onibusbh/infra/RateLimitingFilter.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.dmware.api_onibusbh.infra;
22

33
import com.dmware.api_onibusbh.exceptions.RateLimitExceededException;
4+
import com.dmware.api_onibusbh.utils.ClientIpUtils;
45
import io.github.bucket4j.Bucket;
56
import io.github.bucket4j.ConsumptionProbe;
67
import jakarta.servlet.FilterChain;
@@ -40,16 +41,7 @@ public class RateLimitingFilter extends OncePerRequestFilter {
4041
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
4142
throws ServletException, IOException {
4243

43-
String clientIp = request.getHeader("CF-Connecting-IP");
44-
if (clientIp == null || clientIp.isEmpty()) {
45-
clientIp = request.getHeader("X-Forwarded-For");
46-
if (clientIp != null && !clientIp.isEmpty()) {
47-
clientIp = clientIp.split(",")[0].trim();
48-
}
49-
}
50-
if (clientIp == null || clientIp.isEmpty()) {
51-
clientIp = request.getRemoteAddr();
52-
}
44+
String clientIp = ClientIpUtils.getClientIp(request);
5345
Bucket bucket = buckets.computeIfAbsent(clientIp, this::createNewBucket);
5446

5547
ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);

src/main/java/com/dmware/api_onibusbh/scheduler/CoordenadasScheduler.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import java.util.UUID;
1212
import java.util.concurrent.TimeUnit;
1313

14+
import static net.logstash.logback.argument.StructuredArguments.kv;
15+
1416
@Component
1517
public class CoordenadasScheduler {
1618

@@ -25,12 +27,17 @@ public CoordenadasScheduler(APIService apiService, OnibusService onibusService)
2527

2628
@Scheduled(fixedDelay = 20, timeUnit = TimeUnit.SECONDS)
2729
public void fetchCoordenadasOnibus() {
30+
long startTime = System.currentTimeMillis();
2831
try {
2932
MDC.put("transaction_id", UUID.randomUUID().toString());
30-
logger.info("Job de Coordenadas iniciado.");
33+
logger.info("Job de Coordenadas iniciado.", kv("job_name", "Coordenadas"), kv("status", "STARTED"));
3134
List<CoordenadaDTO> coordenadas = apiService.getOnibusCoordenadaBH();
3235
onibusService.salvaCoordenadas(coordenadas);
33-
logger.info("Job de Coordenadas finalizado.");
36+
long duration = System.currentTimeMillis() - startTime;
37+
logger.info("Job de Coordenadas finalizado.",
38+
kv("job_name", "Coordenadas"),
39+
kv("status", "FINISHED"),
40+
kv("duration_ms", duration));
3441
} finally {
3542
MDC.clear();
3643
}

src/main/java/com/dmware/api_onibusbh/scheduler/DicionarioScheduler.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.UUID;
1010
import java.util.concurrent.TimeUnit;
1111

12+
import static net.logstash.logback.argument.StructuredArguments.kv;
13+
1214
@Component
1315
public class DicionarioScheduler {
1416

@@ -22,11 +24,16 @@ public DicionarioScheduler(DicionarioService dicionarioService) {
2224
@Async
2325
@Scheduled(fixedDelay = 12, timeUnit = TimeUnit.HOURS)
2426
public void salvaDicionarioBanco() {
27+
long startTime = System.currentTimeMillis();
2528
try {
2629
MDC.put("transaction_id", UUID.randomUUID().toString());
27-
logger.info("Job de Dicionário iniciado.");
30+
logger.info("Job de Dicionário iniciado.", kv("job_name", "Dicionario"), kv("status", "STARTED"));
2831
dicionarioService.salvarDicionarioBanco();
29-
logger.info("Job de Dicionário finalizado.");
32+
long duration = System.currentTimeMillis() - startTime;
33+
logger.info("Job de Dicionário finalizado.",
34+
kv("job_name", "Dicionario"),
35+
kv("status", "FINISHED"),
36+
kv("duration_ms", duration));
3037
} finally {
3138
MDC.clear();
3239
}

src/main/java/com/dmware/api_onibusbh/scheduler/LinhaScheduler.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.UUID;
1010
import java.util.concurrent.TimeUnit;
1111

12+
import static net.logstash.logback.argument.StructuredArguments.kv;
13+
1214
@Component
1315
public class LinhaScheduler {
1416

@@ -22,11 +24,16 @@ public LinhaScheduler(LinhasService linhasService) {
2224
@Async
2325
@Scheduled(fixedDelay = 12, timeUnit = TimeUnit.HOURS)
2426
public void fetchCoordenadasOnibus() {
27+
long startTime = System.currentTimeMillis();
2528
try {
2629
MDC.put("transaction_id", UUID.randomUUID().toString());
27-
logger.info("Job agendado iniciado: Atualização de Coordenadas/Linhas.");
30+
logger.info("Job agendado iniciado: Atualização de Coordenadas/Linhas.", kv("job_name", "Linhas"), kv("status", "STARTED"));
2831
linhasService.salvaLinhasNormais();
29-
logger.info("Job agendado finalizado: Atualização de Coordenadas/Linhas.");
32+
long duration = System.currentTimeMillis() - startTime;
33+
logger.info("Job agendado finalizado: Atualização de Coordenadas/Linhas.",
34+
kv("job_name", "Linhas"),
35+
kv("status", "FINISHED"),
36+
kv("duration_ms", duration));
3037
} finally {
3138
MDC.clear();
3239
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.dmware.api_onibusbh.utils;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
5+
public class ClientIpUtils {
6+
7+
private ClientIpUtils() {
8+
9+
}
10+
11+
public static String getClientIp(HttpServletRequest request) {
12+
String clientIp = request.getHeader("CF-Connecting-IP");
13+
if (clientIp == null || clientIp.isEmpty()) {
14+
clientIp = request.getHeader("X-Forwarded-For");
15+
if (clientIp != null && !clientIp.isEmpty()) {
16+
clientIp = clientIp.split(",")[0].trim();
17+
}
18+
}
19+
if (clientIp == null || clientIp.isEmpty()) {
20+
clientIp = request.getRemoteAddr();
21+
}
22+
return clientIp;
23+
}
24+
}

0 commit comments

Comments
 (0)