Spring Sleuth实现主动添加日志的方式

本文详细介绍了Spring Sleuth如何通过修改logging.pattern.level变量实现在日志中注入跟踪信息,包括Span ID和Trace ID等关键数据。在Spring Boot应用中,当启用Sleuth时,它会自动调整日志模式,以便在不额外配置的情况下,可以在日志输出中看到跟踪信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring Sleuth 是通过 logging.pattern.level 变量来实现主动日志添加的。

  • 添加 在 TraceEnvironmentPostProcessor 中
	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		Map<String, Object> map = new HashMap<String, Object>();
		// This doesn't work with all logging systems but it's a useful default so you see
		// traces in logs without having to configure it.
		// 在这里判断开启 sleuth 则修改 logging.pattern.level 的值
		if (Boolean.parseBoolean(environment.getProperty("spring.sleuth.enabled", "true"))) {
			map.put("logging.pattern.level",
					"%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]");
		}
		// TODO: Remove this in 2.0.x. For compatibility we always set to true
		if (!environment.containsProperty(SPRING_AOP_PROXY_TARGET_CLASS)) {
			map.put(SPRING_AOP_PROXY_TARGET_CLASS, "true");
		}
		addOrReplace(environment.getPropertySources(), map);
	}

在 LoggingSystemProperties 将 LOG_LEVEL_PATTERN 的值设置为 logging.pattern.level 的值

	public void apply(LogFile logFile) {
		RelaxedPropertyResolver propertyResolver = RelaxedPropertyResolver
				.ignoringUnresolvableNestedPlaceholders(this.environment, "logging.");
		setSystemProperty(propertyResolver, EXCEPTION_CONVERSION_WORD,
				"exception-conversion-word");
		setSystemProperty(PID_KEY, new ApplicationPid().toString());
		setSystemProperty(propertyResolver, CONSOLE_LOG_PATTERN, "pattern.console");
		setSystemProperty(propertyResolver, FILE_LOG_PATTERN, "pattern.file");
		// LOG_LEVEL_PATTERN = "LOG_LEVEL_PATTERN" 设置到环境变量
		setSystemProperty(propertyResolver, LOG_LEVEL_PATTERN, "pattern.level");
		if (logFile != null) {
			logFile.applyToSystemProperties();
		}
	}

设置

在 PropertyAction#begin 解析配置 在 InterpretationContext 解析当前的字符串

    public String subst(String value) {
        if (value == null) {
            return null;
        }
        return OptionHelper.substVars(value, this, context);
    }
    public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) {
        try {
            return NodeToStringTransformer.substituteVariable(input, pc0, pc1);
        } catch (ScanException e) {
            throw new IllegalArgumentException("Failed to parse input [" + input + "]", e);
        }
    }
    public static String substituteVariable(String input, PropertyContainer pc0, PropertyContainer pc1) throws ScanException {
        Node node = tokenizeAndParseString(input);
        NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, pc0, pc1);
        return nodeToStringTransformer.transform();
    }
    public String transform() throws ScanException {
        StringBuilder stringBuilder = new StringBuilder();
        compileNode(node, stringBuilder, new Stack<Node>());
        return stringBuilder.toString();
    }
    private void compileNode(Node inputNode, StringBuilder stringBuilder, Stack<Node> cycleCheckStack) throws ScanException {
        Node n = inputNode;
        while (n != null) {
            switch (n.type) {
            case LITERAL:
                handleLiteral(n, stringBuilder);
                break;
            case VARIABLE:
                handleVariable(n, stringBuilder, cycleCheckStack);
                break;
            }
            n = n.next;
        }
    }
	private void handleVariable(Node n, StringBuilder stringBuilder, Stack<Node> cycleCheckStack) throws ScanException {
		// ...
		String key = keyBuffer.toString();
        String value = lookupKey(key);
		// ...
	}
	private String lookupKey(String key) {
        String value = propertyContainer0.getProperty(key);
        if (value != null)
            return value;

        if (propertyContainer1 != null) {
            value = propertyContainer1.getProperty(key);
            if (value != null)
                return value;
        }
		// 在这里找到值
        value = OptionHelper.getSystemProperty(key, null);
        if (value != null)
            return value;

        value = OptionHelper.getEnv(key);
        if (value != null) {
            return value;
        }

        return null;
    }
    public static String getSystemProperty(String key, String def) {
        try {
            return System.getProperty(key, def);
        } catch (SecurityException e) {
            return def;
        }
    }

