全局业务日志实现

全局业务日志实现原理

通过aop切面,在拦截通过@WriteBuzLog注解了的方法,将请求参数、请求上下文、响应 结果通过Velocity解析成最后的业务日志打印到系统运行日志中,如果需求将日志持久存 储,就将日志内容推送到mq消息队列中

业务日志组件主要类讲解

1.业务日志注解类:WriteBuzLog

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
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 〈一句话功能简述〉<br>
* 输出业务日志标志
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WriteBuzLog {

/**
* 所属业务模块
* @return
*/
String buzModel();

/**
* 业务名称
* @return
*/
String buzName();

/**
* 业务类型
* @return
*/
String buzType() default "query";

/**
* 业务消息模版:
* 如果为空:则消息模板为:"请求参数:$_ps,响应结果:$_rs"
*
* @return
*/
String messageTemplate() default "";

/**
* 操作日志结果拼接方式:1-涉及提交后启动审批流的操作 2-除符合1、3之外的所有操作 3-多个操作调用同一个方法,例如:发齐/票齐
* WriteLogUtils.createBusinessLog
*
*/
String logOperateType() default "2";
}

2.日志上下文实体对象:LogContext

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
import java.lang.reflect.Method;

/**
* 〈一句话功能简述〉<br>
* Description: 日志上下文信息
*/
public class LogContext {
private Method method;
private Object[] params;
private Object result;
private Throwable throwable;
private WriteBuzLog writeBuzLog;

public Method getMethod() {
return method;
}

public void setMethod(Method method) {
this.method = method;
}

public Object[] getParams() {
return params;
}

public void setParams(Object[] params) {
this.params = params;
}

public Object getResult() {
return result;
}

public void setResult(Object result) {
this.result = result;
}

public Throwable getThrowable() {
return throwable;
}

public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}

public WriteBuzLog getWriteBuzLog() {
return writeBuzLog;
}

public void setWriteBuzLog(WriteBuzLog writeBuzLog) {
this.writeBuzLog = writeBuzLog;
}
}

3.业务日志生成切面:WriteBuzLogAspect

通过spring的aop切面拦截被WriteBuzLog 注解的方法,来达到生成统一业务日志记录的 目的,默认会将日志打印到系统运行的标准输出中,如果该切面创建时指定的 amqpTemplate会将日志传输到mq队列中最后落地到es中,这个对于日志输出,后续可以 通过策略模式来达到日志输出可扩展性和多样性

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
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import java.lang.reflect.Method;

/**
* 〈一句话功能简述〉<br>
* ibatis风格的dao实现中,不能在mybaits拦截中获取到当前调用执行sql的具体dao方法,
* 所以需要通过该aop拦截器拦截dao方法,在执行前将该dao方法存放到线程级变量中,
* 以备在后续的mybatis拦截器中能够获取到当前执行sql的具体dao方法
*/
@Aspect
public class WriteBuzLogAspect {
private Logger logger = LoggerFactory.getLogger(WriteBuzLogAspect.class);

private AmqpTemplate amqpTemplate;
private String queueKey="saas_log_business";

public WriteBuzLogAspect() {
}

public WriteBuzLogAspect(AmqpTemplate amqpTemplate) {
this.amqpTemplate = amqpTemplate;
}

@Around(
value = "@annotation(commons.log.WriteBuzLog)"
)
public Object aroundHandle(ProceedingJoinPoint joinPoint) throws Throwable{

try{
Object objResult = joinPoint.proceed();

writeLogin(joinPoint,objResult);
return objResult;
}catch (Throwable e){
writeLogin(joinPoint,e);
throw e;
}
}

private void writeLogin(ProceedingJoinPoint joinPoint,Throwable throwable){
try{
LogContext logContext = initLogContext(joinPoint);
logContext.setThrowable(throwable);
writeBuzLog(logContext);
}catch (Exception e){
logger.error("记录业务日志异常",e);
}
}

private void writeLogin(ProceedingJoinPoint joinPoint,Object returnVal){
try{
LogContext logContext = initLogContext(joinPoint);
logContext.setResult(returnVal);
writeBuzLog(logContext);
}catch (Exception e){
logger.error("记录业务日志异常",e);
}
}

private void writeBuzLog(LogContext logContext) {
List<BusinessLog> businessLogList = WriteLogUtils.createBusinessLog(logContext);
if (CollectionUtils.isNotEmpty(businessLogList)){
businessLogList.forEach(source->{
String buzMsg = JSONObject.toJSONString(source);
logger.info("业务日志:{}",buzMsg);

if(amqpTemplate != null){
amqpTemplate.convertAndSend(queueKey, buzMsg);
}
});
}

}

private LogContext initLogContext(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Method targetMethod = AspectUtils.getTargetMethod(joinPoint);
WriteBuzLog writeBuzLog = targetMethod.getAnnotation(WriteBuzLog.class);

Object[] args = joinPoint.getArgs();
LogContext logContext = new LogContext();
logContext.setWriteBuzLog(writeBuzLog);
logContext.setMethod(targetMethod);
logContext.setParams(args);
return logContext;
}
}

4.业务日志生成工具类:WriteLogUtils 生成消息的各个占位符描述

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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import com.alibaba.fastjson.JSONObject;

/**
* 〈一句话功能简述〉<br>
* Description: 方法数据解析器
*/
//@Slf4j
public class WriteLogUtils {
private static final VelocityEngine VELOCITY_ENGINE = new VelocityEngine();
static {
VELOCITY_ENGINE.init();
}

/**
* 请求参数前缀,_p${index}表示方法需要打业务日志方法中的第index个请求参数,从0开始
* 如:RequestEntiy write(Map<String,String> mapParam, List<UserInfo> userInfos,UserInfo otherUser)方法中
* _p0表示请求参数mapParam
*/
private static final String PRAM_PREFIX = "_p";
/**
* 方法响应结果参数
*/
private static final String RESULT_NAME = "_r";
/**
* 方法上WriteBuzLog的注释对象
*/
private static final String ANNOTATION_NAME = "_a";

/**
* 默认日志模版,_ps表示请求参数的json串对象,_rs表示响应参数json串对象
*/
private static final String DEFAULT_MESSAGE_TEMPLATE = "请求参数:$_ps,响应结果:$_rs";

/**
* 默认日志模版,_ps表示请求参数的json串对象,_rs表示响应参数json串对象
*/
private static final String EXCEPTION_MESSAGE_TEMPLATE = "请求参数:$_ps,异常信息:$_es";

/**
* 将请求参数转换为key为_p${index}的map对象
* @param params
* @param sort
* @return
*/
private static Map<String,Object> convertParams(Object[] params,boolean sort){
Map<String,Object> paramMaps ;
if(sort){
paramMaps = new LinkedHashMap<>();
}else{
paramMaps = new HashMap<>();
}
if(params != null){
for(int index = 0; index < params.length; index ++){
Object param = params[index];
if (param == null){
paramMaps.put(createParamKey(index), "null");
}else if(param instanceof ServletRequest){
paramMaps.put(createParamKey(index), "ServletRequest");
}else if (param instanceof ServletResponse){
paramMaps.put(createParamKey(index), "ServletResponse");
}else if(param instanceof BindingResult){
paramMaps.put(createParamKey(index), "BindingResult");
}else if(param instanceof CommonsMultipartFile){
CommonsMultipartFile file = (CommonsMultipartFile) param;
paramMaps.put(createParamKey(index), file.getOriginalFilename());
}else if(Objects.equals(param.getClass(), MultipartFile[].class)) {
paramMaps.put(createParamKey(index), MultipartFile[].class.getName());
}else {
paramMaps.put(createParamKey(index), params[index]);
}
}
}

return paramMaps;
}

private static void addResultToMap(Map<String,Object> params, Object result){
params.put(RESULT_NAME, result);
}
private static void addAnnotationToMap(Map<String,Object> params, Object annotation){
params.put(ANNOTATION_NAME, annotation);
}


private static String createParamKey(int index){
return PRAM_PREFIX + index;
}

public static Map<String,Object> convertContextToMap(LogContext logContext){
Map<String, Object> paramMaps = convertParams(logContext.getParams(), false);
addResultToMap(paramMaps, logContext.getResult());
addAnnotationToMap(paramMaps, logContext.getWriteBuzLog());
return paramMaps;
}

public static Method getTargetMethod(Class<?> targetClazz, Method signatureMethod) throws NoSuchMethodException {
Method returnMethod = signatureMethod;
Method daoMethod = targetClazz.getMethod(signatureMethod.getName(), signatureMethod.getParameterTypes());
if(daoMethod != null){
returnMethod = daoMethod;
}
return returnMethod;
}

public static String createLogInfo(LogContext logContext){

if(logContext.getThrowable() != null){
VelocityContext context = new VelocityContext(WriteLogUtils.convertContextToMap(logContext));
if(logContext.getParams() != null ){
context.put("_ps", JSONObject.toJSONString(convertParams(logContext.getParams(), true)));
}else {
context.put("_ps", "");
}

String errStr = String.format("{异常类:%s,异常消息:%s}",
logContext.getThrowable().getClass().getName(),
logContext.getThrowable().getMessage()
);
context.put("_es", errStr);

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", EXCEPTION_MESSAGE_TEMPLATE); // 关键方法

return writer.toString();
}

String messageTemplate = logContext.getWriteBuzLog().messageTemplate();
if(StringUtils.isNotBlank(messageTemplate)){
VelocityContext context = new VelocityContext(WriteLogUtils.convertContextToMap(logContext));

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", messageTemplate); // 关键方法

return writer.toString();
}else{
VelocityContext context = new VelocityContext();

if(logContext.getParams() != null ){
context.put("_ps", JSONObject.toJSONString(convertParams(logContext.getParams(), true)));
}else {
context.put("_ps", "");
}
if(logContext.getResult() != null ){
context.put("_rs", JSONObject.toJSONString(logContext.getResult()));
}else {
context.put("_rs", "");
}

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", DEFAULT_MESSAGE_TEMPLATE); // 关键方法

return writer.toString();
}
}

public static List<BusinessLog> createBusinessLog(LogContext logContext){
ThreadContext<UserInfo> threadContext = SaasParameter.getCurrentThreadContext();

BusinessLog businessLog = new BusinessLog();
WriteBuzLog writeBuzLog = logContext.getWriteBuzLog();

businessLog.setBussinessName(writeBuzLog.buzName());
businessLog.setOperationType(writeBuzLog.buzType());
businessLog.setLogContent(createLogInfo(logContext));
businessLog.setLogSource("web");
businessLog.setIpAddress(threadContext.getIp());
businessLog.setMemberId( threadContext.getMemberId() == null ? "" : threadContext.getMemberId().toString());
businessLog.setUserName(threadContext.getUserName());
businessLog.setWriteTimeStamp(System.currentTimeMillis());
businessLog.setCode("");
businessLog.setResult("");

return assembleCodeAndResult(logContext, writeBuzLog, businessLog);
}

/**
* 这个方法只适用在controller中调用,dubbo服务中拿不到.
* @param param
* @param amqpTemplate
*/
public static void createBusinessLogDirect(Map<String, String> param, AmqpTemplate amqpTemplate){
if (MapUtils.isEmpty(param)){
return ;
}
String queueKey="log_business";
ThreadContext<UserInfo> threadContext = SaasParameter.getCurrentThreadContext();

BusinessLog businessLog = new BusinessLog();
businessLog.setBussinessName(param.getOrDefault("buzName",""));
businessLog.setOperationType(param.getOrDefault("operationType","2"));
businessLog.setLogContent(param.getOrDefault("result",""));
businessLog.setLogSource("web");
businessLog.setIpAddress(threadContext.getIp());
businessLog.setMemberId( SaasParameter.getMemberId() == null ? "" : SaasParameter.getMemberId());
businessLog.setUserName(threadContext.getUserName());
businessLog.setWriteTimeStamp(System.currentTimeMillis());
businessLog.setCode(param.getOrDefault("code",""));
businessLog.setResult(param.getOrDefault("result",""));

List<BusinessLog> businessLogList = new ArrayList<>();
businessLogList.add(businessLog);
if (CollectionUtils.isNotEmpty(businessLogList)){
businessLogList.forEach(source->{
String buzMsg = JSONObject.toJSONString(source);
if(amqpTemplate != null){
amqpTemplate.convertAndSend(queueKey, buzMsg);
}
});
}
}
private static List<BusinessLog> assembleCodeAndResult(LogContext logContext, WriteBuzLog writeBuzLog, BusinessLog businessLog) {
List<BusinessLog> businessLogList = new ArrayList<>();
if (!StringUtils.isNumeric(writeBuzLog.logOperateType())) {
businessLogList.add(businessLog);
return businessLogList;
}
int operateType = Integer.parseInt(writeBuzLog.logOperateType());

@SuppressWarnings("unchecked")
ResponseEntity<LogResultDTO> resultJson = (ResponseEntity<LogResultDTO>) logContext.getResult();
if (resultJson == null || resultJson.getData() == null || !(resultJson.getData() instanceof LogResultDTO)) {
businessLogList.add(businessLog);
return businessLogList;
}
LogResultDTO dto = resultJson.getData();
if (CollectionUtils.isNotEmpty(dto.getLogResultDTOList())){
List<LogResultDTO> logResultDTOList = dto.getLogResultDTOList();
for (int i = 0; i < logResultDTOList.size(); i++) {
LogResultDTO logResultDTO = logResultDTOList.get(i);
BusinessLog businessLog1 = new BusinessLog();
BeanUtils.copyProperties(businessLog,businessLog1);
String code = StringUtils.trimToEmpty(logResultDTO.getCode());
String column1 = StringUtils.trimToEmpty(logResultDTO.getColumn1());
String column2 = StringUtils.trimToEmpty(logResultDTO.getColumn2());
String result = "";
if (StringUtils.isBlank(code)&& CollectionUtils.isEmpty(logResultDTO.getCodes())) {
continue;
}
switch (operateType) {
case 1 :
result = writeBuzLog.buzName();
if (StringUtils.isNotBlank(column2)) {
result += column2;
}
if (column1.equals(AuditStatusEnum.NEW.getDesc())) {
result += "并提交审核";
} else if (column1.contains(AuditStatusEnum.CHECKED.getDesc())) {
result += "并" + column1;
}
break;
case 2 :
if (StringUtils.isNotBlank(column2)) {
result += column2;
}
result += writeBuzLog.buzName();
if (StringUtils.isNotBlank(column1)) {
result += column1;
}
break;
case 3 :
if (StringUtils.isNotBlank(column1)) {
// 合同发齐、票齐共用一个方法
businessLog1.setBussinessName(column1);
result += column1;
}
break;
default :
break;
}
// 是否有附加内容
if (StringUtils.isNotBlank(logResultDTO.getSubFix())){
result = result+","+logResultDTO.getSubFix();
}
//将英文逗号、冒号、转成中文
result =result.replaceAll(",",",");
result=result.replaceAll(":",":");
if (StringUtils.isNotBlank(code)){
businessLog1.setCode(code);
businessLog1.setResult(result);
businessLogList.add(businessLog1);
}else {
businessLogList.add(businessLog1);
}
}
return businessLogList;
}
String code = StringUtils.trimToEmpty(dto.getCode());
String column1 = StringUtils.trimToEmpty(dto.getColumn1());
String column2 = StringUtils.trimToEmpty(dto.getColumn2());
String result = "";
if (StringUtils.isBlank(code)&& CollectionUtils.isEmpty(dto.getCodes())) {
businessLogList.add(businessLog);
return businessLogList;
}
switch (operateType) {
case 1 :
result = writeBuzLog.buzName();
if (StringUtils.isNotBlank(column2)) {
result += column2;
}
if (column1.equals(AuditStatusEnum.NEW.getDesc())) {
result += "并提交审核";
} else if (column1.contains(AuditStatusEnum.CHECKED.getDesc())) {
result += "并" + column1;
}
break;
case 2 :
if (StringUtils.isNotBlank(column2)) {
result += column2;
}
result += writeBuzLog.buzName();
if (StringUtils.isNotBlank(column1)) {
result += column1;
}
break;
case 3 :
if (StringUtils.isNotBlank(column1)) {
// 合同发齐、票齐共用一个方法
businessLog.setBussinessName(column1);
result += column1;
}
break;
default :
break;
}
// 是否有附加内容
if (StringUtils.isNotBlank(dto.getSubFix())){
result = result+","+dto.getSubFix();
}
//将英文逗号、冒号、转成中文
result =result.replaceAll(",",",");
result=result.replaceAll(":",":");
if (StringUtils.isNotBlank(code)){
businessLog.setCode(code);
businessLog.setResult(result);
businessLogList.add(businessLog);
return businessLogList;
}else if (CollectionUtils.isNotEmpty(dto.getCodes())){
String finalResult = result;
return dto.getCodes().stream().filter(Objects::nonNull).map(cd->{
BusinessLog businessLog1 = new BusinessLog();
BeanUtils.copyProperties(businessLog,businessLog1);
businessLog1.setCode(cd);
businessLog1.setResult(finalResult);
return businessLog1;
}).collect(Collectors.toList());
}else {
businessLogList.add(businessLog);
return businessLogList;
}
}
}

请求参数处理方法:

因为日志打印中请求是通过fastjson序列化的,所以在对请求参数处理 时需要将不能通过fastjson序列化的对象特殊处理

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
/**
* 将请求参数转换为key为_p${index}的map对象
* @param params
* @param sort
* @return
*/
private static Map<String,Object> convertParams(Object[] params,boolean sort){
Map<String,Object> paramMaps ;
if(sort){
paramMaps = new LinkedHashMap<>();
}else{
paramMaps = new HashMap<>();
}
if(params != null){
for(int index = 0; index < params.length; index ++){
Object param = params[index];
if (param == null){
paramMaps.put(createParamKey(index), "null");
}else if(param instanceof ServletRequest){
paramMaps.put(createParamKey(index), "ServletRequest");
}else if (param instanceof ServletResponse){
paramMaps.put(createParamKey(index), "ServletResponse");
}else if(param instanceof BindingResult){
paramMaps.put(createParamKey(index), "BindingResult");
}else if(param instanceof CommonsMultipartFile){
CommonsMultipartFile file = (CommonsMultipartFile) param;
paramMaps.put(createParamKey(index), file.getOriginalFilename());
}else if(Objects.equals(param.getClass(), MultipartFile[].class)) {
paramMaps.put(createParamKey(index), MultipartFile[].class.getName());
}else {
paramMaps.put(createParamKey(index), params[index]);
}
}
}

return paramMaps;
}

