一、生成PB(Protobuf)上传日志类
使用插件的话,首先要下载安装Protobuf Support插件。
安装后重启IDEA即可。
接着需要对Protobuf Support插件进行配置.
maven的pom文件添加下面的代码
<!--pb文件处理-->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>net.jpountz.lz4</groupId>
<artifactId>lz4</artifactId>
<version>1.3.0</version>
</dependency>
<!--pb文件处理-->
<!--腾讯云服务-->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.361</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-common</artifactId>
<version>3.1.361</version>
</dependency>
<!--腾讯云服务-->
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
现在开始写.proto文件,在src/main下面新建proto文件夹,在proto里面新建一个文件,注意后缀是.proto,我这里是cls.proto,
package cls;
//syntax = "proto3";
//proto类生成路径
option java_package = "com.site.blog.my.core.entity.log";
option java_outer_classname = "Cls";
message Log
{
message Content
{
required string key = 1; // 每组字段的 key
required string value = 2; // 每组字段的 value
}
required int64 time = 1; // 时间戳,UNIX时间格式
repeated Content contents = 2; // 一条日志里的多个kv组合
}
message LogTag
{
required string key = 1;
required string value = 2;
}
message LogGroup
{
repeated Log logs = 1; // 多条日志合成的日志数组
optional string contextFlow = 2; // 目前暂无效用
optional string filename = 3; // 日志文件名
optional string source = 4; // 日志来源,一般使用机器IP
repeated LogTag logTags = 5;
}
message LogGroupList
{
repeated LogGroup logGroupList = 1; // 日志组列表
}
编写好.proto文件后,使用插件将proto文件转换为java文件
双击 protobuf:compile
即可
出现SUCCESS信息表示转换成功
然后在target/generated-sources/protobuf目录中即可找到生成的java文件,复制到src/main/java中你需要使用的位置即可。
二、上传pb日志工具类
1. 日志类型枚举
/**
* 日志类型
*
* @author Houchengen
* @date 2021/09/01
*/
public enum LogType {
/**
* info日志
*/
INFO(1, "INFO"),
/**
* debug日志
*/
DEBUG(2, "DEBUG"),
/**
* 错误日志
*/
ERROR(3, "ERROR");
LogType(Integer origin, String label) {
this.origin = origin;
this.label = label;
}
/**
* 性质代码
*/
private Integer origin;
/**
* 性质中文
*/
private String label;
public Integer getOrigin() {
return origin;
}
public void setOrigin(Integer origin) {
this.origin = origin;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public static LogType getEnumByKey(Integer origin) {
for (LogType type : LogType.values()) {
if (origin.equals(type.origin)) {
return type;
}
}
return null;
}
}
1. 日志上传类
import com.site.blog.my.core.entity.log.Cls;
import com.site.blog.my.core.entity.log.LogType;
import com.tencentcloudapi.common.CommonClient;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.common.profile.Region;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 发送腾讯日志工具类
*
* @author Houchengen
* @date 2021/09/22
*/
@EnableAsync
@Component
public class SendTencentLogUtil {
//腾讯云账户密钥 secretId.
public static final String SECRET_ID = "*****************";
//腾讯云账户密钥 secretKey.
public static final String SECRET_KEY = "*********************";
public static final String ENDPOINT = "cls.tencentcloudapi.com";
public static final String ROOT_DOMAIN = "tencentcloudapi.com";
public static final String X_CLS_TOPIC_ID = "X-CLS-TopicId";
public static final String X_CLS_HASH_KEY = "X-CLS-HashKey";
public static final String X_CLS_COMPRESS_TYPE = "X-CLS-CompressType";
public static final String LZ_4 = "lz4";
//你的topicId
public static final String TOPIC = "************";
public static final String PRODUCT_NAME_CLS = "cls";
public static final String UPLOAD_LOG_URL = "UploadLog";
public static final String VERSION = "2020-10-16";
/**
* 发送日志
*
* @param logType 日志类型
* @param params 参数
*/
@Async
public void sendLog(LogType logType, Map<String, String> params) {
Credential cred = new Credential(SECRET_ID, SECRET_KEY);
//region 设置http选项中的参数
HttpProfile httpProfile = new HttpProfile();
// post请求 (默认为post请求)
httpProfile.setReqMethod(HttpProfile.REQ_POST);
// 指定接入地域域名(默认就近接入)
httpProfile.setEndpoint(ENDPOINT);
httpProfile.setRootDomain(ROOT_DOMAIN);
// 在外网互通的网络环境下支持http协议(默认是https协议),请选择(https:// or http://)
httpProfile.setProtocol(HttpProfile.REQ_HTTPS);
// 设置读取超时时间,单位为秒(默认0秒)
httpProfile.setReadTimeout(0);
// 设置写入超时时间,单位为秒(默认0秒)
httpProfile.setWriteTimeout(0);
// 请求连接超时时间,单位为秒(默认60秒)
httpProfile.setConnTimeout(HttpProfile.TM_MINUTE);
//endregion
//region 设置client选项中的参数
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 指定签名算法默认("TC3-HMAC-SHA256"),它更安全但是会轻微降低性能。
clientProfile.setSignMethod(ClientProfile.SIGN_TC3_256);
//打印日志,默认是false
clientProfile.setDebug(false);
//endregion
//region headers 和 body 信息
HashMap<String, String> headers = new HashMap<>();
headers.put(X_CLS_TOPIC_ID, TOPIC);
headers.put(X_CLS_HASH_KEY, "");
// body lz4 压缩
headers.put(X_CLS_COMPRESS_TYPE, LZ_4);
byte[] body = compressedByte(getBodyInfo(logType, params));
//endregion
try {
CommonClient client = new CommonClient(PRODUCT_NAME_CLS, VERSION, cred, Region.Chengdu.getValue(), clientProfile);
String resp = client.callOctetStream(UPLOAD_LOG_URL, headers, body);
System.out.println(resp);
} catch (TencentCloudSDKException te) {
System.out.println(te.getMessage());
}
}
/**
* pom依赖:
* <dependency>
* <groupId>net.jpountz.lz4</groupId>
* <artifactId>lz4</artifactId>
* <version>1.3.0</version>
* </dependency>
* <p>
* 进行lz4压缩
*
* @param srcByte
* @return
*/
public static byte[] compressedByte(byte[] srcByte) {
LZ4Factory factory = LZ4Factory.fastestInstance();
LZ4Compressor compressor = factory.fastCompressor();
return compressor.compress(srcByte);
}
/**
* pom依赖:
* <dependency>
* <groupId>com.google.protobuf</groupId>
* <artifactId>protobuf-java</artifactId>
* <version>3.15.3</version>
* </dependency
* <p>
* 构造http body 信息
* protobuf 序列化为 byte[]
*
* @return byte[]
*/
public static byte[] getBodyInfo(LogType logType, Map<String, String> params) {
Cls.Log.Builder logBuilder = Cls.Log.newBuilder();
logBuilder.setTime(System.currentTimeMillis() / 1000L);
logBuilder.addContents(Cls.Log.Content.newBuilder()
.setKey("type")
.setValue(logType.getLabel())
.build());
params.forEach((k, v) -> {
if (v == null) {
v = "null";
}
logBuilder.addContents(Cls.Log.Content.newBuilder()
.setKey(k)
.setValue(v)
.build());
});
Cls.Log log = logBuilder.build();
Cls.LogGroup logGroup = Cls.LogGroup.newBuilder()
.addLogs(log)
.build();
Cls.LogGroupList logGroupList = Cls.LogGroupList.newBuilder()
.addLogGroupList(logGroup)
.build();
//这里我们将封装有数据的对象实例,转换为字节数组,用于数据传输、存储等
return logGroupList.toByteArray();
}
}
三、使用示例
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.site.blog.my.core.entity.log.LogType;
import com.site.blog.my.core.util.SendTencentLogUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.HashMap;
/**
* 请求日志上传切面
*
* @author Houchengen
* @date 2021/09/22
*/
@Aspect
@Component
public class RequestLogAspect {
@Autowired
private SendTencentLogUtil sendTencentLogUtil;
@Pointcut("execution(* com.site.blog.my.core.controller..*(..))")
public void executeController() {
}
@Before("executeController()")
public void doBefore(JoinPoint joinPoint) {
//获取请求报文头部元数据
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获取请求对象
HttpServletRequest request = requestAttributes.getRequest();
HashMap<String, String> map = new HashMap<>();
map.put("params", Arrays.toString(joinPoint.getArgs()));
map.put("method", request.getMethod());
map.put("class_method", joinPoint.getSignature().getDeclaringTypeName() +
"." + joinPoint.getSignature().getName());
map.put("requestUrl", request.getRequestURL().toString());
sendTencentLogUtil.sendLog(LogType.INFO, map);
}
@AfterReturning(returning = "ret", pointcut = "executeController()")
public void doAfter(Object ret) {
HashMap<String, String> map = new HashMap<>();
map.put("responseBody", JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue));
sendTencentLogUtil.sendLog(LogType.INFO, map);
}
}