7. 链路日志打印实现设计

news2024/11/20 20:34:56

前言

在前面的文章中,我们已经实现了一个Starter包,能够在使用RestTemplate作为客户端请求工具时,记录调用链路信息。在本文,将实现Jaeger框架下的链路日志打印,也就是提供一个io.jaegertracing.spi.Reporter来将Span的信息打印出来。

相关版本依赖如下。

opentracing-api版本:0.33.0
opentracing-spring-web版本:4.1.0
jaeger-client版本:1.8.1
Springboot版本:2.7.6

github地址:honey-tracing

正文

一. 链路日志格式回顾

在3. 分布式链路追踪的链路日志设计一文中,定义了要打印的链路日志格式,如下所示。

{
    "traceId": "testTraceId",                
    "spanId": "testSpanId",                  
    "parentSpanId": "testparentSpanId",      
    "timestamp": "1704038400000",            
    "duration": "10",                        
    "httpCode": "200",                       
    "host": "127.0.0.1",                     
    "requestStacks": [                       
        {
            "subSpanId": "testSubSpanId",    
            "subHttpCode": "200",            
            "subTimestamp": "1704038401000", 
            "subDuration": "5",              
            "subHost": "192.168.10.5"        
        }
    ]
}

上述信息中,除了requestStacks字段以外的字段,都是当前节点的Span中记录的信息,而requestStacks则是当前节点请求下游节点,代表下游节点的Span中记录的信息。

二. 链路日志实体对象设计

首先,我们定义HoneySpanReportEntity来作为链路日志对应的实体对象,如下所示。

public class HoneySpanReportEntity {

    public static final Integer SCALE = 0;

    private String traceId;
    private String spanId;
    private String parentSpanId;
    private String timestamp;
    private String duration;
    private String httpCode;
    private String host;
    private List<HoneyRequestStack> requestStacks = new ArrayList<>();

    private HoneySpanReportEntity() {

    }

    public String toPrintString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (Exception e) {
            return StringUtils.EMPTY;
        }
    }

    public void addRequestStack(HoneyRequestStack honeyRequestStack) {
        requestStacks.add(honeyRequestStack);
    }

    

    public static class HoneySpanReportEntityBuilder {
        private JaegerSpan span;

        private HoneySpanReportEntityBuilder() {

        }

        public static HoneySpanReportEntityBuilder builder() {
            return new HoneySpanReportEntityBuilder();
        }

        public HoneySpanReportEntityBuilder withSpan(JaegerSpan span) {
            this.span = span;
            return this;
        }

        public HoneySpanReportEntity build() {
            if (span == null) {
                throw new HoneyTracingException();
            }

            HoneySpanReportEntity honeySpanReportEntity = new HoneySpanReportEntity();

            honeySpanReportEntity.traceId = span.context().getTraceId();
            honeySpanReportEntity.spanId = span.context().toSpanId();
            honeySpanReportEntity.parentSpanId = Utils.to16HexString(span.context().getParentId());
            honeySpanReportEntity.timestamp = new BigDecimal(span.getStart())
                    .divide(BigDecimal.valueOf(1000), SCALE , RoundingMode.DOWN).toString();
            honeySpanReportEntity.duration = new BigDecimal(span.getDuration())
                    .divide(BigDecimal.valueOf(1000), SCALE , RoundingMode.DOWN).toString();

            Map<String, Object> spanTags = span.getTags();
            honeySpanReportEntity.httpCode = String.valueOf(spanTags.get(FIELD_HTTP_CODE));
            honeySpanReportEntity.host = (String) spanTags.get(FIELD_HOST);

            List<LogData> spanLogs = span.getLogs();
            if (span.getLogs() != null) {
                spanLogs.forEach(handleLogData(honeySpanReportEntity));
            }

            return honeySpanReportEntity;
        }

        private Consumer<LogData> handleLogData(HoneySpanReportEntity honeySpanReportEntity) {
            return new Consumer<LogData>() {
                @Override
                public void accept(LogData logData) {
                    if (LOG_EVENT_KIND_REQUEST_STACK.equals(logData.getFields().get(LOG_EVENT_KIND))) {
                        HoneyRequestStack honeyRequestStack = HoneyRequestStack.HoneyRequestStackBuilder
                                .builder()
                                .withLogData(logData)
                                .build();
                        honeySpanReportEntity.addRequestStack(honeyRequestStack);
                    }
                }
            };
        }
    }

}

