Skip to content

Commit

Permalink
Merge pull request #83 from JNU-econovation/only_one_error_log
Browse files Browse the repository at this point in the history
[UPDATE] Only one error log
  • Loading branch information
inferior3x authored Dec 4, 2024
2 parents 7bcb208 + 36245da commit 7a40a52
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 86 deletions.
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

0 comments on commit 7a40a52

Please sign in to comment.