【程式碼全】使用SpringAOP編寫日誌記錄(插入oracle資料庫中)
程式碼較多,請耐心除錯
首先oracle資料庫表建立語句:
drop table cmu_system_log;
CREATE TABLE CMU_SYSTEM_LOG (log_id INTEGER primary key ,
user_id INTEGER ,
username VARCHAR2(20) ,
description VARCHAR2(50) ,
methods VARCHAR2(500) ,
log_type VARCHAR2(50) ,
request_ip INTEGER ,
exceptioncode VARCHAR2(255) ,
exception_detail VARCHAR2(255) ,
params VARCHAR2(255) ,
time DATE DEFAULT SYSDATE
);
需要在:Spring-mvc.xml中增加:
<!-- 最重要:如果放在spring-context.xml中,這裡的aop設定將不會生效,AOP日誌配置 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="systemLogAspect" class="com.security.annotation.SystemLogAspect"></bean>
pom.xml裡面需要匯入:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
編寫以下主要四個檔案:
SysLog.java
package com.security.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
/** 要執行的操作型別比如:add操作 **/
public String operationType() default "";
/** 要執行的具體操作比如:新增使用者 **/
public String operationName() default "";
}
SystemControllerLog.java
package com.security.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemControllerLog {
String description() default "";
}
最重要的檔案:SystemLogAspect.java
package com.security.annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.community.service.SystemLogService;
import com.community.util.IpUtil;
import com.oracle.pojo.SystemLog;
import com.oracle.pojo.Users;
@Aspect
@Component
public class SystemLogAspect {
// 使用service注入功能把日誌寫進資料庫中
@Resource
private SystemLogService systemLogService;
private static final Logger logger = LoggerFactory
.getLogger(SystemLogAspect.class);
// Conntroller層的切點
@Pointcut("@annotation(com.security.annotation.SysLog)")
public void controllerAspect() {
}
/**
* 編寫後置通知,用於攔截Controller 層記錄使用者的操作
*
* joinPoint 切點
*/
@After("controllerAspect()")
public void after(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
Users user = (Users) SecurityUtils.getSubject().getPrincipal();
String userName = null;
String userId = null;
if (user != null) {
Subject currentUser = SecurityUtils.getSubject();
userId = currentUser.getSession().getAttribute("_USER_ID")
.toString();
userName = (String) user.getUsername();
}
// 請求的IP
String ip = request.getRemoteAddr();
try {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String operationType = "";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(SysLog.class)
.operationType();
operationName = method.getAnnotation(SysLog.class)
.operationName();
break;
}
}
}
// 資料庫日誌
SystemLog log = new SystemLog();
log.setUserId(new Integer(userId));// 登入的使用者id
log.setUsername(userName);// 這裡需要獲取使用者名稱
log.setDescription(operationName);
log.setMethods((joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName() + "()"));
log.setLogType(operationType);
log.setRequestIp(IpUtil.ipToInt(ip));
log.setExceptioncode(null);
log.setExceptionDetail(null);
log.setParams(null);
log.setTime(new Date());
// 儲存資料庫
systemLogService.insertSelective(log);
System.out.println("=====controller後置通知成功結束=====");
} catch (Exception e) {
// 記錄本地異常日誌
logger.error("===後置通知異常===");
logger.error("異常資訊:{}", e.getMessage());
}
}
/**
* 異常通知 用於攔截記錄異常日誌
*/
@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
Users user = (Users) SecurityUtils.getSubject().getPrincipal();
String userName = null;
String userId = null;
if (user != null) {
Subject currentUser = SecurityUtils.getSubject();
userId = currentUser.getSession().getAttribute("_USER_ID")
.toString();
userName = (String) user.getUsername();
}
// 請求的IP
String ip = request.getRemoteAddr();
String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
params = Arrays.toString(joinPoint.getArgs());
}
try {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String operationType = "error";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(SysLog.class)
.operationType();
operationName = method.getAnnotation(SysLog.class)
.operationName();
break;
}
}
}
// ====資料庫日誌=====
SystemLog log = new SystemLog();
log.setUserId(new Integer(userId));// 登入的使用者id
log.setUsername(userName);// 這裡需要獲取使用者名稱
log.setDescription(operationName);
log.setMethods((joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName() + "()")
+ "." + operationType);
log.setLogType(operationType);
log.setRequestIp(IpUtil.ipToInt(ip));
log.setExceptioncode(null);
log.setExceptionDetail(null);
log.setParams(null);
log.setTime(new Date());
// 儲存資料庫
systemLogService.insertSelective(log);
System.out.println("=====異常通知結束=====");
} catch (Exception ex) {
// 記錄本地異常日誌
logger.error("==異常通知異常==");
logger.error("異常資訊:{}", ex.getMessage());
}
// 記錄本地異常日誌
logger.error("異常方法:{}異常程式碼:{}異常資訊:{}引數:{}", joinPoint.getTarget()
.getClass().getName()
+ joinPoint.getSignature().getName(), e.getClass().getName(),
e.getMessage(), params);
}
}
SystemServiceLog.java
package com.security.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemServiceLog {
String description() default "";
}
SytemLogcontroller.java 負責與前端互動
package com.community.controller;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.community.pager.PageInfo;
import com.community.service.SystemLogService;
@Controller
@Scope(value = "prototype")
@RequestMapping(value = "systemlog")
public class SystemLogController extends BaseController {
@Autowired
SystemLogService systemLogService;
/**
* 跳轉到日誌展示介面 許可權控制判斷 目前沒有增加判斷,僅跳轉使用
*/
@RequestMapping(value = "tolist")
public ModelAndView tolist(ModelMap map) {
return new ModelAndView("/security/logs/logs");
}
/**
* @param page 頁數
* @param rows 每頁的資料量
* @param searchvalue 搜尋關鍵字
* @param order 排序
* @param sort 按。。順序排 ,desc、asc
* @param starttime 開始時間
* @param endtime 結束時間
* @param response 相應資訊
* @return
*/
@RequestMapping(value = "list")
@ResponseBody
public Object list(Integer page, Integer rows, String searchvalue,
String order, String sort, String starttime, String endtime,
HttpServletResponse response) {
String value = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// ----非空判斷----
if (StringUtils.isNotEmpty(searchvalue)) {
try {
value = URLDecoder.decode(searchvalue, "UTF-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
Date st = null;
Date et = null;
if (StringUtils.isNotEmpty(starttime)) {
try {
st = sdf.parse(starttime);
} catch (ParseException e) {
e.printStackTrace();
}
}
if (StringUtils.isNoneEmpty(endtime)) {
try {
et = sdf.parse(endtime);
} catch (ParseException e) {
e.printStackTrace();
}
}
// ---獲取到登入賬戶的ID值
Subject currentUser = SecurityUtils.getSubject();
String userId = currentUser.getSession().getAttribute("_USER_ID")
.toString();
PageInfo pageInfo = new PageInfo(page, rows);
Map<String, Object> condition = new HashMap<String, Object>();
int start = (page - 1) * rows;
int end = start + rows;
condition.put("st", st);// 開始時間
condition.put("et", et);// 結束時間
condition.put("start", start);
condition.put("end", end);
condition.put("order", order);// 為空沒有使用
condition.put("sort", sort);// 為空沒有使用
condition.put("value", value);// 獲取到搜尋框的值(字元)
condition.put("userId", userId);
condition.put("searchvalue", searchvalue);
pageInfo.setCondition(condition);
systemLogService.findLogs(pageInfo);
return pageInfo;
}
}
systemLogMapper.xml
三個方法:
//插入到資料庫
<insert id="insertSelective" parameterType="com.oracle.pojo.SystemLog">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
insert into CMU_SYSTEM_LOG
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="logId != null">
LOG_ID,
</if>
<if test="userId != null">
USER_ID,
</if>
<if test="username != null">
USERNAME,
</if>
<if test="description != null">
DESCRIPTION,
</if>
<if test="methods != null">
METHODS,
</if>
<if test="logType != null">
LOG_TYPE,
</if>
<if test="requestIp != null">
REQUEST_IP,
</if>
<if test="exceptioncode != null">
EXCEPTIONCODE,
</if>
<if test="exceptionDetail != null">
EXCEPTION_DETAIL,
</if>
<if test="params != null">
PARAMS,
</if>
<if test="time != null">
TIME,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="logId != null">
#{logId,jdbcType=DECIMAL},
</if>
<if test="userId != null">
#{userId,jdbcType=DECIMAL},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="description != null">
#{description,jdbcType=VARCHAR},
</if>
<if test="methods != null">
#{methods,jdbcType=VARCHAR},
</if>
<if test="logType != null">
#{logType,jdbcType=VARCHAR},
</if>
<if test="requestIp != null">
#{requestIp,jdbcType=DECIMAL},
</if>
<if test="exceptioncode != null">
#{exceptioncode,jdbcType=VARCHAR},
</if>
<if test="exceptionDetail != null">
#{exceptionDetail,jdbcType=VARCHAR},
</if>
<if test="params != null">
#{params,jdbcType=VARCHAR},
</if>
<if test="time != null">
#{time,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
//查詢拉取資料(並且有模糊查詢和時間判斷)
<select id="findLogsList" parameterType="com.community.pager.PageInfo" resultMap="BaseResultMap">
SELECT * FROM (SELECT tt.*, ROWNUM AS rowno FROM(
SELECT
CSL.LOG_ID,
CSL.USER_ID,
CSL.USERNAME,
CSL.DESCRIPTION,
CSL.METHODS,
CSL.LOG_TYPE,
CSL.REQUEST_IP,
CSL.EXCEPTIONCODE,
CSL.EXCEPTION_DETAIL,
CSL.PARAMS,
CSL.TIME
FROM(SELECT * FROM CMU_USERS START WITH USER_ID = #{condition.userId} CONNECT BY PRIOR USER_ID = PID) U
LEFT OUTER JOIN CMU_SYSTEM_LOG CSL ON U.USER_ID = CSL.USER_ID
where CSL.USER_ID is NOT NULL
<if test="condition.searchvalue !=null and condition.searchvalue !='' ">
and(CSL.USERNAME LIKE '%'||#{condition.searchvalue}||'%'
or CSL.DESCRIPTION LIKE '%'||#{condition.value}||'%'
or CSL.LOG_TYPE LIKE '%'||#{condition.searchvalue}||'%')
</if>
<if test="condition.st != null">
and CSL.TIME >= #{condition.st}
</if>
<if test="condition.et != null">
AND #{condition.et} >= CSL.TIME
</if>
order by TIME desc) tt
WHERE ROWNUM <= #{condition.end}) table_alias WHERE table_alias.rowno > #{condition.start}
</select>
//計算資料的條數
<select id="findLogsListCount" parameterType="com.community.pager.PageInfo" resultType="int">
select count(*) FROM(SELECT * FROM CMU_USERS START WITH USER_ID = #{condition.userId} CONNECT BY PRIOR USER_ID = PID) U
LEFT OUTER JOIN CMU_SYSTEM_LOG CSL ON U.USER_ID = CSL.USER_ID
where CSL.USER_ID is NOT NULL
<if test="condition.searchvalue !=null and condition.searchvalue !='' ">
and(CSL.USERNAME LIKE '%'||#{condition.searchvalue}||'%'
or CSL.DESCRIPTION LIKE '%'||#{condition.value}||'%'
or CSL.LOG_TYPE LIKE '%'||#{condition.searchvalue}||'%')
</if>
<if test="condition.st != null">
and CSL.TIME >= #{condition.st}
</if>
<if test="condition.et != null">
AND #{condition.et} >= CSL.TIME
</if>
</select>
最後 最重要的是給你需要插入資料庫的介面增加:@SysLog(operationType="submitLogin",operationName="代理商登入") //舉例
前端jsp使用的是easyUI框架:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<style type="text/css">
#treeGrid tr td {
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
overflow: hidden;
}
</style>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="edge" />
<title>日誌列表</title>
<link rel="stylesheet" type="text/css"
href="${ctx}/resources/easyui/themes/default/easyui.css">
<link rel="stylesheet" type="text/css"
href="${ctx}/resources/easyui/themes/icon.css">
<script type="text/javascript"
src="${ctx}/resources/easyui/jquery.min.js"></script>
<script type="text/javascript"
src="${ctx}/resources/easyui/jquery.easyui.min.js"></script>
<script type="text/javascript"
src="${ctx}/resources/easyui/locale/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="${ctx}/resources/js/extJs.js"></script>
<script type="text/javascript">
var gUrl;
$(document).ready(function() {
gUrl = '${ctx}/systemlog/list';
loadDatagrid(gUrl);
});
function loadDatagrid(gUrl) {
$('#treeGrid').datagrid({
method : 'post',
url : gUrl,
title : "日誌列表",
loadMsg : '正在載入資訊...',
pagination : true, // 分頁
fit : true,
fitColumns : true,
striped : true, // 是否顯示行間隔色
queryParams : queryParams,
pageList : [ 15, 20, 30, 50 ],
pageSize : 20,
rownumbers : true,//行號
sidePagination : "server", // 服務端處理分頁
columns : [ [ {
title : '登入名',
field : 'username', // 欄位
width : '10%'
}, {
field : 'description',
title : '描述',
width : '10%'
}, {
title : '日誌型別',
field : 'logType',
width : '10%'
}, {
title : '呼叫方法',
field : 'methods',
width : '40%'
}, {
title : 'IP地址',
field : 'requestIp',
width : '10%',
formatter : function(value, row, index) {
return _int2iP(value);
}
}, {
title : '建立時間',
field : 'time',
align : 'center',
valign : 'middle',
width : 80,
formatter : function(value, row, index) {
if (value) {
return dataformatter(value);
}
}
}, ] ],
});
}
function doSearch(value) {
var starttime = $("#starttime").datebox('getValue');
var endtime = $("#endtime").datebox('getValue');
gUrl = "${ctx}/systemlog/list?searchvalue="+encodeURI(encodeURI(value))+"&starttime="+ starttime + "&endtime="+endtime;
loadDatagrid(gUrl);
}
function searchByTime(){
var starttime = $("#starttime").datebox('getValue');
var endtime = $("#endtime").datebox('getValue');
var gUrl = "${ctx}/systemlog/list?starttime="+ starttime + "&endtime="+endtime;
loadDatagrid(gUrl);
}
function dataformatter(value) {
var date = new Date(value);
var year = date.getFullYear().toString();
var month = (date.getMonth() + 1);
var day = date.getDate().toString();
var hour = date.getHours().toString();
var minutes = date.getMinutes().toString();
var seconds = date.getSeconds().toString();
if (month < 10) {
month = "0" + month;
}
if (day < 10) {
day = "0" + day;
}
if (hour < 10) {
hour = "0" + hour;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
return year + "-" + month + "-" + day + " " + hour + ":" + minutes
+ ":" + seconds;
}
/** 重新整理頁面 */
function refresh() {
$('#treeGrid').bootstrapTable('refresh');
}
function _int2iP(num) {
var str;
var tt = new Array();
tt[0] = (num >>> 24) >>> 0;
tt[1] = ((num << 8) >>> 24) >>> 0;
tt[2] = (num << 16) >>> 24;
tt[3] = (num << 24) >>> 24;
str = String(tt[0]) + "." + String(tt[1]) + "." + String(tt[2]) + "."
+ String(tt[3]);
return str;
}
/**查詢條件與分頁資料 */
function queryParams(pageReqeust) {
pageReqeust.enabled = $("#enabled").val();
pageReqeust.querys = $("#querys").val();
pageReqeust.pageNo = this.pageNumber;
return pageReqeust;
}
$.extend($.fn.validatebox.defaults.rules, {
TimesCheck: {
validator: function (value, param) {
var s = $('#starttime').datebox('getValue');
//因為日期是統一格式的所以可以直接比較字串 否則需要Date.parse(_date)轉換
return value >= s;
},
message: '結束時間必須大於開始時間!!!'
}
});
</script>
</head>
<body>
<div class="easyui-layout" data-options="fit:true">
<div data-options="region:'center',border:false"
style="overflow: hidden;">
<table id="treeGrid" toolbar="#toolbar"></table>
<div id="toolbar">
<div style="float: right;">
<input id="ss" class="easyui-searchbox" searcher="doSearch" prompt="請輸入要查詢的條件(登入名、描述或日誌型別,選擇時間後也可點選此處)..." style="width: 450px; vertical-align: middle;"></input>
</div>
開始時間: <input type="text" class="easyui-datebox" name="starttime" id="starttime" editable="false" style="width:110px;" data-options="buttons:buttons,prompt:'請輸入開始時間'">
結束時間: <input type="text" class="easyui-datebox" name="endtime" id="endtime" editable="false" style="width:110px;" data-options="buttons:buttons,prompt:'請輸入結束時間'" validType="TimesCheck['starttime']" invalidMessage="結束時間必須大於開始時間!">
<a href="#" onclick="javascript:searchByTime();return false;"class="easyui-linkbutton" iconCls="icon-search">查詢</a>
</div>
</div>
</body>
</html>
最終顯示:
本文編輯為:美推網www.zdflshop.com (站長編輯)