Pular para o conteúdo principal

Quiz: Tratamento de Exceções e Logging

Teste seus conhecimentos sobre tratamento de erros e logging.

30 questões - Clique em cada pergunta para revelar a resposta.


Questões 1-15: Tratamento de Exceções​

Pergunta 1: O que é @ControllerAdvice?

Resposta: Um componente que gerencia exceções globalmente para todos os controllers:

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.getMessage()));
}
}

Pergunta 2: Qual a diferença entre @ControllerAdvice e @RestControllerAdvice?

Resposta:

  • @ControllerAdvice: pode retornar views ou objetos
  • @RestControllerAdvice: equivale a @ControllerAdvice + @ResponseBody

Para APIs REST, utilize @RestControllerAdvice.


Pergunta 3: Como criar uma exceção personalizada?

Resposta:

public class ResourceNotFoundException extends RuntimeException {

private final String resourceName;
private final Object resourceId;

public ResourceNotFoundException(String resourceName, Object resourceId) {
super(String.format("%s not found with id: %s", resourceName, resourceId));
this.resourceName = resourceName;
this.resourceId = resourceId;
}

// Getters
}

Pergunta 4: Como mapear uma exceção para um código HTTP?

Resposta:

@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse(ex.getMessage());
}

// Ou com ResponseEntity
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(ex.getMessage()));
}

Pergunta 5: Como tratar erros de validação?

Resposta:

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {

Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);

return ResponseEntity.badRequest().body(errors);
}

Pergunta 6: O que é ProblemDetail (RFC 7807)?

Resposta: Formato padrão para respostas de erro HTTP:

@ExceptionHandler(ResourceNotFoundException.class)
public ProblemDetail handleNotFound(ResourceNotFoundException ex) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, ex.getMessage());
problem.setTitle("Resource Not Found");
problem.setProperty("resourceId", ex.getResourceId());
return problem;
}

Resultado JSON:

{
"type": "about:blank",
"title": "Resource Not Found",
"status": 404,
"detail": "User not found with id: 123",
"resourceId": 123
}

Pergunta 7: Como tratar todas as exceções não tratadas?

Resposta:

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
log.error("Unexpected error", ex);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("An unexpected error occurred"));
}

Pergunta 8: Como adicionar informações à resposta de erro?

Resposta:

public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
private Map<String, Object> details;

// Construtores, getters
}

@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusiness(
BusinessException ex, HttpServletRequest request) {

ErrorResponse error = ErrorResponse.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.BAD_REQUEST.value())
.error("Business Rule Violation")
.message(ex.getMessage())
.path(request.getRequestURI())
.build();

return ResponseEntity.badRequest().body(error);
}

Pergunta 9: Como direcionar um @ControllerAdvice para certos controllers?

Resposta:

// Por anotação
@ControllerAdvice(annotations = RestController.class)

// Por pacote
@ControllerAdvice(basePackages = "com.example.api")

// Por classe
@ControllerAdvice(assignableTypes = {UserController.class, OrderController.class})

Pergunta 10: Como tratar exceções em filtros?

Resposta: Os filtros são executados antes dos controllers, então @ControllerAdvice não se aplica:

public class ExceptionHandlerFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) {
try {
chain.doFilter(request, response);
} catch (JwtException ex) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json");
response.getWriter().write("{\"error\":\"Invalid token\"}");
}
}
}

Pergunta 11: Como tratar erros assíncronos?

Resposta:

@ControllerAdvice
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.error("Async error in {}: {}", method.getName(), ex.getMessage());
}
}

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}

Pergunta 12: Como lançar um erro 404 para um recurso inexistente?

Resposta:

@Service
public class UserService {

public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND, "User not found: " + id));
}
}

// Ou com exceção personalizada
throw new ResourceNotFoundException("User", id);

Pergunta 13: Como desabilitar a página de erro padrão do Spring Boot?

Resposta:

server:
error:
whitelabel:
enabled: false

# Ou personalizar
include-message: always
include-binding-errors: always
include-stacktrace: never
include-exception: false

Pergunta 14: Como tratar erros 404 globalmente?

Resposta:

spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(NoHandlerFoundException ex) {
return ResponseEntity.status(404)
.body(new ErrorResponse("Endpoint not found: " + ex.getRequestURL()));
}

Pergunta 15: Quais são as boas práticas para exceções?

Resposta:

  1. Criar exceções de negócio específicas
  2. Não expor detalhes técnicos ao cliente
  3. Fazer o log das exceções do servidor
  4. Utilizar códigos HTTP apropriados
  5. Formato de resposta consistente
  6. Incluir um ID de correlação para debugging

