java阿里云sls基于LoghubAppender自定义日志上传

news2024/11/23 21:42:16

1、背景:阿里sls日志提供快捷日志平台,平替elk公司使用这个日志服务,需要对接写入日志

目前日志集成有3种

        1)基于封装manager手动写日志手动send

              弊端:本地日志和阿里云日志共用日志代码很臃肿

        2)基于云服务器日志文件写入日志平台

                弊端:日志分割太碎,一次请求分割很多条日志

        3)代码LoghubAppender写入日志平台

                弊端:日志分割太碎,一次请求分割很多条日志

2、期望实现效果:日志按请求维度发送

3、实现思路:

      日志发送在LoghubAppender里面appendEvent(E eventObject)方法里面实现了日志内组装和发送,想法是拆分日志组装和发送,先组装不发送,在请求完成后再发送

 

3、实现方式:

自定义LoghubAppender

自定义log写入类:

 

完整appendEvent方法:

private void appendEvent(E eventObject) {
        if (eventObject instanceof LoggingEvent) {
            LoggingEvent event = (LoggingEvent) eventObject;
            List<LogItem> logItems = new ArrayList<>();
            LogItem item = new LogItem();
            logItems.add(item);
            item.SetTime((int) (event.getTimeStamp() / 1000L));
            if (this.formatter != null) {
                DateTime dateTime = new DateTime(event.getTimeStamp());
                item.PushBack("time", dateTime.toString(this.formatter));
            } else {
                Instant instant = Instant.ofEpochMilli(event.getTimeStamp());
                item.PushBack("time", this.formatter1.format(instant));
            }

            item.PushBack("level", event.getLevel().toString());
            item.PushBack("thread", event.getThreadName());
            StackTraceElement[] caller = event.getCallerData();
            if (caller != null && caller.length > 0) {
                item.PushBack("location", caller[0].toString());
            }

            String message = event.getFormattedMessage();
            item.PushBack("message", message);
            IThrowableProxy iThrowableProxy = event.getThrowableProxy();
            if (iThrowableProxy != null) {
                String throwable = this.getExceptionInfo(iThrowableProxy);
                throwable = throwable + this.fullDump(event.getThrowableProxy().getStackTraceElementProxyArray());
                item.PushBack("throwable", throwable);
            }

            if (this.encoder != null) {
                item.PushBack("log", new String(this.encoder.encode(eventObject)));
            }

            Optional.ofNullable(this.mdcFields).ifPresent(f -> event.getMDCPropertyMap().entrySet().stream()
                    .filter(v -> Arrays.stream(f.split(",")).anyMatch(i -> i.equals(v.getKey())))
                    .forEach(map -> item.PushBack(map.getKey(), map.getValue())));
            AliyunLogManager aliyunLogManager = null;
            try {
                aliyunLogManager = SpringContext.getBean(AliyunLogManager.class);
            } catch (Exception e) {
                log.info("AliyunLogManager bean has not initialized");
            }

            if (aliyunLogManager != null && MDC.get(CommonConst.LOG_TRACE_ID) != null) {
                aliyunLogManager.writeLog("log", logItemToString(logItems));
            } else {
                try {
                    this.producer.send(this.projectConfig.getProject(), this.logStore, this.topic, this.source, logItems
                            , new LoghubAppenderCallback(this, this.projectConfig.getProject(), this.logStore, this.topic, this.source, logItems));
                } catch (Exception var9) {
                    this.addError("Failed to send log, project=");
                }
            }

        }
    }

 

阿里云日志工具类: 

主要思路:写日志时候放至threadlocal中


import com.aliyun.openservices.aliyun.log.producer.LogProducer;
import com.aliyun.openservices.aliyun.log.producer.Producer;
import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
import com.aliyun.openservices.aliyun.log.producer.ProjectConfig;
import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
import com.aliyun.openservices.log.common.LogItem;
import com.google.common.collect.Lists;
import com.ty.mid.common.base.thread.TyThreadPool;
import com.ty.mid.common.base.utils.LocalDateTimeUtil;
import com.ty.mid.common.log.config.AliYunAutoConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author hll
 * @date 2022/9/20
 * 阿里云日志工具类
 */
@Component
@Slf4j
public class AliyunLogManager implements InitializingBean, DisposableBean {

    @Autowired
    private AliYunAutoConfig aliYunAutoConfig;

    private Producer producer;

