298 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.util.eventlog;
 | |
| 
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.io.BufferedReader;
 | |
| import java.io.IOException;
 | |
| import java.io.InputStreamReader;
 | |
| import java.io.OutputStream;
 | |
| import java.io.Reader;
 | |
| import java.nio.ByteBuffer;
 | |
| import java.nio.channels.FileChannel;
 | |
| import java.nio.channels.FileLock;
 | |
| import java.nio.channels.ReadableByteChannel;
 | |
| import java.nio.file.Files;
 | |
| import java.nio.file.LinkOption;
 | |
| import java.nio.file.Path;
 | |
| import java.nio.file.StandardOpenOption;
 | |
| import java.time.LocalDate;
 | |
| import java.time.format.DateTimeFormatter;
 | |
| import java.time.format.DateTimeParseException;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Iterator;
 | |
| import java.util.List;
 | |
| import java.util.ListIterator;
 | |
| import java.util.Objects;
 | |
| import java.util.Set;
 | |
| import java.util.stream.Collectors;
 | |
| import java.util.stream.Stream;
 | |
| import java.util.zip.GZIPInputStream;
 | |
| import java.util.zip.GZIPOutputStream;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.slf4j.Logger;
 | |
| 
 | |
| public class EventLogDirectory {
 | |
| 	static final Logger LOGGER = LogUtils.getLogger();
 | |
| 	private static final int COMPRESS_BUFFER_SIZE = 4096;
 | |
| 	private static final String COMPRESSED_EXTENSION = ".gz";
 | |
| 	private final Path root;
 | |
| 	private final String extension;
 | |
| 
 | |
| 	private EventLogDirectory(Path root, String extension) {
 | |
| 		this.root = root;
 | |
| 		this.extension = extension;
 | |
| 	}
 | |
| 
 | |
| 	public static EventLogDirectory open(Path root, String extension) throws IOException {
 | |
| 		Files.createDirectories(root);
 | |
| 		return new EventLogDirectory(root, extension);
 | |
| 	}
 | |
| 
 | |
| 	public EventLogDirectory.FileList listFiles() throws IOException {
 | |
| 		Stream<Path> stream = Files.list(this.root);
 | |
| 
 | |
| 		EventLogDirectory.FileList var2;
 | |
| 		try {
 | |
| 			var2 = new EventLogDirectory.FileList(
 | |
| 				stream.filter(path -> Files.isRegularFile(path, new LinkOption[0])).map(this::parseFile).filter(Objects::nonNull).toList()
 | |
| 			);
 | |
| 		} catch (Throwable var5) {
 | |
| 			if (stream != null) {
 | |
| 				try {
 | |
| 					stream.close();
 | |
| 				} catch (Throwable var4) {
 | |
| 					var5.addSuppressed(var4);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			throw var5;
 | |
| 		}
 | |
| 
 | |
| 		if (stream != null) {
 | |
| 			stream.close();
 | |
| 		}
 | |
| 
 | |
| 		return var2;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	private EventLogDirectory.File parseFile(Path path) {
 | |
| 		String string = path.getFileName().toString();
 | |
| 		int i = string.indexOf(46);
 | |
| 		if (i == -1) {
 | |
| 			return null;
 | |
| 		} else {
 | |
| 			EventLogDirectory.FileId fileId = EventLogDirectory.FileId.parse(string.substring(0, i));
 | |
| 			if (fileId != null) {
 | |
| 				String string2 = string.substring(i);
 | |
| 				if (string2.equals(this.extension)) {
 | |
| 					return new EventLogDirectory.RawFile(path, fileId);
 | |
| 				}
 | |
| 
 | |
| 				if (string2.equals(this.extension + ".gz")) {
 | |
| 					return new EventLogDirectory.CompressedFile(path, fileId);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return null;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	static void tryCompress(Path path, Path outputPath) throws IOException {
 | |
| 		if (Files.exists(outputPath, new LinkOption[0])) {
 | |
| 			throw new IOException("Compressed target file already exists: " + outputPath);
 | |
| 		} else {
 | |
| 			FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.READ);
 | |
| 
 | |
| 			try {
 | |
| 				FileLock fileLock = fileChannel.tryLock();
 | |
| 				if (fileLock == null) {
 | |
| 					throw new IOException("Raw log file is already locked, cannot compress: " + path);
 | |
| 				}
 | |
| 
 | |
| 				writeCompressed(fileChannel, outputPath);
 | |
| 				fileChannel.truncate(0L);
 | |
| 			} catch (Throwable var6) {
 | |
| 				if (fileChannel != null) {
 | |
| 					try {
 | |
| 						fileChannel.close();
 | |
| 					} catch (Throwable var5) {
 | |
| 						var6.addSuppressed(var5);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				throw var6;
 | |
| 			}
 | |
| 
 | |
| 			if (fileChannel != null) {
 | |
| 				fileChannel.close();
 | |
| 			}
 | |
| 
 | |
| 			Files.delete(path);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void writeCompressed(ReadableByteChannel channel, Path outputPath) throws IOException {
 | |
| 		OutputStream outputStream = new GZIPOutputStream(Files.newOutputStream(outputPath));
 | |
| 
 | |
| 		try {
 | |
| 			byte[] bs = new byte[4096];
 | |
| 			ByteBuffer byteBuffer = ByteBuffer.wrap(bs);
 | |
| 
 | |
| 			while (channel.read(byteBuffer) >= 0) {
 | |
| 				byteBuffer.flip();
 | |
| 				outputStream.write(bs, 0, byteBuffer.limit());
 | |
| 				byteBuffer.clear();
 | |
| 			}
 | |
| 		} catch (Throwable var6) {
 | |
| 			try {
 | |
| 				outputStream.close();
 | |
| 			} catch (Throwable var5) {
 | |
| 				var6.addSuppressed(var5);
 | |
| 			}
 | |
| 
 | |
| 			throw var6;
 | |
| 		}
 | |
| 
 | |
| 		outputStream.close();
 | |
| 	}
 | |
| 
 | |
| 	public EventLogDirectory.RawFile createNewFile(LocalDate date) throws IOException {
 | |
| 		int i = 1;
 | |
| 		Set<EventLogDirectory.FileId> set = this.listFiles().ids();
 | |
| 
 | |
| 		EventLogDirectory.FileId fileId;
 | |
| 		do {
 | |
| 			fileId = new EventLogDirectory.FileId(date, i++);
 | |
| 		} while (set.contains(fileId));
 | |
| 
 | |
| 		EventLogDirectory.RawFile rawFile = new EventLogDirectory.RawFile(this.root.resolve(fileId.toFileName(this.extension)), fileId);
 | |
| 		Files.createFile(rawFile.path());
 | |
| 		return rawFile;
 | |
| 	}
 | |
| 
 | |
| 	public record CompressedFile(Path path, EventLogDirectory.FileId id) implements EventLogDirectory.File {
 | |
| 		@Nullable
 | |
| 		@Override
 | |
| 		public Reader openReader() throws IOException {
 | |
| 			return !Files.exists(this.path, new LinkOption[0]) ? null : new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(this.path))));
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public EventLogDirectory.CompressedFile compress() {
 | |
| 			return this;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public interface File {
 | |
| 		Path path();
 | |
| 
 | |
| 		EventLogDirectory.FileId id();
 | |
| 
 | |
| 		@Nullable
 | |
| 		Reader openReader() throws IOException;
 | |
| 
 | |
| 		EventLogDirectory.CompressedFile compress() throws IOException;
 | |
| 	}
 | |
| 
 | |
| 	public record FileId(LocalDate date, int index) {
 | |
| 		private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE;
 | |
| 
 | |
| 		@Nullable
 | |
| 		public static EventLogDirectory.FileId parse(String fileName) {
 | |
| 			int i = fileName.indexOf("-");
 | |
| 			if (i == -1) {
 | |
| 				return null;
 | |
| 			} else {
 | |
| 				String string = fileName.substring(0, i);
 | |
| 				String string2 = fileName.substring(i + 1);
 | |
| 
 | |
| 				try {
 | |
| 					return new EventLogDirectory.FileId(LocalDate.parse(string, DATE_FORMATTER), Integer.parseInt(string2));
 | |
| 				} catch (DateTimeParseException | NumberFormatException var5) {
 | |
| 					return null;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public String toString() {
 | |
| 			return DATE_FORMATTER.format(this.date) + "-" + this.index;
 | |
| 		}
 | |
| 
 | |
| 		public String toFileName(String extension) {
 | |
| 			return this + extension;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static class FileList implements Iterable<EventLogDirectory.File> {
 | |
| 		private final List<EventLogDirectory.File> files;
 | |
| 
 | |
| 		FileList(List<EventLogDirectory.File> files) {
 | |
| 			this.files = new ArrayList(files);
 | |
| 		}
 | |
| 
 | |
| 		public EventLogDirectory.FileList prune(LocalDate date, int daysToKeep) {
 | |
| 			this.files.removeIf(file -> {
 | |
| 				EventLogDirectory.FileId fileId = file.id();
 | |
| 				LocalDate localDate2 = fileId.date().plusDays(daysToKeep);
 | |
| 				if (!date.isBefore(localDate2)) {
 | |
| 					try {
 | |
| 						Files.delete(file.path());
 | |
| 						return true;
 | |
| 					} catch (IOException var6) {
 | |
| 						EventLogDirectory.LOGGER.warn("Failed to delete expired event log file: {}", file.path(), var6);
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return false;
 | |
| 			});
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public EventLogDirectory.FileList compressAll() {
 | |
| 			ListIterator<EventLogDirectory.File> listIterator = this.files.listIterator();
 | |
| 
 | |
| 			while (listIterator.hasNext()) {
 | |
| 				EventLogDirectory.File file = (EventLogDirectory.File)listIterator.next();
 | |
| 
 | |
| 				try {
 | |
| 					listIterator.set(file.compress());
 | |
| 				} catch (IOException var4) {
 | |
| 					EventLogDirectory.LOGGER.warn("Failed to compress event log file: {}", file.path(), var4);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return this;
 | |
| 		}
 | |
| 
 | |
| 		public Iterator<EventLogDirectory.File> iterator() {
 | |
| 			return this.files.iterator();
 | |
| 		}
 | |
| 
 | |
| 		public Stream<EventLogDirectory.File> stream() {
 | |
| 			return this.files.stream();
 | |
| 		}
 | |
| 
 | |
| 		public Set<EventLogDirectory.FileId> ids() {
 | |
| 			return (Set<EventLogDirectory.FileId>)this.files.stream().map(EventLogDirectory.File::id).collect(Collectors.toSet());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public record RawFile(Path path, EventLogDirectory.FileId id) implements EventLogDirectory.File {
 | |
| 		public FileChannel openChannel() throws IOException {
 | |
| 			return FileChannel.open(this.path, StandardOpenOption.WRITE, StandardOpenOption.READ);
 | |
| 		}
 | |
| 
 | |
| 		@Nullable
 | |
| 		@Override
 | |
| 		public Reader openReader() throws IOException {
 | |
| 			return Files.exists(this.path, new LinkOption[0]) ? Files.newBufferedReader(this.path) : null;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public EventLogDirectory.CompressedFile compress() throws IOException {
 | |
| 			Path path = this.path.resolveSibling(this.path.getFileName().toString() + ".gz");
 | |
| 			EventLogDirectory.tryCompress(this.path, path);
 | |
| 			return new EventLogDirectory.CompressedFile(path, this.id);
 | |
| 		}
 | |
| 	}
 | |
| }
 |