转载于:https://my.oschina.net/seal90/blog/3086404

<think>我们正在设计一个基于云平台的微服务架构,使用Docker/Kubernetes进行容器化部署。架构将包含以下组件: 1. API网关:作为所有客户端请求的入口,负责路由、认证、限流等。 2. Spring Cloud微服务:多个微服务,每个服务负责特定的业务功能,使用Spring Cloud框架实现。 3. 混合数据库:根据不同的数据需求使用不同的数据库,包括MySQL(关系型数据)、Redis(缓存)、MongoDB(文档存储)。 4. ESB FHIR集成层:用于医疗健康数据交换,遵循FHIR标准(Fast Healthcare Interoperability Resources),通过企业服务总线(ESB)进行集成。 由于我们无法直接在此绘制图形,我将用文字描述架构图的结构,并建议使用绘图工具(如draw.io、Visio或PlantUML)来创建。 ### 架构图描述 #### 1. 客户端层 - 用户通过Web、移动端或第三方系统访问应用。 #### 2. API网关 - 所有外部请求首先到达API网关(如Spring Cloud Gateway或Zuul)。 - 网关负责请求路由、身份验证、SSL终止、限流等。 #### 3. 微服务层 - 多个独立的微服务,每个服务运行在独立的容器中(Docker),由Kubernetes管理。 - 示例微服务: - 用户服务(User Service):管理用户信息,使用MySQL数据库。 - 订单服务(Order Service):处理订单,使用MySQL。 - 产品服务(Product Service):管理产品目录,使用MongoDB(因为产品属性灵活)。 - 缓存服务:使用Redis缓存热点数据,提高性能。 - 微服务之间通过RESTful API或消息队列(如Kafka)进行通信。 #### 4. 数据库层 - 每个微服务拥有自己的数据库(数据库隔离原则)。 - 用户服务和订单服务使用MySQL(关系型数据)。 - 产品服务使用MongoDB(文档存储)。 - Redis作为缓存层,被多个微服务共享(例如,用户信息缓存、产品缓存)。 #### 5. ESB FHIR集成层 - 为了与医疗健康系统集成,我们设计了一个ESB(企业服务总线)层,专门处理FHIR标准的医疗数据。 - ESB层提供标准化接口(FHIR RESTful API),将内部微服务的数据转换为FHIR资源。 - 外部医疗系统通过ESB层与我们的系统进行数据交换。 #### 6. 基础设施 - 整个系统部署在Kubernetes集群上,运行在云平台(如AWS、Azure或阿里云)。 - 使用Kubernetes进行服务发现、负载均衡、自动扩缩容和滚动更新。 ### 架构图代码(PlantUML示例) 如果你使用PlantUML,可以尝试以下代码来生成架构图: ```plantuml @startuml !theme plain top to bottom direction cloud "云平台(如AWS)" as cloud { node "Kubernetes集群" as k8s { rectangle "API网关" as gateway package "微服务" { rectangle "用户服务" as user_service rectangle "订单服务" as order_service rectangle "产品服务" as product_service } package "数据库" { database "MySQL" as mysql database "MongoDB" as mongodb database "Redis" as redis } rectangle "ESB FHIR集成层" as esb } } [客户端] --> gateway gateway --> user_service gateway --> order_service gateway --> product_service user_service --> mysql order_service --> mysql product_service --> mongodb user_service --> redis order_service --> redis product_service --> redis esb --> user_service esb --> order_service esb --> product_service @enduml ``` ### 说明 - 客户端请求首先到达API网关。 - 网关将请求路由到相应的微服务。 - 每个微服务连接自己的数据库,同时共享Redis缓存。 - ESB FHIR集成层与各个微服务交互,实现医疗数据的标准化转换和集成。 ### 注意事项 1. **安全性**:在API网关处实施OAuth2/JWT认证。 2. **可观察性**:集成Spring Cloud Sleuth和Zipkin进行分布式追踪,使用Prometheus和Grafana监控。 3. **弹性**:使用Hystrix或Resilience4j实现熔断机制。 4. **配置中心**:使用Spring Cloud Config Server管理配置。 ### 相关问题 1. 如何设计微服务之间的通信机制? 2. 在混合数据库架构下,如何保证数据一致性? 3. 如何实现Kubernetes中的微服务部署和自动扩缩容? 4. ESB与API网关在架构中的角色有何不同? 5. 如何确保FHIR集成的安全性和合规性? 希望这个设计对您有帮助。您可以根据需要调整架构图中的组件。</think>### 基于云平台的微服务架构设计(Docker/Kubernetes部署) 以下是完整的微服务架构设计,包含API网关、Spring Cloud微服务、混合数据库和ESB FHIR集成层: ```mermaid graph TD subgraph 客户端层 A[Web前端] --> B[移动端] C[第三方系统] --> B end subgraph 云平台 subgraph Kubernetes集群 D[API网关] --> E[服务注册中心] subgraph Spring Cloud微服务 F[用户服务] --> G[MySQL] H[订单服务] --> I[Redis] J[产品服务] --> K[MongoDB] L[支付服务] --> I M[库存服务] --> K end subgraph ESB FHIR集成层 N[FHIR转换器] --> O[ESB总线] P[HL7适配器] --> O Q[DICOM适配器] --> O end E --> F E --> H E --> J E --> L E --> M O --> F O --> H O --> J end subgraph 云存储 R[对象存储 S3/OSS] end end B --> D A --> D C --> N M --> R J --> R ``` #### 架构组件说明 1. **API网关** - 使用Spring Cloud Gateway或Zuul实现 - 功能:路由分发、身份认证、限流熔断、请求日志 - 示例配置: ```yaml spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** ``` 2. **Spring Cloud微服务** - 服务注册中心:Eureka或Consul - 关键微服务: - 用户服务:MySQL存储核心数据(ACID事务) - 订单服务:Redis处理高并发订单(读写10万+/秒) - 产品服务:MongoDB存储非结构化数据(JSON文档) - 容器化部署: ```Dockerfile FROM openjdk:17 COPY target/*.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"] ``` 3. **混合数据库层** | 数据库 | 使用场景 | 数据同步机制 | |----------|-------------------------|--------------------------| | MySQL | 用户信息/交易核心数据 | CDC日志同步到数据仓库 | | Redis | 购物车/会话缓存 | 主动过期策略+持久化 | | MongoDB | 产品目录/日志数据 | Change Streams实时流 | 4. **ESB FHIR集成层** - 功能组件: - FHIR转换器:将内部数据转为HL7 FHIR标准格式 - ESB总线:Apache Camel或MuleSoft实现消息路由 - 医疗协议适配器:支持HL7/DICOM等医疗标准 - 数据流转示例: ```mermaid sequenceDiagram 医院系统->>+ESB: HL7消息 ESB->>FHIR转换器: 格式转换 FHIR转换器->>用户服务: 标准化数据 用户服务-->>ESB: 处理结果 ESB-->>医院系统: FHIR响应 ``` 5. **Kubernetes部署架构** ```mermaid graph LR Master[控制平面] --> Node1[工作节点] Master --> Node2[工作节点] subgraph Node1 Pod1[网关Pod] --> Service1[网关Service] Pod2[用户服务Pod] end subgraph Node2 Pod3[订单服务Pod] --> Redis[Redis StatefulSet] Pod4[FHIR集成Pod] end Service1 --> Pod2 Service1 --> Pod3 ``` #### 关键优势 1. **弹性扩展**:Kubernetes HPA根据CPU/内存自动扩缩容 2. **数据隔离**:每个微服务独立数据库,通过API交互[^1] 3. **医疗合规**:FHIR集成满足HIPAA等医疗数据规范 4. **混合存储**:根据CAP定理选择合适数据库[^2]: - MySQL:强一致性(CP) - Redis:高性能缓存(AP) - MongoDB:灵活模式(AP) #### 部署流程 1. 容器化:`docker build -t service-name .` 2. 推送镜像:`docker push registry/service-name` 3. Kubernetes部署: ```bash kubectl apply -f deployment.yaml kubectl expose deployment user-service --port=8080 ``` > **注意**:生产环境需添加服务网格(如Istio)实现细粒度流量控制和mTLS加密 --- ### 相关问题 1. 如何设计微服务数据库之间的数据一致性解决方案? 2. Kubernetes中如何实现跨微服务的全链路监控? 3. FHIR标准在医疗系统集成中的具体实现要点有哪些? 4. 混合数据库架构下如何优化跨数据库查询性能? 5. 云原生环境中微服务安全防护的最佳实践是什么? [^1]: 引用微服务架构定义:Martin Fowler. "Microservices: a definition of this new architectural term" [^2]: 引用数据库分类标准:NoSQL Distilled. Pramod J. Sadalage, Martin Fowler
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值