JAVA代码接入Aliyun日志服务SLS

最近有业务需求,需要看到接口调用次数、用户ip、平均响应时间、状态码等参数,这里使用日志服务SLS实现,在代码中接入Aliyun的日志服务,将日志信息上传到aliyun存储,采用Java代码作为例子,最后通过SQL查出相应的数据生成图表,最终效果如下:

image-20221123114812933

创建日志存储

进入到阿里云的控制台,搜索日志服务SLS,创建日志过程点几下就行,这里留意三个参数Logstore名称和项目名称、访问端点,后面编程会用到

  • Logstore名称
image-20221117161922287
  • 项目名称

    image-20221117162656247
  • 访问端点endpoint

发送日志

代码编写

  1. 将下列依赖加入到您项目的 pom.xml 中。

    <dependency>
        <groupId>com.aliyun.openservices</groupId>
        <artifactId>aliyun-log-producer</artifactId>
        <version>0.3.10</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun.openservices</groupId>
        <artifactId>aliyun-log</artifactId>
        <version>0.6.33</version>
    </dependency>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>2.5.0</version>
    </dependency>
    

    jar-with-dependency 版本,可以解决producer依赖的版本冲突

    <dependency>
        <groupId>com.aliyun.openservices</groupId>
        <artifactId>aliyun-log</artifactId>
        <version>0.6.35</version>
      <classifier>jar-with-dependencies</classifier>
    </dependency>
    
  2. 测试代码如下,填好参数后直接运行即可发送

    public static void main(String[] args) throws InterruptedException, ProducerException {
      //基本参数
      final String project = "xxxx-cn-shanghai-logproject";
      final String logStore = "xxxx-app-api";
      final String endpoint = "cn-shanghai.log.aliyuncs.com";
      final String accessKeyId = "xxxxx";
      final String accessKeySecret = "xxxxx";
      //设置producer参数,后面有参数详细解析
      ProducerConfig producerConfig = new ProducerConfig();
      producerConfig.setBatchSizeThresholdInBytes(3 * 1024 * 1024);
      producerConfig.setBatchCountThreshold(10000);
      producerConfig.setIoThreadCount(10);
      final Producer producer = new LogProducer(producerConfig);
      producer.putProjectConfig(new ProjectConfig(project, endpoint, accessKeyId, accessKeySecret));
      LOGGER.info("Test started.");
      //构建日志
      LogItem logItem = new LogItem();
      logItem.PushBack("request_method", "post");
      logItem.PushBack("path", "/user/login");
      //发送日志
      producer.send(
              project,
              logStore,
              logItem);
      try {
        producer.close();
      } catch (ProducerException e) {
        LOGGER.error("Failed to close producer, e=", e);
      }
    }
    

查看发送结果

在Logstore可以查看到新加的日志

image-20221117165413229

ProducerConfig

ProducerConfig 用于配置发送策略,您可以根据不同的业务场景为参数指定不同的值,各参数含义如下表所示。