Questões 16-30: Logging​

Pergunta 16: Qual framework de logging o Spring Boot usa por padrão?

Resposta: SLF4J como fachada com Logback como implementação.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger log = LoggerFactory.getLogger(MyClass.class);

Ou com Lombok: @Slf4j


Pergunta 17: Quais são os níveis de log?

Resposta: Do menos ao mais verboso:

  1. ERROR: erros críticos
  2. WARN: situações potencialmente problemáticas
  3. INFO: informações gerais
  4. DEBUG: detalhes para debugging
  5. TRACE: muito detalhado
log.error("Payment failed: {}", ex.getMessage());
log.warn("Slow query: {} ms", duration);
log.info("User {} logged in", username);
log.debug("Processing item: {}", item);
log.trace("Entering method with args: {}", Arrays.toString(args));

Pergunta 18: Como configurar os níveis de log?

Resposta:

logging:
level:
root: INFO
com.example: DEBUG
org.springframework.security: TRACE
org.hibernate.SQL: DEBUG

Pergunta 19: Por que usar placeholders ao invés de concatenação?

Resposta: Performance - a mensagem só é construída se o nível estiver ativo:

// Correto - avaliação preguiçosa
log.debug("Processing user: {}", user);

// Errado - sempre avaliado
log.debug("Processing user: " + user.toString());

Pergunta 20: Como configurar o Logback com um arquivo XML?

Resposta: Criar src/main/resources/logback-spring.xml:

<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

Pergunta 21: Como escrever logs em JSON?

Resposta: Com Logstash encoder:

<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

Pergunta 22: O que é MDC (Mapped Diagnostic Context)?

Resposta: Contexto thread-local para adicionar informações aos logs:

// Adicionar no início da requisição
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("userId", user.getId().toString());

// No pattern
<pattern>%d [%X{requestId}] [%X{userId}] %msg%n</pattern>

// Limpar ao final
MDC.clear();

Pergunta 23: Como implementar um filtro MDC?

Resposta:

@Component
public class MdcFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) {
try {
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("clientIp", request.getRemoteAddr());
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}

Pergunta 24: Como configurar logs diferentes por perfil?

Resposta:

<!-- logback-spring.xml -->
<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>

<springProfile name="prod">
<root level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="JSON"/>
</root>
</springProfile>

Pergunta 25: Como mascarar dados sensíveis nos logs?

Resposta:

// Não logar dados sensíveis
log.info("User login: {}", user.getEmail()); // Nunca o password!

// Mascarar parcialmente
String maskedCard = card.substring(0, 4) + "****" + card.substring(12);
log.info("Payment with card: {}", maskedCard);

// Pattern de mascaramento no logback
<pattern>%replace(%msg){'password=[^,]*','password=***'}%n</pattern>

Pergunta 26: Como logar as requisições HTTP?

Resposta:

@Component
public class RequestLoggingFilter extends CommonsRequestLoggingFilter {

public RequestLoggingFilter() {
setIncludeQueryString(true);
setIncludePayload(true);
setMaxPayloadLength(10000);
setIncludeHeaders(false);
}
}

Ou ativar na configuração:

logging:
level:
org.springframework.web.filter.CommonsRequestLoggingFilter: DEBUG

Pergunta 27: Como logar as requisições SQL?

Resposta:

logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE # Parâmetros

# Ou com Spring
spring:
jpa:
show-sql: true
properties:
hibernate:
format_sql: true

Pergunta 28: Como arquivar e comprimir os logs?

Resposta:

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>

Pergunta 29: Como enviar os logs para um serviço externo?

Resposta: Com appenders especializados:

<!-- Logstash -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash:5000</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

<!-- Ou async para performance -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="LOGSTASH"/>
</appender>

Pergunta 30: Quais são as boas práticas de logging?

Resposta:

  1. Usar o nível de log correto
  2. Incluir contexto (userId, requestId)
  3. Não logar dados sensíveis
  4. Usar placeholders
  5. Logar exceções com stacktrace
  6. Logs estruturados em produção (JSON)
  7. Rotação e arquivamento
  8. Centralização para microsserviços

Pontuação​

PontuaçãoNível
27-30Excelente - Exceções e logging dominados
21-26Bom - Algumas revisões
15-20Médio - Releia os capítulos
Menos de 15Precisa revisar

Voltar ao Módulo 10