최초 커밋
commit
23fa45fc12
@ -0,0 +1,170 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>cokr.xit.base</groupId>
|
||||||
|
<artifactId>xit-syslog</artifactId>
|
||||||
|
<version>23.04.01-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>xit-syslog</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
|
||||||
|
<spring.maven.artifact.version>5.3.20</spring.maven.artifact.version>
|
||||||
|
<org.egovframe.rte.version>4.1.0</org.egovframe.rte.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>mvn2s</id>
|
||||||
|
<url>https://repo1.maven.org/maven2/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>egovframe</id>
|
||||||
|
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>maven-public</id>
|
||||||
|
<url>https://nas.xit.co.kr:8888/repository/maven-public/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cokr.xit.base</groupId>
|
||||||
|
<artifactId>xit-file</artifactId>
|
||||||
|
<version>23.04.01-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<defaultGoal>install</defaultGoal>
|
||||||
|
<directory>${basedir}/target</directory>
|
||||||
|
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<resource><directory>${basedir}/src/main/resources</directory></resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
|
<testResources>
|
||||||
|
<testResource><directory>${basedir}/src/test/resources</directory></testResource>
|
||||||
|
<testResource><directory>${basedir}/src/main/resources</directory></testResource>
|
||||||
|
</testResources>
|
||||||
|
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- EMMA -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>emma-maven-plugin</artifactId>
|
||||||
|
<version>1.0-alpha-3</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- PMD manven plugin -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-pmd-plugin</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.3.0</version>
|
||||||
|
<configuration>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.class</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- EMMA -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<skipTests>true</skipTests>
|
||||||
|
<reportFormat>xml</reportFormat>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/Abstract*.java</exclude>
|
||||||
|
<exclude>**/*Suite.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
<includes>
|
||||||
|
<include>**/*Test.java</include>
|
||||||
|
</includes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>emma-maven-plugin</artifactId>
|
||||||
|
<inherited>true</inherited>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!-- Javadoc -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.9.1</version>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<!-- Nexus deploy -->
|
||||||
|
<distributionManagement>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>maven-snapshot</id>
|
||||||
|
<url>https://nas.xit.co.kr:8888/repository/maven-snapshots/</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
|
||||||
|
<repository>
|
||||||
|
<id>maven-release</id>
|
||||||
|
<url>https://nas.xit.co.kr:8888/repository/maven-releases/</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
<!-- Nexus deploy -->
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,105 @@
|
|||||||
|
package cokr.xit.base.syslog;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import cokr.xit.foundation.Access;
|
||||||
|
import cokr.xit.foundation.Assert;
|
||||||
|
import cokr.xit.foundation.UserInfo;
|
||||||
|
|
||||||
|
public class ServiceCall {
|
||||||
|
private static final ThreadLocal<ServiceCall> cache = new ThreadLocal<>();
|
||||||
|
|
||||||
|
public static final ServiceCall get() {
|
||||||
|
ServiceCall call = cache.get();
|
||||||
|
if (call == null) {
|
||||||
|
cache.set(call = new ServiceCall());
|
||||||
|
call.userId = UserInfo.current().getId();
|
||||||
|
call.ipAddress = Access.current().getIpAddress();
|
||||||
|
}
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ServiceLog controllerLog() {
|
||||||
|
ServiceCall call = get();
|
||||||
|
|
||||||
|
List<ServiceLog> logs = call.getLogs();
|
||||||
|
ServiceLog log = !logs.isEmpty() ? logs.get(0) : null;
|
||||||
|
String url = log != null ? log.getUrl() : null;
|
||||||
|
|
||||||
|
return !Assert.isEmpty(url) ? log : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean controllerStarted() {
|
||||||
|
return controllerLog() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void set(ServiceLog log) {
|
||||||
|
if (log == null) return;
|
||||||
|
|
||||||
|
get().add(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final void remove() {
|
||||||
|
ServiceCall call = cache.get();
|
||||||
|
if (call != null) {
|
||||||
|
call.clear();
|
||||||
|
cache.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String
|
||||||
|
userId,
|
||||||
|
ipAddress;
|
||||||
|
private ArrayList<ServiceLog> logs;
|
||||||
|
|
||||||
|
/**사용자 아이디를 반환한다.
|
||||||
|
* @return 사용자 아이디
|
||||||
|
*/
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**사용자 아이디를 설정한다.
|
||||||
|
* @param userId 사용자 아이디
|
||||||
|
*/
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**ip 주소를 반환한다.
|
||||||
|
* @return ip 주소
|
||||||
|
*/
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
/**ip 주소를 설정한다.
|
||||||
|
* @param ipAddress ip 주소
|
||||||
|
*/
|
||||||
|
public void setIpAddress(String ipAddress) {
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ServiceLog> getLogs() {
|
||||||
|
return Assert.ifEmpty(logs, Collections::emptyList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(ServiceLog log) {
|
||||||
|
if (logs == null)
|
||||||
|
logs = new ArrayList<>();
|
||||||
|
|
||||||
|
if (logs.contains(log))
|
||||||
|
return;
|
||||||
|
|
||||||
|
log.setUserId(userId);
|
||||||
|
log.setIpAddress(ipAddress);
|
||||||
|
|
||||||
|
logs.add(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear() {
|
||||||
|
if (logs != null)
|
||||||
|
logs.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,460 @@
|
|||||||
|
package cokr.xit.base.syslog;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
|
||||||
|
import cokr.xit.foundation.Assert;
|
||||||
|
import cokr.xit.foundation.data.JSON;
|
||||||
|
|
||||||
|
/**시스템 로그 정보
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
public class ServiceLog {
|
||||||
|
private static final Config conf;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try (InputStream input = new ClassPathResource("conf/xit-syslog.conf").getInputStream();) {
|
||||||
|
conf = new JSON().parse(input, Config.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw Assert.runtimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Config config() {
|
||||||
|
return conf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String
|
||||||
|
LOG_INOUT = "log-inout",
|
||||||
|
DOWNLOAD = "download",
|
||||||
|
WEB = "web",
|
||||||
|
SERVICE = "service";
|
||||||
|
|
||||||
|
public static ServiceLog create(JoinPoint joinPoint) {
|
||||||
|
ServiceLog log = new ServiceLog();
|
||||||
|
|
||||||
|
log.type = SERVICE;
|
||||||
|
log.className = joinPoint.getTarget().getClass().getName();
|
||||||
|
log.methodName = joinPoint.getSignature().getName();
|
||||||
|
log.args = joinPoint.getArgs();
|
||||||
|
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
url,
|
||||||
|
|
||||||
|
className,
|
||||||
|
methodName,
|
||||||
|
fileName,
|
||||||
|
|
||||||
|
fieldNames,
|
||||||
|
personalInfo,
|
||||||
|
|
||||||
|
userId,
|
||||||
|
ipAddress;
|
||||||
|
|
||||||
|
private Map<String, String[]> params;
|
||||||
|
private Object[] args;
|
||||||
|
private Object returned;
|
||||||
|
private Throwable thrown;
|
||||||
|
private int dataCount;
|
||||||
|
|
||||||
|
/**로그 아이디를 반환한다.
|
||||||
|
* @return 로그 아이디
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**로그 아이디를 설정한다.
|
||||||
|
* @param id 로그 아이디
|
||||||
|
*/
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**로그유형을 반환한다.
|
||||||
|
* @return 로그유형
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**로그유형을 설정한다.
|
||||||
|
* @param type 로그유형
|
||||||
|
*/
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**url을 반환한다.
|
||||||
|
* @return url
|
||||||
|
*/
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**url을 설정한다.
|
||||||
|
* @param url url
|
||||||
|
*/
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsParam(String name) {
|
||||||
|
return !Assert.isEmpty(getParams(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**url의 파라미터를 반환한다.
|
||||||
|
* @return url의 파라미터
|
||||||
|
*/
|
||||||
|
public Map<String, String[]> getParams() {
|
||||||
|
return Assert.ifEmpty(params, Collections::emptyMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**지정한 이름의 파라미터를 반환한다.
|
||||||
|
* @param name 파라미터 이름
|
||||||
|
* @return 파라미터 값
|
||||||
|
*/
|
||||||
|
public String[] getParams(String name) {
|
||||||
|
return params != null ? params.get(name) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**지정한 이름의 파라미터를 반환한다.
|
||||||
|
* @param name 파라미터 이름
|
||||||
|
* @return 파라미터 값
|
||||||
|
*/
|
||||||
|
public String getParam(String name) {
|
||||||
|
String[] vals = getParams(name);
|
||||||
|
return vals != null && vals.length > 0 ? vals[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**url의 파라미터를 설정한다.
|
||||||
|
* @param params url의 파라미터
|
||||||
|
*/
|
||||||
|
public void setParams(Map<String, String[]> params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**클래스 이름을 반환한다.
|
||||||
|
* @return 클래스 이름
|
||||||
|
*/
|
||||||
|
public String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**클래스 이름을 설정한다.
|
||||||
|
* @param className 클래스 이름
|
||||||
|
*/
|
||||||
|
public void setClassName(String className) {
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**메소드 이름을 반환한다.
|
||||||
|
* @return methodName
|
||||||
|
*/
|
||||||
|
public String getMethodName() {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**메소드 이름을 설정한다.
|
||||||
|
* @param methodName 메소드 이름
|
||||||
|
*/
|
||||||
|
public void setMethodName(String methodName) {
|
||||||
|
this.methodName = methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**파일이름을 반환한다.
|
||||||
|
* @return 파일이름
|
||||||
|
*/
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**파일이름을 설정한다.
|
||||||
|
* @param fileName 파일이름
|
||||||
|
*/
|
||||||
|
public void setFileName(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**fieldNames을(를) 반환한다.
|
||||||
|
* @return fieldNames
|
||||||
|
*/
|
||||||
|
public String getFieldNames() {
|
||||||
|
return fieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**fieldNames을(를) 설정한다.
|
||||||
|
* @param fieldNames fieldNames
|
||||||
|
*/
|
||||||
|
public void setFieldNames(String fieldNames) {
|
||||||
|
this.fieldNames = fieldNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataNames(Object obj) {
|
||||||
|
if (!(obj instanceof Iterable)) return;
|
||||||
|
|
||||||
|
Iterable<String> fieldNames = (Iterable<String>)obj;
|
||||||
|
this.fieldNames = StreamSupport
|
||||||
|
.stream(fieldNames.spliterator(), false)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPersonalInfo() {
|
||||||
|
return personalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPersonalInfo(String personalInfo) {
|
||||||
|
this.personalInfo = personalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**args를 반환한다.
|
||||||
|
* @return args
|
||||||
|
*/
|
||||||
|
public Object[] getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**args를 설정한다.
|
||||||
|
* @param args args
|
||||||
|
*/
|
||||||
|
public void setArgs(Object[] args) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**실행결과를 반환한다.
|
||||||
|
* @return 실행결과
|
||||||
|
*/
|
||||||
|
public Object getReturned() {
|
||||||
|
return returned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**실행결과를 설정한다.
|
||||||
|
* @param returned 실행결과
|
||||||
|
* @return 현재 ServiceLog
|
||||||
|
*/
|
||||||
|
public ServiceLog setReturned(Object returned) {
|
||||||
|
this.returned = returned;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**오류를 반환한다.
|
||||||
|
* @return 오류
|
||||||
|
*/
|
||||||
|
public Throwable getThrown() {
|
||||||
|
return thrown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**오류를 설정한다.
|
||||||
|
* @param thrown 오류
|
||||||
|
* @return 현재 ServiceLog
|
||||||
|
*/
|
||||||
|
public ServiceLog setThrown(Throwable thrown) {
|
||||||
|
this.thrown = thrown;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**데이터 갯수를 반환한다.
|
||||||
|
* @return 데이터 갯수
|
||||||
|
*/
|
||||||
|
public int getDataCount() {
|
||||||
|
return dataCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**데이터 갯수를 설정한다.
|
||||||
|
* @param dataSize 데이터 갯수
|
||||||
|
*/
|
||||||
|
public void setDataCount(int dataSize) {
|
||||||
|
this.dataCount = dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**사용자 아이디를 반환한다.
|
||||||
|
* @return 사용자 아이디
|
||||||
|
*/
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**사용자 아이디를 설정한다.
|
||||||
|
* @param userId 사용자 아이디
|
||||||
|
*/
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**ip 주소를 반환한다.
|
||||||
|
* @return ip 주소
|
||||||
|
*/
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**ip 주소를 설정한다.
|
||||||
|
* @param ipAddress ip 주소
|
||||||
|
*/
|
||||||
|
public void setIpAddress(String ipAddress) {
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
UnaryOperator<String> notEmpty = str -> str == null ? "null" : str;
|
||||||
|
|
||||||
|
return String.format(
|
||||||
|
"%s {type: %s, url: %s, class: %s, method: %s, file: %s, userId: %s, ipAddress: %s}",
|
||||||
|
getClass().getSimpleName(),
|
||||||
|
notEmpty.apply(type),
|
||||||
|
notEmpty.apply(url),
|
||||||
|
notEmpty.apply(className),
|
||||||
|
notEmpty.apply(methodName),
|
||||||
|
notEmpty.apply(fileName),
|
||||||
|
notEmpty.apply(userId),
|
||||||
|
notEmpty.apply(ipAddress)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Config {
|
||||||
|
private Exclude exclude;
|
||||||
|
private List<PersonalField> personalFields;
|
||||||
|
|
||||||
|
/**로그 제외 대상을 반환한다.
|
||||||
|
* @return 로그 제외 대상
|
||||||
|
*/
|
||||||
|
public Exclude getExclude() {
|
||||||
|
return exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**로그 제외 대상을 설정한다.
|
||||||
|
* @param exclude 로그 제외 대상
|
||||||
|
*/
|
||||||
|
public void setExclude(Exclude exclude) {
|
||||||
|
this.exclude = exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**개인정보 필드이름를 반환한다.
|
||||||
|
* @return 개인정보 필드이름
|
||||||
|
*/
|
||||||
|
public List<PersonalField> getPersonalFields() {
|
||||||
|
return personalFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**개인정보 필드이름를 설정한다.
|
||||||
|
* @param personalFields 개인정보 필드이름
|
||||||
|
*/
|
||||||
|
public void setPersonalFields(List<PersonalField> personalFields) {
|
||||||
|
this.personalFields = personalFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Exclude {
|
||||||
|
private Set<String>
|
||||||
|
classes,
|
||||||
|
methods;
|
||||||
|
|
||||||
|
/**클래스 이름을 반환한다.
|
||||||
|
* @return 클래스 이름
|
||||||
|
*/
|
||||||
|
public Set<String> getClasses() {
|
||||||
|
return Assert.ifEmpty(classes, Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**클래스 이름을 설정한다.
|
||||||
|
* @param classes 클래스 이름
|
||||||
|
*/
|
||||||
|
public void setClasses(Set<String> classes) {
|
||||||
|
this.classes = classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**메소드 이름을 반환한다.
|
||||||
|
* @return 메소드 이름
|
||||||
|
*/
|
||||||
|
public Set<String> getMethods() {
|
||||||
|
return Assert.ifEmpty(methods, Collections::emptySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**메소드 이름을 설정한다.
|
||||||
|
* @param methods 메소드 이름
|
||||||
|
*/
|
||||||
|
public void setMethods(Set<String> methods) {
|
||||||
|
this.methods = methods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PersonalField {
|
||||||
|
private String
|
||||||
|
name,
|
||||||
|
code;
|
||||||
|
private Set<String>
|
||||||
|
mapKeys,
|
||||||
|
objectProperties;
|
||||||
|
|
||||||
|
/**이름을 반환한다.
|
||||||
|
* @return 이름
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**이름을 설정한다.
|
||||||
|
* @param name 이름
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**코드를 반환한다.
|
||||||
|
* @return 코드
|
||||||
|
*/
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**코드를 설정한다.
|
||||||
|
* @param code 코드
|
||||||
|
*/
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**맵키를 반환한다.
|
||||||
|
* @return 맵키
|
||||||
|
*/
|
||||||
|
public Set<String> getMapKeys() {
|
||||||
|
return mapKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**맵키를 설정한다.
|
||||||
|
* @param mapKeys 맵키
|
||||||
|
*/
|
||||||
|
public void setMapKeys(Set<String> mapKeys) {
|
||||||
|
this.mapKeys = mapKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**객체 프로퍼티를 반환한다.
|
||||||
|
* @return 객체 프로퍼티
|
||||||
|
*/
|
||||||
|
public Set<String> getObjectProperties() {
|
||||||
|
return objectProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**객체 프로퍼티를 설정한다.
|
||||||
|
* @param objectProperties 객체 프로퍼티
|
||||||
|
*/
|
||||||
|
public void setObjectProperties(Set<String> objectProperties) {
|
||||||
|
this.objectProperties = objectProperties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package cokr.xit.base.syslog.dao;
|
||||||
|
|
||||||
|
import org.egovframe.rte.psl.dataaccess.mapper.Mapper;
|
||||||
|
|
||||||
|
import cokr.xit.base.syslog.ServiceLog;
|
||||||
|
import cokr.xit.foundation.component.AbstractMapper;
|
||||||
|
|
||||||
|
/**서비스 로그 DAO
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
@Mapper("loggingMapper")
|
||||||
|
public interface LoggingMapper extends AbstractMapper {
|
||||||
|
int insertLog(ServiceLog log);
|
||||||
|
|
||||||
|
default int insertLogs(Iterable<ServiceLog> logs) {
|
||||||
|
if (isEmpty(logs)) return 0;
|
||||||
|
|
||||||
|
int affected = 0;
|
||||||
|
for (ServiceLog log: logs)
|
||||||
|
affected += insertLog(log);
|
||||||
|
|
||||||
|
return affected;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package cokr.xit.base.syslog.service;
|
||||||
|
|
||||||
|
/**시스템 로깅 서비스
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
public interface LoggingService {}
|
@ -0,0 +1,109 @@
|
|||||||
|
package cokr.xit.base.syslog.service.bean;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import cokr.xit.base.file.web.DownloadView;
|
||||||
|
import cokr.xit.base.file.web.XLSView;
|
||||||
|
import cokr.xit.base.syslog.ServiceLog;
|
||||||
|
import cokr.xit.foundation.AbstractComponent;
|
||||||
|
import cokr.xit.foundation.data.DataObject;
|
||||||
|
|
||||||
|
public class LogFilter extends AbstractComponent {
|
||||||
|
private PersonalInfo personalInfo = new PersonalInfo();
|
||||||
|
|
||||||
|
public boolean filter(ServiceLog svcLog) {
|
||||||
|
for (Predicate<ServiceLog> filter: getFilters()) {
|
||||||
|
if (!filter.test(svcLog))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Predicate<ServiceLog>> getFilters() {
|
||||||
|
ArrayList<Predicate<ServiceLog>> filters = new ArrayList<>();
|
||||||
|
|
||||||
|
filters.add(this::authenticationFilter);
|
||||||
|
filters.add(this::downloadFilter);
|
||||||
|
filters.add(this::configuredFilter);
|
||||||
|
filters.add(this::customFilter);
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean authenticationFilter(ServiceLog log) {
|
||||||
|
if (!log.getClassName().contains("AuthenticationService"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
switch (log.getMethodName()) {
|
||||||
|
case "onLogin":
|
||||||
|
case "onFailure":
|
||||||
|
case "onLogout":
|
||||||
|
log.setType(ServiceLog.LOG_INOUT);
|
||||||
|
return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean configuredFilter(ServiceLog log) {
|
||||||
|
String name = log.getClassName();
|
||||||
|
for (String exclude: ServiceLog.config().getExclude().getClasses()) {
|
||||||
|
if (name.contains(exclude))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
name = log.getMethodName();
|
||||||
|
for (String exclude: ServiceLog.config().getExclude().getMethods()) {
|
||||||
|
if (name.contains(exclude))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean downloadFilter(ServiceLog log) {
|
||||||
|
Object returned = log.getReturned();
|
||||||
|
if (returned instanceof ModelAndView) {
|
||||||
|
Map<String, Object> model = ((ModelAndView)returned).getModel();
|
||||||
|
|
||||||
|
String filename = ifEmpty(DownloadView.getFilename(model), () -> XLSView.getFilename(model));
|
||||||
|
if (!isEmpty(filename)) {
|
||||||
|
log.setType(ServiceLog.DOWNLOAD);
|
||||||
|
log.setFileName(filename);
|
||||||
|
setDownloadData(log, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDownloadData(ServiceLog log, Map<String, Object> model) {
|
||||||
|
Object obj = model.get("downloadData");
|
||||||
|
if (!(obj instanceof List)) return;
|
||||||
|
|
||||||
|
List<?> list = (List<?>)obj;
|
||||||
|
if (list.isEmpty()) return;
|
||||||
|
|
||||||
|
int dataSize = -1;
|
||||||
|
Object first = list.get(0);
|
||||||
|
|
||||||
|
if (first instanceof DataObject) {
|
||||||
|
DataObject row = (DataObject)first;
|
||||||
|
if (row.containsKey("TOT_CNT"))
|
||||||
|
dataSize = row.number("TOT_CNT").intValue();
|
||||||
|
}
|
||||||
|
if (dataSize < 0)
|
||||||
|
dataSize = list.size();
|
||||||
|
log.setDataCount(dataSize);
|
||||||
|
|
||||||
|
log.setPersonalInfo(personalInfo.get(first));
|
||||||
|
log.setDataNames(model.get("dataNames"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean customFilter(ServiceLog log) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package cokr.xit.base.syslog.service.bean;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import cokr.xit.base.syslog.ServiceLog;
|
||||||
|
import cokr.xit.base.syslog.dao.LoggingMapper;
|
||||||
|
import cokr.xit.foundation.component.AbstractBean;
|
||||||
|
|
||||||
|
@Component("loggingBean")
|
||||||
|
public class LoggingBean extends AbstractBean {
|
||||||
|
@Resource(name = "loggingMapper")
|
||||||
|
private LoggingMapper loggingMapper;
|
||||||
|
|
||||||
|
public int create(ServiceLog log) {
|
||||||
|
return log != null ? loggingMapper.insertLog(log) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int create(Iterable<ServiceLog> logs) {
|
||||||
|
return loggingMapper.insertLogs(logs);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package cokr.xit.base.syslog.service.bean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import cokr.xit.base.syslog.ServiceCall;
|
||||||
|
import cokr.xit.base.syslog.ServiceLog;
|
||||||
|
import cokr.xit.base.syslog.service.LoggingService;
|
||||||
|
import cokr.xit.foundation.Access;
|
||||||
|
import cokr.xit.foundation.component.AspectServiceBean;
|
||||||
|
|
||||||
|
/**시스템 로깅 서비스 구현체
|
||||||
|
* @author mjkhan
|
||||||
|
*/
|
||||||
|
@Service("loggingService")
|
||||||
|
public class LoggingServiceBean extends AspectServiceBean implements LoggingService {
|
||||||
|
@Autowired(required = false)
|
||||||
|
private LogFilter logFilter;
|
||||||
|
@Resource(name = "loggingBean")
|
||||||
|
private LoggingBean loggingBean;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeController(JoinPoint joinPoint) {
|
||||||
|
ServiceLog log = ServiceLog.create(joinPoint);
|
||||||
|
log.setType(ServiceLog.WEB);
|
||||||
|
Access access = Access.current();
|
||||||
|
log.setUrl(access.getAction());
|
||||||
|
log.setParams(access.getParams());
|
||||||
|
|
||||||
|
ServiceCall.set(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterServiceReturn(JoinPoint joinPoint, Object returned) {
|
||||||
|
ServiceLog log = ServiceLog
|
||||||
|
.create(joinPoint)
|
||||||
|
.setReturned(returned);
|
||||||
|
|
||||||
|
ServiceCall.set(log);
|
||||||
|
|
||||||
|
if (!ServiceCall.controllerStarted())
|
||||||
|
logAndClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logAndClear() {
|
||||||
|
List<ServiceLog> logs = ServiceCall.get().getLogs();
|
||||||
|
if (logs.isEmpty()) return;
|
||||||
|
|
||||||
|
logs = logs.stream().filter(this::filter).toList();
|
||||||
|
|
||||||
|
loggingBean.create(logs);
|
||||||
|
ServiceCall.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filter(ServiceLog svcLog) {
|
||||||
|
if (logFilter == null)
|
||||||
|
logFilter = new LogFilter();
|
||||||
|
|
||||||
|
return logFilter.filter(svcLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterControllerReturn(JoinPoint joinPoint, Object returned) {
|
||||||
|
ServiceLog log = ServiceCall.controllerLog();
|
||||||
|
if (log != null)
|
||||||
|
log.setReturned(returned);
|
||||||
|
|
||||||
|
logAndClear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package cokr.xit.base.syslog.service.bean;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.beanutils.PropertyUtils;
|
||||||
|
|
||||||
|
import cokr.xit.base.syslog.ServiceLog;
|
||||||
|
|
||||||
|
public class PersonalInfo {
|
||||||
|
public String get(Object obj) {
|
||||||
|
String info = "";
|
||||||
|
|
||||||
|
for (ServiceLog.PersonalField conf: ServiceLog.config().getPersonalFields()) {
|
||||||
|
String name = null;
|
||||||
|
if (obj instanceof Map<?, ?>) {
|
||||||
|
if (contains(conf.getMapKeys(), (Map<?, ?>)obj))
|
||||||
|
name = conf.getName();
|
||||||
|
} else {
|
||||||
|
if (contains(conf.getObjectProperties(), obj))
|
||||||
|
name = conf.getName();
|
||||||
|
}
|
||||||
|
if (name == null) continue;
|
||||||
|
|
||||||
|
info += info.isEmpty() ? name : "," + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !info.isEmpty() ? info : "개인정보 없음";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(Set<String> keys, Map<?, ?> map) {
|
||||||
|
return !Collections.disjoint(keys, map.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(Set<String> properties, Object obj) {
|
||||||
|
for (String property: properties) {
|
||||||
|
if (PropertyUtils.isReadable(obj, property)
|
||||||
|
|| PropertyUtils.isWriteable(obj, property))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"exclude": {
|
||||||
|
"classes": ["CodeService", "MenuService", "ExceptionController"],
|
||||||
|
"methods": []
|
||||||
|
},
|
||||||
|
|
||||||
|
"personalFields": [
|
||||||
|
{ "name": "주민등록번호",
|
||||||
|
"code": "",
|
||||||
|
"mapKeys": [],
|
||||||
|
"objectProperties": []
|
||||||
|
},
|
||||||
|
{ "name": "계좌번호",
|
||||||
|
"code": "",
|
||||||
|
"mapKeys": [],
|
||||||
|
"objectProperties": []
|
||||||
|
},
|
||||||
|
{ "name": "주소",
|
||||||
|
"code": "",
|
||||||
|
"mapKeys": [],
|
||||||
|
"objectProperties": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="cokr.xit.base.syslog.dao.LoggingMapper">
|
||||||
|
|
||||||
|
<resultMap id="logRow" type="cokr.xit.base.syslog.ServiceLog">
|
||||||
|
<result property="id" column="LOG_ID"/> <!-- 로그 ID -->
|
||||||
|
<result property="type" column="LOG_TYPE"/> <!-- 로그 유형 -->
|
||||||
|
<result property="url" column="URL"/> <!-- URL -->
|
||||||
|
<result property="className" column="CLS_NM"/> <!-- 클래스 이름 -->
|
||||||
|
<result property="methodName" column="MTD_NM"/> <!-- 메소드 이름 -->
|
||||||
|
<result property="fileName" column="FILE_NM"/> <!-- 파일 이름 -->
|
||||||
|
<result property="dataCount" column="DATA_CNT"/> <!-- 데이터 수 -->
|
||||||
|
<result property="fieldNames" column="DATA_NM"/> <!-- 데이터 이름 -->
|
||||||
|
<result property="personalInfo" column="PSNL_INFO"/> <!-- 개인 정보 -->
|
||||||
|
<result property="userId" column="USER_ID"/> <!-- 사용자 ID -->
|
||||||
|
<result property="ipAddress" column="IP_ADDR"/> <!-- 등록 일시 -->
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<insert id="insertLog" parameterType="cokr.xit.base.syslog.ServiceLog">/* 시스템 로그 등록(loggingMapper.insertLog) */
|
||||||
|
<selectKey keyProperty="id" keyColumn="NEW_ID" resultType="string" order="BEFORE">
|
||||||
|
SELECT CONCAT(TODAY, LPAD(NVL(SUBSTR(MAX(LOG_ID), 9) + 1, 1), 16, '0')) NEW_ID
|
||||||
|
FROM TB_SYS_LOG A, (<include refid="utility.selectToday" />) B
|
||||||
|
WHERE LOG_ID LIKE CONCAT(TODAY, '%')</selectKey>
|
||||||
|
INSERT INTO TB_SYS_LOG (
|
||||||
|
LOG_ID <!-- 로그 ID -->
|
||||||
|
, LOG_TYPE <!-- 로그 유형 -->
|
||||||
|
, URL <!-- URL -->
|
||||||
|
, CLS_NM <!-- 클래스 이름 -->
|
||||||
|
, MTD_NM <!-- 메소드 이름 -->
|
||||||
|
, FILE_NM <!-- 파일 이름 -->
|
||||||
|
, DATA_CNT <!-- 데이터 수 -->
|
||||||
|
, DATA_NM <!-- 데이터 이름 -->
|
||||||
|
, PSNL_INFO <!-- 개인 정보 -->
|
||||||
|
, USER_ID <!-- 사용자 ID -->
|
||||||
|
, IP_ADDR <!-- IP 주소 -->
|
||||||
|
, REG_DT <!-- 등록 일시 -->
|
||||||
|
) VALUES (
|
||||||
|
#{id} <!-- 로그 ID -->
|
||||||
|
, #{type} <!-- 로그 유형 -->
|
||||||
|
, #{url} <!-- URL -->
|
||||||
|
, #{className} <!-- 클래스 이름 -->
|
||||||
|
, #{methodName} <!-- 메소드 이름 -->
|
||||||
|
, #{fileName} <!-- 파일 이름 -->
|
||||||
|
, #{dataCount} <!-- 데이터 수 -->
|
||||||
|
, #{fieldNames} <!-- 데이터 이름 -->
|
||||||
|
, #{personalInfo} <!-- 개인 정보 -->
|
||||||
|
, #{userId} <!-- 사용자 ID -->
|
||||||
|
, #{ipAddress} <!-- IP 주소 -->
|
||||||
|
,<include refid="utility.now" />
|
||||||
|
)</insert>
|
||||||
|
|
||||||
|
</mapper>
|
@ -0,0 +1,18 @@
|
|||||||
|
package cokr.xit.base.svclog;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import cokr.xit.base.syslog.service.LoggingService;
|
||||||
|
import cokr.xit.foundation.test.TestSupport;
|
||||||
|
|
||||||
|
public class LoggingServiceTest extends TestSupport {
|
||||||
|
@Resource(name = "loggingService")
|
||||||
|
private LoggingService loggingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void onComplete() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue