SpringBoot整合阿里云消息日志服务

13 篇文章 0 订阅
1 篇文章 0 订阅

##1.概述
      日志服务(Log Service,简称 Log)是针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
##2. Producer Library
      阿里官方虽然给出了demo(阿里官方Demo),但是不适用于实际开发,阿里建议开发者使用Producer Library进行日志采集等工作(Producer Library Demo)。LogHub Producer Library 是针对应用程序高并发写LogHub类库,Producer Library 和Consumer Library是对LogHub的读写包装,降低数据收集与消费的门槛。
      Producer Library 的优点:

  • 提供异步的发送接口,线程安全。
  • 可以添加多个Project的配置。
  • 可以配置用于发送的网络 I/O 线程数量。
  • 可以配置merge成的包的日志数量以及大小。
  • 内存使用可控,当内存使用达到用户配置的阈值时,Producer 的 send 接口会阻塞,直到有空闲的内存可用。
    ##3.应用
          在SpringBoot中,使用Java配置方式对LogProducer进行相关配置。
package com.school.config;

import com.aliyun.openservices.log.producer.LogProducer;
import com.aliyun.openservices.log.producer.ProducerConfig;
import com.aliyun.openservices.log.producer.ProjectConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
@Scope("singleton")
public class AliLogConfig {
    public static String accessKeyId = "****";
    public static String accessKeySecret = "****";
    public static String endPoint = "****";
    public static String projectName = "****";

    @Bean
	@ConditionalOnClass(LogProducer.class)
	public LogProducer getLogProducer() {
		LogProducer producer = new LogProducer(producerConfig());
		producer.setProjectConfig(projectConfig());
		return producer;
	}
    
    @Bean
	@ConditionalOnClass(ProducerConfig.class)
	public ProducerConfig producerConfig() {
		ProducerConfig producerConfig = new ProducerConfig();
		//被缓存起来的日志的发送超时时间,如果缓存超时,则会被立即发送,单位是毫秒
		producerConfig.packageTimeoutInMS = 1000;
		//每个缓存的日志包的大小的上限,不能超过5MB,单位是字节
		producerConfig.logsBytesPerPackage = 5 * 1024 * 1024;
		//每个缓存的日志包中包含日志数量的最大值,不能超过4096
		producerConfig.logsCountPerPackage = 4096;
		//单个producer实例可以使用的内存的上限,单位是字节
		producerConfig.memPoolSizeInByte = 1000 * 1024 * 1024;
		//IO线程池最大线程数量,主要用于发送数据到日志服务
		producerConfig.maxIOThreadSizeInPool = 50;
		//当使用指定shardhash的方式发送日志时,这个参数需要被设置,否则不需要关心。后端merge线程会将映射到同一个shard的数据merge在一起,而shard关联的是一个hash区间,
		//producer在处理时会将用户传入的hash映射成shard关联hash区间的最小值。每一个shard关联的hash区间,producer会定时从loghub拉取,该参数的含义是每隔shardHashUpdateIntervalInMS毫秒,
		producerConfig.shardHashUpdateIntervalInMS = 10 * 60 * 1000;
		producerConfig.retryTimes = 3;
		return producerConfig;
	}
	
	@Bean
	@ConditionalOnClass(ProjectConfig.class)
	public ProjectConfig projectConfig() {
    	return new ProjectConfig(projectName, endPoint, accessKeyId, accessKeySecret);
	}
}

producer 的方法是线程安全的,一般情况一个进程的所有线程共用一个 producer。所以在讲Producer配置为单例,全局唯一。

package com.school.utils;

import com.aliyun.openservices.log.common.LogItem;
import com.aliyun.openservices.log.producer.LogProducer;
import com.school.config.AliLogConfig;
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.stereotype.Component;

import java.util.Vector;

@Component
public class AliLogUtil {
	private static Logger logger = LoggerFactory.getLogger(AliLogUtil.class);
	
	public static String projectName = "*****";

	@Autowired
	private AliLogConfig aliLogConfig;
	
	@Value("${aliyun.log_store}")
	private String logStore;
	
	public void saveLog(Vector<LogItem> logGroup, String topic, String source) {
		final LogProducer logProducer = aliLogConfig.getLogProducer();
		// 并发调用 send 发送日志
		logger.info("projectName:" + projectName + ", logStore:" + logStore + ", topic:" + topic + ", source:"
		+ source);
		logger.info("发送日志到" + logStore + ":" + logGroup.get(0).ToJsonString());
		logProducer.send(projectName, logStore, topic, source, logGroup, new CallbackLogInfo(projectName, logStore, topic,
				null, source, logGroup, logProducer));
		// 主动刷新缓存起来的还没有被发送的日志
		logProducer.flush();
		// 等待数据发送完毕
		// Thread.sleep(2 * producerConfig.packageTimeoutInMS);
		// 关闭后台 io 线程,close 会将调用时刻内存中缓存的数据发送出去
		logProducer.close();
	}
}

public class CallbackLogInfo extends ILogCallback {
    private static final Logger logger = LoggerFactory.getLogger(CallbackLogInfo.class);
    // 保存要发送的数据,当时发生异常时,进行重试
    public String project;
    public String logstore;
    public String topic;
    public String shardHash;
    public String source;
    public Vector<LogItem> items;
    public LogProducer producer;
    public int retryTimes = 0;

    public CallbackLogInfo(String project, String logstore, String topic, String shardHash, String source,
            Vector<LogItem> items, LogProducer producer) {
        super();
        this.project = project;
        this.logstore = logstore;
        this.topic = topic;
        this.shardHash = shardHash;
        this.source = source;
        this.items = items;
        this.producer = producer;
    }

    public void onCompletion(PutLogsResponse response, LogException e) {
        if (e != null) {
            // 打印异常
            logger.info(e.GetErrorCode() + ", " + e.GetErrorMessage() + ", " + e.GetRequestId());
            // 最多重试三次
            if (retryTimes++ < 3) {
                producer.send(project, logstore, topic, source, shardHash, items, this);
            }
        } else {
            logger.info("send success, request id: " + response.GetRequestId());
        }
    }
}

在接口中调用方法:

// 保存日志信息
Vector<LogItem> logItems = new Vector<LogItem>();
// 根据学员id查询学员信息
Map<String, Object> logInfo = userMapper.queryStudentLogInfo(smCostOrders.get(0).getStudentId().toString(), schoolId);
if (logInfo != null) {
	LogItem logItem = new LogItem();
	logItem.PushBack("telephoneCode",String.valueOf(logInfo.get("telephoneCode")));
	logItem.PushBack("schoolName",String.valueOf(logInfo.get("schoolName")));
	logItem.PushBack("studentName", String.valueOf(logInfo.get("studentName")));
	logItem.PushBack("cityName", String.valueOf(logInfo.get("cityName")));
	logItem.PushBack("payType", "代缴费支付");
	logItem.PushBack("payMoney", String.valueOf(totalFee));
	logItem.PushBack("payTime", String.valueOf(gmt_payment));
	logItems.add(logItem);
	aliLogUtil.saveLog(logItems, "支付", "学员版");
}

这里需要注意的是,pushBack的时候,一定要注意不能设置空值,否则会失败。
至此,阿里云日志服务就可以使用了,如有不当之处,还请指正。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值