`
sdh88hf
  • 浏览: 69808 次
  • 性别: Icon_minigender_1
  • 来自: 绍兴
社区版块
存档分类
最新评论

Web开发学习(9)全局埋点

 
阅读更多
埋点的作用是把客户端每次访问服务端的操作记录下来,包括请求连接 请求者ip 请求参数 请求结果等等,以便于在出现异常的情况下排查,在用户量庞大的情况下还可以对这些记录做数据分析.这个功能我还是坚持一直以来的原则,一次封装终身受用,使用拦截器的方式全局做埋点记录.

首先创建一个注解类,这个注解类的作用是在action方法上标注当前方法的中文信息,记录到日志以后有便于查阅

package cn.sdh.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebOperateAnno {
	
	String memo() default "无定义";
	
}

这个方法的缺点是需要在每个action上进行配置,对我这种比较爱偷懒的猿来说还是有点不怎么好忍受的,还好我有权限系统(上一篇),记录每个权限对应的链接,也就是我知道客户端访问的链接后就能通过权限数据获取到对应的权限(就是菜单),菜单的名称其实就是我需要的备注,这样我就不需要在每个action方法做备注了,当然有些没进行权限配置的action方法还是需要注解的,先来看下注解的代码

/**
	 * 跳转到主页
	 * 
	 * @throws JSONException
	 */
	@WebOperateAnno(memo="主页")
	public String index() throws JSONException {

		entity = SpringSecurityUtils.getCurrentUser();

		List<Permission> perList = entity.getSignPer();

		HttpServletRequest request = ServletActionContext.getRequest();
		request.setAttribute("perList", perList);
		request.setAttribute("perListJson", JSONUtil.serialize(perList));

		return "index";
	}

很简单的注解,然后在来看下核心代码,action拦截器
package cn.sdh.common.intercepter;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;

import cn.sdh.common.annotation.WebOperateAnno;
import cn.sdh.entity.Account;
import cn.sdh.utils.MyCacheUtil;
import cn.sdh.utils.MyJsonUtil;
import cn.sdh.utils.SpringSecurityUtils;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
@SuppressWarnings("serial")
public class WebOperateIntercepter extends MethodFilterInterceptor {

	private static final Logger logger = Logger.getLogger("oplog");

	private static final Logger sysLogger = Logger.getLogger(WebOperateIntercepter.class);
	
	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception {
		String result = null;
		try {
			//先执行action方法
			result = invocation.invoke();
			
			String enterMethod = invocation.getProxy().getMethod();
			//获取action方法上的WebOperateAnno 注解
			WebOperateAnno webOperateAnno = invocation.getAction().getClass().getMethod(enterMethod, new Class[0]).getAnnotation(WebOperateAnno.class);
			
			Object returnData = invocation.getAction();
			
			writeLog(webOperateAnno,returnData);
		} catch (Exception e) {
			sysLogger.error("操作日志记录失败",e);
		}
		
		
		return result;
	}
	
	private void writeLog(WebOperateAnno ano, Object returnData){
		//当前登录用户
		Account account = SpringSecurityUtils.getCurrentUser();
		
		String operate = "";
		if(account != null){
			operate = account.getUsername();
		}
		
		//从注解中获取备注
		String memo = "";
		if(ano != null){
			memo = ano.memo();
		}
		
		HttpServletRequest request = ServletActionContext.getRequest();
		//请求者ip
		String ip = request.getRemoteAddr();
		//请求类型 post or get
		String method = request.getMethod();
		//请求的链接
		String url = request.getRequestURI();
		//通过请求的链接从权限系统获取到当前模块
		String module = MyCacheUtil.getPerNameByUrl(url);
		
		//对请求的参数进行json格式化
		String entityString = MyJsonUtil.getJsonByObject(request.getParameterMap());
		
		//做日志记录
		logger.info(module+"|"+operate+"|"+ip+"|"+memo+"|"+method+"|"+url+"|"+entityString+"|"+returnData.toString());
		
	}
	

}

简单的一个拦截器记录操作信息到单独的日志文件,你也可以记录到数据库,我是感觉如果记录到数据库的话多多少少会影响一点效率,而且这个数据量也是挺大的.这边还值得一提的是结果值是被调用的action的toString方法,还记不记得上一篇讲的action基类,所有的action类都从它派生,在这里他又发挥作用了,看一下基类里面的toString代码
public String toString(){
		StringBuilder builder = new StringBuilder();
		builder.append(",success:");
		builder.append(this.success);
		if(this.entity != null){
			builder.append("entity:");
			builder.append(this.entity.toString());
		}if(this.searchList != null){
			builder.append("searchList:");
			builder.append(this.searchList.toString());
		}
		
		return builder.toString();
	}

因为我所有的返回值都是基于entity和searchList实现的,所以可以很方便的把返回值获取到,其实程序中有很多模块的实现不是随便想想的,大多数情况下这些设计都是可以复用的,这也体现出了设计模式的重要性.

最后只要在struts文件中配置拦截器就可以了

<interceptors>
			<interceptor name="webOperate"
				class="cn.sdh.common.intercepter.WebOperateIntercepter" />
			<interceptor-stack name="crudStack">
				
				<interceptor-ref name="defaultStack"/> 
				<interceptor-ref name="webOperate" />
			</interceptor-stack>
		</interceptors>
		
		
		
		<default-interceptor-ref name="crudStack" />



  • 大小: 257.8 KB
分享到:
评论

相关推荐

    Asp.Net MVC数据埋点

    Asp.Net MVC数据埋点是一种在Web应用中收集用户行为数据的技术,用于分析用户与网站交互的情况,从而优化用户体验和业务决策。在这个场景下,我们使用了...记住,数据驱动的决策是现代Web开发不可或缺的一部分。

    前端数据采集+行为上报(数据埋点)

    在前端开发中,数据采集和行为上报是至关重要的环节,特别是在现代互联网产品中,数据分析对于优化用户体验、提高转化率和实现精细化运营具有决定性的作用。"前端数据采集+行为上报(数据埋点)"这个主题主要关注...

    vtrack一个基于Vue指令实现的埋点插件

    在现代Web开发中,数据分析和用户行为追踪成为衡量应用性能和优化用户体验的关键因素。"vtrack"是一个专为Vue.js框架设计的埋点插件,它利用Vue的自定义指令机制,实现了对用户交互的无缝跟踪。这种解耦的设计使得...

    Spring Cloud Gateway全局异常处理的方法详解

    Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。 二、Spring Cloud ...

    携程大前端框架NFES-携程魏晓军.pdf

    - **提供统一的Web开发框架**:解决前端技术栈分散的问题。 - **提供统一的SSR解决方案**:针对SEO和性能优化。 - **与现有无线研发支撑平台打通**:确保与现有系统的兼容性和互操作性。 - **方便应对技术出海场景**...

    最新版蚂蚁前端核心面经

    在蚂蚁前端核心面经中,我们可以看到对前端开发者的面试考察主要围绕React、Web开发、跨端技术、安全、性能优化、大型项目经验、Node.js以及SSR(服务器端渲染)等方面的知识点。 1. **ReactsetState的使用和原理**...

    字节前端第一期面试题(1).pdf

    上述知识点涉及前端开发的多个方面,包括但不限于:React和Vue的状态管理、JavaScript ES5和ES6的新特性、Web前端性能优化、Web安全、异步编程模型、JavaScript数组操作和函数式编程概念等。这些内容不仅适用于面试...

    字节最新前端面试题.pdf

    根据文件内容,我们可以得知这是一份关于Web前端技术面试的题目列表,涵盖了广泛的技术知识点。下面我将逐个解析这些题目中涉及到的知识点: 1. 在React或Vue项目中编写列表组件时使用key的原因及其作用: - key...

    详解基于 Nuxt 的 Vue.js 服务端渲染实践

    Nuxt.js是一个基于Vue.js开发的服务端渲染框架,它通过预设各种配置和简化API使用,极大地提升了开发基于Vue的服务端渲染应用的效率。Nuxt.js不仅支持服务端渲染,还支持静态站点生成,提供了热加载等开发便捷功能,...

    Vue axios获取token临时令牌封装案例

    首先,项目的开发架构包括前端页面由Vue.js构建,网络请求通过Axios库进行,使用Vue.addAxios进行集成,并利用Vuex进行全局状态管理,如token的存储。同时,localStorage用于在浏览器端持久化存储token。开发者还...

Global site tag (gtag.js) - Google Analytics