diff --git a/pom.xml b/pom.xml index 31450c9..e9f88e6 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,12 @@ 23.04.01-SNAPSHOT + + cokr.xit.base + xit-docs + 23.04.01-SNAPSHOT + + diff --git a/src/main/java/cokr/xit/base/syslog/LogQuery.java b/src/main/java/cokr/xit/base/syslog/LogQuery.java new file mode 100644 index 0000000..0449795 --- /dev/null +++ b/src/main/java/cokr/xit/base/syslog/LogQuery.java @@ -0,0 +1,81 @@ +package cokr.xit.base.syslog; + +import cokr.xit.foundation.component.QueryRequest; + +public class LogQuery extends QueryRequest { + private static final long serialVersionUID = 1L; + + private String[] + logTypes, + userIDs, + userAccounts; + private String + userName, + fromDate, + toDate; + + public String[] getLogTypes() { + return ifEmpty(logTypes, null); + } + + public void setLogTypes(String... logTypes) { + this.logTypes = logTypes; + } + + public String[] getUserIDs() { + return ifEmpty(userIDs, null); + } + + public void setUserIDs(String... userIDs) { + this.userIDs = userIDs; + } + + public String[] getUserAccounts() { + return ifEmpty(userAccounts, null); + } + + public void setUserAccounts(String[] userAccounts) { + this.userAccounts = userAccounts; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getFromDate() { + return fromDate; + } + + public void setFromDate(String fromDate) { + this.fromDate = fromDate; + } + + public String getToDate() { + return toDate; + } + + public void setToDate(String toDate) { + this.toDate = toDate; + } + + @Override + public String getOrderBy() { + String orderBy = blankIfEmpty(super.getOrderBy()); + if (isEmpty(orderBy)) { + if (!isEmpty(logTypes)) + orderBy += "LOG_TYPE, "; + if (!isEmpty(userIDs)) + orderBy += "USER_ID, "; + if (!isEmpty(userAccounts)) + orderBy += "USER_ACNT, "; + if (!isEmpty(userName)) + orderBy += "USER_NM, "; + orderBy += "LOG_ID DESC"; + } + return orderBy; + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/syslog/dao/LoggingMapper.java b/src/main/java/cokr/xit/base/syslog/dao/LoggingMapper.java index 5fc746d..5d7ef57 100644 --- a/src/main/java/cokr/xit/base/syslog/dao/LoggingMapper.java +++ b/src/main/java/cokr/xit/base/syslog/dao/LoggingMapper.java @@ -1,15 +1,25 @@ package cokr.xit.base.syslog.dao; +import java.util.List; + import org.egovframe.rte.psl.dataaccess.mapper.Mapper; +import cokr.xit.base.syslog.LogQuery; import cokr.xit.base.syslog.ServiceLog; import cokr.xit.foundation.component.AbstractMapper; +import cokr.xit.foundation.data.DataObject; /**서비스 로그 DAO * @author mjkhan */ @Mapper("loggingMapper") public interface LoggingMapper extends AbstractMapper { + /**주어진 조건의 서비스 로그를 조회한다. + * @param req 로그 조회 조건 + * @return 로그 조회 결과 + */ + public List getLogs(LogQuery req); + /**서비스 로그를 등록한다. * @param log 서비스 로그 * @return 저장된 정보수 diff --git a/src/main/java/cokr/xit/base/syslog/service/LoggingService.java b/src/main/java/cokr/xit/base/syslog/service/LoggingService.java index 2721c9d..662e033 100644 --- a/src/main/java/cokr/xit/base/syslog/service/LoggingService.java +++ b/src/main/java/cokr/xit/base/syslog/service/LoggingService.java @@ -1,6 +1,13 @@ package cokr.xit.base.syslog.service; +import java.util.List; + +import cokr.xit.base.syslog.LogQuery; +import cokr.xit.foundation.data.DataObject; + /**시스템 로깅 서비스 * @author mjkhan */ -public interface LoggingService {} \ No newline at end of file +public interface LoggingService { + List getLogs(LogQuery req); +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/base/syslog/service/bean/LogFilter.java b/src/main/java/cokr/xit/base/syslog/service/bean/LogFilter.java index 99f3222..1f88eb4 100644 --- a/src/main/java/cokr/xit/base/syslog/service/bean/LogFilter.java +++ b/src/main/java/cokr/xit/base/syslog/service/bean/LogFilter.java @@ -70,7 +70,9 @@ public class LogFilter extends AbstractComponent { UserInfo current = UserInfo.current(); if (!current.isAuthenticated()) { Object[] args = log.getArgs(); - log.setUserId(current.getId(args.length > 0 ? args[0] : null)); + Object arg = args.length > 0 ? args[0] : null; + if (!"onFailure".equals(log.getMethodName())) + log.setUserId(current.getId(arg)); } return true; default: return false; diff --git a/src/main/java/cokr/xit/base/syslog/service/bean/LoggingBean.java b/src/main/java/cokr/xit/base/syslog/service/bean/LoggingBean.java index a33324e..eab30a8 100644 --- a/src/main/java/cokr/xit/base/syslog/service/bean/LoggingBean.java +++ b/src/main/java/cokr/xit/base/syslog/service/bean/LoggingBean.java @@ -1,12 +1,16 @@ package cokr.xit.base.syslog.service.bean; +import java.util.List; + import javax.annotation.Resource; import org.springframework.stereotype.Component; +import cokr.xit.base.syslog.LogQuery; import cokr.xit.base.syslog.ServiceLog; import cokr.xit.base.syslog.dao.LoggingMapper; import cokr.xit.foundation.component.AbstractBean; +import cokr.xit.foundation.data.DataObject; /**서비스 로깅 빈 * @author mjkhan @@ -16,6 +20,14 @@ public class LoggingBean extends AbstractBean { @Resource(name = "loggingMapper") private LoggingMapper loggingMapper; + /**주어진 조건의 서비스 로그를 조회한다. + * @param req 로그 조회 조건 + * @return 로그 조회 결과 + */ + public List getLogs(LogQuery req) { + return loggingMapper.getLogs(req); + } + /**서비스 로그를 등록한다. * @param log 서비스 로그 * @return 저장된 정보수 diff --git a/src/main/java/cokr/xit/base/syslog/service/bean/LoggingServiceBean.java b/src/main/java/cokr/xit/base/syslog/service/bean/LoggingServiceBean.java index 8918eee..7746797 100644 --- a/src/main/java/cokr/xit/base/syslog/service/bean/LoggingServiceBean.java +++ b/src/main/java/cokr/xit/base/syslog/service/bean/LoggingServiceBean.java @@ -8,11 +8,13 @@ import org.aspectj.lang.JoinPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import cokr.xit.base.syslog.LogQuery; 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; +import cokr.xit.foundation.data.DataObject; /**시스템 로깅 서비스 구현체 * @author mjkhan @@ -24,6 +26,11 @@ public class LoggingServiceBean extends AspectServiceBean implements LoggingServ @Resource(name = "loggingBean") private LoggingBean loggingBean; + @Override + public List getLogs(LogQuery req) { + return loggingBean.getLogs(req); + } + @Override public void beforeController(JoinPoint joinPoint) { ServiceLog log = ServiceLog.create(joinPoint); diff --git a/src/main/java/cokr/xit/base/syslog/web/LoggingController.java b/src/main/java/cokr/xit/base/syslog/web/LoggingController.java new file mode 100644 index 0000000..73fc666 --- /dev/null +++ b/src/main/java/cokr/xit/base/syslog/web/LoggingController.java @@ -0,0 +1,130 @@ +package cokr.xit.base.syslog.web; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import javax.annotation.Resource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import cokr.xit.base.docs.xls.CellDef; +import cokr.xit.base.docs.xls.XLSWriter; +import cokr.xit.base.syslog.LogQuery; +import cokr.xit.base.syslog.service.LoggingService; +import cokr.xit.foundation.data.DataFormat; +import cokr.xit.foundation.data.DataObject; +import cokr.xit.foundation.web.AbstractController; +import cokr.xit.foundation.web.RequestHandlerReader; + +@RequestMapping(name = "시스템 로그", value = "/syslog") +public class LoggingController extends AbstractController { + @Resource(name = "loggingService") + private LoggingService loggingService; + @Autowired + private RequestMappingHandlerMapping requestHandlers; + + private RequestHandlerReader requestReader = new RequestHandlerReader(); + + /**사용자 관리 메인화면(user/user-main)을 연다. + * 조건없는 {@link #getUserList(UserQuery) 사용자 조회 결과}를 포함시킨다. + * @return base/user/user-main + */ + @GetMapping(name="시스템 로그 메인", value="/main.do") + public ModelAndView main() { + ModelAndView mav = getLogs(new LogQuery().setPageNum(1)); + mav.setViewName("base/syslog/syslog-main"); + return mav.addObject("syslogList", toJson(mav.getModel().remove("syslogList"))); + } + + @GetMapping(name = "시스템 로그 조회", value = "/list.do") + public ModelAndView getLogs(LogQuery req) { + boolean download = !isEmpty(req.getDownload()); + if (!download) + setFetchSize(req); + else + req.setPageNum(0).setFetchSize(0); + + String + toDate = req.getToDate(), + fromDate = req.getFromDate(); + if (isEmpty(toDate)) { + Date date = new Date(); + toDate = DataFormat.yyyy_mm_dd(date); + req.setToDate(toDate.replace("-", "")); + + if (isEmpty(fromDate)) { + fromDate = DataFormat.yyyy_mm_dd(new Date(date.getTime() - 1000L * 60L * 60L * 24L * 30L)); + req.setFromDate(fromDate.replace("-", "")); + } + } + + List + list = loggingService.getLogs(req), + web = list.stream() + .filter(row -> "web".equals(row.get("LOG_TYPE"))) + .toList(); + if (!list.isEmpty()) { + Map reqs = requestReader.asTree(requestHandlers).getIndex(); + list.forEach(row -> { + Object obj = row.get("REG_DT"); + row.set("REG_DT", DataFormat.yyyy_mm_dd_hh_mm_ss(obj)); + + obj = row.get("URL"); + if (isEmpty(obj)) return; + + DataObject reqInfo = reqs.get(obj); + row.set("DSCRP", reqInfo != null ? reqInfo.get("name") : ""); + }); + } + + if (!download) + return setCollectionInfo( + new ModelAndView("jsonView") + .addObject("fromDate", fromDate) + .addObject("toDate", toDate), + list, + "syslog" + ); + else + return download(list); + } + + private ModelAndView download(List list) { + List cellDefs = List.of( + new CellDef().setLabel("계정").setField("USER_ACNT"), + new CellDef().setLabel("사용자이름").setField("USER_NM"), + new CellDef().setLabel("IP 주소").setField("IP_ADDR"), + new CellDef().setLabel("로그유형").setField("LOG_TYPE_NM"), + new CellDef().setLabel("접속일시").setField("REG_DT"), + new CellDef().setLabel("URL").setField("URL"), + new CellDef().setLabel("설명").setField("DSCRP"), + new CellDef().setLabel("파일이름").setField("FILE_NM"), + new CellDef().setLabel("민감정보").setField("PSNL_INFO") + ); + XLSWriter xlsx = new XLSWriter() + .worksheet(0) + .trackWidth(IntStream.range(0, cellDefs.size()).toArray()) + .cell(0, 0) + .value("시스템 로그 목록").merge(0, cellDefs.size() - 1) + .cell(2, 0) + .rowValues(CellDef.header(cellDefs, null)) + .cell(3, 0) + .values( + list, + CellDef.values(cellDefs) + ) + .autoWidth(); + + return new ModelAndView("downloadView") + .addObject("download", + xlsx.getDownloadable() + .setFilename("시스템 로그 목록.xlsx") + ); + } +} \ No newline at end of file diff --git a/src/main/resources/sql/mapper/syslog-mapper.xml b/src/main/resources/sql/mapper/syslog-mapper.xml index fea2b2f..9a2fb53 100644 --- a/src/main/resources/sql/mapper/syslog-mapper.xml +++ b/src/main/resources/sql/mapper/syslog-mapper.xml @@ -16,6 +16,46 @@ + + /* 시스템 로그 등록(loggingMapper.insertLog) */ SELECT CONCAT(TODAY, LPAD(NVL(SUBSTR(MAX(LOG_ID), 9) + 1, 1), 16, '0')) NEW_ID