Основные рабочие методы и классы : внутренний класс LogRecord - сущность. описывающая запись в логе. Имеет только поля и конструктор. внутренний класс Query - преобразует строку в запрос. Поля запроса используются в методе execute() Метод private <T> Set<T> filterToSetByFieldNames(List<LogRecord> records, String mapBy, String filterBy, Object value) - возвращает множество того, что указано в параметре mapBy. Фильтрует по параметру filterBy на предмет соответствия значению value. Работает через рефлексию. Из внутреннего класса LogRecord по названию полей берутся их значения и типы значений.
private List<LogRecord> filterByFieldNames(List<LogRecord> records, String filterBy, Object value) {
		if (filterBy == null || value == null) {
			return records;
		}
		return records.stream().filter(r -> value.equals(getByFieldName(r, filterBy))).collect(Collectors.toList());
	}
public Set<Object> execute(String query) {
	Query q = new Query(query);
	List<LogRecord> records = filterBetweenDates(q.after, q.before);
	for (Map.Entry<String, Object> e : q.filterCriteria.entrySet()){
		records = filterByFieldNames(records, e.getKey(), e.getValue());
	}
	return records.stream().map(r -> getByFieldName(r,q.fetch)).collect(Collectors.toSet());
}
Метод сравнеия дат:
private List<LogRecord> filterBetweenDates(Date after, Date before) {
		if (before == null && after == null) return logEntries;
		Date bef = before == null? new Date(Long.MAX_VALUE) : before;
		Date aft = after == null? new Date(Long.MIN_VALUE) : after;
		return logEntries.stream().filter(e -> isBetween(e.date, aft, bef)).collect(Collectors.toList());
	}

	private boolean isBetween(Date date, Date after, Date before) {
		return (before == null || date.before(before)) && (after == null || date.after(after));
	}