生成消息内容方法:生成消息内容时,以WriteBuzLog注解上的messageTemplate为消息模 板通过Velocity生成消息内容,如果messageTemplate没有值时以”请求参数_ps,响应结 果_rs”作为消息模板生成消息,而在抛出异常时,以”请求参数_ps,异常信息_es”作为消息模板生成消息

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
public static String createLogInfo(LogContext logContext){

if(logContext.getThrowable() != null){
VelocityContext context = new VelocityContext(WriteLogUtils.convertContextToMap(logContext));
if(logContext.getParams() != null ){
context.put("_ps", JSONObject.toJSONString(convertParams(logContext.getParams(), true)));
}else {
context.put("_ps", "");
}

String errStr = String.format("{异常类:%s,异常消息:%s}",
logContext.getThrowable().getClass().getName(),
logContext.getThrowable().getMessage()
);
context.put("_es", errStr);

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", EXCEPTION_MESSAGE_TEMPLATE); // 关键方法

return writer.toString();
}

String messageTemplate = logContext.getWriteBuzLog().messageTemplate();
if(StringUtils.isNotBlank(messageTemplate)){
VelocityContext context = new VelocityContext(WriteLogUtils.convertContextToMap(logContext));

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", messageTemplate); // 关键方法

return writer.toString();
}else{
VelocityContext context = new VelocityContext();

if(logContext.getParams() != null ){
context.put("_ps", JSONObject.toJSONString(convertParams(logContext.getParams(), true)));
}else {
context.put("_ps", "");
}
if(logContext.getResult() != null ){
context.put("_rs", JSONObject.toJSONString(logContext.getResult()));
}else {
context.put("_rs", "");
}

// 输出流
StringWriter writer = new StringWriter();
// 转换输出
VELOCITY_ENGINE.evaluate(context, writer, "", DEFAULT_MESSAGE_TEMPLATE); //关键方法

return writer.toString();
}
}