Repository: megaease/easeagent
Branch: master
Commit: ec7bd5f578ef
Files: 1354
Total size: 4.6 MB
Directory structure:
gitextract_4u5zqd_x/
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── build.yml
│ └── license-checker.yml
├── .gitignore
├── .licenserc.yaml
├── AOSP-Checkstyles.xml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── build/
│ ├── pom.xml
│ └── src/
│ ├── assembly/
│ │ └── src.xml
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── StartBootstrap.java
│ └── resources/
│ ├── agent-to-cloud_1.0.properties
│ ├── agent.properties
│ ├── easeagent-log4j2.xml
│ └── user-minimal-cfg.properties
├── config/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── config/
│ │ ├── AutoRefreshConfigItem.java
│ │ ├── CompatibilityConversion.java
│ │ ├── ConfigAware.java
│ │ ├── ConfigFactory.java
│ │ ├── ConfigLoader.java
│ │ ├── ConfigManagerMXBean.java
│ │ ├── ConfigNotifier.java
│ │ ├── ConfigPropertiesUtils.java
│ │ ├── ConfigUtils.java
│ │ ├── Configs.java
│ │ ├── GlobalConfigs.java
│ │ ├── JarFileConfigLoader.java
│ │ ├── OtelSdkConfigs.java
│ │ ├── PluginConfig.java
│ │ ├── PluginConfigManager.java
│ │ ├── PluginProperty.java
│ │ ├── PluginSourceConfig.java
│ │ ├── ValidateUtils.java
│ │ ├── WrappedConfigManager.java
│ │ ├── report/
│ │ │ ├── ReportConfigAdapter.java
│ │ │ └── ReportConfigConst.java
│ │ └── yaml/
│ │ └── YamlReader.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── config/
│ │ ├── CompatibilityConversionTest.java
│ │ ├── ConfigFactoryTest.java
│ │ ├── ConfigPropertiesUtilsTest.java
│ │ ├── ConfigUtilsTest.java
│ │ ├── ConfigsTest.java
│ │ ├── IPluginConfigConstTest.java
│ │ ├── JarFileConfigLoaderTest.java
│ │ ├── OtelSdkConfigsTest.java
│ │ ├── PluginConfigManagerTest.java
│ │ ├── PluginConfigTest.java
│ │ ├── PluginPropertyTest.java
│ │ ├── PluginSourceConfigTest.java
│ │ ├── ValidateUtilsTest.java
│ │ ├── report/
│ │ │ └── ReportConfigAdapterTest.java
│ │ └── yaml/
│ │ └── YamlReaderTest.java
│ └── resources/
│ ├── agent.properties
│ ├── agent.yaml
│ ├── easeagent_config.jar
│ ├── user-spec.properties
│ ├── user-spec2.properties
│ └── user.properties
├── context/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── context/
│ │ ├── AsyncContextImpl.java
│ │ ├── ContextManager.java
│ │ ├── GlobalContext.java
│ │ ├── ProgressFieldsManager.java
│ │ ├── RetBound.java
│ │ ├── SessionContext.java
│ │ └── log/
│ │ ├── LoggerFactoryImpl.java
│ │ ├── LoggerImpl.java
│ │ └── LoggerMdc.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── context/
│ │ ├── AsyncContextImplTest.java
│ │ ├── ContextManagerTest.java
│ │ ├── GlobalContextTest.java
│ │ ├── ProgressFieldsManagerTest.java
│ │ ├── RetBoundTest.java
│ │ ├── SessionContextTest.java
│ │ └── log/
│ │ ├── LoggerFactoryImplTest.java
│ │ ├── LoggerImplTest.java
│ │ └── LoggerMdcTest.java
│ └── resources/
│ └── log4j2.xml
├── core/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── core/
│ │ │ ├── AppendBootstrapClassLoaderSearch.java
│ │ │ ├── Bootstrap.java
│ │ │ ├── GlobalAgentHolder.java
│ │ │ ├── config/
│ │ │ │ ├── CanaryListUpdateAgentHttpHandler.java
│ │ │ │ ├── CanaryUpdateAgentHttpHandler.java
│ │ │ │ ├── ConfigsUpdateAgentHttpHandler.java
│ │ │ │ ├── PluginPropertiesHttpHandler.java
│ │ │ │ ├── PluginPropertyHttpHandler.java
│ │ │ │ └── ServiceUpdateAgentHttpHandler.java
│ │ │ ├── health/
│ │ │ │ └── HealthProvider.java
│ │ │ ├── info/
│ │ │ │ ├── AgentInfoFactory.java
│ │ │ │ └── AgentInfoProvider.java
│ │ │ ├── plugin/
│ │ │ │ ├── BaseLoader.java
│ │ │ │ ├── BridgeDispatcher.java
│ │ │ │ ├── CommonInlineAdvice.java
│ │ │ │ ├── Dispatcher.java
│ │ │ │ ├── PluginLoader.java
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── EaseAgentInstrumented.java
│ │ │ │ │ └── Index.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── InterceptorPluginDecorator.java
│ │ │ │ │ ├── ProviderChain.java
│ │ │ │ │ └── ProviderPluginDecorator.java
│ │ │ │ ├── matcher/
│ │ │ │ │ ├── ClassLoaderMatcherConvert.java
│ │ │ │ │ ├── ClassMatcherConvert.java
│ │ │ │ │ ├── ClassTransformation.java
│ │ │ │ │ ├── Converter.java
│ │ │ │ │ ├── MethodMatcherConvert.java
│ │ │ │ │ └── MethodTransformation.java
│ │ │ │ ├── registry/
│ │ │ │ │ ├── AdviceRegistry.java
│ │ │ │ │ └── PluginRegistry.java
│ │ │ │ └── transformer/
│ │ │ │ ├── AnnotationTransformer.java
│ │ │ │ ├── CompoundPluginTransformer.java
│ │ │ │ ├── DynamicFieldAdvice.java
│ │ │ │ ├── DynamicFieldTransformer.java
│ │ │ │ ├── ForAdviceTransformer.java
│ │ │ │ ├── TypeFieldTransformer.java
│ │ │ │ ├── advice/
│ │ │ │ │ ├── AgentAdvice.java
│ │ │ │ │ ├── AgentForAdvice.java
│ │ │ │ │ ├── AgentJavaConstantValue.java
│ │ │ │ │ ├── BypassMethodVisitor.java
│ │ │ │ │ └── MethodIdentityJavaConstant.java
│ │ │ │ └── classloader/
│ │ │ │ └── CompoundClassloader.java
│ │ │ └── utils/
│ │ │ ├── AgentArray.java
│ │ │ ├── ContextUtils.java
│ │ │ ├── JsonUtil.java
│ │ │ ├── MutableObject.java
│ │ │ ├── ServletUtils.java
│ │ │ └── TextUtils.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── services/
│ │ │ └── com.megaease.easeagent.plugin.bean.BeanProvider
│ │ └── version.txt
│ └── test/
│ └── java/
│ └── com/
│ └── megaease/
│ └── easeagent/
│ └── core/
│ ├── AppendBootstrapClassLoaderSearchTest.java
│ ├── BootstrapTest.java
│ ├── HttpServerTest.java
│ ├── info/
│ │ └── AgentInfoFactoryTest.java
│ ├── instrument/
│ │ ├── ClinitMethodTransformTest.java
│ │ ├── NewInstanceMethodTransformTest.java
│ │ ├── NonStaticMethodTransformTest.java
│ │ ├── OrchestrationTransformTest.java
│ │ ├── StaticMethodTransformTest.java
│ │ ├── TestContext.java
│ │ ├── TestPlugin.java
│ │ └── TransformTestBase.java
│ ├── matcher/
│ │ ├── ClassLoaderMatcherTest.java
│ │ ├── ClassMatcherTest.java
│ │ └── MethodMatcherTest.java
│ ├── plugin/
│ │ └── PluginLoaderTest.java
│ └── utils/
│ └── AgentAttachmentRule.java
├── doc/
│ ├── add-plugin-demo.md
│ ├── benchmark.md
│ ├── context.md
│ ├── criteria-for-configuring-priorities.md
│ ├── development-guide.md
│ ├── how-to-use/
│ │ ├── megacloud-config.md
│ │ ├── use-in-docker.md
│ │ └── use-on-host.md
│ ├── matcher-DSL.md
│ ├── metric-api.md
│ ├── plugin-unit-test.md
│ ├── prometheus-metric-schedule.md
│ ├── report-development-guide.md
│ ├── spring-boot-3.x.x-demo.md
│ ├── spring-boot-upgrade.md
│ ├── spring-petclinic-demo.md
│ ├── tracing-api.md
│ └── user-manual.md
├── httpserver/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── megaease/
│ └── easeagent/
│ └── httpserver/
│ ├── HttpRequest.java
│ ├── HttpResponse.java
│ ├── IHttpHandler.java
│ ├── IHttpServer.java
│ ├── jdk/
│ │ ├── AgentHttpServerV2.java
│ │ ├── RootContextHandler.java
│ │ └── UriResource.java
│ ├── nano/
│ │ ├── AgentHttpHandler.java
│ │ ├── AgentHttpHandlerProvider.java
│ │ └── AgentHttpServer.java
│ └── nanohttpd/
│ ├── protocols/
│ │ └── http/
│ │ ├── ClientHandler.java
│ │ ├── HTTPSession.java
│ │ ├── IHTTPSession.java
│ │ ├── NanoHTTPD.java
│ │ ├── ServerRunnable.java
│ │ ├── content/
│ │ │ ├── ContentType.java
│ │ │ ├── Cookie.java
│ │ │ └── CookieHandler.java
│ │ ├── request/
│ │ │ └── Method.java
│ │ ├── response/
│ │ │ ├── ChunkedOutputStream.java
│ │ │ ├── IStatus.java
│ │ │ ├── Response.java
│ │ │ └── Status.java
│ │ ├── sockets/
│ │ │ ├── DefaultServerSocketFactory.java
│ │ │ └── SecureServerSocketFactory.java
│ │ ├── tempfiles/
│ │ │ ├── DefaultTempFile.java
│ │ │ ├── DefaultTempFileManager.java
│ │ │ ├── DefaultTempFileManagerFactory.java
│ │ │ ├── ITempFile.java
│ │ │ └── ITempFileManager.java
│ │ └── threading/
│ │ ├── DefaultAsyncRunner.java
│ │ └── IAsyncRunner.java
│ ├── router/
│ │ └── RouterNanoHTTPD.java
│ └── util/
│ ├── IFactory.java
│ ├── IFactoryThrowing.java
│ ├── IHandler.java
│ └── ServerRunner.java
├── loader/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ ├── EaseAgentClassLoader.java
│ │ ├── JarCache.java
│ │ ├── Main.java
│ │ └── StringSequence.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── MainTest.java
│ └── resources/
│ └── test-mock-load.jar
├── log4j2/
│ ├── README.md
│ ├── log4j2-api/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── log4j2/
│ │ ├── ClassLoaderUtils.java
│ │ ├── ClassloaderSupplier.java
│ │ ├── FinalClassloaderSupplier.java
│ │ ├── Logger.java
│ │ ├── LoggerFactory.java
│ │ ├── MDC.java
│ │ ├── api/
│ │ │ ├── AgentLogger.java
│ │ │ ├── AgentLoggerFactory.java
│ │ │ ├── ILevel.java
│ │ │ └── Mdc.java
│ │ └── exception/
│ │ └── Log4j2Exception.java
│ ├── log4j2-impl/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── log4j2/
│ │ └── impl/
│ │ ├── AgentLoggerProxy.java
│ │ ├── LoggerProxyFactory.java
│ │ ├── MdcProxy.java
│ │ └── Slf4jLogger.java
│ └── pom.xml
├── metrics/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── metrics/
│ │ │ ├── AgentScheduledReporter.java
│ │ │ ├── AutoRefreshReporter.java
│ │ │ ├── MetricBeanProviderImpl.java
│ │ │ ├── MetricProviderImpl.java
│ │ │ ├── MetricRegistryService.java
│ │ │ ├── PrometheusAgentHttpHandler.java
│ │ │ ├── config/
│ │ │ │ ├── MetricsCollectorConfig.java
│ │ │ │ ├── MetricsConfig.java
│ │ │ │ └── PluginMetricsConfig.java
│ │ │ ├── converter/
│ │ │ │ ├── AbstractConverter.java
│ │ │ │ ├── Converter.java
│ │ │ │ ├── ConverterAdapter.java
│ │ │ │ ├── EaseAgentPrometheusExports.java
│ │ │ │ ├── IgnoreOutputException.java
│ │ │ │ ├── KeyType.java
│ │ │ │ └── MetricsAdditionalAttributes.java
│ │ │ ├── impl/
│ │ │ │ ├── CounterImpl.java
│ │ │ │ ├── GaugeImpl.java
│ │ │ │ ├── HistogramImpl.java
│ │ │ │ ├── MeterImpl.java
│ │ │ │ ├── MetricInstance.java
│ │ │ │ ├── MetricRegistryImpl.java
│ │ │ │ ├── SnapshotImpl.java
│ │ │ │ └── TimerImpl.java
│ │ │ ├── jvm/
│ │ │ │ ├── JvmBeanProvider.java
│ │ │ │ ├── gc/
│ │ │ │ │ └── JVMGCMetricV2.java
│ │ │ │ └── memory/
│ │ │ │ └── JVMMemoryMetricV2.java
│ │ │ └── model/
│ │ │ └── JVMMemoryGaugeMetricModel.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── services/
│ │ └── com.megaease.easeagent.plugin.bean.BeanProvider
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── metrics/
│ │ ├── MetricProviderImplTest.java
│ │ ├── MetricRegistryServiceTest.java
│ │ ├── PrometheusAgentHttpHandlerTest.java
│ │ ├── TestConst.java
│ │ ├── config/
│ │ │ └── PluginMetricsConfigTest.java
│ │ ├── converter/
│ │ │ ├── AbstractConverterTest.java
│ │ │ ├── ConverterAdapterTest.java
│ │ │ ├── IgnoreOutputExceptionTest.java
│ │ │ └── MetricsAdditionalAttributesTest.java
│ │ ├── impl/
│ │ │ ├── CounterImplTest.java
│ │ │ ├── GaugeImplTest.java
│ │ │ ├── HistogramImplTest.java
│ │ │ ├── MeterImplTest.java
│ │ │ ├── MetricInstanceTest.java
│ │ │ ├── MetricRegistryImplTest.java
│ │ │ ├── MetricRegistryMock.java
│ │ │ ├── MetricTestUtils.java
│ │ │ ├── MockClock.java
│ │ │ ├── SnapshotImplTest.java
│ │ │ └── TimerImplTest.java
│ │ └── jvm/
│ │ └── memory/
│ │ └── JVMMemoryMetricV2Test.java
│ └── resources/
│ ├── log4j2.xml
│ └── mock_agent.properties
├── mock/
│ ├── config-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ ├── config/
│ │ │ │ └── MockConfigLoader.java
│ │ │ └── mock/
│ │ │ └── config/
│ │ │ └── MockConfig.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── config/
│ │ │ └── MockConfigTest.java
│ │ └── resources/
│ │ ├── mock_agent.properties
│ │ └── mock_agent.yaml
│ ├── context-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── mock/
│ │ └── context/
│ │ ├── MockContext.java
│ │ └── MockContextManager.java
│ ├── log4j2-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── megaease/
│ │ │ │ └── easeagent/
│ │ │ │ └── mock/
│ │ │ │ └── log4j2/
│ │ │ │ ├── AllUrlsSupplier.java
│ │ │ │ ├── DirUrlsSupplier.java
│ │ │ │ ├── JarPathUrlsSupplier.java
│ │ │ │ ├── JarUrlsSupplier.java
│ │ │ │ ├── URLClassLoaderSupplier.java
│ │ │ │ └── UrlSupplier.java
│ │ │ └── resources/
│ │ │ └── META-INF/
│ │ │ └── services/
│ │ │ └── com.megaease.easeagent.log4j2.ClassloaderSupplier
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── log4j2/
│ │ │ └── impl/
│ │ │ ├── AgentLoggerFactoryTest.java
│ │ │ └── MDCTest.java
│ │ └── resources/
│ │ └── log4j2.xml
│ ├── metrics-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── metrics/
│ │ │ ├── MetricTestUtils.java
│ │ │ └── MockMetricProvider.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── services/
│ │ └── com.megaease.easeagent.mock.utils.MockProvider
│ ├── plugin-api-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── plugin/
│ │ │ └── api/
│ │ │ ├── MockEaseAgent.java
│ │ │ ├── junit/
│ │ │ │ ├── AfterStatement.java
│ │ │ │ ├── BeforeStatement.java
│ │ │ │ ├── EaseAgentJunit4ClassRunner.java
│ │ │ │ └── ScopeMustBeCloseException.java
│ │ │ └── utils/
│ │ │ ├── ConfigTestUtils.java
│ │ │ ├── ContextUtils.java
│ │ │ ├── InterceptorTestUtils.java
│ │ │ ├── SpanTestUtils.java
│ │ │ └── TagVerifier.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── plugin/
│ │ │ └── api/
│ │ │ ├── TestContext.java
│ │ │ ├── TestEaseAgent.java
│ │ │ ├── demo/
│ │ │ │ ├── InterceptorTest.java
│ │ │ │ ├── M1MetricCollect.java
│ │ │ │ └── MockEaseAgentTest.java
│ │ │ └── utils/
│ │ │ └── ConfigTestUtilsTest.java
│ │ └── resources/
│ │ └── log4j2.xml
│ ├── pom.xml
│ ├── report-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── report/
│ │ │ ├── JsonReporter.java
│ │ │ ├── MetricFlushable.java
│ │ │ ├── MockAtomicReferenceReportSpanReport.java
│ │ │ ├── MockReport.java
│ │ │ ├── MockSpan.java
│ │ │ ├── MockSpanReport.java
│ │ │ └── impl/
│ │ │ ├── LastJsonReporter.java
│ │ │ └── ZipkinMockSpanImpl.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── report/
│ │ │ └── MockReportTest.java
│ │ └── resources/
│ │ └── log4j2.xml
│ ├── utils-mock/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── mock/
│ │ └── utils/
│ │ ├── JdkHttpServer.java
│ │ ├── MockProvider.java
│ │ └── MockSystemEnv.java
│ └── zipkin-mock/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── brave/
│ │ │ │ ├── TracerTestUtils.java
│ │ │ │ └── internal/
│ │ │ │ └── collect/
│ │ │ │ └── WeakConcurrentMapTestUtils.java
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── mock/
│ │ │ └── zipkin/
│ │ │ └── MockTracingProvider.java
│ │ └── resources/
│ │ └── META-INF/
│ │ └── services/
│ │ └── com.megaease.easeagent.mock.utils.MockProvider
│ └── test/
│ └── java/
│ └── com/
│ └── megaease/
│ └── easeagent/
│ └── mock/
│ └── zipkin/
│ └── MockTracingProviderTest.java
├── plugin-api/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ ├── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ ├── AgentPlugin.java
│ │ │ ├── AppendBootstrapLoader.java
│ │ │ ├── CodeVersion.java
│ │ │ ├── Ordered.java
│ │ │ ├── Points.java
│ │ │ ├── annotation/
│ │ │ │ ├── AdviceTo.java
│ │ │ │ ├── AdvicesTo.java
│ │ │ │ ├── DynamicField.java
│ │ │ │ └── Injection.java
│ │ │ ├── api/
│ │ │ │ ├── Cleaner.java
│ │ │ │ ├── Context.java
│ │ │ │ ├── InitializeContext.java
│ │ │ │ ├── ProgressFields.java
│ │ │ │ ├── Reporter.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── AutoRefreshConfigSupplier.java
│ │ │ │ │ ├── AutoRefreshPluginConfig.java
│ │ │ │ │ ├── AutoRefreshPluginConfigImpl.java
│ │ │ │ │ ├── AutoRefreshPluginConfigRegistry.java
│ │ │ │ │ ├── ChangeItem.java
│ │ │ │ │ ├── Config.java
│ │ │ │ │ ├── ConfigChangeListener.java
│ │ │ │ │ ├── ConfigConst.java
│ │ │ │ │ ├── Const.java
│ │ │ │ │ ├── IConfigFactory.java
│ │ │ │ │ ├── IPluginConfig.java
│ │ │ │ │ └── PluginConfigChangeListener.java
│ │ │ │ ├── context/
│ │ │ │ │ ├── AsyncContext.java
│ │ │ │ │ ├── ContextCons.java
│ │ │ │ │ ├── ContextUtils.java
│ │ │ │ │ ├── IContextManager.java
│ │ │ │ │ └── RequestContext.java
│ │ │ │ ├── dispatcher/
│ │ │ │ │ └── IDispatcher.java
│ │ │ │ ├── health/
│ │ │ │ │ └── AgentHealth.java
│ │ │ │ ├── logging/
│ │ │ │ │ ├── AccessLogInfo.java
│ │ │ │ │ ├── ILoggerFactory.java
│ │ │ │ │ ├── Logger.java
│ │ │ │ │ └── Mdc.java
│ │ │ │ ├── metric/
│ │ │ │ │ ├── Counter.java
│ │ │ │ │ ├── Gauge.java
│ │ │ │ │ ├── Histogram.java
│ │ │ │ │ ├── Meter.java
│ │ │ │ │ ├── Metric.java
│ │ │ │ │ ├── MetricProvider.java
│ │ │ │ │ ├── MetricRegistry.java
│ │ │ │ │ ├── MetricRegistrySupplier.java
│ │ │ │ │ ├── MetricSupplier.java
│ │ │ │ │ ├── ServiceMetric.java
│ │ │ │ │ ├── ServiceMetricRegistry.java
│ │ │ │ │ ├── ServiceMetricSupplier.java
│ │ │ │ │ ├── Snapshot.java
│ │ │ │ │ ├── Timer.java
│ │ │ │ │ └── name/
│ │ │ │ │ ├── ConverterType.java
│ │ │ │ │ ├── MetricField.java
│ │ │ │ │ ├── MetricName.java
│ │ │ │ │ ├── MetricSubType.java
│ │ │ │ │ ├── MetricType.java
│ │ │ │ │ ├── MetricValueFetcher.java
│ │ │ │ │ ├── NameFactory.java
│ │ │ │ │ └── Tags.java
│ │ │ │ ├── middleware/
│ │ │ │ │ ├── MiddlewareConstants.java
│ │ │ │ │ ├── Redirect.java
│ │ │ │ │ ├── RedirectProcessor.java
│ │ │ │ │ ├── ResourceConfig.java
│ │ │ │ │ └── Type.java
│ │ │ │ ├── otlp/
│ │ │ │ │ └── common/
│ │ │ │ │ ├── AgentAttributes.java
│ │ │ │ │ ├── AgentInstrumentLibInfo.java
│ │ │ │ │ ├── AgentLogData.java
│ │ │ │ │ ├── AgentLogDataImpl.java
│ │ │ │ │ ├── LogMapper.java
│ │ │ │ │ ├── OtlpSpanContext.java
│ │ │ │ │ └── SemanticKey.java
│ │ │ │ └── trace/
│ │ │ │ ├── Extractor.java
│ │ │ │ ├── Getter.java
│ │ │ │ ├── ITracing.java
│ │ │ │ ├── Injector.java
│ │ │ │ ├── Message.java
│ │ │ │ ├── MessagingRequest.java
│ │ │ │ ├── MessagingTracing.java
│ │ │ │ ├── Request.java
│ │ │ │ ├── Response.java
│ │ │ │ ├── Scope.java
│ │ │ │ ├── Setter.java
│ │ │ │ ├── Span.java
│ │ │ │ ├── SpanContext.java
│ │ │ │ ├── Tracing.java
│ │ │ │ ├── TracingContext.java
│ │ │ │ ├── TracingProvider.java
│ │ │ │ └── TracingSupplier.java
│ │ │ ├── asm/
│ │ │ │ └── Modifier.java
│ │ │ ├── async/
│ │ │ │ ├── AgentThreadFactory.java
│ │ │ │ ├── ScheduleHelper.java
│ │ │ │ ├── ScheduleRunner.java
│ │ │ │ ├── ThreadLocalCurrentContext.java
│ │ │ │ └── ThreadUtils.java
│ │ │ ├── bean/
│ │ │ │ ├── AgentInitializingBean.java
│ │ │ │ └── BeanProvider.java
│ │ │ ├── bridge/
│ │ │ │ ├── AgentInfo.java
│ │ │ │ ├── EaseAgent.java
│ │ │ │ ├── NoOpAgentReporter.java
│ │ │ │ ├── NoOpCleaner.java
│ │ │ │ ├── NoOpConfigFactory.java
│ │ │ │ ├── NoOpContext.java
│ │ │ │ ├── NoOpDispatcher.java
│ │ │ │ ├── NoOpIPluginConfig.java
│ │ │ │ ├── NoOpLoggerFactory.java
│ │ │ │ ├── NoOpMetrics.java
│ │ │ │ ├── NoOpReporter.java
│ │ │ │ └── NoOpTracer.java
│ │ │ ├── enums/
│ │ │ │ ├── ClassMatch.java
│ │ │ │ ├── Operator.java
│ │ │ │ ├── Order.java
│ │ │ │ └── StringMatch.java
│ │ │ ├── field/
│ │ │ │ ├── AgentDynamicFieldAccessor.java
│ │ │ │ ├── AgentFieldReflectAccessor.java
│ │ │ │ ├── DynamicFieldAccessor.java
│ │ │ │ ├── NullObject.java
│ │ │ │ └── TypeFieldGetter.java
│ │ │ ├── interceptor/
│ │ │ │ ├── AgentInterceptorChain.java
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── InterceptorProvider.java
│ │ │ │ ├── MethodInfo.java
│ │ │ │ └── NonReentrantInterceptor.java
│ │ │ ├── matcher/
│ │ │ │ ├── ClassMatcher.java
│ │ │ │ ├── IClassMatcher.java
│ │ │ │ ├── IMethodMatcher.java
│ │ │ │ ├── Matcher.java
│ │ │ │ ├── MethodMatcher.java
│ │ │ │ ├── loader/
│ │ │ │ │ ├── ClassLoaderMatcher.java
│ │ │ │ │ ├── IClassLoaderMatcher.java
│ │ │ │ │ └── NegateClassLoaderMatcher.java
│ │ │ │ └── operator/
│ │ │ │ ├── AndClassMatcher.java
│ │ │ │ ├── AndMethodMatcher.java
│ │ │ │ ├── NegateClassMatcher.java
│ │ │ │ ├── NegateMethodMatcher.java
│ │ │ │ ├── Operator.java
│ │ │ │ ├── OrClassMatcher.java
│ │ │ │ └── OrMethodMatcher.java
│ │ │ ├── processor/
│ │ │ │ ├── BeanUtils.java
│ │ │ │ ├── ElementVisitor8.java
│ │ │ │ ├── GenerateProviderBean.java
│ │ │ │ ├── PluginProcessor.java
│ │ │ │ └── RepeatedAnnotationVisitor.java
│ │ │ ├── report/
│ │ │ │ ├── AgentReport.java
│ │ │ │ ├── ByteWrapper.java
│ │ │ │ ├── Call.java
│ │ │ │ ├── Callback.java
│ │ │ │ ├── EncodedData.java
│ │ │ │ ├── Encoder.java
│ │ │ │ ├── Packer.java
│ │ │ │ ├── Sender.java
│ │ │ │ ├── encoder/
│ │ │ │ │ └── JsonEncoder.java
│ │ │ │ ├── metric/
│ │ │ │ │ └── MetricReporterFactory.java
│ │ │ │ └── tracing/
│ │ │ │ ├── Annotation.java
│ │ │ │ ├── Endpoint.java
│ │ │ │ ├── ReportSpan.java
│ │ │ │ └── ReportSpanImpl.java
│ │ │ ├── tools/
│ │ │ │ ├── config/
│ │ │ │ │ └── NameAndSystem.java
│ │ │ │ ├── loader/
│ │ │ │ │ └── AgentHelperClassLoader.java
│ │ │ │ ├── matcher/
│ │ │ │ │ ├── ClassMatcherUtils.java
│ │ │ │ │ └── MethodMatcherUtils.java
│ │ │ │ ├── metrics/
│ │ │ │ │ ├── AccessLogServerInfo.java
│ │ │ │ │ ├── ErrorPercentModelGauge.java
│ │ │ │ │ ├── GaugeMetricModel.java
│ │ │ │ │ ├── HttpLog.java
│ │ │ │ │ ├── LastMinutesCounterGauge.java
│ │ │ │ │ ├── NameFactorySupplier.java
│ │ │ │ │ ├── RedisMetric.java
│ │ │ │ │ └── ServerMetric.java
│ │ │ │ └── trace/
│ │ │ │ ├── BaseHttpClientTracingInterceptor.java
│ │ │ │ ├── HttpRequest.java
│ │ │ │ ├── HttpResponse.java
│ │ │ │ ├── HttpUtils.java
│ │ │ │ └── TraceConst.java
│ │ │ └── utils/
│ │ │ ├── AdditionalAttributes.java
│ │ │ ├── ClassInstance.java
│ │ │ ├── ClassUtils.java
│ │ │ ├── ImmutableMap.java
│ │ │ ├── NoNull.java
│ │ │ ├── Pair.java
│ │ │ ├── SystemClock.java
│ │ │ ├── SystemEnv.java
│ │ │ ├── common/
│ │ │ │ ├── DataSize.java
│ │ │ │ ├── DataUnit.java
│ │ │ │ ├── ExceptionUtil.java
│ │ │ │ ├── HostAddress.java
│ │ │ │ ├── JsonUtil.java
│ │ │ │ ├── StringUtils.java
│ │ │ │ └── WeakConcurrentMap.java
│ │ │ └── jackson/
│ │ │ └── annotation/
│ │ │ └── JsonProperty.java
│ │ └── io/
│ │ └── opentelemetry/
│ │ └── sdk/
│ │ └── resources/
│ │ └── EaseAgentResource.java
│ └── test/
│ └── java/
│ └── com/
│ └── megaease/
│ └── easeagent/
│ └── plugin/
│ ├── api/
│ │ ├── MockSystemEnv.java
│ │ ├── ProgressFieldsTest.java
│ │ ├── metric/
│ │ │ └── name/
│ │ │ └── NameFactoryTest.java
│ │ └── middleware/
│ │ ├── RedirectProcessorTest.java
│ │ ├── RedirectTest.java
│ │ └── ResourceConfigTest.java
│ ├── async/
│ │ └── ThreadLocalCurrentContextTest.java
│ └── tools/
│ └── config/
│ └── AutoRefreshConfigSupplierTest.java
├── plugins/
│ ├── async/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ ├── AsyncPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── CrossThreadAdvice.java
│ │ │ │ └── ReactSchedulersAdvice.java
│ │ │ └── interceptor/
│ │ │ └── RunnableInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── interceptor/
│ │ │ └── RunnableInterceptorTest.java
│ │ └── resources/
│ │ └── log4j2.xml
│ ├── dubbo/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── dubbo/
│ │ │ ├── AlibabaDubboCtxUtils.java
│ │ │ ├── ApacheDubboCtxUtils.java
│ │ │ ├── DubboMetricTags.java
│ │ │ ├── DubboPlugin.java
│ │ │ ├── DubboTraceTags.java
│ │ │ ├── advice/
│ │ │ │ ├── AlibabaDubboAdvice.java
│ │ │ │ ├── AlibabaDubboResponseFutureAdvice.java
│ │ │ │ └── ApacheDubboAdvice.java
│ │ │ ├── config/
│ │ │ │ └── DubboTraceConfig.java
│ │ │ └── interceptor/
│ │ │ ├── DubboBaseInterceptor.java
│ │ │ ├── metrics/
│ │ │ │ ├── DubboBaseMetricsInterceptor.java
│ │ │ │ ├── DubboMetrics.java
│ │ │ │ ├── alibaba/
│ │ │ │ │ ├── AlibabaDubboAsyncMetricsInterceptor.java
│ │ │ │ │ ├── AlibabaDubboMetricsCallback.java
│ │ │ │ │ └── AlibabaDubboMetricsInterceptor.java
│ │ │ │ └── apache/
│ │ │ │ ├── ApacheDubboMetricsAsyncCallback.java
│ │ │ │ └── ApacheDubboMetricsInterceptor.java
│ │ │ └── trace/
│ │ │ ├── alibaba/
│ │ │ │ ├── AlibabaDubboAsyncTraceInterceptor.java
│ │ │ │ ├── AlibabaDubboClientRequest.java
│ │ │ │ ├── AlibabaDubboServerRequest.java
│ │ │ │ ├── AlibabaDubboTraceCallback.java
│ │ │ │ └── AlibabaDubboTraceInterceptor.java
│ │ │ └── apache/
│ │ │ ├── ApacheDubboClientRequest.java
│ │ │ ├── ApacheDubboServerRequest.java
│ │ │ ├── ApacheDubboTraceCallback.java
│ │ │ └── ApacheDubboTraceInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── dubbo/
│ │ │ └── interceptor/
│ │ │ ├── AlibabaDubboBaseTest.java
│ │ │ ├── ApacheDubboBaseTest.java
│ │ │ ├── metrics/
│ │ │ │ ├── alibaba/
│ │ │ │ │ ├── AlibabaDubboAsyncMetricsInterceptorTest.java
│ │ │ │ │ └── AlibabaDubboMetricsInterceptorTest.java
│ │ │ │ └── apache/
│ │ │ │ └── ApacheDubboMetricsInterceptorTest.java
│ │ │ └── trace/
│ │ │ ├── alibaba/
│ │ │ │ ├── AlibabaDubboAsyncTraceInterceptorTest.java
│ │ │ │ └── AlibabaDubboTraceInterceptorTest.java
│ │ │ └── apache/
│ │ │ └── ApacheDubboTraceInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── elasticsearch/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── elasticsearch/
│ │ │ ├── ElasticsearchPlugin.java
│ │ │ ├── ElasticsearchRedirectPlugin.java
│ │ │ ├── advice/
│ │ │ │ └── SpringElasticsearchAdvice.java
│ │ │ ├── interceptor/
│ │ │ │ ├── AsyncResponse4MetricsListener.java
│ │ │ │ ├── AsyncResponse4TraceListener.java
│ │ │ │ ├── ElasticsearchBaseInterceptor.java
│ │ │ │ ├── ElasticsearchBaseMetricsInterceptor.java
│ │ │ │ ├── ElasticsearchBaseTraceInterceptor.java
│ │ │ │ ├── ElasticsearchCtxUtils.java
│ │ │ │ ├── ElasticsearchMetric.java
│ │ │ │ ├── ElasticsearchPerformRequestAsync4MetricsInterceptor.java
│ │ │ │ ├── ElasticsearchPerformRequestAsync4TraceInterceptor.java
│ │ │ │ ├── ElasticsearchPerformRequestMetricsInterceptor.java
│ │ │ │ ├── ElasticsearchPerformRequestTraceInterceptor.java
│ │ │ │ └── redirect/
│ │ │ │ └── SpringElasticsearchInterceptor.java
│ │ │ └── points/
│ │ │ ├── ElasticsearchPerformRequestAsyncPoints.java
│ │ │ └── ElasticsearchPerformRequestPoints.java
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── plugin/
│ │ └── elasticsearch/
│ │ └── interceptor/
│ │ ├── ElasticsearchBaseTest.java
│ │ ├── ElasticsearchCtxUtilsTest.java
│ │ ├── ElasticsearchPerformRequestAsyncMetricsInterceptorTest.java
│ │ ├── ElasticsearchPerformRequestAsyncTraceInterceptorTest.java
│ │ ├── ElasticsearchPerformRequestMetricsInterceptorTest.java
│ │ └── ElasticsearchPerformRequestTraceInterceptorTest.java
│ ├── healthy/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── healthy/
│ │ │ ├── HealthPlugin.java
│ │ │ ├── OnApplicationEventInterceptor.java
│ │ │ └── SpringApplicationAdminMXBeanRegistrarAdvice.java
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── plugin/
│ │ └── healthy/
│ │ └── OnApplicationEventInterceptorTest.java
│ ├── httpclient/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpclient/
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── HttpClientPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── HttpClient5AsyncAdvice.java
│ │ │ │ ├── HttpClient5DoExecuteAdvice.java
│ │ │ │ └── HttpClientDoExecuteAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── HttpClient5AsyncForwardedInterceptor.java
│ │ │ ├── HttpClient5AsyncTracingInterceptor.java
│ │ │ ├── HttpClient5DoExecuteForwardedInterceptor.java
│ │ │ ├── HttpClient5DoExecuteInterceptor.java
│ │ │ ├── HttpClientDoExecuteForwardedInterceptor.java
│ │ │ └── HttpClientDoExecuteInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpclient/
│ │ │ └── interceptor/
│ │ │ ├── HttpClient5AsyncForwardedInterceptorTest.java
│ │ │ ├── HttpClient5AsyncTracingInterceptorTest.java
│ │ │ ├── HttpClient5DoExecuteForwardedInterceptorTest.java
│ │ │ ├── HttpClient5DoExecuteInterceptorTest.java
│ │ │ ├── HttpClientDoExecuteForwardedInterceptorTest.java
│ │ │ ├── HttpClientDoExecuteInterceptorTest.java
│ │ │ └── TestConst.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── httpservlet/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpservlet/
│ │ │ ├── AccessPlugin.java
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── HttpServletPlugin.java
│ │ │ ├── advice/
│ │ │ │ └── DoFilterPoints.java
│ │ │ ├── interceptor/
│ │ │ │ ├── BaseServletInterceptor.java
│ │ │ │ ├── DoFilterForwardedInterceptor.java
│ │ │ │ ├── DoFilterMetricInterceptor.java
│ │ │ │ ├── DoFilterTraceInterceptor.java
│ │ │ │ ├── HttpServerRequest.java
│ │ │ │ ├── ServletAccessLogServerInfo.java
│ │ │ │ └── ServletHttpLogInterceptor.java
│ │ │ └── utils/
│ │ │ ├── InternalAsyncListener.java
│ │ │ └── ServletUtils.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpservlet/
│ │ │ └── interceptor/
│ │ │ ├── BaseServletInterceptorTest.java
│ │ │ ├── DoFilterForwardedInterceptorTest.java
│ │ │ ├── DoFilterMetricInterceptorTest.java
│ │ │ ├── DoFilterTraceInterceptorTest.java
│ │ │ ├── HttpServerRequestTest.java
│ │ │ ├── ServletAccessLogInfoServerInfoTest.java
│ │ │ ├── ServletHttpLogInterceptorTest.java
│ │ │ ├── TestConst.java
│ │ │ └── TestServletUtils.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── httpurlconnection/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpurlconnection/
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── HttpURLConnectionPlugin.java
│ │ │ ├── advice/
│ │ │ │ └── HttpURLConnectionGetResponseCodeAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── HttpURLConnectionGetResponseCodeForwardedInterceptor.java
│ │ │ └── HttpURLConnectionGetResponseCodeInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpurlconnection/
│ │ │ └── interceptor/
│ │ │ ├── HttpURLConnectionGetResponseCodeForwardedInterceptorTest.java
│ │ │ ├── HttpURLConnectionGetResponseCodeInterceptorTest.java
│ │ │ └── TestUtils.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── httpurlconnection-jdk17/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpurlconnection/
│ │ │ └── jdk17/
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── HttpURLConnectionPlugin.java
│ │ │ ├── advice/
│ │ │ │ └── HttpURLConnectionAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── DynamicFieldUtils.java
│ │ │ ├── HttpURLConnectionForwardedInterceptor.java
│ │ │ ├── HttpURLConnectionInterceptor.java
│ │ │ └── HttpURLConnectionUtils.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── httpurlconnection/
│ │ │ └── jdk17/
│ │ │ └── interceptor/
│ │ │ ├── HttpURLConnectionForwardedInterceptorTest.java
│ │ │ ├── HttpURLConnectionInterceptorTest.java
│ │ │ └── TestUtils.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── jdbc/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── jdbc/
│ │ │ ├── JdbcConnectionMetricPlugin.java
│ │ │ ├── JdbcDataSourceMetricPlugin.java
│ │ │ ├── JdbcRedirectPlugin.java
│ │ │ ├── JdbcTracingPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── HikariDataSourceAdvice.java
│ │ │ │ ├── JdbcConnectionAdvice.java
│ │ │ │ ├── JdbcDataSourceAdvice.java
│ │ │ │ └── JdbcStatementAdvice.java
│ │ │ ├── common/
│ │ │ │ ├── DatabaseInfo.java
│ │ │ │ ├── JdbcUtils.java
│ │ │ │ ├── MD5DictionaryItem.java
│ │ │ │ ├── MD5ReportConsumer.java
│ │ │ │ ├── MD5SQLCompression.java
│ │ │ │ ├── SQLCompression.java
│ │ │ │ ├── SQLCompressionFactory.java
│ │ │ │ ├── SQLCompressionWrapper.java
│ │ │ │ └── SqlInfo.java
│ │ │ └── interceptor/
│ │ │ ├── JdbConPrepareOrCreateStmInterceptor.java
│ │ │ ├── JdbcStmPrepareSqlInterceptor.java
│ │ │ ├── metric/
│ │ │ │ ├── JdbcDataSourceMetricInterceptor.java
│ │ │ │ ├── JdbcMetric.java
│ │ │ │ └── JdbcStmMetricInterceptor.java
│ │ │ ├── redirect/
│ │ │ │ └── HikariSetPropertyInterceptor.java
│ │ │ └── tracing/
│ │ │ └── JdbcStmTracingInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── jdbc/
│ │ │ ├── MockJDBCStatement.java
│ │ │ ├── TestUtils.java
│ │ │ ├── common/
│ │ │ │ ├── DatabaseInfoTest.java
│ │ │ │ ├── JdbcUtilsTest.java
│ │ │ │ ├── MD5DictionaryItemTest.java
│ │ │ │ ├── MD5ReportConsumerTest.java
│ │ │ │ ├── MD5SQLCompressionTest.java
│ │ │ │ └── SqlInfoTest.java
│ │ │ └── interceptor/
│ │ │ ├── JdbConPrepareOrCreateStmInterceptorTest.java
│ │ │ ├── JdbcStmPrepareSqlInterceptorTest.java
│ │ │ ├── metric/
│ │ │ │ ├── JdbcDataSourceMetricInterceptorTest.java
│ │ │ │ ├── JdbcMetricTest.java
│ │ │ │ └── JdbcStmMetricInterceptorTest.java
│ │ │ ├── redirect/
│ │ │ │ └── HikariSetPropertyInterceptorTest.java
│ │ │ └── tracing/
│ │ │ └── JdbcStmTracingInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── kafka/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── kafka/
│ │ │ ├── KafkaPlugin.java
│ │ │ ├── KafkaRedirectPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── KafkaConsumerAdvice.java
│ │ │ │ ├── KafkaConsumerConfigAdvice.java
│ │ │ │ ├── KafkaConsumerRecordAdvice.java
│ │ │ │ ├── KafkaMessageListenerAdvice.java
│ │ │ │ ├── KafkaProducerAdvice.java
│ │ │ │ └── KafkaProducerConfigAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── AsyncCallback.java
│ │ │ ├── KafkaUtils.java
│ │ │ ├── initialize/
│ │ │ │ ├── ConsumerRecordInterceptor.java
│ │ │ │ ├── KafkaConsumerConstructInterceptor.java
│ │ │ │ ├── KafkaConsumerPollInterceptor.java
│ │ │ │ └── KafkaProducerConstructInterceptor.java
│ │ │ ├── metric/
│ │ │ │ ├── KafkaConsumerMetricInterceptor.java
│ │ │ │ ├── KafkaMessageListenerMetricInterceptor.java
│ │ │ │ ├── KafkaMetric.java
│ │ │ │ ├── KafkaProducerMetricInterceptor.java
│ │ │ │ └── MetricCallback.java
│ │ │ ├── redirect/
│ │ │ │ ├── KafkaAbstractConfigConstructInterceptor.java
│ │ │ │ ├── KafkaConsumerConfigConstructInterceptor.java
│ │ │ │ └── KafkaProducerConfigConstructInterceptor.java
│ │ │ └── tracing/
│ │ │ ├── KafkaConsumerRequest.java
│ │ │ ├── KafkaConsumerTracingInterceptor.java
│ │ │ ├── KafkaHeaders.java
│ │ │ ├── KafkaMessageListenerTracingInterceptor.java
│ │ │ ├── KafkaProducerDoSendInterceptor.java
│ │ │ ├── KafkaProducerRequest.java
│ │ │ ├── KafkaTags.java
│ │ │ └── TraceCallback.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── kafka/
│ │ │ └── interceptor/
│ │ │ ├── AsyncCallbackTest.java
│ │ │ ├── KafkaTestUtils.java
│ │ │ ├── KafkaUtilsTest.java
│ │ │ ├── MockConsumerRecord.java
│ │ │ ├── MockKafkaConsumer.java
│ │ │ ├── MockKafkaProducer.java
│ │ │ ├── TestConst.java
│ │ │ ├── initialize/
│ │ │ │ ├── ConsumerRecordInterceptorTest.java
│ │ │ │ ├── KafkaConsumerConstructInterceptorTest.java
│ │ │ │ ├── KafkaConsumerPollInterceptorTest.java
│ │ │ │ ├── KafkaProducerConstructInterceptorTest.java
│ │ │ │ └── MockDynamicFieldAccessor.java
│ │ │ ├── metric/
│ │ │ │ ├── KafkaConsumerMetricInterceptorTest.java
│ │ │ │ ├── KafkaMessageListenerMetricInterceptorTest.java
│ │ │ │ ├── KafkaMetricTest.java
│ │ │ │ ├── KafkaProducerMetricInterceptorTest.java
│ │ │ │ └── MetricCallbackTest.java
│ │ │ ├── redirect/
│ │ │ │ └── KafkaAbstractConfigConstructInterceptorTest.java
│ │ │ └── tracing/
│ │ │ ├── KafkaConsumerRequestTest.java
│ │ │ ├── KafkaConsumerTracingInterceptorTest.java
│ │ │ ├── KafkaHeadersTest.java
│ │ │ ├── KafkaMessageListenerTracingInterceptorTest.java
│ │ │ ├── KafkaProducerDoSendInterceptorTest.java
│ │ │ ├── KafkaProducerRequestTest.java
│ │ │ └── TraceCallbackTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── log4j2-log-plugin/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── log4j2/
│ │ ├── Log4j2Plugin.java
│ │ ├── interceptor/
│ │ │ └── Log4j2AppenderInterceptor.java
│ │ ├── log/
│ │ │ └── Log4jLogMapper.java
│ │ └── points/
│ │ └── AbstractLoggerPoints.java
│ ├── logback/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── logback/
│ │ ├── LogbackPlugin.java
│ │ ├── interceptor/
│ │ │ └── LogbackAppenderInterceptor.java
│ │ ├── log/
│ │ │ └── LogbackLogMapper.java
│ │ └── points/
│ │ └── LoggerPoints.java
│ ├── mongodb/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── mongodb/
│ │ │ ├── MongoPlugin.java
│ │ │ ├── MongoRedirectPlugin.java
│ │ │ ├── MongoUtils.java
│ │ │ ├── interceptor/
│ │ │ │ ├── InterceptorHelper.java
│ │ │ │ ├── MetricHelper.java
│ │ │ │ ├── MongoBaseInterceptor.java
│ │ │ │ ├── MongoBaseMetricInterceptor.java
│ │ │ │ ├── MongoBaseTraceInterceptor.java
│ │ │ │ ├── MongoClientConstruct4MetricInterceptor.java
│ │ │ │ ├── MongoClientConstruct4TraceInterceptor.java
│ │ │ │ ├── MongoCtx.java
│ │ │ │ ├── MongoDbRedirectInterceptor.java
│ │ │ │ ├── MongoInternalConnectionSendAndReceiveAsync4MetricInterceptor.java
│ │ │ │ ├── MongoInternalConnectionSendAndReceiveAsync4TraceInterceptor.java
│ │ │ │ ├── MongoMetric.java
│ │ │ │ ├── MongoReactiveInitMetricInterceptor.java
│ │ │ │ ├── MongoReactiveInitTraceInterceptor.java
│ │ │ │ ├── TraceHelper.java
│ │ │ │ └── listener/
│ │ │ │ ├── MongoBaseCommandListener.java
│ │ │ │ ├── MongoBaseMetricCommandListener.java
│ │ │ │ ├── MongoBaseTraceCommandListener.java
│ │ │ │ ├── MongoMetricCommandListener.java
│ │ │ │ ├── MongoReactiveMetricCommandListener.java
│ │ │ │ ├── MongoReactiveTraceCommandListener.java
│ │ │ │ └── MongoTraceCommandListener.java
│ │ │ └── points/
│ │ │ ├── MongoAsyncMongoClientsPoints.java
│ │ │ ├── MongoClientImplPoints.java
│ │ │ ├── MongoDBInternalConnectionPoints.java
│ │ │ └── MongoRedirectPoints.java
│ │ └── test/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── plugin/
│ │ └── mongodb/
│ │ ├── MongoBaseTest.java
│ │ ├── MongoMetricTest.java
│ │ ├── MongoReactiveMetricTest.java
│ │ ├── MongoReactiveTraceTest.java
│ │ ├── MongoTraceTest.java
│ │ └── TraceHelperTest.java
│ ├── motan/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── motan/
│ │ │ ├── MotanPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── MotanConsumerAdvice.java
│ │ │ │ └── MotanProviderAdvice.java
│ │ │ ├── config/
│ │ │ │ └── MotanPluginConfig.java
│ │ │ └── interceptor/
│ │ │ ├── MotanClassUtils.java
│ │ │ ├── MotanCtxUtils.java
│ │ │ ├── metrics/
│ │ │ │ ├── MetricsFutureListener.java
│ │ │ │ ├── MotanBaseMetricsInterceptor.java
│ │ │ │ ├── MotanMetric.java
│ │ │ │ ├── MotanMetricTags.java
│ │ │ │ └── MotanMetricsInterceptor.java
│ │ │ └── trace/
│ │ │ ├── MotanBaseInterceptor.java
│ │ │ ├── MotanTags.java
│ │ │ ├── consumer/
│ │ │ │ ├── MotanConsumerRequest.java
│ │ │ │ ├── MotanConsumerTraceInterceptor.java
│ │ │ │ └── TraceFutureListener.java
│ │ │ └── provider/
│ │ │ ├── MotanProviderRequest.java
│ │ │ └── MotanProviderTraceInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── motan/
│ │ │ └── interceptor/
│ │ │ ├── metrics/
│ │ │ │ └── MotanMetricsInterceptorTest.java
│ │ │ └── trace/
│ │ │ ├── MotanInterceptorTest.java
│ │ │ ├── consumer/
│ │ │ │ └── MotanConsumerInterceptorTest.java
│ │ │ └── provider/
│ │ │ └── MotanProviderInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── okhttp/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── okhttp/
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── OkHttpPlugin.java
│ │ │ ├── advice/
│ │ │ │ └── OkHttpAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── ForwardedRequest.java
│ │ │ ├── InternalRequest.java
│ │ │ ├── InternalResponse.java
│ │ │ ├── OkHttpAsyncTracingInterceptor.java
│ │ │ ├── OkHttpForwardedInterceptor.java
│ │ │ └── OkHttpTracingInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── okhttp/
│ │ │ └── interceptor/
│ │ │ ├── OkHttpAsyncTracingInterceptorTest.java
│ │ │ ├── OkHttpForwardedInterceptorTest.java
│ │ │ ├── OkHttpTestUtils.java
│ │ │ ├── OkHttpTracingInterceptorTest.java
│ │ │ └── TestConst.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── pom.xml
│ ├── rabbitmq/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── rabbitmq/
│ │ │ ├── RabbitMqConsumerMetric.java
│ │ │ ├── RabbitMqPlugin.java
│ │ │ ├── RabbitMqProducerMetric.java
│ │ │ ├── RabbitMqRedirectPlugin.java
│ │ │ ├── spring/
│ │ │ │ ├── RabbitMqMessageListenerAdvice.java
│ │ │ │ └── interceptor/
│ │ │ │ ├── RabbitMqMessageListenerOnMessageInterceptor.java
│ │ │ │ ├── RabbitMqOnMessageMetricInterceptor.java
│ │ │ │ └── RabbitMqOnMessageTracingInterceptor.java
│ │ │ └── v5/
│ │ │ ├── advice/
│ │ │ │ ├── RabbitMqChannelAdvice.java
│ │ │ │ ├── RabbitMqConfigFactoryAdvice.java
│ │ │ │ ├── RabbitMqConsumerAdvice.java
│ │ │ │ └── RabbitMqPropertyAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── RabbitMqChannelConsumeInterceptor.java
│ │ │ ├── RabbitMqChannelConsumerDeliveryInterceptor.java
│ │ │ ├── RabbitMqChannelPublishInterceptor.java
│ │ │ ├── RabbitMqConsumerHandleDeliveryInterceptor.java
│ │ │ ├── metirc/
│ │ │ │ ├── RabbitMqConsumerMetricInterceptor.java
│ │ │ │ └── RabbitMqProducerMetricInterceptor.java
│ │ │ ├── redirect/
│ │ │ │ ├── RabbitMqConfigFactoryInterceptor.java
│ │ │ │ └── RabbitMqPropertyInterceptor.java
│ │ │ └── tracing/
│ │ │ ├── RabbitMqChannelPublishTracingInterceptor.java
│ │ │ └── RabbitMqConsumerTracingInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── rabbitmq/
│ │ │ ├── RabbitMqConsumerMetricTest.java
│ │ │ ├── RabbitMqProducerMetricTest.java
│ │ │ ├── TestUtils.java
│ │ │ ├── spring/
│ │ │ │ └── interceptor/
│ │ │ │ ├── RabbitMqMessageListenerOnMessageInterceptorTest.java
│ │ │ │ ├── RabbitMqOnMessageMetricInterceptorTest.java
│ │ │ │ └── RabbitMqOnMessageTracingInterceptorTest.java
│ │ │ └── v5/
│ │ │ └── interceptor/
│ │ │ ├── MockConsumer.java
│ │ │ ├── RabbitMqChannelConsumeInterceptorTest.java
│ │ │ ├── RabbitMqChannelConsumerDeliveryInterceptorTest.java
│ │ │ ├── RabbitMqChannelPublishInterceptorTest.java
│ │ │ ├── RabbitMqConsumerHandleDeliveryInterceptorTest.java
│ │ │ ├── metirc/
│ │ │ │ ├── RabbitMqConsumerMetricInterceptorTest.java
│ │ │ │ └── RabbitMqProducerMetricInterceptorTest.java
│ │ │ ├── redirect/
│ │ │ │ └── RabbitMqPropertyInterceptorTest.java
│ │ │ └── tracing/
│ │ │ ├── RabbitMqChannelPublishTracingInterceptorTest.java
│ │ │ └── RabbitMqConsumerTracingInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── redis/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── redis/
│ │ │ ├── RedisPlugin.java
│ │ │ ├── RedisRedirectPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── JedisAdvice.java
│ │ │ │ ├── JedisConstructorAdvice.java
│ │ │ │ ├── LettuceRedisClientAdvice.java
│ │ │ │ ├── RedisChannelWriterAdvice.java
│ │ │ │ ├── RedisClusterClientAdvice.java
│ │ │ │ ├── RedisPropertiesAdvice.java
│ │ │ │ ├── RedisPropertiesClusterAdvice.java
│ │ │ │ └── StatefulRedisConnectionAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── RedisClassUtils.java
│ │ │ ├── RedisClientUtils.java
│ │ │ ├── initialize/
│ │ │ │ ├── CommonRedisClientInterceptor.java
│ │ │ │ ├── CompletableFutureWrapper.java
│ │ │ │ ├── ConnectionFutureWrapper.java
│ │ │ │ ├── RedisClientInterceptor.java
│ │ │ │ └── RedisClusterClientInterceptor.java
│ │ │ ├── metric/
│ │ │ │ ├── CommonRedisMetricInterceptor.java
│ │ │ │ ├── JedisMetricInterceptor.java
│ │ │ │ └── LettuceMetricInterceptor.java
│ │ │ ├── redirect/
│ │ │ │ ├── JedisConstructorInterceptor.java
│ │ │ │ ├── LettuceRedisClientConstructInterceptor.java
│ │ │ │ ├── RedisPropertiesClusterSetNodesInterceptor.java
│ │ │ │ └── RedisPropertiesSetPropertyInterceptor.java
│ │ │ └── tracing/
│ │ │ ├── CommonRedisTracingInterceptor.java
│ │ │ ├── JedisTracingInterceptor.java
│ │ │ ├── LettuceTracingInterceptor.java
│ │ │ └── StatefulRedisConnectionInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── redis/
│ │ │ └── interceptor/
│ │ │ ├── RedisUtils.java
│ │ │ ├── TestConst.java
│ │ │ ├── initialize/
│ │ │ │ ├── CommonRedisClientInterceptorTest.java
│ │ │ │ ├── CompletableFutureWrapperTest.java
│ │ │ │ ├── ConnectionFutureWrapperTest.java
│ │ │ │ └── DynamicFieldAccessorObj.java
│ │ │ ├── metric/
│ │ │ │ ├── CommonRedisMetricInterceptorTest.java
│ │ │ │ ├── JedisMetricInterceptorTest.java
│ │ │ │ └── LettuceMetricInterceptorTest.java
│ │ │ ├── redirect/
│ │ │ │ ├── JedisConstructorInterceptorTest.java
│ │ │ │ ├── LettuceRedisClientConstructInterceptorTest.java
│ │ │ │ ├── RedisPropertiesClusterSetNodesInterceptorTest.java
│ │ │ │ └── RedisPropertiesSetPropertyInterceptorTest.java
│ │ │ └── tracing/
│ │ │ ├── CommonRedisTracingInterceptorTest.java
│ │ │ ├── JedisTracingInterceptorTest.java
│ │ │ └── LettuceTracingInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── servicename/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── servicename/
│ │ │ ├── Const.java
│ │ │ ├── ReflectionTool.java
│ │ │ ├── ServiceNamePlugin.java
│ │ │ ├── ServiceNamePluginConfig.java
│ │ │ ├── advice/
│ │ │ │ ├── FeignBlockingLoadBalancerClientAdvice.java
│ │ │ │ ├── FeignLoadBalancerAdvice.java
│ │ │ │ ├── FilteringWebHandlerAdvice.java
│ │ │ │ ├── LoadBalancerFeignClientAdvice.java
│ │ │ │ ├── RestTemplateInterceptAdvice.java
│ │ │ │ └── WebClientFilterAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── BaseServiceNameInterceptor.java
│ │ │ ├── FeignBlockingLoadBalancerClientInterceptor.java
│ │ │ ├── FeignLoadBalancerInterceptor.java
│ │ │ ├── FilteringWebHandlerInterceptor.java
│ │ │ ├── LoadBalancerFeignClientInterceptor.java
│ │ │ ├── RestTemplateInterceptInterceptor.java
│ │ │ └── WebClientFilterInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ └── megaease/
│ │ │ │ └── easeagent/
│ │ │ │ └── plugin/
│ │ │ │ └── servicename/
│ │ │ │ ├── ReflectionToolTest.java
│ │ │ │ └── interceptor/
│ │ │ │ ├── BaseServiceNameInterceptorTest.java
│ │ │ │ ├── CheckUtils.java
│ │ │ │ ├── FeignBlockingLoadBalancerClientInterceptorTest.java
│ │ │ │ ├── FeignLoadBalancerInterceptorTest.java
│ │ │ │ ├── FilteringWebHandlerInterceptorTest.java
│ │ │ │ ├── LoadBalancerFeignClientInterceptorTest.java
│ │ │ │ ├── RestTemplateInterceptInterceptorTest.java
│ │ │ │ ├── TestConst.java
│ │ │ │ └── WebClientFilterInterceptorTest.java
│ │ │ └── org/
│ │ │ └── springframework/
│ │ │ ├── cloud/
│ │ │ │ └── openfeign/
│ │ │ │ └── ribbon/
│ │ │ │ └── MockRibbonRequest.java
│ │ │ └── web/
│ │ │ └── reactive/
│ │ │ └── function/
│ │ │ └── client/
│ │ │ └── MockClientRequest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── sofarpc/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── sofarpc/
│ │ │ ├── SofaRpcCtxUtils.java
│ │ │ ├── SofaRpcMetricsTags.java
│ │ │ ├── SofaRpcPlugin.java
│ │ │ ├── SofaRpcTraceTags.java
│ │ │ ├── adivce/
│ │ │ │ ├── BoltFutureInvokeCallbackConstructAdvice.java
│ │ │ │ ├── ConsumerAdvice.java
│ │ │ │ ├── FutureInvokeCallbackConstructAdvice.java
│ │ │ │ ├── ProviderAdvice.java
│ │ │ │ ├── ResponseCallbackAdvice.java
│ │ │ │ └── ResponseFutureAdvice.java
│ │ │ ├── config/
│ │ │ │ └── SofaRpcTraceConfig.java
│ │ │ └── interceptor/
│ │ │ ├── initalize/
│ │ │ │ └── SofaRpcFutureInvokeCallbackConstructInterceptor.java
│ │ │ ├── metrics/
│ │ │ │ ├── SofaRpcMetrics.java
│ │ │ │ ├── SofaRpcMetricsBaseInterceptor.java
│ │ │ │ ├── callback/
│ │ │ │ │ ├── SofaRpcResponseCallbackMetrics.java
│ │ │ │ │ └── SofaRpcResponseCallbackMetricsInterceptor.java
│ │ │ │ ├── common/
│ │ │ │ │ └── SofaRpcMetricsInterceptor.java
│ │ │ │ └── future/
│ │ │ │ └── SofaRpcResponseFutureMetricsInterceptor.java
│ │ │ └── trace/
│ │ │ ├── SofaRpcTraceBaseInterceptor.java
│ │ │ ├── callback/
│ │ │ │ ├── SofaRpcResponseCallbackTrace.java
│ │ │ │ └── SofaRpcResponseCallbackTraceInterceptor.java
│ │ │ ├── common/
│ │ │ │ ├── SofaClientTraceRequest.java
│ │ │ │ ├── SofaRpcConsumerTraceInterceptor.java
│ │ │ │ ├── SofaRpcProviderTraceInterceptor.java
│ │ │ │ └── SofaServerTraceRequest.java
│ │ │ └── future/
│ │ │ └── SofaRpcResponseFutureTraceInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── sofarpc/
│ │ │ └── interceptor/
│ │ │ ├── BaseInterceptorTest.java
│ │ │ ├── MockBoltResponseFuture.java
│ │ │ ├── metrics/
│ │ │ │ ├── BaseMetricsInterceptorTest.java
│ │ │ │ ├── callback/
│ │ │ │ │ ├── MockSofaResponseCallback.java
│ │ │ │ │ └── SofaRpcResponseCallbackMetricsInterceptorTest.java
│ │ │ │ ├── common/
│ │ │ │ │ └── SofaRpcMetricsInterceptorTest.java
│ │ │ │ └── future/
│ │ │ │ └── SofaRpcResponseFutureMetricsInterceptorTest.java
│ │ │ └── trace/
│ │ │ ├── callback/
│ │ │ │ ├── MockSofaResponseCallback.java
│ │ │ │ └── SofaRpcResponseCallbackTraceInterceptorTest.java
│ │ │ ├── common/
│ │ │ │ ├── SofaRpcConsumerTraceInterceptorTest.java
│ │ │ │ └── SofaRpcProviderTraceInterceptorTest.java
│ │ │ └── future/
│ │ │ └── SofaRpcResponseFutureTraceInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── spring-boot-3.5.3/
│ │ ├── pom.xml
│ │ ├── spring-boot-gateway-3.5.3/
│ │ │ ├── pom.xml
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── java/
│ │ │ │ └── easeagent/
│ │ │ │ └── plugin/
│ │ │ │ └── spring353/
│ │ │ │ └── gateway/
│ │ │ │ ├── AccessPlugin.java
│ │ │ │ ├── ForwardedPlugin.java
│ │ │ │ ├── GatewayCons.java
│ │ │ │ ├── SpringGatewayPlugin.java
│ │ │ │ ├── advice/
│ │ │ │ │ ├── AgentGlobalFilterAdvice.java
│ │ │ │ │ ├── HttpHeadersFilterAdvice.java
│ │ │ │ │ └── InitGlobalFilterAdvice.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── TimeUtils.java
│ │ │ │ │ ├── forwarded/
│ │ │ │ │ │ └── GatewayServerForwardedInterceptor.java
│ │ │ │ │ ├── initialize/
│ │ │ │ │ │ ├── AgentGlobalFilter.java
│ │ │ │ │ │ └── GlobalFilterInterceptor.java
│ │ │ │ │ ├── log/
│ │ │ │ │ │ ├── GatewayAccessLogInterceptor.java
│ │ │ │ │ │ └── SpringGatewayAccessLogServerInfo.java
│ │ │ │ │ ├── metric/
│ │ │ │ │ │ └── GatewayMetricsInterceptor.java
│ │ │ │ │ └── tracing/
│ │ │ │ │ ├── FluxHttpServerRequest.java
│ │ │ │ │ ├── FluxHttpServerResponse.java
│ │ │ │ │ ├── GatewayServerTracingInterceptor.java
│ │ │ │ │ └── HttpHeadersFilterTracingInterceptor.java
│ │ │ │ └── reactor/
│ │ │ │ ├── AgentCoreSubscriber.java
│ │ │ │ └── AgentMono.java
│ │ │ └── test/
│ │ │ ├── java/
│ │ │ │ └── easeagent/
│ │ │ │ └── plugin/
│ │ │ │ └── spring353/
│ │ │ │ └── gateway/
│ │ │ │ ├── TestConst.java
│ │ │ │ ├── TestServerWebExchangeUtils.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── TimeUtilsTest.java
│ │ │ │ │ ├── forwarded/
│ │ │ │ │ │ └── GatewayServerForwardedInterceptorTest.java
│ │ │ │ │ ├── initialize/
│ │ │ │ │ │ ├── AgentGlobalFilterTest.java
│ │ │ │ │ │ └── GlobalFilterInterceptorTest.java
│ │ │ │ │ ├── log/
│ │ │ │ │ │ ├── GatewayAccessLogInfoInterceptorTest.java
│ │ │ │ │ │ └── SpringGatewayAccessLogInfoServerInfoTest.java
│ │ │ │ │ ├── metric/
│ │ │ │ │ │ ├── GatewayMetricsInterceptorTest.java
│ │ │ │ │ │ └── MockRouteBuilder.java
│ │ │ │ │ └── tracing/
│ │ │ │ │ ├── FluxHttpServerRequestTest.java
│ │ │ │ │ ├── FluxHttpServerResponseTest.java
│ │ │ │ │ ├── GatewayServerTracingInterceptorTest.java
│ │ │ │ │ └── HttpHeadersFilterTracingInterceptorTest.java
│ │ │ │ └── reactor/
│ │ │ │ ├── AgentCoreSubscriberTest.java
│ │ │ │ ├── AgentMonoTest.java
│ │ │ │ └── MockCoreSubscriber.java
│ │ │ └── resources/
│ │ │ └── mock_agent.properties
│ │ ├── spring-boot-rest-template-3.5.3/
│ │ │ ├── pom.xml
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── java/
│ │ │ │ └── com/
│ │ │ │ └── megaease/
│ │ │ │ └── easeagent/
│ │ │ │ └── plugin/
│ │ │ │ └── rest/
│ │ │ │ └── template/
│ │ │ │ ├── ForwardedPlugin.java
│ │ │ │ ├── RestTemplatePlugin.java
│ │ │ │ ├── advice/
│ │ │ │ │ └── ClientHttpRequestAdvice.java
│ │ │ │ └── interceptor/
│ │ │ │ ├── forwarded/
│ │ │ │ │ └── RestTemplateForwardedInterceptor.java
│ │ │ │ └── tracing/
│ │ │ │ └── ClientHttpRequestInterceptor.java
│ │ │ └── test/
│ │ │ ├── java/
│ │ │ │ ├── com/
│ │ │ │ │ └── megaease/
│ │ │ │ │ └── easeagent/
│ │ │ │ │ └── plugin/
│ │ │ │ │ └── rest/
│ │ │ │ │ └── template/
│ │ │ │ │ └── interceptor/
│ │ │ │ │ ├── TestConst.java
│ │ │ │ │ ├── forwarded/
│ │ │ │ │ │ └── RestTemplateForwardedInterceptorTest.java
│ │ │ │ │ └── tracing/
│ │ │ │ │ └── ClientHttpRequestInterceptorTest.java
│ │ │ │ └── org/
│ │ │ │ └── springframework/
│ │ │ │ └── http/
│ │ │ │ └── client/
│ │ │ │ └── SimpleClientHttpResponseFactory.java
│ │ │ └── resources/
│ │ │ └── mock_agent.properties
│ │ └── spring-boot-servicename-3.5.3/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── servicename/
│ │ │ └── springboot353/
│ │ │ ├── Const.java
│ │ │ ├── ReflectionTool.java
│ │ │ ├── ServiceNamePlugin.java
│ │ │ ├── ServiceNamePluginConfig.java
│ │ │ ├── advice/
│ │ │ │ ├── FeignClientLoadBalancerClientAdvice.java
│ │ │ │ ├── FilteringWebHandlerAdvice.java
│ │ │ │ ├── RestTemplateInterceptAdvice.java
│ │ │ │ └── WebClientFilterAdvice.java
│ │ │ └── interceptor/
│ │ │ ├── BaseServiceNameInterceptor.java
│ │ │ ├── FeignClientLoadBalancerClientInterceptor.java
│ │ │ ├── FilteringWebHandlerInterceptor.java
│ │ │ ├── RestTemplateInterceptInterceptor.java
│ │ │ └── WebClientFilterInterceptor.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── servicename/
│ │ │ └── springboot353/
│ │ │ ├── ReflectionToolTest.java
│ │ │ └── interceptor/
│ │ │ ├── BaseServiceNameInterceptorTest.java
│ │ │ ├── CheckUtils.java
│ │ │ ├── FeignBlockingLoadBalancerClientInterceptorTest.java
│ │ │ ├── FilteringWebHandlerInterceptorTest.java
│ │ │ ├── RestTemplateInterceptInterceptorTest.java
│ │ │ ├── TestConst.java
│ │ │ └── WebClientFilterInterceptorTest.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── spring-gateway/
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── spring/
│ │ │ └── gateway/
│ │ │ ├── AccessPlugin.java
│ │ │ ├── ForwardedPlugin.java
│ │ │ ├── SpringGatewayPlugin.java
│ │ │ ├── advice/
│ │ │ │ ├── AgentGlobalFilterAdvice.java
│ │ │ │ ├── CodeCons.java
│ │ │ │ ├── HttpHeadersFilterAdvice.java
│ │ │ │ └── InitGlobalFilterAdvice.java
│ │ │ ├── interceptor/
│ │ │ │ ├── GatewayCons.java
│ │ │ │ ├── initialize/
│ │ │ │ │ ├── AgentGlobalFilter.java
│ │ │ │ │ ├── GatewayServerForwardedInterceptor.java
│ │ │ │ │ └── GlobalFilterInterceptor.java
│ │ │ │ ├── metric/
│ │ │ │ │ ├── GatewayMetricsInterceptor.java
│ │ │ │ │ ├── TimeUtils.java
│ │ │ │ │ └── log/
│ │ │ │ │ ├── GatewayAccessLogInterceptor.java
│ │ │ │ │ └── SpringGatewayAccessLogServerInfo.java
│ │ │ │ └── tracing/
│ │ │ │ ├── FluxHttpServerRequest.java
│ │ │ │ ├── FluxHttpServerResponse.java
│ │ │ │ ├── GatewayServerTracingInterceptor.java
│ │ │ │ └── HttpHeadersFilterTracingInterceptor.java
│ │ │ └── reactor/
│ │ │ ├── AgentCoreSubscriber.java
│ │ │ └── AgentMono.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── easeagent/
│ │ │ └── plugin/
│ │ │ └── spring/
│ │ │ └── gateway/
│ │ │ ├── TestConst.java
│ │ │ ├── TestServerWebExchangeUtils.java
│ │ │ ├── interceptor/
│ │ │ │ ├── initialize/
│ │ │ │ │ ├── AgentGlobalFilterTest.java
│ │ │ │ │ ├── GatewayServerForwardedInterceptorTest.java
│ │ │ │ │ └── GlobalFilterInterceptorTest.java
│ │ │ │ ├── metric/
│ │ │ │ │ ├── GatewayMetricsInterceptorTest.java
│ │ │ │ │ ├── MockRouteBuilder.java
│ │ │ │ │ ├── TimeUtilsTest.java
│ │ │ │ │ └── log/
│ │ │ │ │ ├── GatewayAccessLogInfoInterceptorTest.java
│ │ │ │ │ └── SpringGatewayAccessLogInfoServerInfoTest.java
│ │ │ │ └── tracing/
│ │ │ │ ├── FluxHttpServerRequestTest.java
│ │ │ │ ├── FluxHttpServerResponseTest.java
│ │ │ │ ├── GatewayServerTracingInterceptorTest.java
│ │ │ │ └── HttpHeadersFilterTracingInterceptorTest.java
│ │ │ └── reactor/
│ │ │ ├── AgentCoreSubscriberTest.java
│ │ │ ├── AgentMonoTest.java
│ │ │ └── MockCoreSubscriber.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ ├── springweb/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── megaease/
│ │ │ │ ├── easeagent/
│ │ │ │ │ └── plugin/
│ │ │ │ │ └── springweb/
│ │ │ │ │ ├── FeignClientPlugin.java
│ │ │ │ │ ├── ForwardedPlugin.java
│ │ │ │ │ ├── RestTemplatePlugin.java
│ │ │ │ │ ├── SpringWebPlugin.java
│ │ │ │ │ ├── WebClientPlugin.java
│ │ │ │ │ ├── advice/
│ │ │ │ │ │ ├── ClientHttpRequestAdvice.java
│ │ │ │ │ │ ├── FeignClientAdvice.java
│ │ │ │ │ │ ├── WebClientBuilderAdvice.java
│ │ │ │ │ │ └── WebClientFilterAdvice.java
│ │ │ │ │ ├── interceptor/
│ │ │ │ │ │ ├── HeadersFieldFinder.java
│ │ │ │ │ │ ├── forwarded/
│ │ │ │ │ │ │ ├── FeignClientForwardedInterceptor.java
│ │ │ │ │ │ │ ├── RestTemplateForwardedInterceptor.java
│ │ │ │ │ │ │ └── WebClientFilterForwardedInterceptor.java
│ │ │ │ │ │ ├── initialize/
│ │ │ │ │ │ │ └── WebClientBuildInterceptor.java
│ │ │ │ │ │ └── tracing/
│ │ │ │ │ │ ├── ClientHttpRequestInterceptor.java
│ │ │ │ │ │ ├── FeignClientTracingInterceptor.java
│ │ │ │ │ │ └── WebClientFilterTracingInterceptor.java
│ │ │ │ │ └── reactor/
│ │ │ │ │ ├── AgentCoreSubscriber.java
│ │ │ │ │ └── AgentMono.java
│ │ │ │ └── plugin/
│ │ │ │ └── easeagent/
│ │ │ │ └── springweb/
│ │ │ │ └── interceptor/
│ │ │ │ └── tracing/
│ │ │ │ └── WebClientTracingFilter.java
│ │ │ └── resources/
│ │ │ └── application.yaml
│ │ └── test/
│ │ ├── java/
│ │ │ ├── com/
│ │ │ │ └── megaease/
│ │ │ │ └── easeagent/
│ │ │ │ └── plugin/
│ │ │ │ └── springweb/
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── HeadersFieldFinderTest.java
│ │ │ │ │ ├── RequestUtils.java
│ │ │ │ │ ├── TestConst.java
│ │ │ │ │ ├── forwarded/
│ │ │ │ │ │ ├── FeignClientForwardedInterceptorTest.java
│ │ │ │ │ │ ├── RestTemplateForwardedInterceptorTest.java
│ │ │ │ │ │ └── WebClientFilterForwardedInterceptorTest.java
│ │ │ │ │ ├── initialize/
│ │ │ │ │ │ └── WebClientBuildInterceptorTest.java
│ │ │ │ │ └── tracing/
│ │ │ │ │ ├── ClientHttpRequestInterceptorTest.java
│ │ │ │ │ ├── FeignClientTracingInterceptorTest.java
│ │ │ │ │ └── WebClientFilterTracingInterceptorTest.java
│ │ │ │ └── reactor/
│ │ │ │ ├── AgentCoreSubscriberTest.java
│ │ │ │ ├── AgentMonoTest.java
│ │ │ │ ├── MockCoreSubscriber.java
│ │ │ │ └── MockMono.java
│ │ │ └── org/
│ │ │ └── springframework/
│ │ │ ├── http/
│ │ │ │ └── client/
│ │ │ │ └── SimpleClientHttpResponseFactory.java
│ │ │ └── web/
│ │ │ └── reactive/
│ │ │ └── function/
│ │ │ └── client/
│ │ │ ├── MockClientRequest.java
│ │ │ └── MockDefaultClientResponse.java
│ │ └── resources/
│ │ └── mock_agent.properties
│ └── tomcat-jdk17/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── plugin/
│ │ └── tomcat/
│ │ ├── AccessPlugin.java
│ │ ├── ForwardedPlugin.java
│ │ ├── TomcatPlugin.java
│ │ ├── advice/
│ │ │ └── FilterChainPoints.java
│ │ ├── interceptor/
│ │ │ ├── BaseServletInterceptor.java
│ │ │ ├── FilterChainForwardedInterceptor.java
│ │ │ ├── FilterChainMetricInterceptor.java
│ │ │ ├── FilterChainTraceInterceptor.java
│ │ │ ├── HttpServerRequest.java
│ │ │ ├── TomcatAccessLogServerInfo.java
│ │ │ └── TomcatHttpLogInterceptor.java
│ │ └── utils/
│ │ ├── InternalAsyncListener.java
│ │ └── ServletUtils.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── plugin/
│ │ └── tomcat/
│ │ └── interceptor/
│ │ ├── BaseServletInterceptorTest.java
│ │ ├── FilterChainForwardedInterceptorTest.java
│ │ ├── FilterChainMetricInterceptorTest.java
│ │ ├── FilterChainTraceInterceptorTest.java
│ │ ├── HttpServerRequestTest.java
│ │ ├── ServletAccessLogInfoServerInfoTest.java
│ │ ├── TestConst.java
│ │ ├── TestServletUtils.java
│ │ └── TomcatHttpLogInterceptorTest.java
│ └── resources/
│ └── mock_agent.properties
├── pom.xml
├── release
├── report/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ ├── com/
│ │ │ └── megaease/
│ │ │ └── easeagent/
│ │ │ └── report/
│ │ │ ├── AgentReportAware.java
│ │ │ ├── DefaultAgentReport.java
│ │ │ ├── GlobalExtractor.java
│ │ │ ├── OutputProperties.java
│ │ │ ├── ReportConfigChange.java
│ │ │ ├── async/
│ │ │ │ ├── AsyncProps.java
│ │ │ │ ├── AsyncReporter.java
│ │ │ │ ├── AsyncReporterMetrics.java
│ │ │ │ ├── DefaultAsyncReporter.java
│ │ │ │ ├── log/
│ │ │ │ │ ├── AccessLogReporter.java
│ │ │ │ │ ├── ApplicationLogReporter.java
│ │ │ │ │ └── LogAsyncProps.java
│ │ │ │ ├── trace/
│ │ │ │ │ ├── SDKAsyncReporter.java
│ │ │ │ │ └── TraceAsyncProps.java
│ │ │ │ └── zipkin/
│ │ │ │ ├── AgentBufferNextMessage.java
│ │ │ │ ├── AgentByteBoundedQueue.java
│ │ │ │ └── WithSizeConsumer.java
│ │ │ ├── encoder/
│ │ │ │ ├── PackedMessage.java
│ │ │ │ ├── log/
│ │ │ │ │ ├── AccessLogJsonEncoder.java
│ │ │ │ │ ├── AccessLogWriter.java
│ │ │ │ │ ├── LogDataJsonEncoder.java
│ │ │ │ │ ├── LogDataWriter.java
│ │ │ │ │ └── pattern/
│ │ │ │ │ ├── LogDataDatePatternConverterDelegate.java
│ │ │ │ │ ├── LogDataLevelPatternConverter.java
│ │ │ │ │ ├── LogDataLineSeparatorPatternConverter.java
│ │ │ │ │ ├── LogDataLoggerPatternConverter.java
│ │ │ │ │ ├── LogDataMdcPatternConverter.java
│ │ │ │ │ ├── LogDataPatternConverter.java
│ │ │ │ │ ├── LogDataPatternFormatter.java
│ │ │ │ │ ├── LogDataSimpleLiteralPatternConverter.java
│ │ │ │ │ ├── LogDataThreadNamePatternConverter.java
│ │ │ │ │ ├── LogDataThrowablePatternConverter.java
│ │ │ │ │ ├── NamePatternConverter.java
│ │ │ │ │ ├── NoOpPatternConverter.java
│ │ │ │ │ └── SimpleMessageConverter.java
│ │ │ │ ├── metric/
│ │ │ │ │ └── MetricJsonEncoder.java
│ │ │ │ └── span/
│ │ │ │ ├── AbstractAgentV2SpanEndpointWriter.java
│ │ │ │ ├── AgentV2SpanAnnotationsWriter.java
│ │ │ │ ├── AgentV2SpanBaseWriter.java
│ │ │ │ ├── AgentV2SpanGlobalWriter.java
│ │ │ │ ├── AgentV2SpanLocalEndpointWriter.java
│ │ │ │ ├── AgentV2SpanRemoteEndpointWriter.java
│ │ │ │ ├── AgentV2SpanTagsWriter.java
│ │ │ │ ├── AgentV2SpanWriter.java
│ │ │ │ ├── GlobalExtrasSupplier.java
│ │ │ │ ├── SpanJsonEncoder.java
│ │ │ │ └── okhttp/
│ │ │ │ ├── HttpSpanJsonEncoder.java
│ │ │ │ └── OkHttpJsonRequestBody.java
│ │ │ ├── metric/
│ │ │ │ ├── MetricItem.java
│ │ │ │ ├── MetricProps.java
│ │ │ │ └── MetricReporterFactoryImpl.java
│ │ │ ├── plugin/
│ │ │ │ ├── NoOpCall.java
│ │ │ │ ├── NoOpEncoder.java
│ │ │ │ ├── ReporterLoader.java
│ │ │ │ └── ReporterRegistry.java
│ │ │ ├── sender/
│ │ │ │ ├── AgentKafkaSender.java
│ │ │ │ ├── AgentLoggerSender.java
│ │ │ │ ├── NoOpSender.java
│ │ │ │ ├── SenderConfigDecorator.java
│ │ │ │ ├── SenderWithEncoder.java
│ │ │ │ ├── ZipkinCallWrapper.java
│ │ │ │ ├── metric/
│ │ │ │ │ ├── KeySender.java
│ │ │ │ │ ├── MetricKafkaSender.java
│ │ │ │ │ └── log4j/
│ │ │ │ │ ├── AppenderManager.java
│ │ │ │ │ ├── LoggerFactory.java
│ │ │ │ │ ├── MetricRefreshableAppender.java
│ │ │ │ │ ├── RefreshableAppender.java
│ │ │ │ │ └── TestableAppender.java
│ │ │ │ └── okhttp/
│ │ │ │ ├── ByteRequestBody.java
│ │ │ │ ├── HttpCall.java
│ │ │ │ └── HttpSender.java
│ │ │ ├── trace/
│ │ │ │ ├── Platform.java
│ │ │ │ ├── RefreshableReporter.java
│ │ │ │ ├── ReportSpanBuilder.java
│ │ │ │ └── TraceReport.java
│ │ │ └── util/
│ │ │ ├── SpanUtils.java
│ │ │ ├── TextUtils.java
│ │ │ └── Utils.java
│ │ └── zipkin2/
│ │ └── reporter/
│ │ ├── TracerConverter.java
│ │ ├── brave/
│ │ │ ├── ConvertSpanReporter.java
│ │ │ └── ConvertZipkinSpanHandler.java
│ │ └── kafka11/
│ │ ├── SDKKafkaSender.java
│ │ ├── SDKSender.java
│ │ └── SimpleSender.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── report/
│ │ ├── HttpSenderTest.java
│ │ ├── async/
│ │ │ └── zipkin/
│ │ │ └── AgentByteBoundedQueueTest.java
│ │ ├── encoder/
│ │ │ └── log/
│ │ │ ├── LogDataJsonEncoderTest.java
│ │ │ └── LogDataWriterTest.java
│ │ ├── metric/
│ │ │ ├── MetricPropsTest.java
│ │ │ └── MetricReporterFactoryTest.java
│ │ ├── sender/
│ │ │ ├── AgentKafkaSenderTest.java
│ │ │ └── metric/
│ │ │ └── log4j/
│ │ │ └── AppenderManagerTest.java
│ │ ├── trace/
│ │ │ └── TraceReportTest.java
│ │ └── utils/
│ │ └── UtilsTest.java
│ └── resources/
│ └── sender/
│ ├── outputServer_disabled.json
│ ├── outputServer_empty_bootstrapServer_disabled.json
│ ├── outputServer_empty_bootstrapServer_enabled.json
│ └── outputServer_enabled.json
├── resources/
│ ├── rootfs/
│ │ └── Dockerfile
│ └── scripts/
│ ├── Jenkinsfile
│ └── build-image.sh
└── zipkin/
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── megaease/
│ │ └── easeagent/
│ │ └── zipkin/
│ │ ├── CustomTagsSpanHandler.java
│ │ ├── TracingProviderImpl.java
│ │ ├── impl/
│ │ │ ├── AsyncRequest.java
│ │ │ ├── MessageImpl.java
│ │ │ ├── RemoteGetterImpl.java
│ │ │ ├── RemoteSetterImpl.java
│ │ │ ├── RequestContextImpl.java
│ │ │ ├── ScopeImpl.java
│ │ │ ├── SpanContextImpl.java
│ │ │ ├── SpanImpl.java
│ │ │ ├── TracingImpl.java
│ │ │ └── message/
│ │ │ ├── MessagingTracingImpl.java
│ │ │ ├── ZipkinConsumerRequest.java
│ │ │ └── ZipkinProducerRequest.java
│ │ └── logging/
│ │ ├── AgentLogMDC.java
│ │ ├── AgentMDCScopeDecorator.java
│ │ └── LogUtils.java
│ └── resources/
│ └── META-INF/
│ └── services/
│ └── com.megaease.easeagent.plugin.bean.BeanProvider
└── test/
└── java/
├── brave/
│ ├── TracerTestUtils.java
│ └── internal/
│ └── collect/
│ └── WeakConcurrentMapTestUtils.java
└── com/
└── megaease/
└── easeagent/
└── zipkin/
├── CustomTagsSpanHandlerTest.java
├── TracingProviderImplMock.java
├── TracingProviderImplTest.java
├── impl/
│ ├── AsyncRequestTest.java
│ ├── MessageImplTest.java
│ ├── MessagingRequestMock.java
│ ├── RemoteGetterImplTest.java
│ ├── RemoteSetterImplTest.java
│ ├── RequestContextImplTest.java
│ ├── RequestMock.java
│ ├── ScopeImplTest.java
│ ├── SpanContextImplTest.java
│ ├── SpanImplTest.java
│ ├── TracingImplTest.java
│ └── message/
│ ├── MessagingTracingImplTest.java
│ ├── ZipkinConsumerRequestTest.java
│ └── ZipkinProducerRequestTest.java
└── logging/
├── AgentLogMDCTest.java
├── AgentMDCScopeDecoratorTest.java
└── LogUtilsTest.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
---
name: 🐞 Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Execute '...'
2. Send a request to '....'
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Version**
The version number of Easegress.
**Configuration**
* EaseAgent Configuration
```
```
**Logs**
```
EaseAgent logs, if applicable.
```
**OS and Hardware**
- OS: [e.g. Ubuntu 20.04]
- CPU:[e.g. Intel(R) Core(TM) i5-8265U]
- Memory: [e.g. 16GB]
**Additional context**
Add any other context about the problem here.
---
Thanks for contributing 🎉!
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
---
name: 🚀 Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
---
Thanks for contributing 🎉!
================================================
FILE: .github/workflows/build.yml
================================================
# This is a basic workflow to help you get started with Actions
name: Build & Test
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'resources/**'
- '**.md'
pull_request:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'resources/**'
- '**.md'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest ]
java-version: [ 8, 11, 16, 17 ]
java-distribution: [ adopt, adopt-openj9 ]
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout Codebase
uses: actions/checkout@v2
- name: Setup Java Version
uses: actions/setup-java@v3.14.1
with:
java-version: ${{ matrix.java-version }}
distribution: ${{ matrix.java-distribution }}
architecture: x64
cache: 'maven'
# Runs a single command using the runners shell
- name: Build with Maven
run: mvn clean package
================================================
FILE: .github/workflows/license-checker.yml
================================================
name: License checker
on:
push:
branches:
- maste
paths-ignore:
- 'doc/**'
- 'resources/**'
- '**.md'
pull_request:
branches:
- master
paths-ignore:
- 'doc/**'
- 'resources/**'
- '**.md'
jobs:
check-license:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check License Header
uses: apache/skywalking-eyes@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
log: info
================================================
FILE: .gitignore
================================================
.idea/
*.iml
target/
dependency-reduced-pom.xml
*.class
*.swp
**/*.versionsBackup
.patch
.classpath
.factorypath
.project
.settings
.DS_Store
.vscode
logs/
tmp/
================================================
FILE: .licenserc.yaml
================================================
header:
license:
content: |
Copyright (c) 2022, MegaEase
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
paths:
- './'
paths-ignore:
- '.editorconfig'
- 'install'
- 'release'
- '**/target/**'
- '**/*.xml'
- '**/*.yaml'
- '**/*.properties'
- '**/*.md'
- '**/*.iml'
- 'LICENSE'
- '.github/'
- '.git/'
- 'doc/'
- '**/resources/**'
- 'CHANGELOG.md'
- '.gitignore'
- 'README.md'
comment: on-failure
dependency:
files:
- go.mod
================================================
FILE: AOSP-Checkstyles.xml
================================================
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
service@megaease.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# EaseAgent
A lightweight & opening Java Agent for Cloud-Native and APM system
- [EaseAgent](#easeagent)
- [Overview](#overview)
- [Purpose](#purpose)
- [Principles](#principles)
- [Features](#features)
- [Architecture Diagram](#architecture-diagram)
- [Description](#description)
- [QuickStart](#quickstart)
- [Get And Set Environment Variable](#get-and-set-environment-variable)
- [Setup Environment Variable](#setup-environment-variable)
- [Download](#download)
- [Build From the Source](#build-from-the-source)
- [Get Configuration file](#get-configuration-file)
- [Monitor Spring Petclinic](#monitor-spring-petclinic)
- [Prerequisites](#prerequisites)
- [Initialize and Start the project](#initialize-and-start-the-project)
- [Metric](#metric)
- [Tracing](#tracing)
- [Build Spring Petclinic](#build-spring-petclinic)
- [Add an Enhancement Plugin](#add-an-enhancement-plugin)
- [User Manual](#user-manual)
- [Enhancement Plugin Development Guide](#enhancement-plugin-development-guide)
- [Report Plugin Development Guide](#report-plugin-development-guide)
- [Community](#community)
- [Licenses](#licenses)
## Overview
- EaseAgent is the underlying component that provides non-intrusive extensions to applications of the Java ecosystem.
- EaseAgent can collect distributed application tracing, metrics, and logs, which could be used in the APM system and improve the observability of a distributed system. for the tracing, EaseAgent follows the [Google Dapper](https://research.google/pubs/pub36356/) paper.
- EaseAgent also can work with Cloud-Native architecture. For example, it can help Service Mesh (especially for [EaseMesh](https://github.com/megaease/easemesh/) ) to do some control panel work.
- EaseAgent supports plugins mechanism development, which is easy to extend or add new functionality.
### Purpose
- EaseAgent can be a Java agent for APM(Application Performance Management) system.
- EaseAgent collects the basic metrics and the service tracing logs, which is very helpful for performance analysis and troubleshooting.
- EaseAgent is compatible with mainstream monitoring ecosystems, such as Kafka, ElasticSearch, Prometheus, Zipkin, etc.
- EaseAgent majorly focuses on the Spring Boot development environments, but users can support any Java ecosystem applications through plugins.
- EaseAgent can support scenario-specific business requirements through the plugin mechanism, such as traffic redirection, traffic coloring, etc.
### Principles
- Safe to Java application/service.
- Instrumenting a Java application in a non-intrusive way.
- Lightweight and very low CPU, memory, and I/O resource usage.
- Highly extensible, users can easily do extensions through a simple and clear plugin interface.
- Design for Micro-Service architecture, collecting the data from a service perspective.
## Features
* Easy to use. It is right out of the box for Metrics, Tracing and Logs collecting.
* Collecting Metric & Tracing Logs.
* `JDBC 4.0`
* `HTTP Servlet`、`HTTP Filter`
* `Spring Boot 2.2.x`: `WebClient` 、 `RestTemplate`、`FeignClient`
* `RabbitMQ Client 5.x`、 `Kafka Client 2.4.x`
* `Jedis 3.5.x`、 `Lettuce 5.3.x (sync、async)`
* `ElasticSearch Client >= 7.x (sync、async)`
* `Mongodb Client >=4.0.x (sync、async)`
* `Motan`
* `Dubbo`
* `SofaRpc >= 5.3.0`
* Collecting Access Logs.
* `HTTP Servlet`、`HTTP Filter`
* `Spring Cloud Gateway`
* Instrumenting the `traceId` and `spanId` into user application logging automatically
* Supplying the `health check` endpoint
* Supplying the `readiness check` endpoint for `SpringBoot2.2.x`
* Supplying the `agent info` endpoint
* Data Reports
* Console Reporter.
* Prometheus Exports.
* Http Reporter.
* Kafka Reporter.
* Custom Reporter.
* Easy to Extend
* Simple and clear Plugin Interface, creating a plugin as few as three classes.
* Extremely cleanly packaged `Tracing` and `Metric` API, with a small amount of code to achieve business support.
* Standardization
* The tracing data format is fully compatible with the Zipkin data format.
* Metric data format fully supports integration with `Prometheus`.
* The application log format is fully compatible with the `Opentelemetry` data format.
## Architecture Diagram

#### Description
**Plugin Framework** in `core` module is base on [Byte buddy](https://github.com/raphw/byte-buddy) technology.
1. Easeagent's plugin defines where (which classes and methods) to make enhancements by implementing the `Points` and what to do at the point of enhancement by implementing the `Interceptor`.
2. When the program invokes the enhanced method of class defined by Points, the `unique index`(uid) owned by the method will be used as a parameter to call the common interface of `Agent Common Method Advice`, which finds the `Agent Interceptor Chain` by the `Unique Index` and calls the `before` method of each Interceptor in the chain in order of priority.
3. Normally, both the `Metric Interceptor` and the `Tracing Interceptor` are in the agent interceptor chain and are called sequentially.
4. According to call the `Metric API` and `Tracing API` in interceptors, the `Metric` and `Tracing` information will be stored in `MetricRegistry` and `Tracing`.
5. The `Reporter` module will get information from `MetricRegistry` and `Tracing` and send it to `Kafka`.
6. The `after` method of each interceptor in the `Agent Interceptor Chain` will be invoked in the reverse order of the `before` invoked at last.
7. The `tracing` data can be sent to `kafka` server or `zipkin` server, the `metric` data can be sent to `kafka` server and pull by `Prometheus` server.
## QuickStart
### Get And Set Environment Variable
Setup Environment Variable and then download the latest release of `easeagent.jar` or build it from the source.
#### Setup Environment Variable
```
$ cd ~/easeagent #[Replace with agent path]
$ export EASE_AGENT_PATH=`pwd` # export EASE_AGENT_PATH=[Replace with agent path]
$ mkdir plugins
```
#### Download
Download `easeagent.jar` from releases [releases](https://github.com/megaease/easeagent/releases).
```
$ curl -Lk https://github.com/megaease/easeagent/releases/latest/download/easeagent.jar -O
```
#### Build From the Source
You need Java 1.8+ and git:
Download EaseAgent with `git clone https://github.com/megaease/easeagent.git`.
```
$ cd easeagent
$ mvn clean package -Dmaven.test.skip
$ cp ./build/target/easeagent-dep.jar $EASE_AGENT_PATH/easeagent.jar
```
The `./build/target/easeagent-dep.jar` is the agent jar with all the dependencies.
> For the Windows platform, please make sure git `core.autocrlf` is set to false before git clone.
> You can use `git config --global core.autocrlf false` to modify `core.autocrlf`.
[How to use easeagent.jar on host?](doc/how-to-use/use-on-host.md)
[How to use easeagent.jar in docker?](doc/how-to-use/use-in-docker.md)
#### Get Configuration file
Extracting the default configuration file.
```
$ cd $EASE_AGENT_PATH
$ jar xf easeagent.jar agent.properties easeagent-log4j2.xml
```
By default, there is an agent.properties configuration file, which is configured to print all output data to the console.
### Monitor Spring Petclinic
#### Prerequisites
- Make sure you have installed the docker, docker-compose in your environment.
- Make sure your docker version is higher than v19.+.
- Make sure your docker-compose version is higher than v2.+.
[Project Details](https://github.com/megaease/easeagent-spring-petclinic)
#### Initialize and Start the project
```
$ git clone https://github.com/megaease/easeagent-spring-petclinic.git
$ cd easeagent-spring-petclinic
$ git submodule update --init
$ ./spring-petclinic.sh start
```
> The script will download the latest release of EaseAgent.
> If you want to use your own built EaseAgent, copy it to the directory: `easeagent/downloaded`
>> ```$ cp $EASE_AGENT_PATH/easeagent.jar easeagent/downloaded/easeagent-latest.jar```
It requires `Docker` to pull images from the docker hub, be patient.
Open Browser to visit grafana UI: [http://localhost:3000](http://localhost:3000).
#### Metric
Click the `search dashboards`, the first icon in the left menu bar. Choose the `spring-petclinic-easeagent` to open the dashboard we prepare for you.
Prometheus Metric Schedule: [Prometheus Metric](doc/prometheus-metric-schedule.md)

#### Tracing
If you want to check the tracing-data, you could click the explore in the left menu bar. Click the Search - beta to switch search mode. Click search query button in the right up corner, there is a list containing many tracing. Choose one to click.

#### Build Spring Petclinic
[Spring Petclinic Demo](doc/spring-petclinic-demo.md)
### Add an Enhancement Plugin
[Add a Demo Plugin to EaseAgent](doc/add-plugin-demo.md)
## User Manual
For more information, please refer to the [User Manual](./doc/user-manual.md).
## Enhancement Plugin Development Guide
Refer to [Plugin Development Guide](./doc/development-guide.md).
## Report Plugin Development Guide
Report plugin enables user report tracing/metric data to different kinds of the backend in a different format.
Refer to [Report Plugin Development Guide](./doc/report-development-guide.md)
## Community
* [Github Issues](https://github.com/megaease/easeagent/issues)
* [Join Slack Workspace](https://join.slack.com/t/openmegaease/shared_invite/zt-upo7v306-lYPHvVwKnvwlqR0Zl2vveA) for requirement, issue and development.
* [MegaEase on Twitter](https://twitter.com/megaease)
If you have any questions, welcome to discuss them in our community. Welcome to join!
## Licenses
EaseAgent is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE) for the full license text.
================================================
FILE: build/pom.xml
================================================
easeagentcom.megaease.easeagent2.3.04.0.0buildorg.apache.kafkakafka-clientsprovidedjavax.servletjavax.servlet-apiprovidedcom.megaease.easeagentmetrics${project.version}com.megaease.easeagentzipkin${project.version}com.megaease.easeagentlog4j2-api${project.version}com.megaease.easeagentcore${project.version}com.megaease.easeagentloader${project.version}easeagentorg.apache.maven.pluginsmaven-compiler-plugin3.6.1${version.java}${version.java}${encoding.file}-Xlint:uncheckedmaven-antrun-plugin3.0.0prepare-packagerunorg.apache.maven.pluginsmaven-shade-plugin${version.maven-shade-plugin}trueorg.apache.logging.log4j:***packageshadecom.github.edwgizmaven-shade-plugin.log4j2-cachefile-transformer2.14.0pl.project13.mavengit-commit-id-plugin4.9.10revisiontruefalsetrueyyyy-MM-dd'T'HH:mm:ssZtrue${project.build.outputDirectory}/git.properties
git.commit.*org.apache.maven.pluginsmaven-assembly-plugin3.0.0src/assembly/src.xmltruetruecom.megaease.easeagent.Maincom.megaease.easeagent.StartBootstraplog4j.configurationFile${version}falsepackagesingle
================================================
FILE: build/src/assembly/src.xml
================================================
dep/jar${project.build.outputDirectory}git.propertiesagent.yamlagent.propertiesuser-minimal-cfg.propertieseaseagent-log4j2.xml${project.basedir}/target/inner-plugins/pluginsoriginal-*.jar*.jar${project.basedir}/target/log4j2/log4j2*.jar${project.basedir}/target/boot/boot*.jarbootcom.megaease.easeagent:plugin-apifalselib**/com/megaease/easeagent/plugin/***/com/megaease/easeagent/boot/***/com/megaease/easeagent/log4j2/*com.megaease.easeagent:buildcom.megaease.easeagent:loadertrue
================================================
FILE: build/src/main/java/com/megaease/easeagent/StartBootstrap.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent;
import com.megaease.easeagent.core.Bootstrap;
import java.lang.instrument.Instrumentation;
public class StartBootstrap {
private StartBootstrap() {}
public static void premain(String args, Instrumentation inst, String javaAgentJarPath) {
Bootstrap.start(args, inst, javaAgentJarPath);
}
}
================================================
FILE: build/src/main/resources/agent-to-cloud_1.0.properties
================================================
# for v2.0 agent send to megacloud-v1.0 kafka topic setting
plugin.observability.global.metric.topic=application-meter
plugin.observability.access.metric.topic=application-log
plugin.observability.redis.metric.topic=platform-meter
plugin.observability.rabbit.metric.topic=platform-meter
plugin.observability.kafka.metric.topic=platform-meter
plugin.observability.elasticsearch.metric.topic=platform-meter
plugin.observability.jvmGc.metric.topic=platform-meter
plugin.observability.jvmMemory.metric.topic=platform-meter
================================================
FILE: build/src/main/resources/agent.properties
================================================
name=demo-service
system=demo-system
### http server
# When the enabled value = false, agent will not start the http server
# You can use -Deaseagent.server.enabled=[true | false] to override.
easeagent.server.enabled=true
# http server port. You can use -Deaseagent.server.port=[port] to override.
easeagent.server.port=9900
# Enable health/readiness
easeagent.health.readiness.enabled=true
plugin.integrability.global.healthReady.enabled=true
# forwarded headers page
# Pass-through headers from the root process all the way to the end
# format: easeagent.progress.forwarded.headers={headerName}
#easeagent.progress.forwarded.headers=X-Forwarded-For
#easeagent.progress.forwarded.headers=X-Location,X-Mesh-Service-Canary,X-Phone-Os
###
### default tracings configuration
###
# sampledType:
## counting: percentage sampling, sampled limit 0.01 to 1, 1 is always sample, 0 is never sample, 0.1 is ten samples per hundred
## rate_limiting: traces per second, sampled >= 0, 0 is never sample, 10 is max 10 traces per second
## boundary: percentage sampling by traceId, sampled limit 0.0001 to 1, 1 is always sample, 0 is never sample
## if sampled=0.001, when (traceId^random)%10000<=(0.001*10000) sampled
## sampledType must be used with sampled, otherwise the default value is used Sampler.ALWAYS_SAMPLE
observability.tracings.sampledType=
observability.tracings.sampled=1
# get header from response headers then tag to tracing span
# format: observability.tracings.tag.response.headers.{key}={value}
# support ease mesh
# X-EG-Circuit-Breaker
# X-EG-Retryer
# X-EG-Rate-Limiter
# X-EG-Time-Limiter
observability.tracings.tag.response.headers.eg.0=X-EG-Circuit-Breaker
observability.tracings.tag.response.headers.eg.1=X-EG-Retryer
observability.tracings.tag.response.headers.eg.2=X-EG-Rate-Limiter
observability.tracings.tag.response.headers.eg.3=X-EG-Time-Limiter
# -------------------- plugin global config ---------------------
plugin.observability.global.init.enabled=true
plugin.observability.global.tracing.enabled=true
plugin.observability.global.metric.enabled=true
plugin.observability.global.metric.interval=30
plugin.observability.global.metric.topic=application-metrics
plugin.observability.global.metric.url=/application-metrics
##
# if different with reporter.outputServer.appendType,
# following options can be used in user config file to override
# the default or global one
#
## when it's scrape by prometheus, noop can be used
# plugin.observability.global.metric.appendType=noop
## for debug, console can be used
# plugin.observability.global.metric.appendType=console
# plugin.observability.global.metric.appendType=http
#
# add service name to header enabled by name for easemesh
plugin.integrability.global.addServiceNameHead.enabled=true
# redirect the middleware address when env has address, see: com.megaease.easeagent.plugin.api.middleware.RedirectProcessor
# about redirect: jdbc, kafka, rabbitmq, redis,
plugin.integrability.global.redirect.enabled=true
# forwarded headers enabled.
# headers see config: easeagent.progress.forwarded.headers.???=???
plugin.integrability.global.forwarded.enabled=true
plugin.hook.global.foundation.enabled=true
plugin.observability.global.log.enabled=true
plugin.observability.global.log.topic=application-log
plugin.observability.global.log.url=/application-log
#plugin.observability.global.log.appendType=console
plugin.observability.global.log.level=INFO
plugin.observability.global.log.encoder=LogDataJsonEncoder
#plugin.observability.global.log.encoder.collectMdcKeys=
# support pattern:
# "logLevel": "%-5level",
# "threadId": "%thread",
# "location": "%logger{36}",
# "message": "%msg%n",
plugin.observability.global.log.encoder.timestamp=%d{UNIX_MILLIS}
plugin.observability.global.log.encoder.logLevel=%-5level
plugin.observability.global.log.encoder.threadId=%thread
plugin.observability.global.log.encoder.location=%logger{36}
plugin.observability.global.log.encoder.message=%msg%n%xEx{3}
#
# -------------------- access ---------------------
## access: servlet and spring gateway
plugin.observability.access.log.encoder=AccessLogJsonEncoder
# plugin.observability.access.metric.appendType=kafka
#plugin.observability.logback.log.enabled=false
#plugin.observability.log4j2.log.enabled=false
# ----------------------------------------------
# if the plugin configuration is consistent with the global namespace,
# do not add configuration items not commented out in this default configuration file.
# otherwise, they can not be overridden by Global configuration in user's configuration file.
#
# -------------------- jvm ---------------------
# plugin.observability.jvmGc.metric.enabled=true
# plugin.observability.jvmGc.metric.interval=30
plugin.observability.jvmGc.metric.topic=platform-metrics
plugin.observability.jvmGc.metric.url=/platform-metrics
# plugin.observability.jvmGc.metric.appendType=kafka
# plugin.observability.jvmMemory.metric.enabled=true
# plugin.observability.jvmMemory.metric.interval=30
plugin.observability.jvmMemory.metric.topic=platform-metrics
plugin.observability.jvmMemory.metric.url=/platform-metrics
# plugin.observability.jvmMemory.metric.appendType=kafka
#
# -------------------- async ---------------------
# plugin.observability.async.tracing.enabled=true
#
# -------------------- elasticsearch redirect ---------------------
# plugin.integrability.elasticsearch.redirect.enabled=true
# plugin.observability.elasticsearch.tracing.enabled=true
# elasticsearch metric
# plugin.observability.elasticsearch.metric.enabled=true
# plugin.observability.elasticsearch.metric.interval=30
plugin.observability.elasticsearch.metric.topic=platform-metrics
plugin.observability.elasticsearch.metric.url=/platform-metrics
# plugin.observability.elasticsearch.metric.appendType=kafka
#
# -------------------- httpServlet ---------------------
# plugin.observability.httpServlet.tracing.enabled=true
# plugin.observability.httpServlet.metric.enabled=true
# plugin.observability.httpServlet.metric.interval=30
# plugin.observability.httpServlet.metric.topic=application-metrics
# plugin.observability.httpServlet.metric.url=/application-metrics
# plugin.observability.httpServlet.metric.appendType=kafka
#
#
# -------------------- tomcat ---------------------
# plugin.observability.tomcat.tracing.enabled=true
# plugin.observability.tomcat.metric.enabled=true
# plugin.observability.tomcat.metric.interval=30
# plugin.observability.tomcat.metric.topic=application-metrics
# plugin.observability.tomcat.metric.url=/application-metrics
# plugin.observability.tomcat.metric.appendType=kafka
#
# -------------------- jdbc ---------------------
## jdbc tracing
# plugin.observability.jdbc.tracing.enabled=true
# jdbcStatement metric
# plugin.observability.jdbcStatement.metric.enabled=true
# plugin.observability.jdbcStatement.metric.interval=30
# plugin.observability.jdbcStatement.metric.topic=application-metrics
# plugin.observability.jdbcStatement.metric.url=/application-metrics
# plugin.observability.jdbcStatement.metric.appendType=kafka
## jdbcConnection metric
# plugin.observability.jdbcConnection.metric.enabled=true
# plugin.observability.jdbcConnection.metric.interval=30
# plugin.observability.jdbcConnection.metric.topic=application-metrics
# plugin.observability.jdbcConnection.metric.url=/application-metrics
# plugin.observability.jdbcConnection.metric.appendType=kafka
## sql compress
## compress.enabled=true, can use md5Dictionary to compress
## compress.enabled=false, use original sql
plugin.observability.jdbc.sql.compress.enabled=true
## md5Dictionary metric
# plugin.observability.md5Dictionary.metric.enabled=true
# plugin.observability.md5Dictionary.metric.interval=30
# plugin.observability.md5Dictionary.metric.topic=application-metrics
# plugin.observability.md5Dictionary.metric.url=/application-metrics
# plugin.observability.md5Dictionary.metric.appendType=kafka
## jdbc redirect
# plugin.integrability.jdbc.redirect.enabled=true
#
# -------------------- kafka ---------------------
# kafka tracing
# plugin.observability.kafka.tracing.enabled=true
# kafka metric
# plugin.observability.kafka.metric.enabled=true
# plugin.observability.kafka.metric.interval=30
plugin.observability.kafka.metric.topic=platform-metrics
plugin.observability.kafka.metric.url=/platform-metrics
# plugin.observability.kafka.metric.appendType=kafka
# kafka redirect
# plugin.integrability.kafka.redirect.enabled=true
#
# -------------------- rabbitmq ---------------------
# rabbitmq tracing
# plugin.observability.rabbitmq.tracing.enabled=true
# rabbitmq metric
# plugin.observability.rabbitmq.metric.enabled=true
# plugin.observability.rabbitmq.metric.interval=30
plugin.observability.rabbitmq.metric.topic=platform-metrics
plugin.observability.rabbitmq.metric.url=/platform-metrics
# plugin.observability.rabbitmq.metric.appendType=kafka
# rabbitmq redirect
# plugin.integrability.rabbitmq.redirect.enabled=true
#
# -------------------- redis ---------------------
# redis tracing
# plugin.observability.redis.tracing.enabled=true
# redis metric
# plugin.observability.redis.metric.enabled=true
# plugin.observability.redis.metric.interval=30
plugin.observability.redis.metric.topic=platform-metrics
plugin.observability.redis.metric.url=/platform-metrics
# plugin.observability.redis.metric.appendType=kafka
# redis redirect
# plugin.integrability.redis.redirect.enabled=true
#
# -------------------- springGateway ---------------------
# springGateway tracing
# plugin.observability.springGateway.tracing.enabled=true
# springGateway metric
# plugin.observability.springGateway.metric.enabled=true
# plugin.observability.springGateway.metric.interval=30
# plugin.observability.springGateway.metric.topic=application-metrics
# plugin.observability.springGateway.metric.url=/application-metrics
# plugin.observability.springGateway.metric.appendType=kafka
#
# -------------------- request ---------------------
## httpclient tracing\uFF1Ahttpclient and httpclient5
# plugin.observability.httpclient.tracing.enabled=true
## okHttp tracing
# plugin.observability.okHttp.tracing.enabled=true
## webclient tracing
# plugin.observability.webclient.tracing.enabled=true
## feignClient tracing
# plugin.observability.feignClient.tracing.enabled=true
## restTemplate tracing
# plugin.observability.restTemplate.tracing.enabled=true
## httpURLConnection tracing
# plugin.observability.httpURLConnection.tracing.enabled=true
# -------------------- service name ---------------------
## add service name to header by name for easemesh. default name: X-Mesh-RPC-Service
# plugin.integrability.serviceName.addServiceNameHead.propagate.head=X-Mesh-RPC-Service
#
# -------------------- mongodb ---------------------
## mongodb tracing
# plugin.observability.mongodb.tracing.enabled=true
## mongodb metric
# plugin.observability.mongodb.metric.enabled=true
# plugin.observability.mongodb.metric.interval=30
plugin.observability.mongodb.metric.topic=platform-metrics
plugin.observability.mongodb.metric.url=/platform-metrics
# plugin.observability.mongodb.metric.appendType=kafka
## mongodb redirect
# plugin.integrability.mongodb.redirect.enabled=true
## mongodb foundation
# plugin.hook.mongodb.foundation.enabled=true
#
# -------------------- motan ---------------------
# motan tracing
# plugin.observability.motan.tracing.enabled=true
## motan args collect switch
# plugin.observability.motan.tracing.args.collect.enabled=false
## motan result collect switch
# plugin.observability.motan.tracing.result.collect.enabled=false
# motan metric
# plugin.observability.motan.metric.enabled=true
# plugin.observability.motan.metric.interval=30
plugin.observability.motan.metric.topic=platform-metrics
plugin.observability.motan.metric.url=/platform-metrics
# plugin.observability.motan.metric.appendType=kafka
#
# -------------------- dubbo ---------------------
## dubbo tracing
#plugin.observability.dubbo.tracing.enabled=true
## dubbo arguments collect switch
#plugin.observability.dubbo.tracing.args.collect.enabled=false
## dubbo return result collect switch
#plugin.observability.dubbo.tracing.result.collect.enabled=false
## dubbo metric
#plugin.observability.dubbo.metric.enabled=true
#plugin.observability.dubbo.metric.interval=30
#plugin.observability.dubbo.metric.topic=platform-metrics
#plugin.observability.dubbo.metric.url=/platform-metrics
# plugin.observability.dubbo.metric.appendType=kafka
# -------------------- sofarpc ---------------------
# sofarpc tracing
# plugin.observability.sofarpc.tracing.enabled=true
## sofarpc args collect switch
# plugin.observability.sofarpc.tracing.args.collect.enabled=false
## sofarpc result collect switch
# plugin.observability.sofarpc.tracing.result.collect.enabled=false
# sofarpc metric
# plugin.observability.sofarpc.metric.enabled=true
# plugin.observability.sofarpc.metric.interval=30
plugin.observability.sofarpc.metric.topic=platform-metrics
plugin.observability.sofarpc.metric.url=/platform-metrics
# plugin.observability.sofarpc.metric.appendType=kafka
# -------------- output ------------------
## http/kafka/zipkin server host and port for tracing and metric
###### example ######
## http: [http|https]://127.0.0.1:8080/report
## kafka: 192.168.1.2:9092, 192.168.1.3:9092, 192.168.1.3:9092
## zipkin: [http|https]://127.0.0.1:8080/zipkin
reporter.outputServer.bootstrapServer=127.0.0.1:9092
reporter.outputServer.appendType=console
reporter.outputServer.timeout=1000
## enabled=false: disable output tracing and metric
## enabled=true: output tracing and metric
reporter.outputServer.enabled=true
## username and password for http basic auth
reporter.outputServer.username=
reporter.outputServer.password=
## enable=false: disable mtls
## enable=true: enable tls
## key, cert, ca_cert is enabled when tls.enable=true
reporter.outputServer.tls.enable=false
reporter.outputServer.tls.key=
reporter.outputServer.tls.cert=
reporter.outputServer.tls.ca_cert=
# --- redefine to output properties
reporter.log.output.messageMaxBytes=999900
reporter.log.output.reportThread=1
reporter.log.output.queuedMaxSpans=1000
reporter.log.output.queuedMaxSize=1000000
reporter.log.output.messageTimeout=1000
## sender.appendType config
## [http] send to http server
## [kafka] send to kafka
## [console] send to console
## reporter.log.sender.appendType=console
## enabled=true:
# reporter.log.sender.enabled=true
# reporter.log.sender.url=/application-log
## sender.appendType config
## [http] send to http server
## [kafka] send to kafka
## [console] send to console
# reporter.tracing.sender.appendType=http
# reporter.tracing.sender.appendType=console
## enabled=true:
reporter.tracing.sender.enabled=true
## url is only used in http
## append to outputServer.bootstrapServer
###### example ######
## reporter.outputServer.bootstrapServer=http://127.0.0.1:8080/report
## reporter.tracing.sender.url=/tracing
## final output url: http://127.0.0.1:8080/report/tracing
## if url is start with [http|https], url override reporter.outputServer.bootstrapServer
###### example ######
## reporter.outputServer.bootstrapServer=http://127.0.0.1:8080/report
## reporter.tracing.sender.url=http://127.0.0.10:9090/tracing
## final output url: http://127.0.0.10:9090/tracing
reporter.tracing.sender.url=/application-tracing-log
## topic for kafka use
reporter.tracing.sender.topic=application-tracing-log
reporter.tracing.encoder=SpanJsonEncoder
# --- redefine to output properties
reporter.tracing.output.messageMaxBytes=999900
reporter.tracing.output.reportThread=1
reporter.tracing.output.queuedMaxSpans=1000
reporter.tracing.output.queuedMaxSize=1000000
reporter.tracing.output.messageTimeout=1000
## sender.appendType config
## [http] send to http server
## [metricKafka] send to kafka
## [console] send to console
#reporter.metric.sender.appendType=http
#reporter.metric.sender.appendType=console
## url is only used in http
## append to outputServer.bootstrapServer
###### example ######
## reporter.outputServer.bootstrapServer=http://127.0.0.1:8080/report
## reporter.metric.sender.url=/metric
## final output url: http://127.0.0.1:8080/report/metric
## if url is start with [http|https], url override reporter.outputServer.bootstrapServer
###### example ######
## reporter.outputServer.bootstrapServer=http://127.0.0.1:8080/report
## reporter.metric.sender.url=http://127.0.0.10:9090/metric
## final output url: http://127.0.0.10:9090/metric
#reporter.metric.sender.url=/metrics
## support spring boot 3.5.3: jdk17+3.5.3
#runtime.code.version.points.jdk=jdk17
#runtime.code.version.points.spring-boot=3.x.x
================================================
FILE: build/src/main/resources/easeagent-log4j2.xml
================================================
%d [%10.10t] %5p %10.10c{1} - %msg%n
================================================
FILE: build/src/main/resources/user-minimal-cfg.properties
================================================
## This a minimal configuration required to start the EaseAgent.
## In most cases, this is all the configuration items that the normal user needs to be concerned about.
##
## When the user specifies a user configration file as this one, the items in user config file will override the default
## configuration in agent.properties packaged in easeagent.jar
##
## -Deaseagent.config.path=/path/to/user-cfg-file
name=demo-springweb
system=demo-system
###
### report configuration
###
reporter.outputServer.bootstrapServer=http://127.0.0.1:9411
reporter.outputServer.appendType=console
##
## Global metric configuration
## the appendType is same as outputServer, so comment out
# plugin.observability.global.metric.appendType=console
##
## tracing sender
## [http] send to http server
## [kafka] send to kafka
## [console] send to console
#
reporter.tracing.sender.appendType=
# reporter.tracing.sender.url=http://tempo:9411/api/v2/spans
reporter.tracing.sender.url=http://localhost:9411/api/v2/spans
## access log sender
## [http] send to http server
## [kafka] send to kafka
## [console] send to console
## the appendType is same as outputServer, so comment out
## reporter.log.sender.appendType=console
================================================
FILE: config/pom.xml
================================================
easeagentcom.megaease.easeagent2.3.04.0.0configcom.megaease.easeagentplugin-apicom.fasterxml.jackson.corejackson-databindcom.megaease.easeagentlog4j2-apiorg.slf4jslf4j-apicom.megaease.easeagentlog4j2-mock${project.version}testorg.yamlsnakeyamlcom.google.guavaguavacom.github.stefanbirknersystem-rules1.19.0test
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/AutoRefreshConfigItem.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.plugin.api.config.Config;
import java.util.function.BiFunction;
public class AutoRefreshConfigItem {
private volatile T value;
public AutoRefreshConfigItem(Config config, String name, BiFunction func) {
ConfigUtils.bindProp(name, config, func, v -> this.value = v);
}
public T getValue() {
return value;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/CompatibilityConversion.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.ProgressFields;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.function.BiFunction;
public class CompatibilityConversion {
private static final Logger LOGGER = LoggerFactory.getLogger(CompatibilityConversion.class);
protected static final String[] REQUEST_NAMESPACE = new String[]{
ConfigConst.Namespace.HTTPCLIENT,
ConfigConst.Namespace.OK_HTTP,
ConfigConst.Namespace.WEB_CLIENT,
ConfigConst.Namespace.FEIGN_CLIENT,
ConfigConst.Namespace.REST_TEMPLATE,
};
private static final Map>> KEY_TO_NAMESPACE;
private static final Set METRIC_SKIP;
private static final Set TRACING_SKIP;
static {
Map>> map = new HashMap<>();
map.put(ConfigConst.Observability.KEY_METRICS_ACCESS, SingleBuilder.observability(ConfigConst.Namespace.ACCESS));
map.put(ConfigConst.Observability.KEY_METRICS_REQUEST, MultipleBuilder.observability(Arrays.asList(REQUEST_NAMESPACE)));
map.put(ConfigConst.Observability.KEY_METRICS_JDBC_STATEMENT, SingleBuilder.observability(ConfigConst.Namespace.JDBC_STATEMENT));
map.put(ConfigConst.Observability.KEY_METRICS_JDBC_CONNECTION, SingleBuilder.observability(ConfigConst.Namespace.JDBC_CONNECTION));
map.put(ConfigConst.Observability.KEY_METRICS_MD5_DICTIONARY, SingleBuilder.observability(ConfigConst.Namespace.MD5_DICTIONARY));
map.put(ConfigConst.Observability.KEY_METRICS_RABBIT, SingleBuilder.observability(ConfigConst.Namespace.RABBITMQ));
map.put(ConfigConst.Observability.KEY_METRICS_KAFKA, SingleBuilder.observability(ConfigConst.Namespace.KAFKA));
map.put(ConfigConst.Observability.KEY_METRICS_CACHE, SingleBuilder.observability(ConfigConst.Namespace.REDIS));
map.put(ConfigConst.Observability.KEY_METRICS_JVM_GC, null);
map.put(ConfigConst.Observability.KEY_METRICS_JVM_MEMORY, null);
map.put(ConfigConst.Observability.KEY_TRACE_REQUEST, MultipleBuilder.observability(Arrays.asList(REQUEST_NAMESPACE)));
map.put(ConfigConst.Observability.KEY_TRACE_REMOTE_INVOKE, SingleBuilder.observability(ConfigConst.Namespace.WEB_CLIENT));
map.put(ConfigConst.Observability.KEY_TRACE_KAFKA, SingleBuilder.observability(ConfigConst.Namespace.KAFKA));
map.put(ConfigConst.Observability.KEY_TRACE_JDBC, SingleBuilder.observability(ConfigConst.Namespace.JDBC));
map.put(ConfigConst.Observability.KEY_TRACE_CACHE, SingleBuilder.observability(ConfigConst.Namespace.REDIS));
map.put(ConfigConst.Observability.KEY_TRACE_RABBIT, SingleBuilder.observability(ConfigConst.Namespace.RABBITMQ));
KEY_TO_NAMESPACE = map;
TRACING_SKIP = new HashSet<>();
TRACING_SKIP.add(ConfigConst.Observability.KEY_COMM_ENABLED);
TRACING_SKIP.add(ConfigConst.Observability.KEY_COMM_SAMPLED_TYPE);
TRACING_SKIP.add(ConfigConst.Observability.KEY_COMM_SAMPLED);
TRACING_SKIP.add(ConfigConst.Observability.KEY_COMM_OUTPUT);
TRACING_SKIP.add(ConfigConst.Observability.KEY_COMM_TAG);
METRIC_SKIP = new HashSet<>();
METRIC_SKIP.add(ConfigConst.Observability.KEY_METRICS_JVM_GC);
METRIC_SKIP.add(ConfigConst.Observability.KEY_METRICS_JVM_MEMORY);
}
public static Map transform(Map oldConfigs) {
Map changedKeys = new HashMap<>();
Map newConfigs = new HashMap<>();
for (Map.Entry entry : oldConfigs.entrySet()) {
Conversion> conversion = transformConversion(entry.getKey());
Object changed = conversion.transform(newConfigs, entry.getValue());
if (conversion.isChange()) {
changedKeys.put(entry.getKey(), changed);
}
}
if (changedKeys.isEmpty()) {
return oldConfigs;
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("config key has transform: ");
for (Map.Entry entry : changedKeys.entrySet()) {
LOGGER.info("{} to {}", entry.getKey(), entry.getValue());
}
}
return newConfigs;
}
private static Conversion> transformConversion(String key) {
if (key.startsWith("observability.metrics.")) {
return metricConversion(key);
} else if (key.startsWith("observability.tracings.")) {
return tracingConversion(key);
} else if (key.startsWith(ConfigConst.GlobalCanaryLabels.SERVICE_HEADERS + ".")) {
// return forwardedHeadersConversion(key);
}
return new FinalConversion(key, false);
}
private static Conversion> metricConversion(String key) {
if (key.equals(ConfigConst.Observability.METRICS_ENABLED)) {
return new MultipleFinalConversion(Arrays.asList(new FinalConversion(ConfigConst.Observability.METRICS_ENABLED, true),
new FinalConversion(ConfigConst.Plugin.OBSERVABILITY_GLOBAL_METRIC_ENABLED, true)), true);
}
return conversion(key, METRIC_SKIP, ConfigConst.PluginID.METRIC);
}
private static Conversion> tracingConversion(String key) {
if (key.equals(ConfigConst.Observability.TRACE_ENABLED)) {
return new FinalConversion(ConfigConst.Plugin.OBSERVABILITY_GLOBAL_TRACING_ENABLED, true);
}
return conversion(key, TRACING_SKIP, ConfigConst.PluginID.TRACING);
}
// private static Conversion> forwardedHeadersConversion(String key) {
// return new FinalConversion(key.replace(ConfigConst.GlobalCanaryLabels.SERVICE_HEADERS + ".", ProgressFields.EASEAGENT_PROGRESS_FORWARDED_HEADERS_CONFIG + "."), true);
// }
private static Conversion> conversion(String key, Set skipSet, String pluginId) {
String[] keys = ConfigConst.split(key);
if (keys.length < 4) {
return new FinalConversion(key, false);
}
String key2 = keys[2];
if (skipSet.contains(key2)) {
return new FinalConversion(key, false);
}
BiFunction> builder = KEY_TO_NAMESPACE.get(key2);
if (builder == null) {
builder = SingleBuilder.observability(key2);
}
String[] properties = new String[keys.length - 3];
int index = 0;
for (int i = 3; i < keys.length; i++) {
properties[index++] = keys[i];
}
return builder.apply(pluginId, ConfigConst.join(properties));
}
interface Conversion {
K transform(Map configs, String value);
boolean isChange();
}
static class FinalConversion implements Conversion {
private final String key;
private final boolean change;
public FinalConversion(String key, boolean change) {
this.key = key;
this.change = change;
}
@Override
public String transform(Map configs, String value) {
configs.put(key, value);
return key;
}
public boolean isChange() {
return change;
}
}
static class MultipleFinalConversion implements Conversion> {
private final List conversions;
private final boolean change;
MultipleFinalConversion(@Nonnull List conversions, boolean change) {
this.conversions = conversions;
this.change = change;
}
@Override
public List transform(Map configs, String value) {
List result = new ArrayList<>();
for (FinalConversion conversion : conversions) {
result.add(conversion.transform(configs, value));
}
return result;
}
@Override
public boolean isChange() {
return change;
}
}
static class SingleConversion implements Conversion {
private final String domain;
private final String namespace;
private final String id;
private final String properties;
public SingleConversion(String domain, String namespace, String id, String properties) {
this.domain = domain;
this.namespace = namespace;
this.id = id;
this.properties = properties;
}
@Override
public String transform(Map configs, String value) {
String key = ConfigUtils.buildPluginProperty(domain, namespace, id, properties);
configs.put(key, value);
return key;
}
@Override
public boolean isChange() {
return true;
}
}
static class MultipleConversion implements Conversion> {
private final String domain;
private final List namespaces;
private final String id;
private final String properties;
public MultipleConversion(String domain, List namespaces, String id, String properties) {
this.domain = domain;
this.namespaces = namespaces;
this.id = id;
this.properties = properties;
}
@Override
public List transform(Map configs, String value) {
List keys = new ArrayList<>();
for (String namespace : namespaces) {
String key = ConfigUtils.buildPluginProperty(domain, namespace, id, properties);
keys.add(key);
configs.put(key, value);
}
return keys;
}
@Override
public boolean isChange() {
return true;
}
}
static class SingleBuilder implements BiFunction> {
private final String domain;
private final String namespace;
public SingleBuilder(String domain, String namespace) {
this.domain = domain;
this.namespace = namespace;
}
@Override
public Conversion apply(String id, String properties) {
return new SingleConversion(domain, namespace, id, properties);
}
static SingleBuilder observability(String namespace) {
return new SingleBuilder(ConfigConst.OBSERVABILITY, namespace);
}
}
static class MultipleBuilder implements BiFunction> {
private final String domain;
private final List namespaces;
public MultipleBuilder(String domain, List namespaces) {
this.domain = domain;
this.namespaces = namespaces;
}
@Override
public Conversion apply(String id, String properties) {
return new MultipleConversion(domain, namespaces, id, properties);
}
static MultipleBuilder observability(List namespaces) {
return new MultipleBuilder(ConfigConst.OBSERVABILITY, namespaces);
}
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigAware.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.plugin.api.config.Config;
public interface ConfigAware {
void setConfig(Config config);
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigFactory.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.utils.ImmutableMap;
import com.megaease.easeagent.plugin.utils.SystemEnv;
import com.megaease.easeagent.plugin.utils.common.JsonUtil;
import com.megaease.easeagent.plugin.utils.common.StringUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class ConfigFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigFactory.class);
private static final String CONFIG_PROP_FILE = "agent.properties";
private static final String CONFIG_YAML_FILE = "agent.yaml";
public static final String AGENT_CONFIG_PATH_PROP_KEY = "easeagent.config.path";
public static final String AGENT_SERVICE = "name";
public static final String AGENT_SYSTEM = "system";
public static final String AGENT_SERVER_PORT = "easeagent.server.port";
public static final String AGENT_SERVER_ENABLED = "easeagent.server.enabled";
public static final String EASEAGENT_ENV_CONFIG = "EASEAGENT_ENV_CONFIG";
private static final Map AGENT_CONFIG_KEYS_TO_PROPS =
ImmutableMap.builder()
.put("easeagent.name", AGENT_SERVICE)
.put("easeagent.system", AGENT_SYSTEM)
.put("easeagent.server.port", AGENT_SERVER_PORT)
.put("easeagent.server.enabled", AGENT_SERVER_ENABLED)
.build();
// OTEL_SERVICE_NAME=xxx
private static final Map AGENT_ENV_KEY_TO_PROPS = new HashMap<>();
static {
for (Map.Entry entry : AGENT_CONFIG_KEYS_TO_PROPS.entrySet()) {
// dot.case -> UPPER_UNDERSCORE
AGENT_ENV_KEY_TO_PROPS.put(
ConfigPropertiesUtils.toEnvVarName(entry.getKey()),
entry.getValue()
);
}
}
/**
* update config value from environment variables and java properties
*
* java properties > environment variables > env:EASEAGENT_ENV_CONFIG={} > default
*/
static Map updateEnvCfg() {
Map envCfg = new TreeMap<>();
String configEnv = SystemEnv.get(EASEAGENT_ENV_CONFIG);
if (StringUtils.isNotEmpty(configEnv)) {
Map map = JsonUtil.toMap(configEnv);
Map strMap = new HashMap<>();
if (!map.isEmpty()) {
for (Map.Entry entry : map.entrySet()) {
strMap.put(entry.getKey(), entry.getValue().toString());
}
}
envCfg.putAll(strMap);
}
// override by environment variables, eg: export EASEAGENT_NAME=xxx
for (Map.Entry entry : AGENT_ENV_KEY_TO_PROPS.entrySet()) {
String value = SystemEnv.get(entry.getKey());
if (!StringUtils.isEmpty(value)) {
envCfg.put(entry.getValue(), value);
}
}
// override by java properties; eg: java -Deaseagent.name=xxx
for (Map.Entry entry : AGENT_CONFIG_KEYS_TO_PROPS.entrySet()) {
String value = System.getProperty(entry.getKey());
if (!StringUtils.isEmpty(value)) {
envCfg.put(entry.getValue(), value);
}
}
return envCfg;
}
private ConfigFactory() {
}
/**
* Get config file path from system properties or environment variables
*/
public static String getConfigPath() {
// get config path from -Deaseagent.config.path=/easeagent/agent.properties || export EASEAGENT_CONFIG_PATH=/easeagent/agent.properties
String path = ConfigPropertiesUtils.getString(AGENT_CONFIG_PATH_PROP_KEY);
if (StringUtils.isEmpty(path)) {
// eg: -Dotel.javaagent.configuration-file=/easeagent/agent.properties || export OTEL_JAVAAGENT_CONFIGURATION_FILE=/easeagent/agent.properties
path = OtelSdkConfigs.getConfigPath();
}
return path;
}
public static GlobalConfigs loadConfigs(String pathname, ClassLoader loader) {
// load property configuration file if exist
GlobalConfigs configs = loadDefaultConfigs(loader, CONFIG_PROP_FILE);
// load yaml configuration file if exist
GlobalConfigs yConfigs = loadDefaultConfigs(loader, CONFIG_YAML_FILE);
configs.mergeConfigs(yConfigs);
// override by user special config file
if (StringUtils.isNotEmpty(pathname)) {
GlobalConfigs configsFromOuterFile = ConfigLoader.loadFromFile(new File(pathname));
LOGGER.info("Loaded user special config file: {}", pathname);
configs.mergeConfigs(configsFromOuterFile);
}
// override by opentelemetry sdk env config
configs.updateConfigsNotNotify(OtelSdkConfigs.updateEnvCfg());
// check environment cfg override
configs.updateConfigsNotNotify(updateEnvCfg());
if (LOGGER.isDebugEnabled()) {
final String display = configs.toPrettyDisplay();
LOGGER.debug("Loaded conf:\n{}", display);
}
return configs;
}
private static GlobalConfigs loadDefaultConfigs(ClassLoader loader, String file) {
GlobalConfigs globalConfigs = JarFileConfigLoader.load(file);
if (globalConfigs != null) {
return globalConfigs;
}
return ConfigLoader.loadFromClasspath(loader, file);
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigLoader.java
================================================
/*
* Copyright (c) 2021, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.config.yaml.YamlReader;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import org.yaml.snakeyaml.parser.ParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ConfigLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigLoader.class);
private static boolean checkYaml(String filename) {
return filename.endsWith(".yaml") || filename.endsWith(".yml");
}
static GlobalConfigs loadFromFile(File file) {
try (FileInputStream in = new FileInputStream(file)) {
return ConfigLoader.loadFromStream(in, file.getAbsolutePath());
} catch (IOException e) {
LOGGER.warn("Load config file failure: {}", file.getAbsolutePath());
}
return new GlobalConfigs(Collections.emptyMap());
}
static GlobalConfigs loadFromStream(InputStream in, String filename) throws IOException {
if (in != null) {
Map map;
if (checkYaml(filename)) {
try {
map = new YamlReader().load(in).compress();
} catch (ParserException e) {
LOGGER.warn("Wrong Yaml format, load config file failure: {}", filename);
map = Collections.emptyMap();
}
} else {
map = extractPropsMap(in);
}
return new GlobalConfigs(map);
} else {
return new GlobalConfigs(Collections.emptyMap());
}
}
private static HashMap extractPropsMap(InputStream in) throws IOException {
Properties properties = new Properties();
properties.load(in);
HashMap map = new HashMap<>();
for (String one : properties.stringPropertyNames()) {
map.put(one, properties.getProperty(one));
}
return map;
}
static GlobalConfigs loadFromClasspath(ClassLoader classLoader, String file) {
try (InputStream in = classLoader.getResourceAsStream(file)) {
return ConfigLoader.loadFromStream(in, file);
} catch (IOException e) {
LOGGER.warn("Load config file:{} by classloader:{} failure: {}", file, classLoader.toString(), e);
}
return new GlobalConfigs(Collections.emptyMap());
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigManagerMXBean.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface ConfigManagerMXBean {
void updateConfigs(Map configs);
void updateService(String json, String version) throws IOException;
void updateCanary(String json, String version) throws IOException;
void updateService2(Map configs, String version);
void updateCanary2(Map configs, String version);
Map getConfigs();
List availableConfigNames();
default void healthz() {
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigNotifier.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.config.ChangeItem;
import com.megaease.easeagent.plugin.api.config.ConfigChangeListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
public class ConfigNotifier {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigNotifier.class);
private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>();
private final String prefix;
public ConfigNotifier(String prefix) {
this.prefix = prefix;
}
public Runnable addChangeListener(ConfigChangeListener listener) {
final boolean add = listeners.add(listener);
return () -> {
if (add) {
listeners.remove(listener);
}
};
}
public void handleChanges(List list) {
final List changes = this.prefix.isEmpty() ? list : filterChanges(list);
if (changes.isEmpty()) {
return;
}
listeners.forEach(one -> {
try {
one.onChange(changes);
} catch (Exception e) {
LOGGER.warn("Notify config changes to listener failure: {}", e);
}
});
}
private List filterChanges(List list) {
return list.stream().filter(one -> one.getFullName().startsWith(prefix))
.map(e -> new ChangeItem(e.getFullName().substring(prefix.length()), e.getFullName(), e.getOldValue(), e.getNewValue()))
.collect(Collectors.toList());
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigPropertiesUtils.java
================================================
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.plugin.utils.SystemEnv;
import javax.annotation.Nullable;
import java.util.Locale;
/**
* Get config from system properties or environment variables.
*/
final class ConfigPropertiesUtils {
public static boolean getBoolean(String propertyName, boolean defaultValue) {
String strValue = getString(propertyName);
return strValue == null ? defaultValue : Boolean.parseBoolean(strValue);
}
public static int getInt(String propertyName, int defaultValue) {
String strValue = getString(propertyName);
if (strValue == null) {
return defaultValue;
}
try {
return Integer.parseInt(strValue);
} catch (NumberFormatException ignored) {
return defaultValue;
}
}
@Nullable
public static String getString(String propertyName) {
String value = System.getProperty(propertyName);
if (value != null) {
return value;
}
return SystemEnv.get(toEnvVarName(propertyName));
}
/**
* dot.case -> UPPER_UNDERSCORE
*/
public static String toEnvVarName(String propertyName) {
return propertyName.toUpperCase(Locale.ROOT).replace('-', '_').replace('.', '_');
}
private ConfigPropertiesUtils() {
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ConfigUtils.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.megaease.easeagent.plugin.api.config.ChangeItem;
import com.megaease.easeagent.plugin.api.config.Config;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import java.io.IOException;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static com.megaease.easeagent.plugin.api.config.ConfigConst.*;
public class ConfigUtils {
private ConfigUtils() {
}
public static void bindProp(String name, Config configs, BiFunction func, Consumer consumer, R def) {
Runnable process = () -> {
R result = func.apply(configs, name);
result = firstNotNull(result, def);
if (result != null) {
consumer.accept(result);
}
};
process.run();
configs.addChangeListener(list -> {
boolean hasChange = list.stream().map(ChangeItem::getFullName).anyMatch(fn -> fn.equals(name));
if (hasChange) {
process.run();
}
});
}
@SafeVarargs
private static R firstNotNull(R... ars) {
for (R one : ars) {
if (one != null) {
return one;
}
}
return null;
}
public static void bindProp(String name, Config configs, BiFunction func, Consumer consumer) {
bindProp(name, configs, func, consumer, null);
}
public static Map json2KVMap(String json) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
List> list = extractKVs(null, node);
return list.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public static List> extractKVs(String prefix, JsonNode node) {
List> rst = new LinkedList<>();
if (node.isObject()) {
Iterator names = node.fieldNames();
while (names.hasNext()) {
String current = names.next();
rst.addAll(extractKVs(join(prefix, current), node.path(current)));
}
} else if (node.isArray()) {
int len = node.size();
for (int i = 0; i < len; i++) {
rst.addAll(extractKVs(join(prefix, i + ""), node.path(i)));
}
} else {
rst.add(new AbstractMap.SimpleEntry<>(prefix, node.asText("")));
}
return rst;
}
private static String join(String prefix, String current) {
return prefix == null ? current : ConfigConst.join(prefix, current);
}
public static boolean isGlobal(String namespace) {
return PLUGIN_GLOBAL.equals(namespace);
}
public static boolean isPluginConfig(String key) {
return key != null && key.startsWith(PLUGIN_PREFIX);
}
public static boolean isPluginConfig(String key, String domain, String namespace, String id) {
return key != null && key.startsWith(ConfigConst.join(PLUGIN, domain, namespace, id));
}
public static PluginProperty pluginProperty(String path) {
String[] configs = path.split("\\" + DELIMITER);
if (configs.length < 5) {
throw new ValidateUtils.ValidException(String.format("Property[%s] must be format: %s", path, ConfigConst.join(PLUGIN, "", "", "", "")));
}
for (int idOffsetEnd = 3; idOffsetEnd < configs.length - 1; idOffsetEnd++) {
new PluginProperty(configs[1], configs[2],
ConfigConst.join(Arrays.copyOfRange(configs, 3, idOffsetEnd)),
ConfigConst.join(Arrays.copyOfRange(configs, idOffsetEnd + 1, configs.length)));
}
return new PluginProperty(configs[1], configs[2], configs[3], ConfigConst.join(Arrays.copyOfRange(configs, 4, configs.length)));
}
public static String requireNonEmpty(String obj, String message) {
if (obj == null || obj.trim().isEmpty()) {
throw new ValidateUtils.ValidException(message);
}
return obj.trim();
}
public static String buildPluginProperty(String domain, String namespace, String id, String property) {
return String.format(PLUGIN_FORMAT, domain, namespace, id, property);
}
public static String buildCodeVersionKey(String key) {
return RUNTIME_CODE_VERSION_POINTS_PREFIX + key;
}
/**
* extract config item with a fromPrefix to and convert the prefix to 'toPrefix' for configuration Compatibility
*
* @param cfg config source map
* @param fromPrefix from
* @param toPrefix to
* @return Extracted and converted KV map
*/
public static Map extractAndConvertPrefix(Map cfg, String fromPrefix, String toPrefix) {
Map convert = new HashMap<>();
Set keys = new HashSet<>();
cfg.forEach((key, value) -> {
if (key.startsWith(fromPrefix)) {
keys.add(key);
key = toPrefix + key.substring(fromPrefix.length());
convert.put(key, value);
}
});
// override, new configuration KV override previous KV
convert.putAll(extractByPrefix(cfg, toPrefix));
return convert;
}
/**
* Extract config items from config by prefix
*
* @param config config
* @param prefix prefix
* @return Extracted KV
*/
public static Map extractByPrefix(Config config, String prefix) {
return extractByPrefix(config.getConfigs(), prefix);
}
public static Map extractByPrefix(Map cfg, String prefix) {
Map extract = new TreeMap<>();
// override, new configuration KV override previous KV
cfg.forEach((key, value) -> {
if (key.startsWith(prefix)) {
extract.put(key, value);
}
});
return extract;
}
public static int isChanged(String name, Map map, String check) {
if (map.get(name) == null || map.get(name).equals(check)) {
return 0;
}
return 1;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/Configs.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.config.ChangeItem;
import com.megaease.easeagent.plugin.api.config.Config;
import com.megaease.easeagent.plugin.api.config.ConfigChangeListener;
import java.util.*;
import java.util.stream.Collectors;
public class Configs implements Config {
private static final Logger LOGGER = LoggerFactory.getLogger(Configs.class);
protected Map source;
protected ConfigNotifier notifier;
protected Configs() {
}
public Configs(Map source) {
this.source = new TreeMap<>(source);
notifier = new ConfigNotifier("");
}
public void updateConfigsNotNotify(Map changes) {
this.source.putAll(changes);
}
public void updateConfigs(Map changes) {
Map dump = new TreeMap<>(this.source);
List items = new LinkedList<>();
changes.forEach((name, value) -> {
String old = dump.get(name);
if (!Objects.equals(old, value)) {
dump.put(name, value);
items.add(new ChangeItem(name, name, old, value));
}
});
if (!items.isEmpty()) {
LOGGER.info("change items: {}", items);
this.source = dump;
this.notifier.handleChanges(items);
}
}
protected boolean hasText(String text) {
return text != null && text.trim().length() > 0;
}
@Override
public Map getConfigs() {
return new TreeMap<>(this.source);
}
public String toPrettyDisplay() {
return this.source.toString();
}
public boolean hasPath(String path) {
return this.source.containsKey(path);
}
public String getString(String name) {
return this.source.get(name);
}
public String getString(String name, String defVal) {
String val = this.source.get(name);
return val == null ? defVal : val;
}
public Integer getInt(String name) {
String value = this.source.get(name);
if (value == null) {
return null;
}
try {
return Integer.parseInt(value);
} catch (Exception e) {
return null;
}
}
@Override
public Integer getInt(String name, int defValue) {
Integer anInt = getInt(name);
if (anInt == null) {
return defValue;
}
return anInt;
}
public Boolean getBooleanNullForUnset(String name) {
String value = this.source.get(name);
if (value == null) {
return null;
}
return value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true");
}
public Boolean getBoolean(String name) {
String value = this.source.get(name);
if (value == null) {
return false;
}
return value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true");
}
@Override
public Boolean getBoolean(String name, boolean defValue) {
Boolean aBoolean = getBooleanNullForUnset(name);
if (aBoolean == null) {
return defValue;
}
return aBoolean;
}
public Double getDouble(String name) {
String value = this.source.get(name);
if (value == null) {
return null;
}
try {
return Double.parseDouble(value);
} catch (Exception e) {
return null;
}
}
@Override
public Double getDouble(String name, double defValue) {
Double aDouble = getDouble(name);
if (aDouble == null) {
return defValue;
}
return aDouble;
}
public Long getLong(String name) {
String value = this.source.get(name);
if (value == null) {
return null;
}
try {
return Long.parseLong(value);
} catch (Exception e) {
return null;
}
}
@Override
public Long getLong(String name, long defValue) {
Long aLong = getLong(name);
if (aLong == null) {
return defValue;
}
return aLong;
}
public List getStringList(String name) {
String value = this.source.get(name);
if (value == null) {
return Collections.emptyList();
}
return Arrays.stream(value.split(",")).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
public Runnable addChangeListener(ConfigChangeListener listener) {
return notifier.addChangeListener(listener);
}
@Override
public Set keySet() {
return this.source.keySet();
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/GlobalConfigs.java
================================================
/*
* Copyright (c) 2021, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.config.report.ReportConfigAdapter;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class GlobalConfigs extends Configs implements ConfigManagerMXBean {
Configs originalConfig;
public GlobalConfigs(Map source) {
super();
this.originalConfig = new Configs(source);
// reporter adapter
Map map = new TreeMap<>(source);
ReportConfigAdapter.convertConfig(map);
// check environment config
this.source = new TreeMap<>(map);
this.notifier = new ConfigNotifier("");
}
public Configs getOriginalConfig() {
return this.originalConfig;
}
@Override
public void updateConfigsNotNotify(Map changes) {
// update original config
Map newGlobalCfg = new TreeMap<>(this.originalConfig.getConfigs());
newGlobalCfg.putAll(changes);
this.originalConfig.updateConfigsNotNotify(changes);
// report adapter
ReportConfigAdapter.convertConfig(newGlobalCfg);
super.updateConfigsNotNotify(newGlobalCfg);
}
@Override
public void updateConfigs(Map changes) {
// update original config
Map newGlobalCfg = new TreeMap<>(this.originalConfig.getConfigs());
newGlobalCfg.putAll(changes);
this.originalConfig.updateConfigsNotNotify(changes);
// report adapter
ReportConfigAdapter.convertConfig(newGlobalCfg);
super.updateConfigs(newGlobalCfg);
}
public void mergeConfigs(GlobalConfigs configs) {
Map merged = configs.getOriginalConfig().getConfigs();
if (merged.isEmpty()) {
return;
}
this.updateConfigsNotNotify(merged);
return;
}
@Override
public List availableConfigNames() {
throw new UnsupportedOperationException();
}
@Override
public void updateService(String json, String version) throws IOException {
this.updateConfigs(ConfigUtils.json2KVMap(json));
}
@Override
public void updateCanary(String json, String version) throws IOException {
Map originals = ConfigUtils.json2KVMap(json);
HashMap rst = new HashMap<>();
originals.forEach((k, v) -> rst.put(ConfigConst.join(ConfigConst.GLOBAL_CANARY_LABELS, k), v));
this.updateConfigs(rst);
}
@Override
public void updateService2(Map configs, String version) {
this.updateConfigs(configs);
}
@Override
public void updateCanary2(Map configs, String version) {
HashMap rst = new HashMap<>();
for (Map.Entry entry : configs.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
rst.put(ConfigConst.join(ConfigConst.GLOBAL_CANARY_LABELS, k), v);
}
this.updateConfigs(CompatibilityConversion.transform(rst));
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/JarFileConfigLoader.java
================================================
/*
* Copyright (c) 2021, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
public class JarFileConfigLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(JarFileConfigLoader.class);
static GlobalConfigs load(String file) {
String agentJarPath = System.getProperty(ConfigConst.AGENT_JAR_PATH);
if (agentJarPath == null) {
return null;
}
try {
JarFile jarFile = new JarFile(new File(agentJarPath));
ZipEntry zipEntry = jarFile.getEntry(file);
if (zipEntry == null) {
return null;
}
try (InputStream in = jarFile.getInputStream(zipEntry)) {
return ConfigLoader.loadFromStream(in, file);
} catch (IOException e) {
LOGGER.debug("Load config file:{} failure: {}", file, e);
}
} catch (IOException e) {
LOGGER.debug("create JarFile:{} failure: {}", agentJarPath, e);
}
return null;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/OtelSdkConfigs.java
================================================
/*
* Copyright (c) 2022, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.google.common.base.Splitter;
import com.megaease.easeagent.plugin.utils.ImmutableMap;
import com.megaease.easeagent.plugin.utils.SystemEnv;
import com.megaease.easeagent.plugin.utils.common.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Compatible with opentelemetry-java.
*
* {@see https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#disabling-opentelemetrysdk}
*/
public class OtelSdkConfigs {
private static final String OTEL_RESOURCE_ATTRIBUTES_KEY = "otel.resource.attributes";
private static final String CONFIG_PATH_PROP_KEY = "otel.javaagent.configuration-file";
private static final Splitter.MapSplitter OTEL_RESOURCE_ATTRIBUTES_SPLITTER
= Splitter.on(",")
.omitEmptyStrings()
.withKeyValueSeparator("=");
private static final Map SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS =
ImmutableMap.builder()
.put("sdk.disabled", "easeagent.server.enabled")
.put("service.name", "name") //"easeagent.name"
.put("service.namespace", "system") //"easeagent.system"
.build();
// -Dotel.service.name=xxx
private static final Map OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS = new HashMap<>();
// OTEL_SERVICE_NAME=xxx
private static final Map OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS = new HashMap<>();
static {
for (Map.Entry entry : SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS.entrySet()) {
// lower.hyphen -> UPPER_UNDERSCORE
OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.put(
"otel." + entry.getKey(),
entry.getValue()
);
}
for (Map.Entry entry : OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.entrySet()) {
// dot.case -> UPPER_UNDERSCORE
OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS.put(
ConfigPropertiesUtils.toEnvVarName(entry.getKey()),
entry.getValue()
);
}
}
/**
* Get config path from java properties or environment variables
*/
static String getConfigPath() {
return ConfigPropertiesUtils.getString(CONFIG_PATH_PROP_KEY);
}
/**
* update config value from environment variables and java properties
*
* java properties > environment variables > OTEL_RESOURCE_ATTRIBUTES
*/
static Map updateEnvCfg() {
Map envCfg = new TreeMap<>();
String configEnv = ConfigPropertiesUtils.getString(OTEL_RESOURCE_ATTRIBUTES_KEY);
if (StringUtils.isNotEmpty(configEnv)) {
Map map = OTEL_RESOURCE_ATTRIBUTES_SPLITTER.split(configEnv);
if (!map.isEmpty()) {
for (Map.Entry entry : SDK_ATTRIBUTES_TO_EASE_AGENT_PROPS.entrySet()) {
String value = map.get(entry.getKey());
if (!StringUtils.isEmpty(value)) {
envCfg.put(entry.getValue(), value);
}
}
}
}
// override by environment variables, eg: export OTEL_SERVICE_NAME=xxx
for (Map.Entry entry : OTEL_SDK_ENV_VAR_TO_EASE_AGENT_PROPS.entrySet()) {
String value = SystemEnv.get(entry.getKey());
if (!StringUtils.isEmpty(value)) {
envCfg.put(entry.getValue(), value);
}
}
// override by java properties; eg: java -Dotel.service.name=xxx
for (Map.Entry entry : OTEL_SDK_PROPS_TO_EASE_AGENT_PROPS.entrySet()) {
String value = System.getProperty(entry.getKey());
if (!StringUtils.isEmpty(value)) {
envCfg.put(entry.getValue(), value);
}
}
return envCfg;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/PluginConfig.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.config.Const;
import com.megaease.easeagent.plugin.api.config.IPluginConfig;
import com.megaease.easeagent.plugin.api.config.PluginConfigChangeListener;
import com.megaease.easeagent.plugin.utils.common.StringUtils;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class PluginConfig implements IPluginConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(PluginConfig.class);
private final Set listeners;
private final String domain;
private final String namespace;
private final String id;
private final Map global;
private final Map cover;
private final boolean enabled;
protected PluginConfig(@Nonnull String domain, @Nonnull String id, @Nonnull Map global, @Nonnull String namespace, @Nonnull Map cover, @Nonnull Set listeners) {
this.domain = domain;
this.namespace = namespace;
this.id = id;
this.global = global;
this.cover = cover;
this.listeners = listeners;
Boolean b = getBoolean(Const.ENABLED_CONFIG);
if (b == null) {
enabled = false;
} else {
enabled = b;
}
}
public static PluginConfig build(@Nonnull String domain, @Nonnull String id,
@Nonnull Map global, @Nonnull String namespace,
@Nonnull Map cover, PluginConfig oldConfig) {
Set listeners;
if (oldConfig == null) {
listeners = new HashSet<>();
} else {
listeners = oldConfig.listeners;
}
return new PluginConfig(domain, id, global, namespace, cover, listeners);
}
@Override
public String domain() {
return domain;
}
@Override
public String namespace() {
return namespace;
}
@Override
public String id() {
return id;
}
@Override
public boolean hasProperty(String property) {
return global.containsKey(property) || cover.containsKey(property);
}
@Override
public String getString(String property) {
String value = cover.get(property);
if (value != null) {
return value;
}
return global.get(property);
}
@Override
public Integer getInt(String property) {
String value = this.getString(property);
if (value == null) {
return null;
}
try {
return Integer.parseInt(value);
} catch (Exception e) {
return null;
}
}
private boolean isTrue(String value) {
return value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("true");
}
@Override
public Boolean getBoolean(String property) {
String value = cover.get(property);
boolean implB = true;
if (value != null) {
implB = isTrue(value);
}
value = global.get(property);
boolean globalB = false;
if (value != null) {
globalB = isTrue(value);
}
return implB && globalB;
}
@Override
public boolean enabled() {
return enabled;
}
@Override
public Double getDouble(String property) {
String value = this.getString(property);
if (value == null) {
return null;
}
try {
return Double.parseDouble(value);
} catch (Exception e) {
return null;
}
}
@Override
public Long getLong(String property) {
String value = this.getString(property);
if (value == null) {
return null;
}
try {
return Long.parseLong(value);
} catch (Exception e) {
return null;
}
}
@Override
public List getStringList(String property) {
String value = this.getString(property);
if (StringUtils.isEmpty(value)) {
return Collections.emptyList();
}
return Arrays.stream(value.split(","))
.map(String::trim)
.collect(Collectors.toList());
}
@Override
public IPluginConfig getGlobal() {
return new Global(domain, id, global, namespace);
}
@Override
public Set keySet() {
Set keys = new HashSet<>(global.keySet());
keys.addAll(cover.keySet());
return keys;
}
@Override
public void addChangeListener(PluginConfigChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public void foreachConfigChangeListener(Consumer action) {
Set oldListeners;
synchronized (listeners) {
oldListeners = new HashSet<>(listeners);
}
for (PluginConfigChangeListener oldListener : oldListeners) {
try {
action.accept(oldListener);
} catch (Exception e) {
LOGGER.error("PluginConfigChangeListener<{}> change plugin config fail : {}", oldListener.getClass(), e.getMessage());
}
}
}
public class Global extends PluginConfig implements IPluginConfig {
public Global(String domain, String id, Map global, String namespace) {
super(domain, id, global, namespace, Collections.emptyMap(), Collections.emptySet());
}
@Override
public void addChangeListener(PluginConfigChangeListener listener) {
PluginConfig.this.addChangeListener(listener);
}
@Override
public void foreachConfigChangeListener(Consumer action) {
PluginConfig.this.foreachConfigChangeListener(action);
}
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/PluginConfigManager.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.log4j2.Logger;
import com.megaease.easeagent.log4j2.LoggerFactory;
import com.megaease.easeagent.plugin.api.config.ChangeItem;
import com.megaease.easeagent.plugin.api.config.Config;
import com.megaease.easeagent.plugin.api.config.ConfigChangeListener;
import com.megaease.easeagent.plugin.api.config.IConfigFactory;
import java.util.*;
import static com.megaease.easeagent.plugin.api.config.ConfigConst.PLUGIN_GLOBAL;
public class PluginConfigManager implements IConfigFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(PluginConfigManager.class);
private Runnable shutdownRunnable;
private final Configs configs;
private final Map pluginSourceConfigs;
private final Map pluginConfigs;
private PluginConfigManager(Configs configs, Map pluginSourceConfigs, Map pluginConfigs) {
this.configs = Objects.requireNonNull(configs, "configs must not be null.");
this.pluginSourceConfigs = Objects.requireNonNull(pluginSourceConfigs, "pluginSourceConfigs must not be null.");
this.pluginConfigs = Objects.requireNonNull(pluginConfigs, "pluginConfigs must not be null.");
}
public static PluginConfigManager.Builder builder(Configs configs) {
PluginConfigManager pluginConfigManager = new PluginConfigManager(configs, new HashMap<>(), new HashMap<>());
return pluginConfigManager.new Builder();
}
@Override
public Config getConfig() {
return this.configs;
}
@Override
public String getConfig(String property) {
return configs.getString(property);
}
@Override
public String getConfig(String property, String defaultValue) {
return configs.getString(property, defaultValue);
}
public PluginConfig getConfig(String domain, String namespace, String id) {
return getConfig(domain, namespace, id, null);
}
public synchronized PluginConfig getConfig(String domain, String namespace, String id, PluginConfig oldConfig) {
Key key = new Key(domain, namespace, id);
PluginConfig pluginConfig = pluginConfigs.get(key);
if (pluginConfig != null) {
return pluginConfig;
}
Map globalConfig = getGlobalConfig(domain, id);
Map coverConfig = getCoverConfig(domain, namespace, id);
PluginConfig newPluginConfig = PluginConfig.build(domain, id, globalConfig, namespace, coverConfig, oldConfig);
pluginConfigs.put(key, newPluginConfig);
return newPluginConfig;
}
private Map getGlobalConfig(String domain, String id) {
return getConfigSource(domain, PLUGIN_GLOBAL, id);
}
private Map getCoverConfig(String domain, String namespace, String id) {
return getConfigSource(domain, namespace, id);
}
private Map getConfigSource(String domain, String namespace, String id) {
PluginSourceConfig sourceConfig = pluginSourceConfigs.get(new Key(domain, namespace, id));
if (sourceConfig == null) {
return Collections.emptyMap();
}
return sourceConfig.getProperties();
}
private Set keys(Set keys) {
Set propertyKeys = new HashSet<>();
for (String k : keys) {
if (!ConfigUtils.isPluginConfig(k)) {
continue;
}
PluginProperty property = ConfigUtils.pluginProperty(k);
Key key = new Key(property.getDomain(), property.getNamespace(), property.getId());
propertyKeys.add(key);
}
return propertyKeys;
}
public void shutdown() {
shutdownRunnable.run();
}
protected synchronized void onChange(Map sources) {
Set sourceKeys = keys(sources.keySet());
Map newSources = buildNewSources(sourceKeys, sources);
for (Key sourceKey : sourceKeys) {
pluginSourceConfigs.put(sourceKey, PluginSourceConfig.build(sourceKey.getDomain(), sourceKey.getNamespace(), sourceKey.getId(), newSources));
}
Set changeKeys = buildChangeKeys(sourceKeys);
for (Key changeKey : changeKeys) {
final PluginConfig oldConfig = pluginConfigs.remove(changeKey);
final PluginConfig newConfig = getConfig(changeKey.getDomain(), changeKey.getNamespace(), changeKey.id, oldConfig);
if (oldConfig == null) {
continue;
}
try {
oldConfig.foreachConfigChangeListener(listener -> listener.onChange(oldConfig, newConfig));
} catch (Exception e) {
LOGGER.warn("change config<{}> fail: {}", changeKey.toString(), e.getMessage());
}
}
}
private Map buildNewSources(Set sourceKeys, Map sources) {
Map newSources = new HashMap<>();
for (Key sourceKey : sourceKeys) {
PluginSourceConfig pluginSourceConfig = pluginSourceConfigs.get(sourceKey);
if (pluginSourceConfig == null) {
continue;
}
newSources.putAll(pluginSourceConfig.getSource());
}
newSources.putAll(sources);
return newSources;
}
private Set buildChangeKeys(Set sourceKeys) {
Set changeKeys = new HashSet<>(sourceKeys);
for (Key key : sourceKeys) {
if (!ConfigUtils.isGlobal(key.getNamespace())) {
continue;
}
for (Key oldKey : pluginConfigs.keySet()) {
if (!key.id.equals(oldKey.id)) {
continue;
}
changeKeys.add(oldKey);
}
}
return changeKeys;
}
class Key {
private final String domain;
private final String namespace;
private final String id;
public Key(String domain, String namespace, String id) {
this.domain = domain;
this.namespace = namespace;
this.id = id;
}
public String getDomain() {
return domain;
}
public String getNamespace() {
return namespace;
}
public String getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
return Objects.equals(domain, key.domain) &&
Objects.equals(namespace, key.namespace) &&
Objects.equals(id, key.id);
}
@Override
public int hashCode() {
return Objects.hash(domain, namespace, id);
}
@Override
public String toString() {
return "Key{" +
"domain='" + domain + '\'' +
", namespace='" + namespace + '\'' +
", id='" + id + '\'' +
'}';
}
}
public class Builder {
public PluginConfigManager build() {
synchronized (PluginConfigManager.this) {
Map sources = configs.getConfigs();
Set sourceKeys = keys(sources.keySet());
for (Key sourceKey : sourceKeys) {
pluginSourceConfigs.put(sourceKey, PluginSourceConfig.build(sourceKey.getDomain(), sourceKey.getNamespace(), sourceKey.getId(), sources));
}
for (Key key : pluginSourceConfigs.keySet()) {
getConfig(key.getDomain(), key.getNamespace(), key.getId());
}
shutdownRunnable = configs.addChangeListener(new ChangeListener());
}
return PluginConfigManager.this;
}
}
class ChangeListener implements ConfigChangeListener {
@Override
public void onChange(List list) {
Map sources = new HashMap<>();
for (ChangeItem changeItem : list) {
sources.put(changeItem.getFullName(), changeItem.getNewValue());
}
PluginConfigManager.this.onChange(sources);
}
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/PluginProperty.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import java.util.Objects;
public class PluginProperty {
private final String domain;
private final String namespace;
private final String id;
private final String property;
public PluginProperty(String domain, String namespace, String id, String property) {
this.domain = domain;
this.namespace = namespace;
this.id = id;
this.property = property;
}
public String getDomain() {
return domain;
}
public String getNamespace() {
return namespace;
}
public String getId() {
return id;
}
public String getProperty() {
return property;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PluginProperty that = (PluginProperty) o;
return Objects.equals(domain, that.domain) &&
Objects.equals(namespace, that.namespace) &&
Objects.equals(id, that.id) &&
Objects.equals(property, that.property);
}
@Override
public int hashCode() {
return Objects.hash(domain, namespace, id, property);
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/PluginSourceConfig.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class PluginSourceConfig {
private final String domain;
private final String namespace;
private final String id;
private final Map source;
private final Map properties;
public PluginSourceConfig(String domain, String namespace, String id, Map source, Map properties) {
this.domain = Objects.requireNonNull(domain, "domain must not be null.");
this.namespace = Objects.requireNonNull(namespace, "namespace must not be null.");
this.id = Objects.requireNonNull(id, "id must not be null.");
this.source = Objects.requireNonNull(source, "source must not be null.");
this.properties = Objects.requireNonNull(properties, "properties must not be null.");
}
public static PluginSourceConfig build(String domain, String namespace, String id, Map source) {
Map pluginSource = new HashMap<>();
Map properties = new HashMap<>();
for (Map.Entry sourceEntry : source.entrySet()) {
String key = sourceEntry.getKey();
if (!ConfigUtils.isPluginConfig(key, domain, namespace, id)) {
continue;
}
pluginSource.put(key, sourceEntry.getValue());
PluginProperty property = ConfigUtils.pluginProperty(key);
properties.put(property, sourceEntry.getValue());
}
return new PluginSourceConfig(domain, namespace, id, pluginSource, properties);
}
public Map getSource() {
return source;
}
public String getDomain() {
return domain;
}
public String getNamespace() {
return namespace;
}
public String getId() {
return id;
}
public Map getProperties() {
Map result = new HashMap<>();
for (Map.Entry propertyEntry : properties.entrySet()) {
result.put(propertyEntry.getKey().getProperty(), propertyEntry.getValue());
}
return result;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/ValidateUtils.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
public class ValidateUtils {
public static class ValidException extends RuntimeException {
public ValidException(String message) {
super(message);
}
}
public interface Validator {
void validate(String name, String value);
}
public static void validate(Configs configs, String name, Validator... vs){
String value = configs.getString(name);
for (Validator one : vs) {
one.validate(name, value);
}
}
public static final Validator HasText = (name, value) -> {
if (value == null || value.trim().length() == 0) {
throw new ValidException(String.format("Property[%s] has no non-empty value", name));
}
};
public static final Validator Bool = (name, value) -> {
String upper = value.toUpperCase();
if (upper.equals("TRUE") || upper.equals("FALSE")) {
return;
}
throw new ValidException(String.format("Property[%s] has no boolean value", name));
};
public static final Validator NumberInt = (name, value) -> {
try {
Integer.parseInt(value.trim());
} catch (Exception e) {
throw new ValidException(String.format("Property[%s] has no integer value", name));
}
};
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/WrappedConfigManager.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config;
import com.megaease.easeagent.plugin.async.ThreadUtils;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class WrappedConfigManager implements ConfigManagerMXBean {
private final ClassLoader customClassLoader;
private final ConfigManagerMXBean conf;
public WrappedConfigManager(ClassLoader customClassLoader, ConfigManagerMXBean config) {
this.customClassLoader = customClassLoader;
this.conf = config;
}
@Override
public void updateConfigs(Map configs) {
ThreadUtils.callWithClassLoader(customClassLoader, () -> {
conf.updateConfigs(configs);
return null;
});
}
@Override
public void updateService(String json, String version) throws IOException {
try {
ThreadUtils.callWithClassLoader(customClassLoader, () -> {
try {
conf.updateService(json, version);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
});
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
}
throw e;
}
}
@Override
public void updateCanary(String json, String version) throws IOException {
try {
ThreadUtils.callWithClassLoader(customClassLoader, () -> {
try {
conf.updateCanary(json, version);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
});
} catch (RuntimeException e) {
if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
}
throw e;
}
}
@Override
public void updateService2(Map configs, String version) {
ThreadUtils.callWithClassLoader(customClassLoader, () -> {
conf.updateService2(configs, version);
return null;
});
}
@Override
public void updateCanary2(Map configs, String version) {
ThreadUtils.callWithClassLoader(customClassLoader, () -> {
conf.updateCanary2(configs, version);
return null;
});
}
@Override
public Map getConfigs() {
return ThreadUtils.callWithClassLoader(customClassLoader, conf::getConfigs);
}
@Override
public List availableConfigNames() {
return ThreadUtils.callWithClassLoader(customClassLoader, conf::availableConfigNames);
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/report/ReportConfigAdapter.java
================================================
/*
* Copyright (c) 2021, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.megaease.easeagent.config.report;
import com.megaease.easeagent.plugin.api.config.Config;
import com.megaease.easeagent.plugin.api.config.ConfigConst;
import com.megaease.easeagent.plugin.api.config.Const;
import com.megaease.easeagent.plugin.utils.NoNull;
import com.megaease.easeagent.plugin.utils.common.StringUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import static com.megaease.easeagent.config.ConfigUtils.extractAndConvertPrefix;
import static com.megaease.easeagent.config.ConfigUtils.extractByPrefix;
import static com.megaease.easeagent.config.report.ReportConfigConst.*;
@Slf4j
public class ReportConfigAdapter {
private ReportConfigAdapter() {}
public static void convertConfig(Map config) {
Map cfg = extractAndConvertReporterConfig(config);
config.putAll(cfg);
}
public static Map extractReporterConfig(Config configs) {
Map cfg = extractByPrefix(configs.getConfigs(), REPORT);
// default config
cfg.put(TRACE_ENCODER, NoNull.of(cfg.get(TRACE_ENCODER), SPAN_JSON_ENCODER_NAME));
cfg.put(METRIC_ENCODER, NoNull.of(cfg.get(METRIC_ENCODER), METRIC_JSON_ENCODER_NAME));
cfg.put(LOG_ENCODER, NoNull.of(cfg.get(LOG_ENCODER), LOG_DATA_JSON_ENCODER_NAME));
cfg.put(LOG_ACCESS_ENCODER, NoNull.of(cfg.get(LOG_ACCESS_ENCODER), ACCESS_LOG_JSON_ENCODER_NAME));
cfg.put(TRACE_SENDER_NAME, NoNull.of(cfg.get(TRACE_SENDER_NAME), getDefaultAppender(cfg)));
cfg.put(METRIC_SENDER_NAME, NoNull.of(cfg.get(METRIC_SENDER_NAME), getDefaultAppender(cfg)));
cfg.put(LOG_ACCESS_SENDER_NAME, NoNull.of(cfg.get(LOG_ACCESS_SENDER_NAME), getDefaultAppender(cfg)));
cfg.put(LOG_SENDER_NAME, NoNull.of(cfg.get(LOG_SENDER_NAME), getDefaultAppender(cfg)));
return cfg;
}
public static String getDefaultAppender(Map cfg) {
String outputAppender = cfg.get(join(OUTPUT_SERVER_V2, APPEND_TYPE_KEY));
if (StringUtils.isEmpty(outputAppender)) {
return Const.DEFAULT_APPEND_TYPE;
}
return outputAppender;
}
private static Map extractAndConvertReporterConfig(Map srcConfig) {
Map extract = extractTracingConfig(srcConfig);
Map outputCfg = new TreeMap<>(extract);
// metric config
extract = extractMetricPluginConfig(srcConfig);
outputCfg.putAll(extract);
// log config
extract = extractLogPluginConfig(srcConfig);
outputCfg.putAll(extract);
// if there are access log in metric
updateAccessLogCfg(outputCfg);
// all extract configuration will be overridden by config items start with "report" in srcConfig
extract = extractByPrefix(srcConfig, REPORT);
outputCfg.putAll(extract);
return outputCfg;
}
/**
* this can be deleted if there is not any v1 configuration needed to compatible with
* convert v1 tracing config to v2
*/
private static Map extractTracingConfig(Map srcCfg) {
// outputServer config
Map extract = extractAndConvertPrefix(srcCfg, OUTPUT_SERVER_V1, OUTPUT_SERVER_V2);
Map outputCfg = new TreeMap<>(extract);
// async output config
extract = extractAndConvertPrefix(srcCfg, TRACE_OUTPUT_V1, TRACE_ASYNC);
String target = srcCfg.get(join(TRACE_OUTPUT_V1, "target"));
extract.remove(join(TRACE_ASYNC, "target"));
if (!StringUtils.isEmpty(outputCfg.get(TRACE_SENDER_NAME))) {
log.info("Reporter V2 config trace sender as: {}", outputCfg.get(TRACE_SENDER_NAME));
} else if ("system".equals(target)) {
// check output servers
if (StringUtils.hasText(outputCfg.get(BOOTSTRAP_SERVERS))) {
outputCfg.put(TRACE_SENDER_NAME, KAFKA_SENDER_NAME);
outputCfg.put(TRACE_SENDER_TOPIC_V2, extract.remove(join(TRACE_ASYNC, TOPIC_KEY)));
} else {
outputCfg.put(TRACE_SENDER_NAME, CONSOLE_SENDER_NAME);
}
} else if ("zipkin".equals(target)) {
outputCfg.put(TRACE_SENDER_NAME, ZIPKIN_SENDER_NAME);
String url = extract.remove(join(TRACE_ASYNC, "target.zipkinUrl"));
if (StringUtils.isEmpty(url)) {
outputCfg.put(TRACE_SENDER_NAME, CONSOLE_SENDER_NAME);
} else {
outputCfg.put(join(TRACE_SENDER, "url"), url);
}
} else if (!StringUtils.isEmpty(target)) {
outputCfg.put(TRACE_SENDER_NAME, CONSOLE_SENDER_NAME);
log.info("Unsupported output configuration item:{}={}", TRACE_OUTPUT_TARGET_V1, target);
}
outputCfg.putAll(extract);
return outputCfg;
}
/**
* For Compatibility, call after metric config adapter
*
* extract 'reporter.metric.access.*' to 'reporter.log.access.*'
*/
private static void updateAccessLogCfg(Map outputCfg) {
// reporter.metric.access.*
String prefix = join(METRIC_V2, ConfigConst.Namespace.ACCESS);
Map metricAccess = extractByPrefix(outputCfg, prefix);
Map accessLog = extractAndConvertPrefix(metricAccess, prefix, LOG_ACCESS);
// access log use `kafka` sender
if (METRIC_KAFKA_SENDER_NAME.equals(accessLog.get(LOG_ACCESS_SENDER_NAME))) {
accessLog.put(LOG_ACCESS_SENDER_NAME, KAFKA_SENDER_NAME);
}
outputCfg.putAll(accessLog);
}
/**
* metric report configuration
*
* extract `plugin.observability.global.metric.*` config items to reporter.metric.sender.*`
*
* extract `plugin.observability.[namespace].metric.*` config items
* to reporter.metric.[namespace].sender.*`
*
* @param srcCfg source configuration map
* @return metric reporter config start with 'reporter.metric.[namespace].sender'
*/
private static Map extractMetricPluginConfig(Map srcCfg) {
final String globalKey = "." + ConfigConst.PLUGIN_GLOBAL + ".";
final String prefix = join(ConfigConst.PLUGIN, ConfigConst.OBSERVABILITY);
int metricKeyLength = ConfigConst.METRIC_SERVICE_ID.length();
Map global = extractGlobalMetricConfig(srcCfg);
HashSet namespaces = new HashSet<>();
Map metricConfigs = new HashMap<>(global);
for (Map.Entry e : srcCfg.entrySet()) {
String key = e.getKey();
if (!key.startsWith(prefix)) {
continue;
}
int idx = key.indexOf(ConfigConst.METRIC_SERVICE_ID, prefix.length());
if (idx < 0) {
continue;
}
String namespaceWithSeparator = key.substring(prefix.length(), idx);
String suffix = key.substring(idx + metricKeyLength + 1);
String newKey;
if (namespaceWithSeparator.equals(globalKey)) {
continue;
} else {
if (!namespaces.contains(namespaceWithSeparator)) {
namespaces.add(namespaceWithSeparator);
Map d = extractAndConvertPrefix(global,
METRIC_V2 + ".", METRIC_V2 + namespaceWithSeparator);
metricConfigs.putAll(d);
}
}
if (suffix.startsWith(ENCODER_KEY) || suffix.startsWith(ASYNC_KEY)) {
newKey = METRIC_V2 + namespaceWithSeparator + suffix;
} else if (suffix.equals(INTERVAL_KEY)) {
newKey = METRIC_V2 + namespaceWithSeparator + join(ASYNC_KEY, suffix);
} else {
newKey = METRIC_V2 + namespaceWithSeparator + join(SENDER_KEY, suffix);
}
if (newKey.endsWith(APPEND_TYPE_KEY) && e.getValue().equals("kafka")) {
metricConfigs.put(newKey, METRIC_KAFKA_SENDER_NAME);
} else {
metricConfigs.put(newKey, e.getValue());
}
}
return metricConfigs;
}
private static Map extractGlobalMetricConfig(Map srcCfg) {
final String prefix = join(ConfigConst.PLUGIN, ConfigConst.OBSERVABILITY,
ConfigConst.PLUGIN_GLOBAL,
ConfigConst.PluginID.METRIC);
Map global = new TreeMap<>();
Map extract = extractAndConvertPrefix(srcCfg, prefix, METRIC_SENDER);
for (Map.Entry e : extract.entrySet()) {
if (e.getKey().startsWith(ENCODER_KEY, METRIC_SENDER.length() + 1)) {
global.put(join(METRIC_V2, e.getKey().substring(METRIC_SENDER.length() + 1)), e.getValue());
} else if (e.getKey().endsWith(INTERVAL_KEY)) {
global.put(join(METRIC_ASYNC, INTERVAL_KEY), e.getValue());
} else if (e.getKey().endsWith(APPEND_TYPE_KEY) && e.getValue().equals("kafka")) {
global.put(e.getKey(), METRIC_KAFKA_SENDER_NAME);
} else {
global.put(e.getKey(), e.getValue());
}
}
// global log level (async)
global.putAll(extractByPrefix(srcCfg, METRIC_SENDER));
global.putAll(extractByPrefix(srcCfg, METRIC_ASYNC));
global.putAll(extractByPrefix(srcCfg, METRIC_ENCODER));
return global;
}
/**
* metric report configuration
*
* extract `plugin.observability.global.metric.*` config items to reporter.metric.sender.*`
*
* extract `plugin.observability.[namespace].metric.*` config items
* to reporter.metric.[namespace].sender.*`
*
* @param srcCfg source configuration map
* @return metric reporter config start with 'reporter.metric.[namespace].sender'
*/
private static Map extractLogPluginConfig(Map srcCfg) {
final String globalKey = "." + ConfigConst.PLUGIN_GLOBAL + ".";
final String prefix = join(ConfigConst.PLUGIN, ConfigConst.OBSERVABILITY);
String typeKey = join("", ConfigConst.PluginID.LOG, "");
int typeKeyLength = ConfigConst.PluginID.LOG.length();
final String reporterPrefix = LOGS;
Map global = extractGlobalLogConfig(srcCfg);
HashSet namespaces = new HashSet<>();
Map outputConfigs = new TreeMap<>(global);
for (Map.Entry e : srcCfg.entrySet()) {
String key = e.getKey();
if (!key.startsWith(prefix)) {
continue;
}
int idx = key.indexOf(typeKey, prefix.length());
if (idx < 0) {
continue;
} else {
idx += 1;
}
String namespaceWithSeparator = key.substring(prefix.length(), idx);
String suffix = key.substring(idx + typeKeyLength + 1);
String newKey;
if (namespaceWithSeparator.equals(globalKey)) {
continue;
} else {
if (!namespaces.contains(namespaceWithSeparator)) {
namespaces.add(namespaceWithSeparator);
Map d = extractAndConvertPrefix(global,
reporterPrefix + ".", reporterPrefix + namespaceWithSeparator);
outputConfigs.putAll(d);
}
}
if (suffix.startsWith(ENCODER_KEY) || suffix.startsWith(ASYNC_KEY)) {
newKey = reporterPrefix + namespaceWithSeparator + suffix;
} else {
newKey = reporterPrefix + namespaceWithSeparator + join(SENDER_KEY, suffix);
}
outputConfigs.put(newKey, e.getValue());
}
return outputConfigs;
}
/**
* extract `plugin.observability.global.log.*` config items to `reporter.log.sender.*`
* extract `plugin.observability.global.log.output.*` config items to `reporter.log.output.*`
*
* @param srcCfg source config map
* @return reporter log config
*/
private static Map extractGlobalLogConfig(Map srcCfg) {
final String prefix = join(ConfigConst.PLUGIN, ConfigConst.OBSERVABILITY,
ConfigConst.PLUGIN_GLOBAL,
ConfigConst.PluginID.LOG);
Map global = new TreeMap<>();
Map extract = extractAndConvertPrefix(srcCfg, prefix, LOG_SENDER);
for (Map.Entry e : extract.entrySet()) {
String key = e.getKey();
if (key.startsWith(ENCODER_KEY, LOG_SENDER.length() + 1)) {
global.put(join(LOGS, key.substring(LOG_SENDER.length() + 1)), e.getValue());
} else if (key.startsWith(ASYNC_KEY, LOG_SENDER.length() + 1)) {
global.put(join(LOGS, key.substring(LOG_SENDER.length() + 1)), e.getValue());
} else {
global.put(e.getKey(), e.getValue());
}
}
// global log level (async)
global.putAll(extractByPrefix(srcCfg, LOG_SENDER));
global.putAll(extractByPrefix(srcCfg, LOG_ASYNC));
global.putAll(extractByPrefix(srcCfg, LOG_ENCODER));
return global;
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/report/ReportConfigConst.java
================================================
/*
* Copyright (c) 2021, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.megaease.easeagent.config.report;
@SuppressWarnings("unused")
public class ReportConfigConst {
private ReportConfigConst() {}
public static final String KAFKA_SENDER_NAME = "kafka";
public static final String METRIC_KAFKA_SENDER_NAME = "metricKafka";
public static final String CONSOLE_SENDER_NAME = "console";
public static final String ZIPKIN_SENDER_NAME = "http";
public static final String NOOP_SENDER_NAME = "noop";
public static final String SPAN_JSON_ENCODER_NAME = "SpanJsonEncoder";
public static final String METRIC_JSON_ENCODER_NAME = "MetricJsonEncoder";
public static final String LOG_DATA_JSON_ENCODER_NAME = "LogDataJsonEncoder";
public static final String ACCESS_LOG_JSON_ENCODER_NAME = "AccessLogJsonEncoder";
public static final String HTTP_SPAN_JSON_ENCODER_NAME = "HttpSpanJsonEncoder";
public static final String LOG_ENCODER_NAME = "StringEncoder";
static final String DELIMITER = ".";
public static final String TOPIC_KEY = "topic";
public static final String LOG_APPENDER_KEY = "appenderName";
public static final String ENABLED_KEY = "enabled";
public static final String SENDER_KEY = "sender";
public static final String ENCODER_KEY = "encoder";
public static final String ASYNC_KEY = "output";
public static final String APPEND_TYPE_KEY = "appendType";
public static final String INTERVAL_KEY = "interval";
public static final String ASYNC_THREAD_KEY = "reportThread";
public static final String ASYNC_MSG_MAX_BYTES_KEY = "messageMaxBytes";
public static final String ASYNC_MSG_TIMEOUT_KEY = "messageTimeout";
public static final String ASYNC_QUEUE_MAX_SIZE_KEY = "queuedMaxSize";
public static final String ASYNC_QUEUE_MAX_LOGS_KEY = "queuedMaxLogs";
public static final String ASYNC_QUEUE_MAX_ITEMS_KEY = "queuedMaxItems";
/**
* Reporter v2 configuration
*/
// -- lv1 --
public static final String REPORT = "reporter";
// ---- lv2 ----
public static final String OUTPUT_SERVER_V2 = join(REPORT, "outputServer");
public static final String TRACE_V2 = join(REPORT, "tracing");
public static final String LOGS = join(REPORT, "log");
public static final String METRIC_V2 = join(REPORT, "metric");
public static final String GENERAL = join(REPORT, "general");
// ------ lv3 ------
public static final String BOOTSTRAP_SERVERS = join(OUTPUT_SERVER_V2, "bootstrapServer");
public static final String OUTPUT_SERVERS_ENABLE = join(OUTPUT_SERVER_V2, ENABLED_KEY);
public static final String OUTPUT_SERVERS_TIMEOUT = join(OUTPUT_SERVER_V2, "timeout");
public static final String OUTPUT_SECURITY_PROTOCOL_V2 = join(OUTPUT_SERVER_V2, "security.protocol");
public static final String OUTPUT_SERVERS_SSL = join(OUTPUT_SERVER_V2, "ssl");
public static final String LOG_ASYNC = join(LOGS, ASYNC_KEY);
public static final String LOG_SENDER = join(LOGS, SENDER_KEY);
public static final String LOG_ENCODER = join(LOGS, ENCODER_KEY);
public static final String LOG_ACCESS = join(LOGS, "access");
public static final String LOG_ACCESS_SENDER = join(LOG_ACCESS, SENDER_KEY);
public static final String LOG_ACCESS_ENCODER = join(LOG_ACCESS, ENCODER_KEY);
public static final String TRACE_SENDER = join(TRACE_V2, SENDER_KEY);
public static final String TRACE_ENCODER = join(TRACE_V2, ENCODER_KEY);
public static final String TRACE_ASYNC = join(TRACE_V2, ASYNC_KEY);
public static final String METRIC_SENDER = join(METRIC_V2, SENDER_KEY);
public static final String METRIC_ENCODER = join(METRIC_V2, ENCODER_KEY);
public static final String METRIC_ASYNC = join(METRIC_V2, ASYNC_KEY);
// -------- lv4 --------
public static final String LOG_SENDER_TOPIC = join(LOG_SENDER, TOPIC_KEY);
public static final String LOG_SENDER_NAME = join(LOG_SENDER, APPEND_TYPE_KEY);
public static final String LOG_ACCESS_SENDER_NAME = join(LOG_ACCESS_SENDER, APPEND_TYPE_KEY);
public static final String LOG_ACCESS_SENDER_ENABLED = join(LOG_ACCESS_SENDER, ENABLED_KEY);
public static final String LOG_ACCESS_SENDER_TOPIC = join(LOG_ACCESS_SENDER, TOPIC_KEY);
public static final String LOG_ASYNC_MESSAGE_MAX_BYTES = join(LOG_ASYNC, ASYNC_MSG_MAX_BYTES_KEY);
public static final String LOG_ASYNC_REPORT_THREAD = join(LOG_ASYNC, ASYNC_THREAD_KEY);
public static final String LOG_ASYNC_MESSAGE_TIMEOUT = join(LOG_ASYNC, ASYNC_MSG_TIMEOUT_KEY);
public static final String LOG_ASYNC_QUEUED_MAX_LOGS = join(LOG_ASYNC, ASYNC_QUEUE_MAX_LOGS_KEY);
public static final String LOG_ASYNC_QUEUED_MAX_SIZE = join(LOG_ASYNC, ASYNC_QUEUE_MAX_SIZE_KEY);
public static final String TRACE_SENDER_NAME = join(TRACE_SENDER, APPEND_TYPE_KEY);
public static final String TRACE_SENDER_ENABLED_V2 = join(TRACE_SENDER, ENABLED_KEY);
public static final String TRACE_SENDER_TOPIC_V2 = join(TRACE_SENDER, TOPIC_KEY);
public static final String TRACE_ASYNC_MESSAGE_MAX_BYTES_V2 = join(TRACE_ASYNC, ASYNC_MSG_MAX_BYTES_KEY);
public static final String TRACE_ASYNC_REPORT_THREAD_V2 = join(TRACE_ASYNC, ASYNC_THREAD_KEY);
public static final String TRACE_ASYNC_MESSAGE_TIMEOUT_V2 = join(TRACE_ASYNC, ASYNC_MSG_TIMEOUT_KEY);
public static final String TRACE_ASYNC_QUEUED_MAX_SPANS_V2 = join(TRACE_ASYNC, "queuedMaxSpans");
public static final String TRACE_ASYNC_QUEUED_MAX_SIZE_V2 = join(TRACE_ASYNC, ASYNC_QUEUE_MAX_SIZE_KEY);
public static final String METRIC_SENDER_NAME = join(METRIC_SENDER, APPEND_TYPE_KEY);
public static final String METRIC_SENDER_ENABLED = join(METRIC_SENDER, ENABLED_KEY);
public static final String METRIC_SENDER_TOPIC = join(METRIC_SENDER, TOPIC_KEY);
public static final String METRIC_SENDER_APPENDER = join(METRIC_SENDER, LOG_APPENDER_KEY);
public static final String METRIC_ASYNC_INTERVAL = join(METRIC_ASYNC, INTERVAL_KEY);
public static final String METRIC_ASYNC_QUEUED_MAX_ITEMS = join(METRIC_ASYNC, ASYNC_QUEUE_MAX_ITEMS_KEY);
public static final String METRIC_ASYNC_MESSAGE_MAX_BYTES = join(METRIC_ASYNC, ASYNC_MSG_MAX_BYTES_KEY);
public static final String OUTPUT_SSL_KEYSTORE_TYPE_V2 = join(OUTPUT_SERVERS_SSL, "keystore.type");
public static final String OUTPUT_KEY_V2 = join(OUTPUT_SERVERS_SSL, "keystore.key");
public static final String OUTPUT_CERT_V2 = join(OUTPUT_SERVERS_SSL, "keystore.certificate.chain");
public static final String OUTPUT_TRUST_CERT_V2 = join(OUTPUT_SERVERS_SSL, "truststore.certificates");
public static final String OUTPUT_TRUST_CERT_TYPE_V2 = join(OUTPUT_SERVERS_SSL, "truststore.type");
public static final String OUTPUT_ENDPOINT_IDENTIFICATION_ALGORITHM_V2 = join(OUTPUT_SERVERS_SSL, "endpoint.identification.algorithm");
/**
* Reporter v1 configuration
*/
public static final String OBSERVABILITY = "observability";
// ---- lv2 ----
public static final String TRACING = join(OBSERVABILITY, "tracings");
public static final String OUTPUT_SERVER_V1 = join(OBSERVABILITY, "outputServer");
// ------ lv3 ------
public static final String TRACE_OUTPUT_V1 = join(TRACING, ASYNC_KEY);
public static final String BOOTSTRAP_SERVERS_V1 = join(OUTPUT_SERVER_V1, "bootstrapServer");
// --------- lv4 ---------
public static final String TRACE_OUTPUT_ENABLED_V1 = join(TRACE_OUTPUT_V1, ENABLED_KEY);
public static final String TRACE_OUTPUT_TOPIC_V1 = join(TRACE_OUTPUT_V1, TOPIC_KEY);
public static final String TRACE_OUTPUT_TARGET_V1 = join(TRACE_OUTPUT_V1, "target");
public static final String TRACE_OUTPUT_TARGET_ZIPKIN_URL = join(TRACE_OUTPUT_V1, "target.zipkinUrl");
public static final String TRACE_OUTPUT_REPORT_THREAD_V1 = join(TRACE_OUTPUT_V1, ASYNC_THREAD_KEY);
public static final String TRACE_OUTPUT_MESSAGE_TIMEOUT_V1 = join(TRACE_OUTPUT_V1, ASYNC_MSG_TIMEOUT_KEY);
public static final String TRACE_OUTPUT_QUEUED_MAX_SPANS_V1 = join(TRACE_OUTPUT_V1, "queuedMaxSpans");
public static final String TRACE_OUTPUT_QUEUED_MAX_SIZE_V1 = join(TRACE_OUTPUT_V1, ASYNC_QUEUE_MAX_SIZE_KEY);
public static final String GLOBAL_METRIC = "plugin.observability.global.metric";
public static final String GLOBAL_METRIC_ENABLED = join(GLOBAL_METRIC, ENABLED_KEY);
public static final String GLOBAL_METRIC_TOPIC = join(GLOBAL_METRIC, TOPIC_KEY);
public static final String GLOBAL_METRIC_APPENDER = join(GLOBAL_METRIC, APPEND_TYPE_KEY);
public static String join(String... texts) {
return String.join(DELIMITER, texts);
}
}
================================================
FILE: config/src/main/java/com/megaease/easeagent/config/yaml/YamlReader.java
================================================
/*
* Copyright (c) 2017, MegaEase
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.megaease.easeagent.config.yaml;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.InputStream;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class YamlReader {
private Map yaml;
private static final DumperOptions DUMPER_OPTIONS;
static {
DUMPER_OPTIONS = new DumperOptions();
DUMPER_OPTIONS.setLineBreak(DumperOptions.LineBreak.getPlatformLineBreak());
}
public YamlReader() {
// ignored
}
public YamlReader load(InputStream in) {
if (in != null) {
yaml = new Yaml(DUMPER_OPTIONS).load(in);
}
return this;
}
public Map getYaml() {
return yaml;
}
public static YamlReader merge(YamlReader target, YamlReader source) {
Map targetMap = target.yaml;
Map sourceMap = source.yaml;
merge(targetMap, sourceMap);
YamlReader result = new YamlReader();
result.yaml = new HashMap<>(targetMap);
return result;
}
@SuppressWarnings("unchecked")
private static void merge(Map target, Map source) {
source.forEach((key, value) -> {
Object existing = target.get(key);
if (value instanceof Map && existing instanceof Map) {
Map result = new LinkedHashMap<>((Map) existing);
merge(result, (Map) value);
target.put(key, result);
} else {
target.put(key, value);
}
});
}
public Map compress() {
if (Objects.isNull(yaml) || yaml.size() == 0) {
return Collections.emptyMap();
}
final Deque keyStack = new LinkedList<>();
final Map resultMap = new HashMap<>();
compress(yaml, keyStack, resultMap);
return resultMap;
}
@SuppressWarnings("unchecked")
private void compress(Map, Object> result, Deque keyStack, Map resultMap) {
result.forEach((k, v) -> {
keyStack.addLast(String.valueOf(k));
if (v instanceof Map) {
compress((Map, Object>) v, keyStack, resultMap);
keyStack.removeLast();
return;
}
if (v instanceof List) {
String value = ((List