日志服务接入方式之log producer library

本文涉及的产品
对象存储 OSS,20GB 3个月
文件存储 NAS,50GB 3个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: Producer Library解决的问题: 客户端日志不落盘:既数据生产后直接通过网络发往服务端。 客户端高并发写入:例如一秒钟会有百次以上写操作。 客户端计算与IO逻辑分离:打日志不影响计算耗时。

日志服务(原SLS)团队提供LogHub Producer Library方便客户端接入日志,Producer Library和Consumer Library是对LogHub功能的包装,降低数据收集与消费的门槛。

Producer Library解决的问题:

  1. 客户端日志不落盘:既数据产生后直接通过网络发往服务端。
  2. 客户端高并发写入:例如一秒钟会有百次以上写操作。
  3. 客户端计算与IO逻辑分离:打日志不影响计算耗时。

在以上场景中,Producer Library会简化你程序开发的代价,帮助你批量聚合写请求,通过异步的方式发往LogHub服务端。在整个过程中,用户可以配置批量聚合的参数,服务端异常处理的逻辑等。

0c5e22da184eec0f93979cec8ff159394b1143e0

以上各种接入方式的对比:

接入方式 优点/缺点 针对场景
日志落盘+Logtail 日志收集与打日志解耦,无需修改代码 常用场景
Syslog + Logtail 性能较好(80MB/S),日志不落盘,需支持syslog协议 Syslog场景
SDK直发 不落盘,直接发往服务端,需要处理好网络IO与程序IO之间的切换 日志不落盘
Producer Library 不落盘,异步合并发送服务端,吞吐量较好 日志不落盘,客户端QPS高

(目前Producer Library只支持Java 版本,其他语言待开发)

LogHub Producer Library功能

  1. 提供异步的发送接口,线程安全。
  2. 可以添加多个project的配置。
  3. 用于发送的网络IO线程数量可以配置。
  4. merge成的包的日志数量以及大小都可以配置。
  5. 内存使用可控,当内存使用达到用户配置的阈值时,producer的send接口会阻塞,直到有空闲的内存可用。

使用方法

producer使用分为以下几个步骤:

1: maven工程中添加依赖:

<dependency>
    <groupId>com.aliyun.openservices</groupId>
    <artifactId>log-loghub-producer</artifactId>
    <version>0.1.4</version>
</dependency>

2:程序中配置ProducerConfig,其中各个参数说明如下。

public class ProducerConfig 
{
    //被缓存起来的日志的发送超时时间,如果缓存超时,则会被立即发送,单位是毫秒
    public int packageTimeoutInMS = 3000;
    //每个缓存的日志包中包含日志数量的最大值,不能超过4096
    public int logsCountPerPackage = 4096;
    //每个缓存的日志包的大小的上限,不能超过5MB,单位是字节
    public int logsBytesPerPackage = 5 * 1024 * 1024;
    //单个producer实例可以使用的内存的上限,单位是字节
    public int memPoolSizeInByte = 1000 * 1024 * 1024;
    //IO线程池最大线程数量,主要用于发送数据到日志服务
    public int maxIOThreadSizeInPool = 50;
    //当使用指定shardhash的方式发送日志时,这个参数需要被设置,否则不需要关心。后端merge线程会将映射到同一个shard的数据merge在一起,而shard关联的是一个hash区间,
    //producer在处理时会将用户传入的hash映射成shard关联hash区间的最小值。每一个shard关联的hash区间,producer会定时从loghub拉取,该参数的含义是每隔shardHashUpdateIntervalInMS毫秒,
    //更新一次shard的hash区间。
    public int shardHashUpdateIntervalInMS = 10 * 60 * 1000;
    //如果发送失败,重试的次数,如果超过该值,就会将异常作为callback的参数,交由用户处理。
    public int retryTimes = 3;
}

3:继承ILogCallback,callback主要用于日志发送结果的处理,结果包括发送成功和发生异常。用户也可以选择不处理,这样就不需要继承ILogCallback。

4:创建producer实例,调用send接口发数据。

下面是一个完整的示例。

示例

main:

public class ProducerSample {
    private final static int ThreadsCount = 25;

