本文目录导读:

我来为你提供几个Java日志检索的完整案例实现。
基础日志文件检索
1 按关键词搜索
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
public class LogSearcher {
/**
* 在单个日志文件中搜索关键词
*/
public static List<String> searchInFile(String filePath, String keyword) throws IOException {
List<String> results = new ArrayList<>();
Path path = Paths.get(filePath);
try (Stream<String> lines = Files.lines(path)) {
lines.filter(line -> line.contains(keyword))
.forEach(results::add);
}
return results;
}
/**
* 在多个日志文件中搜索
*/
public static Map<String, List<String>> searchInFiles(List<String> filePaths, String keyword) {
Map<String, List<String>> results = new HashMap<>();
for (String filePath : filePaths) {
try {
List<String> matches = searchInFile(filePath, keyword);
if (!matches.isEmpty()) {
results.put(filePath, matches);
}
} catch (IOException e) {
System.err.println("Error reading file: " + filePath);
}
}
return results;
}
}
2 带时间范围的检索
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.*;
public class TimeBasedLogSearcher {
private static final DateTimeFormatter LOG_DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 按时间范围检索日志
*/
public static List<String> searchByTimeRange(String filePath,
LocalDateTime startTime,
LocalDateTime endTime) throws IOException {
List<String> results = new ArrayList<>();
Pattern timePattern = Pattern.compile("(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})");
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = timePattern.matcher(line);
if (matcher.find()) {
LocalDateTime logTime = LocalDateTime.parse(matcher.group(1), LOG_DATE_FORMAT);
if (!logTime.isBefore(startTime) && !logTime.isAfter(endTime)) {
results.add(line);
}
}
}
}
return results;
}
}
高级日志检索系统
1 完整的日志检索服务
import java.io.*;
import java.nio.file.*;
import java.time.*;
import java.time.format.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.util.stream.*;
public class AdvancedLogSearchService {
private final Path logDirectory;
private final ExecutorService executor;
public AdvancedLogSearchService(String logDirectory) {
this.logDirectory = Paths.get(logDirectory);
this.executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
}
/**
* 综合日志检索
*/
public LogSearchResult search(LogSearchCriteria criteria) throws IOException, InterruptedException {
List<Path> logFiles = findLogFiles(criteria);
List<Future<List<LogEntry>>> futures = new ArrayList<>();
for (Path logFile : logFiles) {
futures.add(executor.submit(() -> searchInFile(logFile, criteria)));
}
LogSearchResult result = new LogSearchResult(criteria);
for (Future<List<LogEntry>> future : futures) {
result.addEntries(future.get());
}
return result;
}
private List<Path> findLogFiles(LogSearchCriteria criteria) throws IOException {
try (Stream<Path> stream = Files.walk(logDirectory)) {
return stream
.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".log"))
.filter(path -> {
if (criteria.getFilePattern() == null) return true;
return path.getFileName().toString().matches(criteria.getFilePattern());
})
.collect(Collectors.toList());
}
}
private List<LogEntry> searchInFile(Path filePath, LogSearchCriteria criteria) {
List<LogEntry> entries = new ArrayList<>();
try (BufferedReader reader = Files.newBufferedReader(filePath)) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
LogEntry entry = parseLogEntry(filePath, line, lineNumber);
if (matchesCriteria(entry, criteria)) {
entries.add(entry);
}
}
} catch (IOException e) {
System.err.println("Error reading file: " + filePath);
}
return entries;
}
private LogEntry parseLogEntry(Path filePath, String line, int lineNumber) {
LogEntry entry = new LogEntry();
entry.setFilePath(filePath.toString());
entry.setLineNumber(lineNumber);
entry.setRawLine(line);
// 解析日志级别
Pattern levelPattern = Pattern.compile("\\b(ERROR|WARN|INFO|DEBUG|TRACE)\\b");
Matcher levelMatcher = levelPattern.matcher(line);
if (levelMatcher.find()) {
entry.setLevel(LogLevel.valueOf(levelMatcher.group(1)));
}
// 解析时间戳
Pattern timePattern = Pattern.compile("(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[.,]\\d{3})");
Matcher timeMatcher = timePattern.matcher(line);
if (timeMatcher.find()) {
try {
entry.setTimestamp(LocalDateTime.parse(
timeMatcher.group(1).replace(",", "."),
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
));
} catch (Exception e) {
// 时间解析失败,忽略
}
}
// 解析异常信息
if (line.contains("Exception") || line.contains("Error")) {
extractExceptionInfo(entry, line);
}
return entry;
}
private void extractExceptionInfo(LogEntry entry, String line) {
Pattern exceptionPattern = Pattern.compile("(\\w+\\.\\w+(?:\\.\\w+)*Exception|\\w+Error)");
Matcher matcher = exceptionPattern.matcher(line);
if (matcher.find()) {
entry.setExceptionType(matcher.group(1));
}
}
private boolean matchesCriteria(LogEntry entry, LogSearchCriteria criteria) {
if (criteria.getLevel() != null && entry.getLevel() != criteria.getLevel()) {
return false;
}
if (criteria.getKeyword() != null &&
!entry.getRawLine().toLowerCase().contains(criteria.getKeyword().toLowerCase())) {
return false;
}
if (criteria.getStartTime() != null && entry.getTimestamp() != null) {
if (entry.getTimestamp().isBefore(criteria.getStartTime())) {
return false;
}
}
if (criteria.getEndTime() != null && entry.getTimestamp() != null) {
if (entry.getTimestamp().isAfter(criteria.getEndTime())) {
return false;
}
}
if (criteria.getExceptionType() != null && entry.getExceptionType() != null) {
if (!entry.getExceptionType().equals(criteria.getExceptionType())) {
return false;
}
}
return true;
}
public void shutdown() {
executor.shutdown();
}
}
// 日志搜索条件
class LogSearchCriteria {
private String keyword;
private LogLevel level;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String filePattern;
private String exceptionType;
// Getters and setters
// ...
}
// 日志条目
class LogEntry {
private String filePath;
private int lineNumber;
private LocalDateTime timestamp;
private LogLevel level;
private String rawLine;
private String exceptionType;
private String threadName;
private String className;
// Getters and setters
// ...
}
// 日志级别枚举
enum LogLevel {
TRACE, DEBUG, INFO, WARN, ERROR, FATAL
}
// 搜索结果
class LogSearchResult {
private LogSearchCriteria criteria;
private List<LogEntry> entries = new ArrayList<>();
private long searchTime;
public LogSearchResult(LogSearchCriteria criteria) {
this.criteria = criteria;
}
public void addEntries(List<LogEntry> entries) {
this.entries.addAll(entries);
}
public void addEntry(LogEntry entry) {
this.entries.add(entry);
}
public List<LogEntry> getEntries() {
return entries;
}
public int getTotalCount() {
return entries.size();
}
// Getters and setters
// ...
}
2 日志索引优化
import java.util.concurrent.ConcurrentHashMap;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class LogIndexer {
private final Map<String, List<LogIndex>> indexCache = new ConcurrentHashMap<>();
/**
* 创建日志索引
*/
public void createIndex(String filePath) throws IOException {
List<LogIndex> indices = new ArrayList<>();
try (RandomAccessFile file = new RandomAccessFile(filePath, "r");
FileChannel channel = file.getChannel()) {
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_ONLY, 0, channel.size());
long position = 0;
int lineNumber = 0;
StringBuilder line = new StringBuilder();
while (buffer.hasRemaining()) {
byte b = buffer.get();
if (b == '\n') {
processLine(filePath, line.toString(), position, lineNumber++, indices);
position = buffer.position();
line.setLength(0);
} else {
line.append((char) b);
}
}
}
indexCache.put(filePath, indices);
}
private void processLine(String filePath, String line, long position,
int lineNumber, List<LogIndex> indices) {
LogIndex index = new LogIndex();
index.setFilePath(filePath);
index.setPosition(position);
index.setLineNumber(lineNumber);
index.setLength(line.length());
// 提取关键词
extractKeywords(line, index);
indices.add(index);
}
private void extractKeywords(String line, LogIndex index) {
// 提取时间、级别、异常等关键信息
Pattern pattern = Pattern.compile(
"(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[.,]\\d{3})\\s+" +
"(ERROR|WARN|INFO|DEBUG|TRACE)\\s+");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
index.setTimestamp(matcher.group(1));
index.setLevel(matcher.group(2));
}
}
/**
* 使用索引快速检索
*/
public List<String> searchByIndex(String filePath, String keyword) throws IOException {
List<String> results = new ArrayList<>();
List<LogIndex> indices = indexCache.get(filePath);
if (indices == null) {
createIndex(filePath);
indices = indexCache.get(filePath);
}
try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
for (LogIndex index : indices) {
// 快速定位并读取日志行
file.seek(index.getPosition());
String line = file.readLine();
if (line != null && line.contains(keyword)) {
results.add(line);
}
}
}
return results;
}
}
class LogIndex {
private String filePath;
private long position;
private int lineNumber;
private int length;
private String timestamp;
private String level;
// Getters and setters
// ...
}
3 使用示例
public class LogSearchDemo {
public static void main(String[] args) {
// 基础使用
basicSearch();
// 高级使用
advancedSearch();
// 索引优化使用
indexedSearch();
}
private static void basicSearch() {
try {
// 单文件搜索
List<String> results = LogSearcher.searchInFile(
"/var/log/application.log", "NullPointerException");
results.forEach(System.out::println);
// 多文件搜索
Map<String, List<String>> allResults = LogSearcher.searchInFiles(
Arrays.asList("/var/log/app1.log", "/var/log/app2.log"),
"ERROR");
allResults.forEach((file, matches) -> {
System.out.println("File: " + file);
matches.forEach(line -> System.out.println(" " + line));
});
} catch (IOException e) {
e.printStackTrace();
}
}
private static void advancedSearch() {
AdvancedLogSearchService searchService =
new AdvancedLogSearchService("/var/log/myapp");
try {
// 创建搜索条件
LogSearchCriteria criteria = new LogSearchCriteria();
criteria.setKeyword("NullPointerException");
criteria.setLevel(LogLevel.ERROR);
criteria.setStartTime(LocalDateTime.now().minusHours(24));
criteria.setEndTime(LocalDateTime.now());
criteria.setFilePattern("application.*\\.log");
// 执行搜索
LogSearchResult result = searchService.search(criteria);
// 输出结果
System.out.println("Total matches: " + result.getTotalCount());
result.getEntries().forEach(entry -> {
System.out.printf("[%s] %s:%d - %s%n",
entry.getTimestamp(),
entry.getFilePath(),
entry.getLineNumber(),
entry.getRawLine());
});
} catch (Exception e) {
e.printStackTrace();
} finally {
searchService.shutdown();
}
}
private static void indexedSearch() {
LogIndexer indexer = new LogIndexer();
try {
// 使用索引搜索(首次会创建索引)
List<String> results = indexer.searchByIndex(
"/var/log/large-application.log", "OutOfMemoryError");
results.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
性能优化建议
1 多线程搜索
public CompletableFuture<List<LogEntry>> parallelSearch(
List<Path> files, LogSearchCriteria criteria) {
List<CompletableFuture<List<LogEntry>>> futures = files.stream()
.map(file -> CompletableFuture.supplyAsync(() -> {
try {
return searchInFile(file, criteria);
} catch (Exception e) {
return Collections.emptyList();
}
}))
.collect(Collectors.toList());
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.flatMap(future -> future.join().stream())
.collect(Collectors.toList()));
}
2 内存映射文件
public void searchWithMemoryMappedFile(String filePath, String keyword)
throws IOException {
try (RandomAccessFile file = new RandomAccessFile(filePath, "r");
FileChannel channel = file.getChannel()) {
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_ONLY, 0, channel.size());
int bufferSize = 1024 * 1024; // 1MB缓冲区
byte[] chunk = new byte[bufferSize];
while (buffer.hasRemaining()) {
int bytesToRead = Math.min(buffer.remaining(), bufferSize);
buffer.get(chunk, 0, bytesToRead);
String content = new String(chunk, 0, bytesToRead);
if (content.contains(keyword)) {
// 处理匹配的内容
System.out.println("Found match at position: " + buffer.position());
}
}
}
}
3 缓存优化
public class CachedLogSearcher {
private final Cache<String, List<String>> searchCache;
private final Cache<String, List<LogIndex>> indexCache;
public CachedLogSearcher() {
// 使用Caffeine缓存
this.searchCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
this.indexCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(1, TimeUnit.HOURS)
.build();
}
public List<String> search(String filePath, String keyword) {
String cacheKey = filePath + "::" + keyword;
return searchCache.get(cacheKey, k -> {
try {
return performSearch(filePath, keyword);
} catch (IOException e) {
return Collections.emptyList();
}
});
}
}
这个日志检索案例实现了:
- 基础关键词搜索
- 时间范围过滤
- 多条件组合搜索
- 多线程并行处理
- 日志索引优化
- 缓存机制
你可以根据实际需求选择合适的实现方式,并在此基础上进行扩展。