package ch.rasc.wampspring.demo.various.tail; import java.io.IOException; import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.PreDestroy; import org.apache.commons.io.input.Tailer; import org.apache.commons.io.input.TailerListenerAdapter; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.AddressNotFoundException; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CityResponse; import ch.rasc.wampspring.EventMessenger; import net.sf.uadetector.ReadableUserAgent; import net.sf.uadetector.UserAgentFamily; import net.sf.uadetector.UserAgentStringParser; import net.sf.uadetector.service.UADetectorServiceFactory; @Service public class TailService { private final Pattern accessLogPattern = Pattern.compile(getAccessLogRegex(), Pattern.CASE_INSENSITIVE | Pattern.DOTALL); private final UserAgentStringParser parser = UADetectorServiceFactory .getResourceModuleParser(); private final EventMessenger eventMessenger; public ExecutorService executor; private final List<Tailer> tailers; private DatabaseReader reader = null; @Autowired public TailService(@Value("${geoip2.cityfile}") String cityFile, @Value("${access.logs}") String accessLogs, EventMessenger eventMessenger) { this.eventMessenger = eventMessenger; String databaseFile = cityFile; if (databaseFile != null) { Path database = Paths.get(databaseFile); if (Files.exists(database)) { try { this.reader = new DatabaseReader.Builder(database.toFile()).build(); } catch (IOException e) { LoggerFactory.getLogger(getClass()).error("GeoIPCityService init", e); } } } this.tailers = new ArrayList<>(); for (String logFile : accessLogs.split(",")) { Path p = Paths.get(logFile.trim()); this.tailers.add(new Tailer(p.toFile(), new ListenerAdapter())); } this.executor = Executors.newFixedThreadPool(this.tailers.size()); for (Tailer tailer : this.tailers) { this.executor.execute(tailer); } } @PreDestroy public void preDestroy() { if (this.tailers != null) { for (Tailer tailer : this.tailers) { tailer.stop(); } } if (this.executor != null) { this.executor.shutdown(); } } private class ListenerAdapter extends TailerListenerAdapter { @Override public void handle(String line) { Matcher matcher = TailService.this.accessLogPattern.matcher(line); if (!matcher.matches()) { // System.out.println(line); return; } String ip = matcher.group(1); if (!"-".equals(ip) && !"127.0.0.1".equals(ip)) { CityResponse cr = lookupCity(ip); if (cr != null) { Access access = new Access(); access.setIp(ip); access.setDate(Instant.now().toEpochMilli()); access.setCity(cr.getCity().getName()); access.setCountry(cr.getCountry().getName()); String userAgent = matcher.group(9); ReadableUserAgent ua = TailService.this.parser.parse(userAgent); if (ua != null && ua.getFamily() != UserAgentFamily.UNKNOWN) { String uaString = ua.getName() + " " + ua.getVersionNumber().toVersionString(); uaString += "; " + ua.getOperatingSystem().getName(); uaString += "; " + ua.getFamily(); uaString += "; " + ua.getTypeName(); uaString += "; " + ua.getProducer(); access.setMessage(matcher.group(4) + "; " + uaString); } else { access.setMessage(null); } access.setLl(new Double[] { cr.getLocation().getLatitude(), cr.getLocation().getLongitude() }); TailService.this.eventMessenger.sendToAll("/queue/geoip", access); } } } } public CityResponse lookupCity(String ip) { if (this.reader != null) { CityResponse response; try { try { response = this.reader.city(InetAddress.getByName(ip)); return response; } catch (AddressNotFoundException e) { return null; } } catch (IOException | GeoIp2Exception e) { LoggerFactory.getLogger(getClass()).error("lookupCity", e); } } return null; } private static String getAccessLogRegex() { String regex1 = "^([\\d.-]+)"; // Client IP String regex2 = " (\\S+)"; // - String regex3 = " (\\S+)"; // - String regex4 = " \\[([\\w:/]+\\s[+\\-]\\d{4})\\]"; // Date String regex5 = " \"(.*?)\""; // request method and url String regex6 = " (\\d{3})"; // HTTP code String regex7 = " (\\d+|.+?)"; // Number of bytes String regex8 = " \"([^\"]+|.+?)\""; // Referer String regex9 = " \"([^\"]+|.+?)\""; // Agent return regex1 + regex2 + regex3 + regex4 + regex5 + regex6 + regex7 + regex8 + regex9; } }