]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java
Generic HTTP REST Client/Server AuditLogging framework
[simantics/platform.git] / bundles / org.simantics.auditlogging / src / org / simantics / audit / AuditLogging.java
diff --git a/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java b/bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java
new file mode 100644 (file)
index 0000000..cbbf142
--- /dev/null
@@ -0,0 +1,131 @@
+package org.simantics.audit;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class AuditLogging {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AuditLogging.class);
+
+    private static ObjectMapper mapper = new ObjectMapper();
+
+    public enum Level {
+        INFO,
+        ERROR,
+        TRACE
+    }
+
+    public static String register(String id) throws AuditLoggingException {
+        try {
+            String entryRoot = id + "_" + UUID.randomUUID().toString();
+            Files.createDirectories(getEntryRoot(entryRoot));
+            return entryRoot;
+        } catch (Exception e) {
+            throw new AuditLoggingException("Could not register service with id " + id, e);
+        }
+    }
+    
+    public static List<String> getLogEvents(String uuid, String level, String startDate, String endDate) throws AuditLoggingException {
+        Path entryRoot = getEntryRoot(uuid);
+        try {
+            LocalDate localStartDate = LocalDate.parse(startDate);
+            LocalDate localEndDate = LocalDate.parse(endDate).plusDays(1);
+            List<String> allLines = new ArrayList<>();
+            while (localStartDate.isBefore(localEndDate)) {
+                String fileName = resolveLogFileName(uuid, Level.valueOf(level.toUpperCase()), localStartDate);
+                try {
+                    List<String> lines = Files.readAllLines(entryRoot.resolve(fileName));
+                    allLines.addAll(lines);
+                } catch (FileSystemException e) {
+                    // presumably file not found but lets not throw this cause forward, log here
+                    LOGGER.error("Could not read file {}", fileName, e);
+                } finally {
+                    localStartDate = localStartDate.plusDays(1);
+                }
+            }
+            return allLines;
+        } catch (Exception e) {
+            throw new AuditLoggingException(e);
+        }
+    }
+    
+
+    public static Path getEntryRoot(String uuid) {
+        return Activator.getLogLocation().resolve(uuid);
+    }
+
+    public static Path getLogFile(String uuid, Level level) {
+        Path root = getEntryRoot(uuid);
+        String fileName = resolveLogFileName(uuid, level, LocalDate.now());
+        return root.resolve(fileName);
+    }
+    
+    private static String resolveLogFileName(String uuid, Level level, LocalDate date) {
+        return date.toString() + "_" + uuid + "." + level.toString().toLowerCase();
+    }
+
+    public static void log(String uuid, Map<String, Object> json) throws AuditLoggingException {
+        write(uuid, Level.INFO, json);
+    }
+
+    public static void error(String uuid, Map<String, Object> json) throws AuditLoggingException {
+        write(uuid, Level.ERROR, json);
+    }
+
+    public static void trace(String uuid, Map<String, Object> json) throws AuditLoggingException {
+        write(uuid, Level.TRACE, json);
+    }
+
+    private static void write(String uuid, Level level, Map<String, Object> json) throws AuditLoggingException {
+        Map<String, Object> wrappedAuditEvent = wrapAndAddAuditMetadata(uuid, level, json);
+        try {
+            String jsonLine = mapper.writeValueAsString(wrappedAuditEvent);
+            Path logFile = getLogFile(uuid, level);
+            if (!Files.exists(logFile))
+                Files.createFile(logFile);
+            String lineWithNewline = jsonLine + "\n";
+            Files.write(logFile, lineWithNewline.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.APPEND);
+        } catch (JsonProcessingException e) {
+            throw new AuditLoggingException("Could not serialize input", e);
+        } catch (IOException e) {
+            throw new AuditLoggingException("Could not write line to log", e);
+        }
+    }
+
+    private static final String timestamp = "timestamp";
+    
+    private static Map<String, Object> wrapAndAddAuditMetadata(String uuid, Level level, Map<String, Object> original) {
+        Map<String, Object> wrapped = new HashMap<>();
+        long newValue = System.currentTimeMillis();
+        Object possibleExisting = wrapped.put(timestamp, newValue);
+        if (possibleExisting != null) {
+            LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, timestamp, newValue);
+        }
+        possibleExisting = wrapped.put("uuid", uuid);
+        if (possibleExisting != null) {
+            LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "uuid", uuid);
+        }
+        possibleExisting = wrapped.put("level", level.toString());
+        if (possibleExisting != null) {
+            LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "level", level.toString());
+        }
+        wrapped.put("original", original);
+        return wrapped;
+    }
+}