This is an automated email from the ASF dual-hosted git repository.

AlexStocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/develop by this push:
     new 25d2f0c7f expose (#3481)
25d2f0c7f is described below

commit 25d2f0c7f5367b7f7bf6e22754126578209c378d
Author: Xuetao Li <[email protected]>
AuthorDate: Sat Jul 11 07:50:40 2026 +0000

    expose (#3481)
---
 dubbo.go                 | 36 +++++++++++++++++++-
 dubbo_test.go            | 85 ++++++++++++++++++++++++++++++++++++++++++++++++
 global/config_test.go    |  2 ++
 global/tracing_config.go | 20 ++++++++++++
 loader.go                | 16 +++++----
 options.go               | 59 +++++++++++++++++++++++++++------
 6 files changed, 201 insertions(+), 17 deletions(-)

diff --git a/dubbo.go b/dubbo.go
index 4fedea24b..70751aa91 100644
--- a/dubbo.go
+++ b/dubbo.go
@@ -29,6 +29,7 @@ import (
 import (
        "dubbo.apache.org/dubbo-go/v3/client"
        "dubbo.apache.org/dubbo-go/v3/common"
+       "dubbo.apache.org/dubbo-go/v3/global"
        "dubbo.apache.org/dubbo-go/v3/server"
 )
 
@@ -40,7 +41,7 @@ var (
        startOnce        sync.Once
 )
 
-// Instance is the highest layer conception that user could touch. It is 
mapped from RootConfig.
+// Instance is the highest layer conception that user could touch.
 // When users want to inject global configurations and configure common 
modules for client layer
 // and server layer, user-side code would be like this:
 //
@@ -61,6 +62,39 @@ func NewInstance(opts ...InstanceOption) (*Instance, error) {
        return &Instance{insOpts: newInsOpts}, nil
 }
 
+// GetOptionsSnapshot returns a detached snapshot of this instance's options.
+func (ins *Instance) GetInstanceOptionsSnapshot() *InstanceOptions {
+       if ins == nil || ins.insOpts == nil {
+               return nil
+       }
+       return ins.insOpts.Clone()
+}
+
+// GetCustomConfig returns a detached snapshot of this instance's custom 
config.
+func (ins *Instance) GetCustomConfigSnapshot() *global.CustomConfig {
+       if ins == nil || ins.insOpts == nil {
+               return nil
+       }
+       return ins.insOpts.CloneCustom()
+}
+
+// GetInstanceOptionsSnapshot returns a detached snapshot of the options 
loaded by Load.
+func GetInstanceOptionsSnapshot() *InstanceOptions {
+       instanceOptionsMutex.Lock()
+       defer instanceOptionsMutex.Unlock()
+       return instanceOptions.Clone()
+}
+
+// GetCustomConfig returns a detached snapshot of the custom config loaded by 
Load.
+func GetCustomConfigSnapshot() *global.CustomConfig {
+       instanceOptionsMutex.Lock()
+       defer instanceOptionsMutex.Unlock()
+       if instanceOptions == nil {
+               return nil
+       }
+       return instanceOptions.CloneCustom()
+}
+
 // NewClient is like client.NewClient, but inject configurations from 
RootConfig and
 // ConsumerConfig
 func (ins *Instance) NewClient(opts ...client.ClientOption) (*client.Client, 
error) {
diff --git a/dubbo_test.go b/dubbo_test.go
index 3c8e123b0..0a6e7e069 100644
--- a/dubbo_test.go
+++ b/dubbo_test.go
@@ -119,6 +119,91 @@ func TestIndependentConfig(t *testing.T) {
        }
 }
 
+func TestInstanceOptionsSnapshotIsDetached(t *testing.T) {
+       useAgent := false
+       ins, err := NewInstance(func(opts *InstanceOptions) {
+               opts.Application.Name = "snapshot-app"
+               opts.Registries = map[string]*global.RegistryConfig{
+                       "zk": {
+                               Protocol:        constant.ZookeeperKey,
+                               Address:         "127.0.0.1:2181",
+                               UseAsMetaReport: "false",
+                       },
+               }
+               opts.Tracing = map[string]*global.TracingConfig{
+                       "jaeger": {
+                               Name:     "jaeger",
+                               Address:  "127.0.0.1:6831",
+                               UseAgent: &useAgent,
+                       },
+               }
+               opts.Custom = &global.CustomConfig{
+                       ConfigMap: map[string]any{"k": "v"},
+               }
+       })
+       require.NoError(t, err)
+
+       snapshot := ins.GetInstanceOptionsSnapshot()
+       require.NotNil(t, snapshot)
+       require.NotNil(t, snapshot.Application)
+       require.NotNil(t, snapshot.Registries["zk"])
+       require.NotNil(t, snapshot.Tracing["jaeger"])
+       require.NotNil(t, snapshot.Tracing["jaeger"].UseAgent)
+       require.NotNil(t, snapshot.Custom)
+
+       snapshot.Application.Name = "changed-app"
+       snapshot.Registries["zk"].Address = "127.0.0.1:2182"
+       *snapshot.Tracing["jaeger"].UseAgent = true
+       snapshot.Custom.ConfigMap["k"] = "changed"
+
+       assert.Equal(t, "snapshot-app", ins.insOpts.Application.Name)
+       assert.Equal(t, "127.0.0.1:2181", ins.insOpts.Registries["zk"].Address)
+       assert.False(t, *ins.insOpts.Tracing["jaeger"].UseAgent)
+       assert.Equal(t, "v", ins.insOpts.Custom.ConfigMap["k"])
+}
+
+func TestWithCustom(t *testing.T) {
+       configMap := map[string]any{"k": "v"}
+       ins, err := NewInstance(WithCustom(configMap))
+       require.NoError(t, err)
+
+       configMap["k"] = "changed"
+
+       custom := ins.GetCustomConfigSnapshot()
+       require.NotNil(t, custom)
+       assert.Equal(t, "v", custom.ConfigMap["k"])
+
+       custom.ConfigMap["k"] = "changed-again"
+       assert.Equal(t, "v", ins.insOpts.Custom.ConfigMap["k"])
+}
+
+func TestGlobalInstanceOptionsSnapshotIsDetached(t *testing.T) {
+       prevIns := instanceOptions
+       defer func() { instanceOptions = prevIns }()
+
+       instanceOptions = defaultInstanceOptions()
+       instanceOptions.Application.Name = "global-snapshot-app"
+       instanceOptions.Custom = &global.CustomConfig{
+               ConfigMap: map[string]any{"k": "v"},
+       }
+
+       snapshot := GetInstanceOptionsSnapshot()
+       require.NotNil(t, snapshot)
+       require.NotNil(t, snapshot.Application)
+       require.NotNil(t, snapshot.Custom)
+
+       snapshot.Application.Name = "changed-app"
+       snapshot.Custom.ConfigMap["k"] = "changed"
+
+       assert.Equal(t, "global-snapshot-app", instanceOptions.Application.Name)
+       assert.Equal(t, "v", instanceOptions.Custom.ConfigMap["k"])
+
+       custom := GetCustomConfigSnapshot()
+       require.NotNil(t, custom)
+       custom.ConfigMap["k"] = "changed-again"
+       assert.Equal(t, "v", instanceOptions.Custom.ConfigMap["k"])
+}
+
 func TestInstanceInitKeepsGlobalOnlyConfigWithConfigCenter(t *testing.T) {
        resetDynamicConfiguration(t)
 
diff --git a/global/config_test.go b/global/config_test.go
index 449cd3f96..90d6abd29 100644
--- a/global/config_test.go
+++ b/global/config_test.go
@@ -1751,8 +1751,10 @@ func TestDefaultCustomConfig(t *testing.T) {
        t.Run("default_custom_config", func(t *testing.T) {
                custom := DefaultCustomConfig()
                assert.NotNil(t, custom)
+               assert.NotNil(t, custom.ConfigMap)
        })
 }
+
 func TestMethodConfigClone(t *testing.T) {
        t.Run("clone_full_method_config", func(t *testing.T) {
                method := &MethodConfig{
diff --git a/global/tracing_config.go b/global/tracing_config.go
index d4c9b423f..a7228b69e 100644
--- a/global/tracing_config.go
+++ b/global/tracing_config.go
@@ -25,3 +25,23 @@ type TracingConfig struct {
        Address     string `yaml:"address" json:"address,omitempty" 
property:"address"`
        UseAgent    *bool  `default:"false" yaml:"use-agent" 
json:"use-agent,omitempty" property:"use-agent"`
 }
+
+// Clone a new TracingConfig
+func (c *TracingConfig) Clone() *TracingConfig {
+       if c == nil {
+               return nil
+       }
+
+       var useAgent *bool
+       if c.UseAgent != nil {
+               v := *c.UseAgent
+               useAgent = &v
+       }
+
+       return &TracingConfig{
+               Name:        c.Name,
+               ServiceName: c.ServiceName,
+               Address:     c.Address,
+               UseAgent:    useAgent,
+       }
+}
diff --git a/loader.go b/loader.go
index 06dc4c838..d855f7bfd 100644
--- a/loader.go
+++ b/loader.go
@@ -68,22 +68,26 @@ var watcher = &fileWatcher{
 
 func Load(opts ...LoaderConfOption) error {
        conf := NewLoaderConf(opts...)
+       newOpts := conf.opts
        if conf.opts == nil {
+               newOpts = defaultInstanceOptions()
                koan := GetConfigResolver(conf)
                koan = conf.MergeConfig(koan)
-               if err := koan.UnmarshalWithConf(instanceOptions.Prefix(),
-                       instanceOptions, koanf.UnmarshalConf{Tag: "yaml"}); err 
!= nil {
+               if err := koan.UnmarshalWithConf(newOpts.Prefix(),
+                       newOpts, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
                        return err
                }
-       } else {
-               instanceOptions = conf.opts
        }
 
-       if err := instanceOptions.init(); err != nil {
+       if err := newOpts.init(); err != nil {
                return err
        }
 
-       instance := &Instance{insOpts: instanceOptions}
+       instanceOptionsMutex.Lock()
+       instanceOptions = newOpts
+       instanceOptionsMutex.Unlock()
+
+       instance := &Instance{insOpts: newOpts}
        // start the file watcher
        once.Do(func() {
                watcher.watcherWg.Add(1)
diff --git a/options.go b/options.go
index 349f0055a..1e2b5157f 100644
--- a/options.go
+++ b/options.go
@@ -18,6 +18,7 @@
 package dubbo
 
 import (
+       "maps"
        "strconv"
        "time"
 )
@@ -218,6 +219,17 @@ func (rc *InstanceOptions) CloneMetrics() 
*global.MetricsConfig {
        return rc.Metrics.Clone()
 }
 
+func (rc *InstanceOptions) CloneTracing() map[string]*global.TracingConfig {
+       if rc.Tracing == nil {
+               return nil
+       }
+       tracing := make(map[string]*global.TracingConfig, len(rc.Tracing))
+       for k, v := range rc.Tracing {
+               tracing[k] = v.Clone()
+       }
+       return tracing
+}
+
 func (rc *InstanceOptions) CloneOtel() *global.OtelConfig {
        if rc.Otel == nil {
                return nil
@@ -271,6 +283,36 @@ func (rc *InstanceOptions) CloneTLSConfig() 
*global.TLSConfig {
        return rc.TLSConfig.Clone()
 }
 
+// Clone returns a snapshot of InstanceOptions. The returned value is detached
+// from the running instance, so callers can inspect or modify it without
+// changing live framework configuration.
+func (rc *InstanceOptions) Clone() *InstanceOptions {
+       if rc == nil {
+               return nil
+       }
+
+       return &InstanceOptions{
+               Application:         rc.CloneApplication(),
+               Protocols:           rc.CloneProtocols(),
+               Registries:          rc.CloneRegistries(),
+               ConfigCenter:        rc.CloneConfigCenter(),
+               MetadataReport:      rc.CloneMetadataReport(),
+               Provider:            rc.CloneProvider(),
+               Consumer:            rc.CloneConsumer(),
+               Metrics:             rc.CloneMetrics(),
+               Tracing:             rc.CloneTracing(),
+               Otel:                rc.CloneOtel(),
+               Logger:              rc.CloneLogger(),
+               Shutdown:            rc.CloneShutdown(),
+               Router:              rc.CloneRouter(),
+               EventDispatcherType: rc.EventDispatcherType,
+               CacheFile:           rc.CacheFile,
+               Custom:              rc.CloneCustom(),
+               Profiles:            rc.CloneProfiles(),
+               TLSConfig:           rc.CloneTLSConfig(),
+       }
+}
+
 type InstanceOption func(*InstanceOptions)
 
 func WithOrganization(organization string) InstanceOption {
@@ -453,16 +495,13 @@ func WithRouter(opts ...router.Option) InstanceOption {
 //     }
 //}
 
-//func WithCustom(opts ...global.CustomOption) InstanceOption {
-//     cusCfg := new(global.CustomConfig)
-//     for _, opt := range opts {
-//             opt(cusCfg)
-//     }
-//
-//     return func(cfg *InstanceOptions) {
-//             cfg.Custom = cusCfg
-//     }
-//}
+func WithCustom(configMap map[string]any) InstanceOption {
+       return func(cfg *InstanceOptions) {
+               custom := global.DefaultCustomConfig()
+               maps.Copy(custom.ConfigMap, configMap)
+               cfg.Custom = custom
+       }
+}
 
 //func WithProfiles(opts ...global.ProfilesOption) InstanceOption {
 //     proCfg := new(global.ProfilesConfig)

Reply via email to