Я работаю над небольшим приложением, которое может указывать на журнал Apache HTTP-сервера, следить за журналом (как 'tail -f' в Linux) и записывать записи в базу данных Oracle таблица.
Я установил приложение JPA Spring Boot / Spring Data и создал классы для моей сущности, интерфейс CrudRepository
, службу для интерфейса (хотя я считаю, что это технически не нужно для этой реализации), и бегун, чтобы начать процесс. Я также настроил TailerListenerAdapter
, чтобы выполнить отбор для разбора файла журнала. Я опубликую весь этот код ниже.
Проблема в том, что я могу успешно записать тестовую запись в базу данных до до запуска прослушивателя Tailer. Однако, когда слушатель работает, служба Autowired в TailerListenerAdapter
имеет значение NULL и выдает исключение.
java.lang.NullPointerException
at sbx.demo.logauditor.util.AccessListener.handle(AccessListener.java:49)
at org.apache.commons.io.input.Tailer.readLines(Tailer.java:525)
at org.apache.commons.io.input.Tailer.run(Tailer.java:457)
at sbx.demo.logauditor.LogAuditRunner.run(LogAuditRunner.java:40)
{... more stack trace ...}
Вот используемые классы (у меня, вероятно, есть некоторые ненужные аннотации, оставленные после экспериментов ) -
LogAuditRunner. java
package sbx.demo.logauditor;
import java.io.File;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.transaction.Transactional;
import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import lombok.var;
import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.service.AccessService;
import sbx.demo.logauditor.util.AccessListener;
@Component
public class LogAuditRunner implements CommandLineRunner {
@Autowired
AccessService accServ;
final String datePattern = "dd/MMM/yyyy:HH:mm:ss Z";
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
@Override
@Transactional
public void run(String... args) throws Exception {
// This test code works if uncommented
//LocalDateTime TS = LocalDateTime.from(formatter.parse("31/Jan/2020:14:28:32 -0500"));
//var logTest = new AccessRecord("10.154.103.2",Timestamp.valueOf(TS),"/cs/resources/layouts/Top%20Menus/Oracle/tree_T_collection_closed.gif","304");
//System.out.println("Testing repository with " + logTest.toString());
//accServ.save(logTest);
TailerListener listener = new AccessListener();
Tailer tailer = new Tailer(new File("D:\\access_log"), listener);
tailer.run();
}
}
AccessService. java
package sbx.demo.logauditor.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Service;
import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.repository.AccessRepository;
@Service
@Configurable
public class AccessService {
@Autowired(required = true)
AccessRepository accessRepo;
public void save(AccessRecord ar) {
try {
System.out.println("Writing record to database: " + ar.toString());
accessRepo.save(ar);
} catch (Exception e) {
e.printStackTrace();
}
}
public List<AccessRecord> findAll() {
List<AccessRecord> recList = new ArrayList<AccessRecord>();
try {
System.out.println("Searching database for all access records");
for(AccessRecord ar : accessRepo.findAll()) {
recList.add(ar);
}
} catch (Exception e) {
e.printStackTrace();
}
return recList;
}
}
AccessRepository. java
package sbx.demo.logauditor.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import sbx.demo.logauditor.model.AccessRecord;
@Repository
public interface AccessRepository extends CrudRepository<AccessRecord, Long>{
}
AccessRecord. java
package sbx.demo.logauditor.model;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;
@Entity
@Table(name="ACCESS_LOG")
@Data
public class AccessRecord {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Column(name="PROXY_AGENT")
private String agent;
@Column(name="SOURCE_IP")
private String sourceip;
@Column(name="ACCESS_TS")
private Timestamp reqts;
@Column(name="URI")
private String requri;
@Column(name="HTTP_STATUS")
private String respcode;
public AccessRecord() {}
public AccessRecord(String source, Timestamp ts, String uri, String status) {
this.sourceip = source;
this.reqts = ts;
this.requri = uri;
this.respcode = status;
try {
InetAddress ip = InetAddress.getLocalHost();
String hostname = ip.getHostName();
this.agent = hostname;
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String toString() {
String record = "Record: [" + agent + "] [" + sourceip + "] [" + reqts + "] [" + requri + "] [" + respcode + "]";
return record;
}
}
AccessListener. java
package sbx.demo.logauditor.util;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.transaction.Transactional;
import org.apache.commons.io.input.TailerListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import lombok.var;
import sbx.demo.logauditor.model.AccessRecord;
import sbx.demo.logauditor.repository.AccessRepository;
import sbx.demo.logauditor.service.AccessService;
@Component
public class AccessListener extends TailerListenerAdapter {
final String regex = "^(\\S+) (\\S+) (\\S+) " +
"\\[([\\w:/]+\\s[+\\-]\\d{4})\\] \"(\\S+)" +
" (\\S+)\\s*(\\S+)?\\s*\" (\\d{3}) (\\S+)";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final String datePattern = "dd/MMM/yyyy:HH:mm:ss Z";
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
@Autowired
AccessService accServ;
@Override
@Transactional
public void handle(String line) {
LogRecorder lr = new LogRecorder();
try {
final Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String IP = matcher.group(1);
//String TS = matcher.group(4);
String URL = matcher.group(6);
String STATUS = matcher.group(8);
LocalDateTime TS = LocalDateTime.from(formatter.parse(matcher.group(4)));
var ar = new AccessRecord(IP,Timestamp.valueOf(TS),URL,STATUS);
accServ.save(ar);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
И наконец LogauditorApplication. java
package sbx.demo.logauditor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LogauditorApplication {
public static void main(String[] args) {
SpringApplication.run(LogauditorApplication.class, args);
}
}
Примечание, я заметил, что если я вручную создаю AccessService
(вместо того, чтобы полагаться на Autowiring) ), Я могу вызвать его, но тогда на интерфейсе Autowired AccessRepository
происходит NullPointerException
. Мне ясно, что это связано с Autowiring, я просто не понимаю, почему.
Я знаю, что есть способы следить и отправлять журналы через командную строку (это будет работать в Linux окружающая среда), но я хочу убедиться, что он достаточно устойчив, чтобы, скажем, перезапустить его, если он умрет, обработать опрокидывания журнала и т. д. c. Кроме того, я планирую написать дополнительную проверку, чтобы убедиться, что записи не перекрываются (т. Е. В случае, если приложение перезапускается и перечитывает весь файл). Но я хотел, чтобы это сначала заработало. Я думал, что это будет просто, поскольку Tailer требует так мало кода, и я уже чувствую себя комфортно с Spring.