-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 🎸 Add concatenated logs to elastic (#932)
- Loading branch information
Showing
6 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
dossierfacile-api-tenant/src/main/java/fr/dossierfacile/api/front/log/CustomAppender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package fr.dossierfacile.api.front.log; | ||
|
||
import ch.qos.logback.classic.Level; | ||
import ch.qos.logback.classic.Logger; | ||
import ch.qos.logback.classic.spi.ILoggingEvent; | ||
import ch.qos.logback.classic.spi.IThrowableProxy; | ||
import ch.qos.logback.classic.spi.StackTraceElementProxy; | ||
import ch.qos.logback.core.AppenderBase; | ||
import ch.qos.logback.core.Context; | ||
import org.slf4j.LoggerFactory; | ||
import org.slf4j.MDC; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
public class CustomAppender extends AppenderBase<ILoggingEvent> { | ||
private static final Map<String, List<LogModel>> logsByRequestId = new ConcurrentHashMap<>(); | ||
|
||
@Override | ||
protected void append(ILoggingEvent eventObject) { | ||
String requestId = MDC.get("request_id"); | ||
if (requestId != null) { | ||
String message = eventObject.getFormattedMessage(); | ||
|
||
if (eventObject.getLevel().isGreaterOrEqual(Level.ERROR)) { | ||
IThrowableProxy throwableProxy = eventObject.getThrowableProxy(); | ||
if (throwableProxy != null) { | ||
StringBuilder stackTrace = new StringBuilder(); | ||
for (StackTraceElementProxy line : throwableProxy.getStackTraceElementProxyArray()) { | ||
stackTrace.append(line).append("\n"); | ||
} | ||
message += "\n" + stackTrace; | ||
} | ||
} | ||
|
||
LogModel log = new LogModel(message, eventObject.getLevel()); | ||
logsByRequestId.computeIfAbsent(requestId, id -> new ArrayList<>()) | ||
.add(log); | ||
} | ||
} | ||
|
||
public static List<LogModel> getLogsForRequest(String requestId) { | ||
return logsByRequestId.getOrDefault(requestId, List.of()); | ||
} | ||
|
||
public static void clearLogsForRequest(String requestId) { | ||
logsByRequestId.remove(requestId); | ||
} | ||
|
||
public static void attachToRootLogger() { | ||
Logger rootLogger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); | ||
CustomAppender customAppender = new CustomAppender(); | ||
customAppender.setName("CUSTOM"); | ||
Context context = rootLogger.getLoggerContext(); | ||
customAppender.setContext(context); | ||
customAppender.start(); | ||
rootLogger.addAppender(customAppender); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...acile-api-tenant/src/main/java/fr/dossierfacile/api/front/log/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package fr.dossierfacile.api.front.log; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
|
||
@ControllerAdvice | ||
@Slf4j | ||
public class GlobalExceptionHandler { | ||
|
||
@ExceptionHandler(Exception.class) | ||
public ResponseEntity<String> handleException(Exception e) { | ||
log.error("Unhandled exception: ", e); | ||
return new ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
...rfacile-api-tenant/src/main/java/fr/dossierfacile/api/front/log/LogAggregationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package fr.dossierfacile.api.front.log; | ||
|
||
import ch.qos.logback.classic.Level; | ||
import ch.qos.logback.classic.Logger; | ||
import ch.qos.logback.classic.LoggerContext; | ||
import ch.qos.logback.classic.spi.LoggingEvent; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import net.logstash.logback.appender.LogstashTcpSocketAppender; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.slf4j.LoggerFactory; | ||
import org.slf4j.MDC; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import javax.annotation.PostConstruct; | ||
import java.io.IOException; | ||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
@Component | ||
public class LogAggregationFilter extends OncePerRequestFilter { | ||
private LogstashTcpSocketAppender logstashAppender; | ||
|
||
@PostConstruct | ||
public void init() { | ||
Logger rootLogger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); | ||
logstashAppender = (LogstashTcpSocketAppender) rootLogger.getAppender("LOGSTASH"); | ||
if (logstashAppender == null) { | ||
throw new IllegalStateException("Logstash appender (LOGSTASH) not found in Logback configuration."); | ||
} | ||
} | ||
|
||
@Override | ||
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain filterChain) | ||
throws ServletException, IOException { | ||
String requestId = MDC.get("request_id"); | ||
try { | ||
filterChain.doFilter(request, response); | ||
} finally { | ||
List<LogModel> logs = CustomAppender.getLogsForRequest(requestId); | ||
String logMessage = logs.stream().map(LogModel::getMessage).collect(Collectors.joining()); | ||
|
||
String enrichedLogs = String.format( | ||
"Request completed: URI:%s, Method:%s, Status:%d, Logs:%s", | ||
request.getRequestURI(), | ||
request.getMethod(), | ||
response.getStatus(), | ||
logMessage | ||
); | ||
|
||
Level logLevel = logs.stream().map(LogModel::getLevel).max(Comparator.comparingInt(Level::toInt)).orElse(Level.INFO); | ||
|
||
LoggingEvent enrichedEvent = new LoggingEvent(); | ||
enrichedEvent.setTimeStamp(System.currentTimeMillis()); | ||
enrichedEvent.setLoggerName("AggregatedHttpLogger"); | ||
enrichedEvent.setLevel(logLevel); | ||
enrichedEvent.setThreadName(Thread.currentThread().getName()); | ||
enrichedEvent.setMessage(enrichedLogs); | ||
|
||
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); | ||
enrichedEvent.setLoggerContext(loggerContext); | ||
|
||
logstashAppender.doAppend(enrichedEvent); | ||
|
||
CustomAppender.clearLogsForRequest(requestId); | ||
} | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
dossierfacile-api-tenant/src/main/java/fr/dossierfacile/api/front/log/LogConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package fr.dossierfacile.api.front.log; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
import javax.annotation.PostConstruct; | ||
|
||
@Component | ||
public class LogConfiguration { | ||
|
||
@PostConstruct | ||
public void setupCustomLogging() { | ||
CustomAppender.attachToRootLogger(); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
dossierfacile-api-tenant/src/main/java/fr/dossierfacile/api/front/log/LogModel.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package fr.dossierfacile.api.front.log; | ||
|
||
import ch.qos.logback.classic.Level; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class LogModel { | ||
String message; | ||
Level level; | ||
|
||
public LogModel(String message, Level level) { | ||
this.message = message; | ||
this.level = level; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters