]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.auditlogging/src/org/simantics/audit/AuditLogging.java
Fetch all audit logging events
[simantics/platform.git] / bundles / org.simantics.auditlogging / src / org / simantics / audit / AuditLogging.java
1 package org.simantics.audit;
2
3 import java.io.IOException;
4 import java.nio.charset.StandardCharsets;
5 import java.nio.file.FileSystemException;
6 import java.nio.file.Files;
7 import java.nio.file.Path;
8 import java.nio.file.StandardOpenOption;
9 import java.time.LocalDate;
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.UUID;
15
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 import com.fasterxml.jackson.core.JsonProcessingException;
20 import com.fasterxml.jackson.databind.ObjectMapper;
21
22 public class AuditLogging {
23
24     private static final Logger LOGGER = LoggerFactory.getLogger(AuditLogging.class);
25
26     private static ObjectMapper mapper = new ObjectMapper();
27
28     public enum Level {
29         INFO,
30         ERROR,
31         TRACE
32     }
33
34     public static String register(String id) throws AuditLoggingException {
35         try {
36             String entryRoot = id + "_" + UUID.randomUUID().toString();
37             Files.createDirectories(getEntryRoot(entryRoot));
38             return entryRoot;
39         } catch (Exception e) {
40             throw new AuditLoggingException("Could not register service with id " + id, e);
41         }
42     }
43
44     public static Map<String, List<String>> allLogEvents(String level, int days) throws AuditLoggingException {
45         Map<String, List<String>> results = new HashMap<>();
46         try {
47             Files.walk(Activator.getLogLocation()).forEach(uuid -> {
48                 String fileName = uuid.getFileName().toString();
49                 try {
50                     List<String> events = getLogEventsDays(fileName, level, days);
51                     results.put(fileName, events);
52                 } catch (AuditLoggingException e) {
53                     LOGGER.error("Could not get audit log events for {}", fileName, e);
54                 }
55             });
56         } catch (IOException e) {
57             throw new AuditLoggingException(e);
58         }
59         return results;
60     }
61
62     /**
63      * Gets audit events for the last 5 days
64      * 
65      * @param uuid
66      * @param level
67      * @return
68      * @throws AuditLoggingException
69      */
70     public static List<String> getLogEventsDays(String uuid, String level, int days) throws AuditLoggingException {
71         LocalDate endDate = LocalDate.now().plusDays(1);
72         LocalDate startDate = endDate.minusDays(days);
73         return getLogEvents(uuid, level, startDate, endDate);
74     }
75
76     public static List<String> getLogEvents(String uuid, String level, String startDate, String endDate) throws AuditLoggingException {
77         try {
78             LocalDate localStartDate = LocalDate.parse(startDate);
79             LocalDate localEndDate = LocalDate.parse(endDate).plusDays(1);
80             return getLogEvents(uuid, level, localStartDate, localEndDate);
81         } catch (Exception e) {
82             throw new AuditLoggingException(e);
83         }
84     }
85
86     private static List<String> getLogEvents(String uuid, String level, LocalDate localStartDate, LocalDate localEndDate) throws AuditLoggingException {
87         Path entryRoot = getEntryRoot(uuid);
88         try {
89             List<String> allLines = new ArrayList<>();
90             while (localStartDate.isBefore(localEndDate)) {
91                 String fileName = resolveLogFileName(uuid, Level.valueOf(level.toUpperCase()), localStartDate);
92                 try {
93                     Path fileToRead = entryRoot.resolve(fileName);
94                     if (Files.exists(fileToRead)) {
95                         List<String> lines = Files.readAllLines(fileToRead);
96                         allLines.addAll(lines);
97                     } else {
98                         LOGGER.info("No logging events for " + fileName);
99                     }
100                 } catch (FileSystemException e) {
101                     // presumably file not found but lets not throw this cause forward, log here
102                     LOGGER.error("Could not read file {}", fileName, e);
103                 } finally {
104                     localStartDate = localStartDate.plusDays(1);
105                 }
106             }
107             return allLines;
108         } catch (Exception e) {
109             throw new AuditLoggingException(e);
110         }
111     }
112
113     public static Path getEntryRoot(String uuid) {
114         return Activator.getLogLocation().resolve(uuid);
115     }
116
117     public static Path getLogFile(String uuid, Level level) {
118         Path root = getEntryRoot(uuid);
119         String fileName = resolveLogFileName(uuid, level, LocalDate.now());
120         return root.resolve(fileName);
121     }
122     
123     private static String resolveLogFileName(String uuid, Level level, LocalDate date) {
124         return date.toString() + "_" + uuid + "." + level.toString().toLowerCase();
125     }
126
127     public static void log(String uuid, Map<String, Object> json) throws AuditLoggingException {
128         write(uuid, Level.INFO, json);
129     }
130
131     public static void error(String uuid, Map<String, Object> json) throws AuditLoggingException {
132         write(uuid, Level.ERROR, json);
133     }
134
135     public static void trace(String uuid, Map<String, Object> json) throws AuditLoggingException {
136         write(uuid, Level.TRACE, json);
137     }
138
139     private static void write(String uuid, Level level, Map<String, Object> json) throws AuditLoggingException {
140         Map<String, Object> wrappedAuditEvent = wrapAndAddAuditMetadata(uuid, level, json);
141         try {
142             String jsonLine = mapper.writeValueAsString(wrappedAuditEvent);
143             Path logFile = getLogFile(uuid, level);
144             if (!Files.exists(logFile))
145                 Files.createFile(logFile);
146             String lineWithNewline = jsonLine + "\n";
147             Files.write(logFile, lineWithNewline.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.APPEND);
148         } catch (JsonProcessingException e) {
149             throw new AuditLoggingException("Could not serialize input", e);
150         } catch (IOException e) {
151             throw new AuditLoggingException("Could not write line to log", e);
152         }
153     }
154
155     private static final String timestamp = "timestamp";
156     
157     private static Map<String, Object> wrapAndAddAuditMetadata(String uuid, Level level, Map<String, Object> original) {
158         Map<String, Object> wrapped = new HashMap<>();
159         long newValue = System.currentTimeMillis();
160         Object possibleExisting = wrapped.put(timestamp, newValue);
161         if (possibleExisting != null) {
162             LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, timestamp, newValue);
163         }
164         possibleExisting = wrapped.put("uuid", uuid);
165         if (possibleExisting != null) {
166             LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "uuid", uuid);
167         }
168         possibleExisting = wrapped.put("level", level.toString());
169         if (possibleExisting != null) {
170             LOGGER.warn("Replacing existing value {} for key {} - new value is {}", possibleExisting, "level", level.toString());
171         }
172         wrapped.put("original", original);
173         return wrapped;
174     }
175 }