Java日志检索案例怎么实现

wen java案例 28

本文目录导读:

Java日志检索案例怎么实现

  1. 基础日志文件检索
  2. 高级日志检索系统
  3. 性能优化建议

我来为你提供几个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();
            }
        });
    }
}

这个日志检索案例实现了:

  1. 基础关键词搜索
  2. 时间范围过滤
  3. 多条件组合搜索
  4. 多线程并行处理
  5. 日志索引优化
  6. 缓存机制

你可以根据实际需求选择合适的实现方式,并在此基础上进行扩展。

抱歉,评论功能暂时关闭!