backend/src/main/java/dev/dinauer/inspect/log/LogWebsocket.java

114 lines
3.8 KiB
Java

package dev.dinauer.inspect.log;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import dev.dinauer.utils.ClientProvider;
import io.fabric8.kubernetes.client.dsl.LogWatch;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.eclipse.microprofile.context.ManagedExecutor;
import org.jboss.logging.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Future;
@ServerEndpoint("/logs/{namespace}/{name}")
@ApplicationScoped
public class LogWebsocket
{
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().registerModule(new JavaTimeModule()).disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
private final Map<Session, LogWatch> sessions = new HashMap<>();
@Inject
Logger LOG;
@Inject
ClientProvider clientProvider;
@Inject
ManagedExecutor executor;
@OnOpen
public void onOpen(Session session, @PathParam("namespace") String namespace, @PathParam("name") String name)
{
executor.submit(() -> {
List<String> existingLogs = Arrays.stream(clientProvider.getClient().pods().inNamespace(namespace).withName(name).usingTimestamps().tailingLines(200).getLog().split("\n")).toList();
send(session, toLog(existingLogs));
LogWatch watch = clientProvider.getClient().pods().inNamespace(namespace).withName(name).usingTimestamps().tailingLines(0).watchLog();
sessions.put(session, watch);
try(BufferedReader reader = new BufferedReader(new InputStreamReader(watch.getOutput())))
{
String line;
while ((line = reader.readLine()) != null && !Thread.currentThread().isInterrupted())
{
send(session, toLog(List.of(line)));
}
LOG.info("Ended");
}
catch (Exception e)
{
LOG.errorf("Error reading output of log watch: %s", e.getMessage());
}
});
}
@OnClose
public void onClose(Session session) throws IOException
{
LogWatch watch = sessions.remove(session);
if (watch != null)
{
watch.close();
}
session.close();
}
private void send(Session session, List<KubernetesLog> logs)
{
try
{
session.getAsyncRemote().sendText(OBJECT_MAPPER.writeValueAsString(logs));
}
catch (Exception e)
{
LOG.errorf("Error sending logs to frontend via websocket: %s", e.getMessage());
}
}
private List<KubernetesLog> toLog(List<String> logs)
{
List<KubernetesLog> result = new ArrayList<>();
for (String log : logs)
{
int indexFirstSpace = log.indexOf(" ");
if (indexFirstSpace != -1)
{
String timestampRaw = log.substring(0, indexFirstSpace);
String message = log.substring(indexFirstSpace).trim();
try
{
result.add(new KubernetesLog(LocalDateTime.parse(timestampRaw, DateTimeFormatter.ISO_DATE_TIME), message));
}
catch (Exception e)
{
LOG.errorf("Error parsing log: %s", e.getMessage());
}
}
}
return result;
}
}