Aller au contenu principal

Quiz: Gestion des Exceptions et Logging

Testez vos connaissances sur la gestion d'erreurs et le logging.

30 questions - Cliquez sur chaque question pour révéler la réponse.


Questions 1-15: Gestion des Exceptions​

Question 1: Qu'est-ce que @ControllerAdvice?

Réponse: Un composant qui gère les exceptions globalement pour tous les contrôleurs:

@ControllerAdvice
public class GlobalExceptionHandler {

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

Question 2: Quelle est la différence entre @ControllerAdvice et @RestControllerAdvice?

Réponse:

  • @ControllerAdvice: peut retourner des vues ou des objets
  • @RestControllerAdvice: équivaut à @ControllerAdvice + @ResponseBody

Pour les API REST, utilisez @RestControllerAdvice.


Question 3: Comment créer une exception personnalisée?

Réponse:

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
}

Question 4: Comment mapper une exception à un code HTTP?

Réponse:

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

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

Question 5: Comment gérer les erreurs de validation?

Réponse:

@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);
}

Question 6: Qu'est-ce que ProblemDetail (RFC 7807)?

Réponse: Format standard pour les réponses d'erreur 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;
}

Résultat JSON:

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

Question 7: Comment gérer toutes les exceptions non gérées?

Réponse:

@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"));
}

Question 8: Comment ajouter des informations à la réponse d'erreur?

Réponse:

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

// Constructeurs, 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);
}

Question 9: Comment cibler un @ControllerAdvice à certains contrôleurs?

Réponse:

// Par annotation
@ControllerAdvice(annotations = RestController.class)

// Par package
@ControllerAdvice(basePackages = "com.example.api")

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

Question 10: Comment gérer les exceptions dans les filtres?

Réponse: Les filtres sont avant les contrôleurs, donc @ControllerAdvice ne s'applique pas:

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\"}");
}
}
}

Question 11: Comment gérer les erreurs async?

Réponse:

@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();
}
}

Question 12: Comment lever une erreur 404 pour une ressource manquante?

Réponse:

@Service
public class UserService {

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

// Ou avec exception personnalisée
throw new ResourceNotFoundException("User", id);

Question 13: Comment désactiver la page d'erreur par défaut de Spring Boot?

Réponse:

server:
error:
whitelabel:
enabled: false

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

Question 14: Comment gérer les erreurs 404 globalement?

Réponse:

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()));
}

Question 15: Quelles sont les bonnes pratiques pour les exceptions?

Réponse:

  1. Créer des exceptions métier spécifiques
  2. Ne pas exposer les détails techniques au client
  3. Logger les exceptions serveur
  4. Utiliser des codes HTTP appropriés
  5. Format de réponse cohérent
  6. Inclure un ID de corrélation pour le debugging

Questions 16-30: Logging​

Question 16: Quel framework de logging utilise Spring Boot par défaut?

Réponse: SLF4J comme façade avec Logback comme implémentation.

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

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

Ou avec Lombok: @Slf4j


Question 17: Quels sont les niveaux de log?

Réponse: Du moins au plus verbeux:

  1. ERROR: erreurs critiques
  2. WARN: situations potentiellement problématiques
  3. INFO: informations générales
  4. DEBUG: détails pour le debugging
  5. TRACE: très détaillé
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));

Question 18: Comment configurer les niveaux de log?

Réponse:

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

Question 19: Pourquoi utiliser les placeholders au lieu de la concaténation?

Réponse: Performance - le message n'est construit que si le niveau est actif:

// Bon - évaluation paresseuse
log.debug("Processing user: {}", user);

// Mauvais - toujours évalué
log.debug("Processing user: " + user.toString());

Question 20: Comment configurer Logback avec un fichier XML?

Réponse: Créer 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>

Question 21: Comment écrire les logs en JSON?

Réponse: Avec 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>

Question 22: Qu'est-ce que le MDC (Mapped Diagnostic Context)?

Réponse: Contexte thread-local pour ajouter des informations aux logs:

// Ajouter au début de la requête
MDC.put("requestId", UUID.randomUUID().toString());
MDC.put("userId", user.getId().toString());

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

// Nettoyer à la fin
MDC.clear();

Question 23: Comment implémenter un filtre MDC?

Réponse:

@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();
}
}
}

Question 24: Comment configurer différents logs par profil?

Réponse:

<!-- 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>

Question 25: Comment masquer les données sensibles dans les logs?

Réponse:

// Ne pas logger les données sensibles
log.info("User login: {}", user.getEmail()); // Pas le password!

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

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

Question 26: Comment logger les requêtes HTTP?

Réponse:

@Component
public class RequestLoggingFilter extends CommonsRequestLoggingFilter {

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

Ou activer dans la config:

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

Question 27: Comment logger les requêtes SQL?

Réponse:

logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE # Paramètres

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

Question 28: Comment archiver et compresser les logs?

Réponse:

<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>

Question 29: Comment envoyer les logs à un service externe?

Réponse: Avec des appenders spécialisés:

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

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

Question 30: Quelles sont les bonnes pratiques de logging?

Réponse:

  1. Utiliser le bon niveau de log
  2. Inclure le contexte (userId, requestId)
  3. Ne pas logger de données sensibles
  4. Utiliser les placeholders
  5. Logger les exceptions avec le stacktrace
  6. Logs structurés en production (JSON)
  7. Rotation et archivage
  8. Centralisation pour les microservices

Score​

ScoreNiveau
27-30Excellent - Exceptions et logging maîtrisés
21-26Bien - Quelques révisions
15-20Moyen - Relisez les chapitres
Moins de 15À revoir

Retour au Module 10