Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UPDATE] Only one error log #83

Merged
merged 6 commits into from
Dec 4, 2024
1 change: 0 additions & 1 deletion modules/infrastructure/log-writer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'

runtimeOnly 'com.mysql:mysql-connector-j:8.3.0'
compileOnly 'jakarta.annotation:jakarta.annotation-api:2.1.1'

testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private void checkRegularly(){
}

private void checkCommandInstalled(String command) {
List<String> results = new TransientProcess("which " + command).results();
List<String> results = new TransientProcess("which " + command).resultList();
if (results.isEmpty() || !results.get(0).contains("/")) {
throw new IllegalStateException(command + "가 설치되지 않았습니다.");
}
Expand All @@ -78,7 +78,7 @@ private void checkNetworkInterfaces(List<NetworkInterface> system, List<NetworkI
private List<NetworkInterface> getSystemNetworkInterfaces() {
List<NetworkInterface> interfaces = new ArrayList<>();

List<String> iwconfigOutput = new TransientProcess("iwconfig").results();
List<String> iwconfigOutput = new TransientProcess("iwconfig").resultList();

String currentName = null;
String currentEssid = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@
public class ContinuousProcess {
protected Process process;
protected NonBlockingBufferedReader br;
//기본 생성자를 호출했을 땐 ebr이 null일 수 있다.
@Nullable
protected NonBlockingBufferedReader ebr = null;

//sub class에서 자신만의 Process를 정의하고 싶을 때 사용
//super()는 생성자의 첫 번째 줄에 있어야 하기 때문에 만든 것임
public ContinuousProcess() {}

public ContinuousProcess(Process process) {
this.process = process;
this.br = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(process.getInputStream())));
}
public ContinuousProcess(String command) {
this(command, null);
}
public ContinuousProcess(String command, @Nullable String sudoPassword) {
try {
this.process = new ProcessBuilder(command.split(" "))
.redirectErrorStream(true)
.start();
this.br = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(this.process.getInputStream())));
this.ebr = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(this.process.getErrorStream())));
if (sudoPassword==null) return;
Writer writer = new OutputStreamWriter(this.process.getOutputStream());
writer.write(sudoPassword + System.lineSeparator());
Expand All @@ -37,7 +38,6 @@ public ContinuousProcess(String command, @Nullable String sudoPassword) {
throw new RuntimeException(command + " 실행 실패");
}
}

/**
* @return 프로세스의 출력에서 한 줄을 읽어들인다.
* 읽을 줄이 없을경우 null을 출력한다.
Expand All @@ -49,6 +49,30 @@ public String readLine(){
throw new RuntimeException(e);
}
}

/**
* @return 프로세스의 에러 출력에서 한 줄을 읽어들인다.
* 읽을 줄이 없을경우 null을 출력한다.
*/
public String readErrorLine(){
if (this.ebr == null)
throw new RuntimeException("error stream을 초기화하지 않았습니다!");
try {
return this.ebr.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public String readErrorLines(){
StringBuilder sb = new StringBuilder();
String line;
while((line = readErrorLine()) != null){
sb.append(line).append("\n");
}
return sb.toString();
}

public boolean isAlive(){
return this.process.isAlive();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@

//실행 후 종료되어 모든 출력을 얻는 프로세스
//출력 스트림을 통한 프로세스와의 상호작용은 없다.

public class TransientProcess {

protected BufferedReader br;
protected BufferedReader ebr;
protected Process process;

public TransientProcess() {}

public TransientProcess(Process process){
this.process = process;
this.br = new BufferedReader(new InputStreamReader(process.getInputStream()));
}
public TransientProcess(String command){
this(command, null);
}
Expand All @@ -30,6 +29,7 @@ public TransientProcess(String command, @Nullable String sudoPassword) {
.redirectErrorStream(true)
.start();
this.br = new BufferedReader(new InputStreamReader(process.getInputStream()));
this.ebr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
if (sudoPassword==null) return;
Writer writer = new OutputStreamWriter(this.process.getOutputStream());
writer.write(sudoPassword + System.lineSeparator());
Expand All @@ -39,12 +39,28 @@ public TransientProcess(String command, @Nullable String sudoPassword) {
}
}

public String resultString(){
return resultString(this.br);
}

public List<String> resultList(){
return resultList(this.br);
}

public String errorResultString(){
return resultString(this.ebr);
}

public List<String> errorResultList(){
return resultList(this.ebr);
}

//종료되었을 때 출력을 얻는다.
//종료되지 않았다면 블로킹된다.
//출력이 없는 프로세스의 경우 빈 리스트를 출력한다.
public List<String> results(){
private List<String> resultList(BufferedReader br){
waitTermination();
List<String> logs = new ArrayList<>();

try {
String line;
while((line = br.readLine()) != null){
Expand All @@ -53,7 +69,31 @@ public List<String> results(){
} catch (IOException e) {
throw new RuntimeException(e);
}

return logs;
}

//결과를 하나의 String으로 반환
private String resultString(BufferedReader br){
waitTermination();
StringBuilder sb = new StringBuilder();
try {
int size=1024;
char[] buff = new char[size];
int read;
while((read = br.read(buff, 0, size)) != -1){
sb.append(buff, 0, read);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}

public void waitTermination(){
try {
process.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,10 @@

import com.whoz_in.log_writer.common.process.TransientProcess;
import com.whoz_in.log_writer.managed.ManagedInfo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class ArpLogProcess extends TransientProcess {
public ArpLogProcess(ManagedInfo info, String password) {
try {
//TODO: error 처리 로직 수정
super.process = new ProcessBuilder(info.command().split(" "))
//arp-scan 실행할 때마다 아래와 같은 오류가 떠서 일단 Error Stream 출력 안하도록 했음
// WARNING: Cannot open MAC/Vendor file ieee-oui.txt: Permission denied
// .redirectError(Redirect.INHERIT)
.start();
super.br = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
bw.write(password);
bw.newLine();
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
super(info.command(), password);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

//TODO: 에러 로그 어떻게 관리할지 생각. 일단 TransientProcess라서 구현 안함
@Slf4j
@Component
public class ArpLogWriter {
Expand All @@ -38,7 +39,7 @@ private void scan() {
List<ManagedLog> logs= arpList.stream()
.flatMap(arpInfo-> {
ArpLogProcess proc = new ArpLogProcess(arpInfo, sudoPassword); //프로세스 실행
List<String> lines = proc.results(); //프로세스의 모든 출력 가져오기
List<String> lines = proc.resultList(); //프로세스의 모든 출력 가져오기
Set<ManagedLog> procLogs = lines.stream() //출력 라인들을 ManagedLog 변환하며 ssid도 넣어줌
.filter(parser::validate)
.map(line->{
Expand All @@ -54,6 +55,7 @@ private void scan() {
따라서 Arp-scan의 경우 무조건 1개 이상의 결과가 나오므로 0개라면 실행 실패라고 판단한다.
*/
if (procLogs.isEmpty()) {
//SystemValidator가 시스템의 네트워크 인터페이스가 올바른지 검증하기 때문에 여기서는 warn으로 로깅
log.warn("[managed - arp({})] 실행 실패 : ERROR", arpInfo.ssid());
return Stream.empty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
package com.whoz_in.log_writer.managed.mdns;

import com.whoz_in.log_writer.common.process.ContinuousProcess;
import com.whoz_in.log_writer.common.util.NonBlockingBufferedReader;
import com.whoz_in.log_writer.managed.ManagedInfo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.ProcessBuilder.Redirect;

public class MdnsLogProcess extends ContinuousProcess {

public MdnsLogProcess(ManagedInfo info, String sudoPassword) {
try {
super.process = new ProcessBuilder(info.command().split(" "))
.redirectError(Redirect.INHERIT)
.start();
super.br = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(this.process.getInputStream())));
Writer writer = new OutputStreamWriter(this.process.getOutputStream());
writer.write(sudoPassword + System.lineSeparator());
writer.flush();
} catch (IOException e) {

throw new RuntimeException(info.command() + " 실행 실패");
}
super(info.command(), sudoPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

//TODO: tshark가 종료되지 않더라도 오류가 발생할 수 있으려나? 이 경우에도 로깅이 필요하긴 함
@Slf4j
@Component
public class MdnsLogWriter {
private final Map<ManagedInfo, MdnsLogProcess> processes;
private final Map<ManagedInfo, Boolean> wasDead;
private final MdnsLogParser parser;
private final ManagedLogDAO dao;
private final String sudoPassword;
Expand All @@ -25,11 +26,13 @@ public MdnsLogWriter(ManagedLogDAO dao, MdnsLogParser parser, NetworkConfig conf
this.dao = dao;
this.parser = parser;
this.sudoPassword = sudoPassword;
this.processes = config.getMdnsList().parallelStream()
.collect(Collectors.toMap(
managedInfo -> managedInfo,
managedInfo -> new MdnsLogProcess(managedInfo, sudoPassword)
));
this.processes = new HashMap<>();
this.wasDead = new HashMap<>();
config.getMdnsList().parallelStream()
.forEach(managedInfo -> {
this.processes.put(managedInfo, new MdnsLogProcess(managedInfo, sudoPassword));
this.wasDead.put(managedInfo, false);
});
}

@Scheduled(initialDelay = 10000, fixedDelay = 10000)
Expand All @@ -39,7 +42,10 @@ private void writeLogs() {
ManagedInfo managedInfo = entry.getKey();
MdnsLogProcess process = entry.getValue();
boolean alive = process.isAlive();
if (!alive) log.error("[managed - mdns({})] dead", managedInfo.ssid());
if (!alive && wasDead.get(managedInfo).equals(Boolean.FALSE)) {
wasDead.put(managedInfo, true);
log.error("[managed - mdns({})] dead :\n{}", managedInfo.ssid(), process.readErrorLines());
}
return alive;})
.flatMap(entry -> {
ManagedInfo managedInfo = entry.getKey();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ChannelHopper(NetworkInterface monitor, @Value("${sudo_password}") String
@Scheduled(initialDelay = 5000, fixedDelay = 1000)
public void hop(){
if (!channelsToHop.iterator().hasNext()) {
Set<Integer> channels = new TransientProcess("nmcli -f SSID,CHAN dev wifi").results()
Set<Integer> channels = new TransientProcess("nmcli -f SSID,CHAN dev wifi").resultList()
.stream()
.map(line -> line.trim().split("\\s+"))
.filter(split -> (split.length == 2) && split[1].matches("\\d+"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,12 @@
package com.whoz_in.log_writer.monitor;

import com.whoz_in.log_writer.common.process.ContinuousProcess;
import com.whoz_in.log_writer.common.util.NonBlockingBufferedReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.ProcessBuilder.Redirect;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public final class MonitorLogProcess extends ContinuousProcess {

public MonitorLogProcess(MonitorInfo info, String sudoPassword) {
try {
super.process = new ProcessBuilder(info.command().split(" "))
.redirectError(Redirect.INHERIT)
.start();
super.br = new NonBlockingBufferedReader(new BufferedReader(new InputStreamReader(process.getInputStream())));

Writer writer = new OutputStreamWriter(process.getOutputStream());
writer.write(sudoPassword + System.lineSeparator());
writer.flush();
} catch (IOException e) {
String message = info.command() + " 실행 실패";
log.error(message);
throw new RuntimeException(message);
}
super(info.command(), sudoPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@Component
public class MonitorLogWriter {
private MonitorLogProcess process; //교체될 수 있으므로 final X
private Boolean wasDead = false;
private final MonitorLogParser parser;
private final MonitorLogDAO repo;
private final String sudoPassword;
Expand All @@ -27,7 +28,10 @@ public MonitorLogWriter(MonitorLogParser parser, MonitorLogDAO repo, NetworkConf
@Scheduled(initialDelay = 10000, fixedDelay = 10000)
private void saveLogs(){
if (!process.isAlive()) {
log.error("[monitor] dead");
if (wasDead.equals(Boolean.FALSE)) {
log.error("[monitor] dead:\n{}", process.readErrorLines());
wasDead = true;
}
return;
}
Set<String> macs = new HashSet<>();
Expand Down
Loading