    public static String RandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int num = random.nextInt(62);
            buf.append(str.charAt(num));
        }
        return buf.toString();
    }

    public static void main(String args[]) throws InterruptedException {
        ProducerConfig producerConfig = new ProducerConfig();
        // 使用默认producer配置
        final LogProducer producer = new LogProducer(producerConfig);
        // 添加多个project配置
        producer.setProjectConfig(new ProjectConfig("your project 1",
                "endpoint", "your accesskey id", "your accesskey"));
        producer.setProjectConfig(new ProjectConfig("your project 2",
                "endpoint", "your accesskey id", "your accesskey",
                "your sts token"));
        // 更新project 1的配置
        producer.setProjectConfig(new ProjectConfig("your project 1",
                "endpoint", "your new accesskey id", "your new accesskey"));
        // 删除project 2的配置
        producer.removeProjectConfig("your project 2");
        // 生成日志集合,用于测试
        final Vector<Vector<LogItem>> logGroups = new Vector<Vector<LogItem>>();
        for (int i = 0; i < 100000; ++i) {
            Vector<LogItem> tmpLogGroup = new Vector<LogItem>();
            LogItem logItem = new LogItem((int) (new Date().getTime() / 1000));
            logItem.PushBack("level", "info" + System.currentTimeMillis());
            logItem.PushBack("message", "test producer send perf "
                    + RandomString(50));
            logItem.PushBack("method", "SenderToServer " + RandomString(10));
            tmpLogGroup.add(logItem);
            logGroups.add(tmpLogGroup);
        }
        // 并发调用send发送日志
        Thread[] threads = new Thread[ThreadsCount];
        for (int i = 0; i < ThreadsCount; ++i) {
            threads[i] = new Thread(null, new Runnable() {
                Random random = new Random();

                public void run() {
                    int j = 0, rand = random.nextInt(99999);
                    while (++j < Integer.MAX_VALUE) {
                        producer.send("project 1", "logstore 1", "topic",
                                "source ip", logGroups.get(rand),
                                new CallbackSample("project 1", "logstore 1", "topic", "source ip", null, logGroups.get(rand), producer));
                    }
                }
            }, i + "");
            threads[i].start();
        }
        //等待发送线程退出
        Thread.sleep(1 * 60 * 60 * 1000);
        //主动刷新缓存起来的还没有被发送的日志
        producer.flush();
        //关闭后台io线程,close会将调用时刻内存中缓存的数据发送出去
        producer.close();
    }
}

callback:

public class CallbackSample extends ILogCallback {
    //保存要发送的数据,当时发生异常时,进行重试
    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 CallbackSample(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) {
            // 打印异常
            System.out.println(e.GetErrorCode() + ", " + e.GetErrorMessage() + ", " + e.GetRequestId());
            //最多重试三次
            if(retryTimes++ < 3)
            {
                producer.send(project, logstore, topic, source, shardHash, items, this);
            }
        }
        else{
            System.out.println("send success, request id: " + response.GetRequestId());
        }
    }

}
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
11天前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
117 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
1月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
218 3
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1626 14
|
1月前
|
Python
log日志学习
【10月更文挑战第9天】 python处理log打印模块log的使用和介绍
30 0
|
1月前
|
数据可视化
Tensorboard可视化学习笔记(一):如何可视化通过网页查看log日志
关于如何使用TensorBoard进行数据可视化的教程,包括TensorBoard的安装、配置环境变量、将数据写入TensorBoard、启动TensorBoard以及如何通过网页查看日志文件。
193 0
|
1月前
|
存储 分布式计算 NoSQL
大数据-136 - ClickHouse 集群 表引擎详解1 - 日志、Log、Memory、Merge
大数据-136 - ClickHouse 集群 表引擎详解1 - 日志、Log、Memory、Merge
40 0
|
1月前
|
缓存 Linux 编译器
【C++】CentOS环境搭建-安装log4cplus日志组件包及报错解决方案
通过上述步骤,您应该能够在CentOS环境中成功安装并使用log4cplus日志组件。面对任何安装或使用过程中出现的问题,仔细检查错误信息,对照提供的解决方案进行调整,通常都能找到合适的解决之道。log4cplus的强大功能将为您的项目提供灵活、高效的日志管理方案,助力软件开发与维护。
54 0
|
2月前
|
Java
日志框架log4j打印异常堆栈信息携带traceId,方便接口异常排查
日常项目运行日志,异常栈打印是不带traceId,导致排查问题查找异常栈很麻烦。
|
2月前
|
存储 监控 数据可视化
SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
【9月更文挑战第2天】SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
150 9
|
3月前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
159 0

相关产品

  • 日志服务