    public static final ThreadLocal<List<Map<String, String>>> LOCAL_LOG = new ThreadLocal<>();
    public static final ThreadLocal<List<Map<String, String>>> LOCAL_TAG = new ThreadLocal<>();

    @Override
    public void afterPropertiesSet() {
        ProducerConfig producerConfig = new ProducerConfig();
        producerConfig.setBatchSizeThresholdInBytes(3145728);
        producerConfig.setBatchCountThreshold(40960);
        producerConfig.setIoThreadCount(this.aliYunAutoConfig.getThreadCount());
        producerConfig.setTotalSizeInBytes(this.aliYunAutoConfig.getTotalSize());
        this.producer = new LogProducer(producerConfig);
        this.producer.putProjectConfig(new ProjectConfig(this.aliYunAutoConfig.getProject(), this.aliYunAutoConfig.getEndpoint()
                , this.aliYunAutoConfig.getAccessKey(), this.aliYunAutoConfig.getAccessSecret()));
    }

    @Override
    public void destroy() throws Exception {
        if (this.producer != null) {
            try {
                this.producer.close();
            } catch (ProducerException var2) {
                log.error("Failed to close producer, e=", var2);
            }
        }

    }

    public void writeLog(String title, String value) {
        List<Map<String, String>> logList = LOCAL_LOG.get();
        if (logList == null) {
            logList = new ArrayList<>();
        }

        Map<String, String> logMap = new HashMap<>(1);
        logMap.put(String.format("%s_%s", LocalDateTimeUtil.getSystemDateStr(), title), value);
        (logList).add(logMap);
        LOCAL_LOG.set(logList);
    }

    public void setTag(String tagName, String tagValue) {
        List<Map<String, String>> logList = LOCAL_TAG.get();
        if (logList == null) {
            logList = new ArrayList<>();
        }

        Map<String, String> logMap = new HashMap<>(1);
        logMap.put(String.format("__tag__:__%s__", tagName), tagValue);
        (logList).add(logMap);
        LOCAL_TAG.set(logList);
    }

    public void send() {
        List<Map<String, String>> logList = LOCAL_LOG.get();
        List<Map<String, String>> tagList = LOCAL_TAG.get();
        //防止日志数量过大 阿里日志拒绝写入/读取
        List<List<Map<String, String>>> partition = Lists.partition(CollectionUtils.isEmpty(logList) ? new ArrayList<>() : logList, 500);
        for (List<Map<String, String>> maps : partition) {
            handleSend(maps, tagList);
        }
        LOCAL_LOG.remove();
        LOCAL_TAG.remove();
    }

    private void handleSend(List<Map<String, String>> logList, List<Map<String, String>> tagList) {
        if (CollectionUtils.isNotEmpty(logList)) {
            final List<Map<String, String>> logListCopy = new ArrayList<>();
            int i = 0;
            Map<String, String> map;

            for (Map<String, String> log : logList) {
                map = new HashMap<>(log.size());

                for (String key : log.keySet()) {
                    StringBuilder var10001 = (new StringBuilder()).append(key).append("_");
                    ++i;
                    if (i < 10) {
                        map.put(var10001.append("0").append(i).toString(), log.get(key));
                    } else {
                        map.put(var10001.append(i).toString(), log.get(key));
                    }

                }

                logListCopy.add(map);
            }

            //tag补充
            if (CollectionUtils.isNotEmpty(tagList)) {
                for (Map<String, String> log : tagList) {
                    map = new HashMap<>(log.size());

                    for (String key : log.keySet()) {
                        StringBuilder var10001 = (new StringBuilder()).append(key).append("_");
                        ++i;
                        if (i < 10) {
                            map.put(var10001.append("0").append(i).toString(), log.get(key));
                        } else {
                            map.put(var10001.append(i).toString(), log.get(key));
                        }
                    }

                    logListCopy.add(map);
                }
            }


            ThreadPool.execute(() -> {
                try {
                    this.producer.send(this.aliYunAutoConfig.getProject(), this.aliYunAutoConfig.getLogStore()
                            , this.aliYunAutoConfig.getTopic(), this.aliYunAutoConfig.getSource(), this.generateLogItem(logListCopy), result -> {
                                if (!result.isSuccessful()) {
                                    log.error("send log failed");
                                }
                            });
                } catch (Exception var2) {
                    log.error(var2.getMessage(), var2);
                }
            });
        }
    }

