Skywalking Agent源码解读 - 从premain方法开始

文章详细介绍了SkyWalkingAgent的初始化过程,包括从MANIFEST.MF中获取入口,初始化配置,加载插件,构建类增强器以及启动服务管理。核心步骤涉及配置文件读取,插件定义加载,以及使用ByteBuddy进行字节码操作。服务管理模块通过SPI机制加载并启动多个BootService实现。

 

源代码解读的版本为8.7.0。

入口

从MANIFEST.MF文件中找到,

Premain-Class: org.apache.skywalking.apm.agent.SkyWalkingAgent

SkyWalkingAgent

premain方法

主要做了这些逻辑:

//SkyWalking agent initialized

SnifferConfigInitializer.initializeCoreConfig(agentArgs);

//SkyWalking agent initialized

pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());

//SkyWalking agent inject bootstrap instrumentation

agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);

//SkyWalking agent open read edge in JDK 9+

agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE));

// SkyWalking agent active class cache

if (Config.Agent.IS_CACHE_ENHANCED_CLASS) {

agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE));

}

//Skywalking agent boot

ServiceManager.INSTANCE.boot();

1. SnifferConfigInitializer.initializeCoreConfig(agentArgs);

Java
 public static void initializeCoreConfig(String agentOptions) {
 // AGENT_SETTINGS 为Properties类变量
    AGENT_SETTINGS = new Properties();
    // 加载默认配置:config/agent.config,解析读入至AGENT_SETTINGS变量中
    try (final InputStreamReader configFileStream = loadConfig()) {
        AGENT_SETTINGS.load(configFileStream);
        for (String key : AGENT_SETTINGS.stringPropertyNames()) {
            String value = (String) AGENT_SETTINGS.get(key);
            AGENT_SETTINGS.put(key, PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value, AGENT_SETTINGS));
        }

    } catch (Exception e) {
        LOGGER.error(e, "Failed to read the config file, skywalking is going to run in default config.");
    }
// 读取可能存在的system properties,覆盖默认配置
    try {
        overrideConfigBySystemProp();
    } catch (Exception e) {
        LOGGER.error(e, "Failed to read the system properties.");
    }
// 读取agentArgs可能传入的参数,覆盖默认配置
    agentOptions = StringUtil.trim(agentOptions, ',');
    if (!StringUtil.isEmpty(agentOptions)) {
        try {
            agentOptions = agentOptions.trim();
            LOGGER.info("Agent options is {}.", agentOptions);

            overrideConfigByAgentOptions(agentOptions);
        } catch (Exception e) {
            LOGGER.error(e, "Failed to parse the agent options, val is {}.", agentOptions);
        }
    }

    initializeConfig(Config.class);
    // reconfigure logger after config initialization
    configureLogger();
    LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);

    if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
        throw new ExceptionInInitializerError("`agent.service_name` is missing.");
    }
    if (StringUtil.isEmpty(Config.Collector.BACKEND_SERVICE)) {
        throw new ExceptionInInitializerError("`collector.backend_service` is missing.");
    }
    if (Config.Plugin.PEER_MAX_LENGTH <= 3) {
        LOGGER.warn(
            "PEER_MAX_LENGTH configuration:{} error, the default value of 200 will be used.",
            Config.Plugin.PEER_MAX_LENGTH
        );
        Config.Plugin.PEER_MAX_LENGTH = 200;
    }

    IS_INIT_COMPLETED = true;
}

SkyWalking Agent 是通过 Java Agent 技术在 JVM 启动时进行字节码增强,从而实现对 Spring 项目的监控。其核心原理可以分为以下几个方面: ### Agent 的入口方法 SkyWalking Agent 在 JVM 启动时通过 `-javaagent` 参数加载,其入口方法是 `SkyWalkingAgent#premain`。该方法会在应用程序启动前执行,并负责初始化 Agent 的核心组件,包括插件加载器、字节码增强引擎等。`premain` 方法通过反射调用 `Agent` 类的 `main` 方法,进一步启动 Agent 的运行时环境[^2]。 ### 插件加载机制 SkyWalking Agent 采用插件化架构,支持多种框架的监控能力。在初始化过程中,Agent 会加载 `agent` 目录下的插件模块。每个插件通常对应一个特定的框架,如 Spring、HttpClient、Netty 等。插件加载器会扫描插件目录并解析插件配置,将插件注册到 Agent 的运行时中。插件的加载和执行是通过字节码增强技术实现的,具体通过 `ByteBuddy` 或 `ASM` 等字节码操作库对目标类进行修改,插入监控逻辑[^2]。 ### 字节码增强技术 SkyWalking 使用字节码增强技术对目标类进行修改,从而实现对方法调用的拦截和监控。例如,在 Spring 应用中,Agent 会拦截 `DispatcherServlet` 的请求处理方法,记录请求的开始和结束时间,并生成调用链路信息。字节码增强的核心是通过修改类的字节码,在方法入口和出口插入监控代码,收集上下文信息(如 Trace ID、Span ID)并上报到 OAP 服务器[^3]。 ### Trace 上下文传播 为了实现分布式调用链追踪,SkyWalking Agent 会自动在请求上下文中注入 Trace ID 和 Span ID。这些信息会在请求链路中传递,确保跨服务调用的上下文一致性。例如,在 HTTP 请求中,Trace ID 会被添加到响应头中;在日志系统中,Trace ID 会被集成到日志输出中,便于日志与调用链的关联分析[^3]。 ### 与日志系统的集成 SkyWalking 支持与主流日志框架(如 Logback 和 Log4j2)集成,将 Trace ID 嵌入到日志输出中。这种集成是通过日志框架的 MDC(Mapped Diagnostic Contexts)机制实现的。在请求处理过程中,Trace ID 会被设置到 MDC 中,并在日志格式中引用该字段,从而使得每条日志都包含对应的调用链信息。这种机制有助于在日志分析时快速定位问题链路[^4]。 ### 与 Spring Boot 的集成 在 Spring Boot 应用中,SkyWalking Agent 会自动识别并增强 Spring 框架中的关键类,如 `DispatcherServlet`、`RequestMappingHandlerAdapter` 等。这些增强操作会捕获请求的上下文信息,并生成相应的调用链路数据。同时,SkyWalking 还支持对数据库访问、消息队列等组件的监控,进一步丰富了调用链的数据维度。 ### 示例代码:手动注入 Trace ID 到日志 虽然 SkyWalking 提供了自动化的 Trace 上下文传播机制,但在某些场景下可能需要手动处理 Trace ID 的注入。以下是一个简单的示例,展示如何将 Trace ID 注入到日志中: ```java import org.slf4j.MDC; import org.apache.skywalking.apm.toolkit.trace.TraceContext; // 在请求处理的入口处设置 Trace ID 到 MDC MDC.put("traceId", TraceContext.traceId()); ``` 在日志配置文件中,可以通过 `%X{traceId}` 的方式引用 MDC 中的 Trace ID: ```properties # Logback 配置示例 pattern: %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg [traceId=%X{traceId}]%n ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值