# 日志切面

# @Log

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {

    String operateType() default "";

    String objType() default "";

    String desc() default "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# LogAspect.java

import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;

import chances.smart.commons.aspect.log.entity.Oplog;
import chances.smart.commons.mvc.interceptor.OpInfo;
import chances.smart.commons.rest.RestResponse;

@Aspect
@Component
public class LogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger("opLog");

    private static final Logger SystemLogger = LoggerFactory.getLogger("sysLog");

    ExpressionParser parser = new SpelExpressionParser();

    @Autowired
    private ApplicationContext applicationContext;

    @Value("${system-name:cms}")
    private String source;

    @Around("@annotation(annotationLog)")
    @Order(2)
    public Object postLogAspect(ProceedingJoinPoint pjp, Log annotationLog) throws Throwable {
        Oplog oplog = createOpLog(pjp, annotationLog);
        Object result = proceedController(pjp, oplog);
        try {
            setDesc(pjp, annotationLog, oplog, result);
            setObjType(pjp, annotationLog, oplog, result);
            setResponseCode(oplog, result);
            applicationContext.publishEvent(oplog);
            LOGGER.info(oplog.toString());
        } catch (Throwable e) {
            SystemLogger.error(e.toString(), e);
        }
        return result;
    }

    private void setResponseCode(Oplog oplog, Object result) {
        if (result != null && RestResponse.class.isInstance(result)) {
            RestResponse restResult = (RestResponse) result;
            oplog.setResponseCode(restResult.getCode());
        }
    }

    private Object proceedController(ProceedingJoinPoint pjp, Oplog oplog) throws Throwable {
        Long spendTime = System.currentTimeMillis();
        Object result = pjp.proceed(pjp.getArgs());
        spendTime = (System.currentTimeMillis() - spendTime) / 1000;
        oplog.setSpendTime(spendTime);
        return result;
    }

    private void setDesc(ProceedingJoinPoint pjp, Log log, Oplog oplog, Object result) {
        String desc = parserDescription(pjp, result, log.desc());
        oplog.setDesc(desc);
    }

    private void setObjType(ProceedingJoinPoint pjp, Log log, Oplog oplog, Object result) {
        String objType = parserDescription(pjp, result, log.objType());
        oplog.setObjType(objType);
    }

    private Oplog createOpLog(ProceedingJoinPoint pjp, Log annotationLog) {
        HttpServletRequest request = getHttpRequest();
        Oplog oplog = new Oplog(annotationLog);
        setBody(request, oplog);
        setUser(oplog);
        oplog.setSource(source);
        oplog.setLogId(UUID.randomUUID().toString());
        oplog.setOperateTime(new Date());
        oplog.setUrl(request.getServletPath());
        oplog.setIp(this.getIpAddr(request));
        return oplog;
    }

    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    private void setUser(Oplog oplog) {
        if (OpInfo.get() != null) {
            OpInfo userInfo = OpInfo.get();
            oplog.setUserId(String.valueOf(userInfo.getId()));
            oplog.setUserName(userInfo.getDisplayName());
        }
    }

    private void setBody(HttpServletRequest request, Oplog oplog) {
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
            String body = StringUtils.toEncodedString(wrapper.getContentAsByteArray(),
                    Charset.forName(wrapper.getCharacterEncoding()));
            oplog.setBody(body);
        }
    }

    private Map<String, Object> getParamMap(ProceedingJoinPoint pjp) {
        Map<String, Object> map = new HashMap<>();
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = pjp.getArgs();
        for (int i = 0; i < parameterNames.length; i++) {
            map.put(parameterNames[i], args[i]);
        }
        return map;
    }

    /**
     * Hello, #{#user},#{#cast?.name2}
     * @param pjp
     * @param result
     * @param desc
     * @return
     */
    private String parserDescription(ProceedingJoinPoint pjp, Object result, String desc) {
        if (StringUtils.isBlank(desc)) {
            return StringUtils.EMPTY;
        }
        Map<String, Object> map = getParamMap(pjp);

        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("ctrlResponse", result);
        for (Entry<String, Object> entry : map.entrySet()) {
            context.setVariable(entry.getKey(), entry.getValue());
        }
        Expression expression = parser.parseExpression(desc, new TemplateParserContext("${", "}"));
        return expression.getValue(context, String.class);
    }

    private HttpServletRequest getHttpRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178