    private LogItem generateLogItem(List<Map<String, String>> logList) {
        LogItem logItem = new LogItem();
        Iterator<Map<String, String>> var3 = logList.iterator();
        while (var3.hasNext()) {
            Map logMap = var3.next();
            for (Object o : logMap.entrySet()) {
                Map.Entry<String, String> entry = (Map.Entry) o;
                logItem.PushBack(entry.getKey(), entry.getValue());
            }
        }

        return logItem;
    }
}

基于请求维度发送日志(logback日志为例):


/**
 * @author hll
 * @date 2022/9/27
 * logback 阿里云日志对接处理
 */
@Component
@Order(-1)
public class LogbackRequestLoggingFilter extends AbstractRequestLoggingFilter {

    @Autowired
    private AliyunLogManager aliyunLogManager;

    @Override
    protected void beforeRequest(HttpServletRequest httpServletRequest, String s) {
        //http请求标记 便于直接按请求打印日志
        aliyunLogManager.setTag("traceId", MDC.get(CommonConst.LOG_TRACE_ID));
        aliyunLogManager.setTag("url", httpServletRequest.getRequestURI());
    }

    @Override
    protected void afterRequest(HttpServletRequest httpServletRequest, String s) {
        //set send
        aliyunLogManager.send();
    }
}

写日志:log.info即可 

最终效果:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/668786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开启数字时代,分享电脑监控和录制工具

近年来&#xff0c;随着网络技术的快速发展和普及&#xff0c;电脑屏幕录制和监控越来越成为企业、学校、家庭等不可或缺的工具。无论是在线教学、远程工作&#xff0c;还是家长对孩子上网行为的关注&#xff0c;电脑屏幕录制和监控都具有极大的帮助和重要性。今天就给大家推荐…

【Visual Studio】使用 C++ 语言,配合 Qt,开发了一个串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 1. 获取串口名字1.1 文件 GUI.ui1.2 文件 GUI.h1.3 文件 GUI.cpp 2. 配置串口连接2.1 文件 GUI.ui2.2 文件 GUI.h2.3 文件 GUI.cpp 3. 配置串口连接…

chatgpt赋能python:Python排错大全:10年经验总结,快速定位并解决问题!

Python排错大全&#xff1a;10年经验总结&#xff0c;快速定位并解决问题&#xff01; 作为一名有着10年Python编程经验的工程师&#xff0c;在这篇文章中&#xff0c;我将详细介绍常见的Python排错技巧&#xff0c;以及我在实际工作中使用的一些技巧和最佳实践。我们将学习如…

《网络安全0-100》安全策略制定

安全策略制定 安全策略制定是指制定一系列的规范、标准和 流程&#xff0c;以保护企业或组织的信息资源和业务活 动&#xff0c;确保其安全性和可靠性。安全策略制定通 常包括以下几个步骤&#xff1a; 风险评估&#xff1a;对企业或组织的信息系统进行全面 评估&#xff…

Electron 和 Angular 项目升级

Electron 和 Angular 项目升级: Angular4Electron1.7.8 升级到 Angular13Electron2 原项目 Angular 和 Electron 版本: angular/cli: 1.4.9angular/core: 4.4.6Electron: 1.7.8 升级后 Angular 和 Electron 版本: Angular: 13.3.1Electron: 21.2.1 流程: angular-electro…

一次服务器被入侵的处理过程分享

一、服务器入侵现象 近期有一个朋友的服务器(自己做了网站)好像遭遇了入侵&#xff0c;具体现象是&#xff1a; 服务器 CPU 资源长期 100%&#xff0c;负载较高。 服务器上面的服务不能正常提供服务。 ​ 朋友处理了一会没有解决&#xff0c;我开始想说我不是搞安全的&#xf…

【Visual Studio】报错 LNK2019,使用 C++ 语言,配合 Qt 开发串口通信界面

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 问题解决方案Ref. 问题 使用 C 语言&#xff0c;配合 Qt 开发串口通信界面时&#xff0c;报错代码 LNK2019。 复制以下错误信息&#xff0c;方便别…

15、SQL注入之Oracel,MongoDB等注入

这里写目录标题 引言补充上篇文章Json注入案例分析 简要学习各种数据库的注入特点Access数据库Mssql数据库PostgreSQL数据库Oracle数据库MongoDB数据库 简要学习各种注入工具的使用指南 引言 mysql的注入方法跟其它的数据库注入方法是差不多的&#xff0c;是可以举一反三的&am…

【Pandas】pandas用法解析(下)