参数 类型 描述
totalSizeInBytes 整型 单个 producer 实例能缓存的日志大小上限,默认为 100MB。
maxBlockMs 整型 如果 producer 可用空间不足,调用者在 send 方法上的最大阻塞时间,默认为 60 秒。 如果超过这个时间后所需空间仍无法得到满足,send 方法会抛出 TimeoutException。 如果将该值设为0,当所需空间无法得到满足时,send 方法会立即抛出 TimeoutException。 如果您希望 send 方法一直阻塞直到所需空间得到满足,可将该值设为负数。
ioThreadCount 整型 执行日志发送任务的线程池大小,默认为可用处理器个数。
batchSizeThresholdInBytes 整型 当一个 ProducerBatch 中缓存的日志大小大于等于 batchSizeThresholdInBytes 时,该 batch 将被发送,默认为 512 KB,最大可设置成 5MB。
batchCountThreshold 整型 当一个 ProducerBatch 中缓存的日志条数大于等于 batchCountThreshold 时,该 batch 将被发送,默认为 4096,最大可设置成 40960。
lingerMs 整型 一个 ProducerBatch 从创建到可发送的逗留时间,默认为 2 秒,最小可设置成 100 毫秒。
retries 整型 如果某个 ProducerBatch 首次发送失败,能够对其重试的次数,默认为 10 次。 如果 retries 小于等于 0,该 ProducerBatch 首次发送失败后将直接进入失败队列。
maxReservedAttempts 整型 每个 ProducerBatch 每次被尝试发送都对应着一个 Attempt,此参数用来控制返回给用户的 attempt 个数,默认只保留最近的 11 次 attempt 信息。 该参数越大能让您追溯更多的信息,但同时也会消耗更多的内存。
baseRetryBackoffMs 整型 首次重试的退避时间,默认为 100 毫秒。 Producer 采样指数退避算法,第 N 次重试的计划等待时间为 baseRetryBackoffMs * 2^(N-1)。
maxRetryBackoffMs 整型 重试的最大退避时间,默认为 50 秒。
adjustShardHash 布尔 如果调用 send 方法时指定了 shardHash,该参数用于控制是否需要对其进行调整,默认为 true。
buckets 整型 当且仅当 adjustShardHash 为 true 时,该参数才生效。此时,producer 会自动将 shardHash 重新分组,分组数量为 buckets。 如果两条数据的 shardHash 不同,它们是无法合并到一起发送的,会降低 producer 吞吐量。将 shardHash 重新分组后,能让数据有更多地机会被批量发送。 该参数的取值范围是 [1, 256],且必须是 2 的整数次幂,默认为 64。

最佳实践

生成Producer

应该单例模式使用Producer ,使用@Bean注解,在启动时注入Producer,后面从容器中取用

关闭Producer

  • **服务器上的Spring应用:**在aop类中继承DisposableBean,实现destroy()方法,在该方法中执行 Producer 的close()方法

  • **ServerLess上的HTTP函数:**编写接口/pre-stop,在接口中执行Producer 的close()方法

图表生成

生成图表必须先开启索引,新加的索引,不会统计之前的数据,需要的话可以进行索引重建,点击属性进行设置索引页面

image-20221119164301741

使用自动生成索引可以很方便的添加索引

image-20221119164336049

SQL案例

  • 统计ip地区,及通过ip_to_province函数得出ip对应的省地址,用group by对地址聚合,用count函数计算出每个地址出现的次数 通过地图进行展示,鼠标悬浮可以展示,对应省的次数,图表类型–地图
* |
select
  count(1) as c,
  ip_to_province(client_ip) as address
group by
  address
limit
  100
  • 访问前十的地址,通过split_part函数将request_uri按?分割成array,取分割后的第一个字符串,得出请求的路径,按这个路径group by进行聚合,用count函数计算每个路径访问的次数,用order by对次数进行排序,desc表示顺序是从大到小 通过表格进行展示,

    图表类型–表格

* |
select
  count(1) as pv,
  split_part(uri, '?', 1) as path
group by
  path
order by
  pv desc
limit
  10
  • 请求状态及数量跟随时间顺序展示,通过 date_trunc 函数对日志时间按照分钟对齐,使用 date_format 函数提取出小时、分钟,将提取后的时间与访问状态码status通过group by聚合,获取每分钟每个状态码的count值, 最后使用流图展示数据,x轴为time,y轴count,聚合列是status,图表类型–流图pro
* |
select
  date_format(date_trunc('minute', __time__), '%H:%i') as time,
  COUNT(1) as c,
  status
GROUP by
  time,
  status
ORDER by
  time
LIMIT
  1000
  • 接口返回错误次数统计(前十),图表类型–表格
*|SELECT code,tip,COUNT(*) as num WHERE code != 0 GROUP by code,tip ORDER by num DESC LIMIT 10
  • 统计用户代理访问次数,图表类型–表格
*|SELECT user_agent,COUNT(*) as num GROUP by user_agent ORDER by num DESC LIMIT 10
  • 统计所有接口平均响应时间 ,图表类型–线图pro
* |
select
  date_format(date_trunc('minute', __time__), '%H:%i') as time,
 ceiling(avg(request_time)) as avg_request_time
  WHERE  status = 200
GROUP by
  time
ORDER by
  time

相关链接

日志查询语法

发送日志包示例代码

Aliyun LOG Java Producer 快速入门

查询语法与功能