1 package de.spiritscorp.datasync.io;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import java.io.BufferedWriter;
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.StandardCopyOption;
28 import java.nio.file.StandardOpenOption;
29 import java.time.LocalDateTime;
30 import java.time.ZoneId;
31 import java.time.format.DateTimeFormatter;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.concurrent.locks.ReentrantLock;
36
37 import de.spiritscorp.datasync.model.FileAttributes;
38 import jakarta.json.Json;
39 import jakarta.json.JsonArray;
40 import jakarta.json.JsonException;
41 import jakarta.json.JsonObject;
42
43
44
45
46
47
48 public class Logger {
49
50
51 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern( "dd.MM.yyyy HH:mm:ss" );
52
53
54 private final Path baseLogPath;
55
56
57 private final long maxFileSize;
58
59
60 private final int maxBackupIndex;
61
62
63 private final List<JsonArray> logCache = new ArrayList<>();
64
65
66 private final ReentrantLock threadLock = new ReentrantLock();
67
68
69
70
71
72 public Logger() {
73 this(
74 PreferenceManager.getInstance().getLogPath(),
75 10_485_760,
76 5 );
77 }
78
79
80
81
82
83
84
85
86
87 Logger( final Path baseLogPath, final long maxFileSize, final int maxBackupIndex ) {
88 this.baseLogPath = Objects.requireNonNull( baseLogPath, "baseLogPath must not be null" );
89 this.maxFileSize = maxFileSize;
90 this.maxBackupIndex = maxBackupIndex;
91
92 executeLogRotationIfNeeded();
93 }
94
95
96
97
98
99
100
101
102 public void setEntry( final String filePath, final String changeStatus, final FileAttributes fileAttributes ) {
103 threadLock.lock();
104 try {
105 final JsonObject jsonObject = Json.createObjectBuilder()
106 .add( "Dateiname", fileAttributes.getFileName() )
107 .add( "erstellt", fileAttributes.getCreateTimeString() )
108 .add( "zuletzt modifiziert", fileAttributes.getModTimeString() )
109 .add( "Größe", fileAttributes.getSize() )
110 .add( "Fingerabdruck", ( fileAttributes.getFileHash() == null ) ? "null" : fileAttributes.getFileHash() )
111 .build();
112
113 final JsonArray jsonArray = Json.createArrayBuilder()
114 .add( filePath )
115 .add( LocalDateTime.now( ZoneId.systemDefault() ).format( DATE_FORMATTER ) )
116 .add( changeStatus )
117 .add( jsonObject )
118 .build();
119
120 logCache.add( jsonArray );
121 }finally {
122 threadLock.unlock();
123 }
124 }
125
126
127
128
129
130 public void printStatus() {
131 threadLock.lock();
132 try {
133 if( logCache.isEmpty() ) { return; }
134
135 try( BufferedWriter writer = Files.newBufferedWriter(
136 baseLogPath,
137 StandardOpenOption.CREATE,
138 StandardOpenOption.APPEND ) ) {
139
140 for( final JsonArray logEntry : logCache ) {
141 writer.write( logEntry.toString() );
142 writer.newLine();
143 }
144
145 logCache.clear();
146 }catch( final IOException e ) {
147 Debug.printDebug( "[Logger] can´t write log file at -> %s", baseLogPath );
148 Debug.printException( this.getClass(), e );
149 }
150 }finally {
151 threadLock.unlock();
152 }
153 }
154
155
156
157
158
159
160
161 public List<JsonArray> readLogForGui() {
162 final List<JsonArray> invertedGuiList = new ArrayList<>();
163
164 if( !Files.exists( baseLogPath ) ) { return invertedGuiList; }
165
166 threadLock.lock();
167 String currentLine = "";
168 try {
169 final List<String> lines = Files.readAllLines( baseLogPath );
170 for( int i = lines.size() - 1; i >= 0; i-- ) {
171 currentLine = lines.get( i ).trim();
172 if( currentLine.isEmpty() ) continue;
173 invertedGuiList.add( Json.createArrayBuilder().add( currentLine ).build() );
174 }
175 }catch( final JsonException e ) {
176 Debug.printDebug( "[Logger] Invalid JSON in log line: %s", currentLine );
177 Debug.printException( this.getClass(), e );
178 }catch( final IOException e ) {
179 Debug.printDebug( "[Logger] can´t read log file at -> %s", baseLogPath );
180 Debug.printException( this.getClass(), e );
181 }finally {
182 threadLock.unlock();
183 }
184 return invertedGuiList;
185 }
186
187
188
189
190
191 private void executeLogRotationIfNeeded() {
192 if( !Files.exists( baseLogPath ) ) { return; }
193
194 try {
195 if( Files.size( baseLogPath ) < maxFileSize ) { return; }
196
197
198 for( int i = maxBackupIndex - 1; i >= 1; i-- ) {
199 final Path sourceBackup = resolveBackupPath( i );
200 if( Files.exists( sourceBackup ) ) {
201 final Path targetBackup = resolveBackupPath( i + 1 );
202 Files.move( sourceBackup, targetBackup, StandardCopyOption.REPLACE_EXISTING );
203 }
204 }
205
206
207 final Path firstBackup = resolveBackupPath( 1 );
208 Files.move( baseLogPath, firstBackup, StandardCopyOption.REPLACE_EXISTING );
209
210 }catch( final IOException e ) {
211 Debug.printDebug( "[Logger] Execution of log rotation failed at -> %s", baseLogPath );
212 Debug.printException( this.getClass(), e );
213 }
214 }
215
216
217
218
219
220
221
222 private Path resolveBackupPath( final int index ) {
223 return baseLogPath.resolveSibling( baseLogPath.getFileName().toString() + "." + index );
224 }
225 }