一、生成数据表 二、数据表信息查看 三、数据表清洗 四、数据预处理 ———————————————— 目录 五、数据提取 1.按索引提取单行的数值 2.按索引提取区域行数值 3.重设索引 4.设置日期为索引 5.提取4日之前的所有数据 6.使用iloc按位置区域提取数据 7…

elasticsearch8.5.2 报错(SearchPhaseExecutionException: all shards failed)

一、问题 logstash突然无法对elasticsearch服务进行读写操作了&#xff0c;提示elasticsearch的地址有问题&#xff0c;检测elasticsearch发现端口存在。查看日志发现有报错。 二、问题原因 有一些索引的数据损坏了 三、解决 官网文档&#xff1a;https://www.elastic.co/…

记录HBuilderX将uniapp项目运行到华为手机

解压并运行刚从官网下载的HBuilder X&#xff0c;新建一个项目 一、电脑下载【华为手机助手】并安装 下载地址&#xff1a; https://consumer.huawei.com/cn/support/hisuite/ 二、华为手机设置 1、手机准备&#xff1a;华为&#xff08;没有插入手机卡&#xff09;&#x…

《网络安全》0-100 零基础

网络安全基础 什么是网络安全 网络安全是指保护计算机网络不受未经授权的攻击、损伤、窃取或破坏的一系列措施。它包括保护计算机系统、网络和数据的完整性、可用性和保密性&#xff0c;以及防止未经授权的访问、使用、披露、破坏、修改、记录或丢失数据。 网络安全是保护信息…

探秘华为交换机:端口类型全解析

在下列情况下&#xff0c;判断的一般方法是什么&#xff1f; 1.交换机某个端口下的用户丢包。 2.交换机下的所有用户都在丢失数据包。 3、用户反映网速缓慢 检查网络电缆&#xff0c;重做水晶头&#xff0c;检查用户的计算机网卡&#xff0c;并重新启动交换机。 这几种做法都能…

在 PyTorch 中实现可解释的神经网络模型

动动发财的小手&#xff0c;点个赞吧&#xff01; 目的 深度学习系统缺乏可解释性对建立人类信任构成了重大挑战。这些模型的复杂性使人类几乎不可能理解其决策背后的根本原因。 ❝ 深度学习系统缺乏可解释性阻碍了人类的信任。 ❞ 为了解决这个问题&#xff0c;研究人员一直在…

chatgpt赋能python:Python中算法的几种描述方法

Python中算法的几种描述方法 在Python中&#xff0c;我们可以采用不同的方法来描述和实现不同的算法。本文将介绍三种常见的描述算法的方法&#xff0c;希望能够帮助读者更好地理解算法和Python编程。 方法一&#xff1a;自然语言描述 自然语言是我们最熟悉的方式来描述算法…

三层交换器与可配置的二层交换机通信配置(华为交换机)

#三层交换器与可配置的二层交换机通信配置 三层交换机配置 #进入系统视图 <Huawei>system-view #关闭系统提示信息 [Huawei]undo info-center enable #启动DHCP功能 [Huawei]dhcp enable #创建vlan 10 并配置 vlanif 地址 作为二层交换机默认网关 [Huawei]vlan 10 …

nodejs高版本降为低版本的详细解决方案

部分老旧项目需要使用低版本的node,网上很多是无效的,高版本无法直接安装低版本node,但是低版本nodejs可以安装部分高版本node,从而达到升级效果,下面这篇文章主要给大家介绍了关于nodejs高版本降为低版本的详细解决方案,需要的朋友可以参考下 1.首先通过控制面板应用卸载当前环…

如何使用 Swagger2 自动生成 RESTful API 文档

如何使用 Swagger2 自动生成 RESTful API 文档 在开发 RESTful API 的过程中&#xff0c;文档是非常重要的一部分。它可以帮助开发者了解 API 的功能和使用方法&#xff0c;同时也是接口设计和测试的重要依据。而手动编写 API 文档往往比较耗时且容易出错&#xff0c;这时候 S…

【kubernetes】部署controller-manager与kube-scheduler

前言:二进制部署kubernetes集群在企业应用中扮演着非常重要的角色。无论是集群升级,还是证书设置有效期都非常方便,也是从事云原生相关工作从入门到精通不得不迈过的坎。通过本系列文章,你将从虚拟机准备开始,到使用二进制方式从零到一搭建起安全稳定的高可用kubernetes集…

NodeJS 配置HTTPS协议证书⑩⑤

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言HTTPS ❓配置证书工具 CertbotCertbot 使用步骤总结 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&…