HoneySpanReportEntity是通过建造者HoneySpanReportEntityBuilder进行构建,当前节点的信息主要从SpanSpanContextTags中获取,下游节点的信息主要从SpanLogs中获取。

然后我们定义了HoneyRequestStack来作为链路日志中的requestStacks字段对应的实体对象,实现如下。

public class HoneyRequestStack {

    private String subSpanId;
    private String subHttpCode;
    private String subTimestamp;
    private String subDuration;
    private String subHost;

    private HoneyRequestStack() {

    }

    

    public static class HoneyRequestStackBuilder {
        private LogData logData;

        private HoneyRequestStackBuilder() {

        }

        public static HoneyRequestStackBuilder builder() {
            return new HoneyRequestStackBuilder();
        }

        public HoneyRequestStackBuilder withLogData(LogData logData) {
            this.logData = logData;
            return this;
        }

        public HoneyRequestStack build() {
            if (logData == null || logData.getFields() == null) {
                throw new HoneyTracingException();
            }
            Map<String, ?> logDataFields = logData.getFields();
            HoneyRequestStack honeyRequestStack = new HoneyRequestStack();
            honeyRequestStack.subSpanId = (String) logDataFields.get(FIELD_SUB_SPAN_ID);
            honeyRequestStack.subHttpCode = String.valueOf(logDataFields.get(FIELD_SUB_HTTP_CODE));
            honeyRequestStack.subTimestamp = new BigDecimal(String.valueOf(logDataFields.get(FIELD_SUB_TIMESTAMP)))
                    .divide(BigDecimal.valueOf(1000), SCALE , RoundingMode.DOWN).toString();
            honeyRequestStack.subDuration = new BigDecimal(String.valueOf(logDataFields.get(FIELD_SUB_DURATION)))
                    .divide(BigDecimal.valueOf(1000), SCALE , RoundingMode.DOWN).toString();
            honeyRequestStack.subHost = (String) logDataFields.get(FIELD_SUB_HOST);
            return honeyRequestStack;
        }
    }

}

同样也是基于建造者来构建,并且每有一个包含键为logEventKind,值为requestStack的键值对,就会创建一个HoneyRequestStack出来。

三. Reporter实现设计

TracingFilterdoFilter() 方法的最后,会调用Spanfinish() 方法,该方法最终会调用到注册到Tracer中的Reporter对象的report() 方法中来,所以本节给出Reporter的实现,如下所示。

public class HoneySpanReporter implements Reporter {

    public void report(JaegerSpan span) {
        if (Tags.SPAN_KIND_CLIENT.equals(span.getTags().get(Tags.SPAN_KIND.getKey()))) {
            return;
        }

        System.out.println(HoneySpanReportEntity.HoneySpanReportEntityBuilder
                .builder()
                .withSpan(span)
                .build()
                .toPrintString());
    }

    public void close() {

    }

}

有一点需要注意,我们只有当Spanspan.kindserver时,才会打印链路日志,这样就能确保在一次链路请求中,一个节点,只会打印一条链路日志。

最后给出本文中新使用到的异常对象的实现,如下所示。

public class HoneyTracingException extends RuntimeException {

    public HoneyTracingException() {

    }

    public HoneyTracingException(String message) {
        super(message);
    }

    public HoneyTracingException(String message, Throwable cause) {
        super(message, cause);
    }

    public HoneyTracingException(Throwable cause) {
        super(cause);
    }

    public HoneyTracingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

}

四. 测试链路日志打印

我们使用4. 分布式链路追踪客户端工具包Starter设计中搭建好的测试demo,来验证我们的链路日志打印。

example-service-1打印链路日志如下。

{
    "traceId": "c3daffb4096da5907efc352889a8d14c",
    "spanId": "7efc352889a8d14c",
    "parentSpanId": "0000000000000000",
    "timestamp": "1707133953950",
    "duration": "292",
    "httpCode": "200",
    "host": "http://localhost:8080",
    "requestStacks": [
        {
            "subSpanId": "3a65454f65988c9a",
            "subHttpCode": "200",
            "subTimestamp": "1707133954015",
            "subDuration": "206",
            "subHost": "localhost:8081"
        }
    ]
}

example-service-2打印链路日志如下。

{
    "traceId": "c3daffb4096da5907efc352889a8d14c",
    "spanId": "3a65454f65988c9a",
    "parentSpanId": "7efc352889a8d14c",
    "timestamp": "1707133954093",
    "duration": "27",
    "httpCode": "200",
    "host": "http://localhost:8081",
    "requestStacks": []
}

可见链路日志是成功打印的。

总结

本文回顾了链路日志的打印格式,并定义了其对应的实体对象,最后实现了打印链路日志的ReporterStarter包工程目录结构如下所示。

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

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

相关文章

idea运行SpringBoot项目爆红提示出现:Java HotSpot(TM) 64-Bit Server VM warning...让我来看看~

在运行SpringBoot项目的时候&#xff0c;发现总有这个警告提示出现&#xff0c;有点强迫症真的每次运行项目都很难受啊&#xff01;那么今天便来解决这个问题&#xff01; 先来看一下提示内容&#xff1a;Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none an…

kaldi学习参考

HMM模型 https://www.cnblogs.com/baixf-xyz/p/16777438.htmlhttps://www.cnblogs.com/baixf-xyz/p/16777438.htmlGMM-HMM 基于GMM-HMM的语音识别系统https://www.cnblogs.com/baixf-xyz/p/16777439.html https://www.cnblogs.com/baixf-xyz/p/16777426.htmlhttps://www.cnbl…

Tomcat中服务启动失败,如何查看启动失败日志?

1. 查看 localhost.log 这个日志文件通常包含有关特定 web 应用的详细错误信息。运行以下命令查看 localhost.log 中的错误&#xff1a; sudo tail -n 100 /opt/tomcat/latest/logs/localhost.YYYY-MM-DD.log请替换 YYYY-MM-DD 为当前日期&#xff0c;或选择最近的日志文件日…

官宣:vAsterNOS正式发布!开放网络操作系统免费试用!

近期&#xff0c;vAsterNOS&#xff08;设备模拟器&#xff09;正式发布&#xff0c;可以满足用户快速了解 AsterNOS、体验实际操作、搭建模拟网络的需求&#xff0c;可运行在GNS3、EVE-NG等网络虚拟软件中。 AsterNOS 网络操作系统是星融元为人工智能、机器学习、高性能计算、…

AI换脸原理(3)——人脸对齐介绍

人脸对齐简介 人脸对齐其实包含两个步骤:人脸关键点检测、人脸对齐,英文术语有facial landmark和face alignment,主要用于精确标识眉毛、眼睛、鼻子、嘴巴以及人脸轮廓等特征部位。不同数据集对于关键点的数量有不同的设定,最少的是标记5个关键点,通常包括两只眼睛的瞳孔…

springboot-aop-学习笔记

什么是AOP&#xff1f; AOP英文全称&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实说白了&#xff0c;就是 需要 某个通用的方法时&#xff0c;可以创建一个模板&#xff0c;模板里面就有这些通用的方法&#xf…

mysql 其他类型转换为BIT

看官网说明,BIT没什么特殊之处。但实际操作却不能将任何其他类型字段转为BIT,下面两个都报语法错误 CAST(column AS BIT(1)) AS aa , CAST(column AS BIT) AS bb, BIT value则模式是VARBINARY b1 as cc, -- cc为VARBINARY类型 下面是《高性能MySQL(第四版)》中关于BIT类型的…

JDK不同版本里中国夏令时时间

什么是夏令时&#xff1f; 夏令时&#xff0c;&#xff08;Daylight Saving Time&#xff1a;DST&#xff09;&#xff0c;也叫夏时制&#xff0c;又称“日光节约时制”和“夏令时间”&#xff0c;是一种为节约能源而人为规定地方时间的制度&#xff0c;在这一制度实行期间所采…

物业收费管理小程序源码搭建/部署/上线/运营/售后/更新

一款基于FastAdminUniApp开发的一款物业收费管理小程序。包含房产管理、收费标准、家属管理、抄表管理、在线缴费、业主公告、统计报表、业主投票、可视化大屏等功能。为物业量身打造的小区收费管理系统&#xff0c;贴合物业工作场景&#xff0c;轻松提高物业费用收缴率&#x…

代码随想录算法训练营第六十天| LeetCode647. 回文子串 、516.最长回文子序列

一、LeetCode647. 回文子串 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0647.%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.html 状态&#xff1a;已解决 1.思路 这道题我只想出来了暴力解法&#xff0c;动规解法并没有想出来。根据视频讲解才把它想出来。…

MT8370_联发科MTK8370(Genio 510)芯片性能规格参数

MT8370芯片是一款利用超高效的6nm制程工艺打造的边缘AI平台&#xff0c;具有强大的性能和功能。这款芯片集成了六核CPU(2x2.2 GHz Arm Cortex-A78 & 4x2.0 GHz Arm Cortex-A55)、Arm Mali-G57 MC2 GPU、集成的APU(AI处理器)和DSP&#xff0c;以及一个HEVC编码加速引擎&…

zip file is empty

从下找到报错的jar包。展开这个jar包&#xff0c;看下是否正常&#xff0c;正常的是能够展开看到一些文件夹以及里面的类&#xff0c;如下&#xff1a;如果不正常&#xff0c;就删除这个jar包&#xff0c;同时找到这个jar包在本地maven仓库的地址&#xff0c;也删除掉&#xff…

鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄

句柄 | handle int open(const char* pathname,int flags); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd);只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大…

ssrf漏洞学习——基础知识

一、SSRF是什么&#xff1f; SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。 一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统。&#xff08;正是因为它是由服务端发起的&#xff0c;所以它能…

天府锋巢直播产业基地构建成都电商直播高地

天府锋巢直播产业基地自成立以来&#xff0c;一直秉承着创新、协同、共赢的发展理念&#xff0c;吸引了众多直播企业纷纷入驻。随着直播产业的迅猛发展&#xff0c;改成都直播基地内的配套服务也显得尤为重要。本文将深入探讨入驻天府锋巢直播产业基地后&#xff0c;配套的直播…

Agent AI智能体:我们的生活即将如何改变?

你有没有想过&#xff0c;那个帮你设置闹钟、提醒你朋友的生日&#xff0c;甚至帮你订外卖的智能助手&#xff0c;其实就是Agent AI智能体&#xff1f;它们已经在我们生活中扮演了越来越重要的角色。现在&#xff0c;让我们一起想象一下&#xff0c;随着这些AI智能体变得越来越…

20240510每日后端---聊聊文件预览,doc,image,ppt转PDF预览

一、引入依赖 <dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>15.8</version></dependency><dependency><groupId>com.aspose</groupId><artifactId>crac…

从头开始的建材类电商小程序开发指南

在当今数字化时代&#xff0c;小程序已经成为了许多企业推广和销售的重要渠道。对于建筑材料行业来说&#xff0c;开发一个属于自己的小程序商城不仅可以提升产品曝光度&#xff0c;还可以提供更好的用户购物体验。下面&#xff0c;我们将逐步教你如何开发建筑材料行业小程序。…

佛山市举办2024年护士节庆祝活动

“作为一名在护理岗位上工作了39年的护士,能够在退休前参加这样温情的护士节活动,获得一个纪念胸章和一份荣誉证书,让我很感动!”第七届“南粤好护士”、佛山市第一人民医院急诊科护士长罗银秋说道。5月10日下午,由佛山市卫生健康局主办、佛山市护理学会协办、佛山市第一人民医…

CSS-页面导航栏实现-每文一言(过有意义的生活,做最好的自己)

&#x1f390;每文一言 过有意义的生活,做最好的自己 目录 &#x1f390;每文一言 &#x1f6d2;盒子模型 &#x1f453;外间距 (margin) &#x1f97c;边框 &#x1f45c;内边距 切换盒子模型计算方案&#xff1a; &#x1f3a2; 浮动布局 浮动特点 &#x1f3c6;导航…