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

Alanxtl 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 d9557d572 feat(metadata): expose observability for metadata source, 
storage typ… (#3685)
d9557d572 is described below

commit d9557d57219a3fcdcbabb6191c16ce97ac2410f3
Author: Modo <[email protected]>
AuthorDate: Tue Aug 25 10:25:20 2026 +0800

    feat(metadata): expose observability for metadata source, storage typ… 
(#3685)
    
    * feat(metadata): expose observability for metadata source, storage type, 
revision, and cache hit/miss along the metadata path
    
    * modified HELP information
    
    * fix ci
    
    * feat: restore metadata fetch metrics after listener refactor
    
    * feat: add logs for cache hit/miss
---
 metrics/metadata/collector.go                      | 24 +++++++++
 metrics/metadata/metric_set.go                     | 50 +++++++++++++++++
 .../customizer/service_revision_customizer.go      |  6 +++
 .../servicediscovery/service_discovery_registry.go |  2 +
 .../service_instances_changed_listener_impl.go     | 62 ++++++++++++++++++----
 5 files changed, 134 insertions(+), 10 deletions(-)

diff --git a/metrics/metadata/collector.go b/metrics/metadata/collector.go
index d0d0a9aa8..81dea8d64 100644
--- a/metrics/metadata/collector.go
+++ b/metrics/metadata/collector.go
@@ -66,6 +66,10 @@ func (c *MetadataMetricCollector) start() {
                                        c.handleMetadataMappingListen(event)
                                case MetadataMappingRemove:
                                        c.handleMetadataMappingRemove(event)
+                               case MetadataCache:
+                                       c.handleMetadataCache(event)
+                               case MetadataFetch:
+                                       c.handleMetadataFetch(event)
                                default:
                                }
                        }
@@ -120,6 +124,26 @@ func (c *MetadataMetricCollector) 
handleMetadataMappingRemove(event *MetadataMet
        c.R.Rt(metrics.NewMetricId(metadataMappingRemoveRt, level), 
&metrics.RtOpts{}).Observe(event.CostMs())
 }
 
+func (c *MetadataMetricCollector) handleMetadataCache(event 
*MetadataMetricEvent) {
+       labels := metrics.GetApplicationLevel().Tags()
+       labels[TagProviderApp] = event.Attachment[TagProviderApp]
+       c.R.Counter(metrics.NewMetricIdByLabels(metadataCacheNum, labels)).Inc()
+       if event.Succ {
+               c.R.Counter(metrics.NewMetricIdByLabels(metadataCacheHit, 
labels)).Inc()
+       } else {
+               c.R.Counter(metrics.NewMetricIdByLabels(metadataCacheMiss, 
labels)).Inc()
+       }
+}
+
+func (c *MetadataMetricCollector) handleMetadataFetch(event 
*MetadataMetricEvent) {
+       labels := metrics.GetApplicationLevel().Tags()
+       labels[TagProviderApp] = event.Attachment[TagProviderApp]
+       labels[TagSource] = event.Attachment[TagSource]
+       labels[TagStorageType] = event.Attachment[TagStorageType]
+       labels[TagResult] = event.Attachment[TagResult]
+       c.R.Counter(metrics.NewMetricIdByLabels(metadataFetchNum, labels)).Inc()
+}
+
 type metadataMappingMetricLevel struct {
        *metrics.ApplicationMetricLevel
        attachment map[string]string
diff --git a/metrics/metadata/metric_set.go b/metrics/metadata/metric_set.go
index c760e201c..4f937fcfe 100644
--- a/metrics/metadata/metric_set.go
+++ b/metrics/metadata/metric_set.go
@@ -35,6 +35,8 @@ const (
        MetadataMappingGet
        MetadataMappingListen
        MetadataMappingRemove
+       MetadataCache
+       MetadataFetch
 )
 
 const (
@@ -53,6 +55,8 @@ const (
        dubboMetadataMappingListenRt   = 
"dubbo_metadata_mapping_listen_rt_milliseconds"
        dubboMetadataMappingRemove     = "dubbo_metadata_mapping_remove_num"
        dubboMetadataMappingRemoveRt   = 
"dubbo_metadata_mapping_remove_rt_milliseconds"
+       dubboMetadataCache             = "dubbo_metadata_cache"
+       dubboMetadataFetch             = "dubbo_metadata_fetch"
 )
 
 const (
@@ -116,4 +120,50 @@ var (
        metadataMappingRemoveSucceed = 
metrics.NewMetricKey(dubboMetadataMappingRemove+succSuffix, "Succeed Metadata 
Mapping Remove Num")
        metadataMappingRemoveFailed  = 
metrics.NewMetricKey(dubboMetadataMappingRemove+failedSuffix, "Failed Metadata 
Mapping Remove Num")
        metadataMappingRemoveRt      = 
metrics.NewMetricKey(dubboMetadataMappingRemoveRt, "Metadata Mapping Remove 
Time")
+
+       /*
+          # HELP dubbo_metadata_cache_total Total Metadata Cache Lookup Num
+          # TYPE dubbo_metadata_cache_total counter
+          
dubbo_metadata_cache_total{application_name="metrics-consumer",hostname="localhost",ip="10.252.156.213",provider_app="metrics-provider",}
 5.0
+          
dubbo_metadata_cache_hit_total{application_name="metrics-consumer",hostname="localhost",ip="10.252.156.213",provider_app="metrics-provider",}
 4.0
+          
dubbo_metadata_cache_miss_total{application_name="metrics-consumer",hostname="localhost",ip="10.252.156.213",provider_app="metrics-provider",}
 1.0
+       */
+       // app level, tagged by provider app
+       metadataCacheNum  = 
metrics.NewMetricKey(dubboMetadataCache+totalSuffix, "Total Lookups Against the 
Metadata LRU Cache")
+       metadataCacheHit  = 
metrics.NewMetricKey(dubboMetadataCache+"_hit"+totalSuffix, "Successful Lookups 
Against the Metadata LRU Cache")
+       metadataCacheMiss = 
metrics.NewMetricKey(dubboMetadataCache+"_miss"+totalSuffix, "Failed Lookups 
Against the Metadata LRU Cache")
+
+       /*
+          # HELP dubbo_metadata_fetch_total Total Metadata Fetch Num
+          # TYPE dubbo_metadata_fetch_total counter
+          
dubbo_metadata_fetch_total{application_name="metrics-consumer",hostname="localhost",ip="10.252.156.213",provider_app="metrics-provider",result="success",source="report",storage_type="remote",}
 1.0
+       */
+       // app level, tagged by provider app, fetch source, storage type and 
result
+       metadataFetchNum = metrics.NewMetricKey(dubboMetadataFetch+totalSuffix, 
"Total Metadata Fetch Num")
+)
+
+const (
+       TagProviderApp = "provider_app"
+       TagSource      = "source"
+       TagStorageType = "storage_type"
+       TagResult      = "result"
+)
+
+// Metadata fetch source values
+const (
+       SourceCache  = "cache"
+       SourceReport = "report"
+       SourceRpc    = "rpc"
+)
+
+// Metadata storage type values
+const (
+       StorageTypeLocal  = "local"
+       StorageTypeRemote = "remote"
+)
+
+// Metadata fetch result values
+const (
+       ResultSuccess = "success"
+       ResultFailure = "failure"
 )
diff --git 
a/registry/servicediscovery/customizer/service_revision_customizer.go 
b/registry/servicediscovery/customizer/service_revision_customizer.go
index 158df9c4a..8d2af2178 100644
--- a/registry/servicediscovery/customizer/service_revision_customizer.go
+++ b/registry/servicediscovery/customizer/service_revision_customizer.go
@@ -56,7 +56,9 @@ func (e *exportedServicesRevisionMetadataCustomizer) 
Customize(instance registry
        }
        metaInfo := metadata.GetMetadataInfo(registryId)
        var urls []*common.URL
+       app := ""
        if metaInfo != nil {
+               app = metaInfo.App
                urls = metaInfo.GetExportedServiceURLs()
        }
        revision := resolveRevision(urls)
@@ -64,6 +66,7 @@ func (e *exportedServicesRevisionMetadataCustomizer) 
Customize(instance registry
                revision = defaultRevision
        }
        instance.GetMetadata()[constant.ExportedServicesRevisionPropertyName] = 
revision
+       logger.Infof("[Metadata][Revision] calculated %s revision, app=%s 
registryId=%s urls=%d revision=%s", "exported", app, registryId, len(urls), 
revision)
 }
 
 // subscribedServicesRevisionMetadataCustomizer writes a revision derived from
@@ -84,7 +87,9 @@ func (e *subscribedServicesRevisionMetadataCustomizer) 
Customize(instance regist
        }
        metaInfo := metadata.GetMetadataInfo(registryId)
        var urls []*common.URL
+       app := ""
        if metaInfo != nil {
+               app = metaInfo.App
                urls = metaInfo.GetSubscribedURLs()
        }
        revision := resolveRevision(urls)
@@ -92,6 +97,7 @@ func (e *subscribedServicesRevisionMetadataCustomizer) 
Customize(instance regist
                revision = defaultRevision
        }
        instance.GetMetadata()[constant.SubscribedServicesRevisionPropertyName] 
= revision
+       logger.Infof("[Metadata][Revision] calculated %s revision, app=%s 
registryId=%s urls=%d revision=%s", "subscribed", app, registryId, len(urls), 
revision)
 }
 
 // resolveRevision calculates a deterministic revision from the given URLs.
diff --git a/registry/servicediscovery/service_discovery_registry.go 
b/registry/servicediscovery/service_discovery_registry.go
index d19e5ef89..ebba18e4a 100644
--- a/registry/servicediscovery/service_discovery_registry.go
+++ b/registry/servicediscovery/service_discovery_registry.go
@@ -151,6 +151,8 @@ func (s *serviceDiscoveryRegistry) RegisterService() error {
                if err := s.metadataReport.PublishAppMetadata(metaInfo.App, 
metaInfo.Revision, metaInfo); err != nil {
                        return err
                }
+               logger.Infof("[Metadata][Publish] published app metadata, 
app=%s revision=%s urls=%d",
+                       metaInfo.App, metaInfo.Revision, len(urls))
        }
 
        for _, instance := range instances {
diff --git 
a/registry/servicediscovery/service_instances_changed_listener_impl.go 
b/registry/servicediscovery/service_instances_changed_listener_impl.go
index ae5916f16..e6275102f 100644
--- a/registry/servicediscovery/service_instances_changed_listener_impl.go
+++ b/registry/servicediscovery/service_instances_changed_listener_impl.go
@@ -40,6 +40,8 @@ import (
        "dubbo.apache.org/dubbo-go/v3/common/constant"
        "dubbo.apache.org/dubbo-go/v3/metadata"
        "dubbo.apache.org/dubbo-go/v3/metadata/info"
+       "dubbo.apache.org/dubbo-go/v3/metrics"
+       metricsMetadata "dubbo.apache.org/dubbo-go/v3/metrics/metadata"
        "dubbo.apache.org/dubbo-go/v3/registry"
        "dubbo.apache.org/dubbo-go/v3/registry/servicediscovery/store"
        "dubbo.apache.org/dubbo-go/v3/remoting"
@@ -384,23 +386,61 @@ func GetMetadataInfoWithContext(ctx context.Context, app 
string, instance regist
        })
        cacheKey := metadataCacheKey(app, registryId, revision)
        if metadataInfo, ok := metaCache.Get(cacheKey); ok {
+               logger.Debugf("[Metadata][Cache] app=%s registry=%s revision=%s 
host=%s result=hit",
+                       app, registryId, revision, instance.GetHost())
+               publishMetadataCacheEvent(app, true)
+               publishMetadataFetchEvent(app, metricsMetadata.SourceCache, "", 
nil)
                return metadataInfo.(*info.MetadataInfo), nil
        }
+       logger.Debugf("[Metadata][Cache] app=%s registry=%s revision=%s host=%s 
result=miss",
+               app, registryId, revision, instance.GetHost())
+       publishMetadataCacheEvent(app, false)
 
        var metadataInfo *info.MetadataInfo
+       var metricStorageType string
+       var storageType string
+       var source string
        var err error
-       if getMetadataStorageType(instance) == 
constant.RemoteMetadataStorageType {
-               metadataInfo, err = getRemoteMetadataInfo(ctx, app, instance, 
revision, registryId)
+
+       storageType = getMetadataStorageType(instance)
+       metricStorageType = metricsMetadata.StorageTypeLocal
+
+       if storageType == constant.RemoteMetadataStorageType {
+               metricStorageType = metricsMetadata.StorageTypeRemote
+               metadataInfo, source, err = getRemoteMetadataInfo(ctx, app, 
instance, revision, registryId)
        } else {
-               metadataInfo, err = getMetadataInfoFromRPC(ctx, app, instance, 
revision, registryId)
+               metadataInfo, source, err = getMetadataInfoFromRPC(ctx, app, 
instance, revision, registryId)
        }
        if err != nil {
+               publishMetadataFetchEvent(app, source, metricStorageType, err)
                return nil, err
        }
        metaCache.Set(cacheKey, metadataInfo)
+       publishMetadataFetchEvent(app, source, metricStorageType, nil)
        return metadataInfo, nil
 }
 
+func publishMetadataCacheEvent(app string, hit bool) {
+       event := 
metricsMetadata.NewMetadataMetricTimeEvent(metricsMetadata.MetadataCache)
+       event.Succ = hit
+       event.Attachment[metricsMetadata.TagProviderApp] = app
+       metrics.Publish(event)
+}
+
+func publishMetadataFetchEvent(app, source, storageType string, err error) {
+       event := 
metricsMetadata.NewMetadataMetricTimeEvent(metricsMetadata.MetadataFetch)
+       event.Succ = err == nil
+       event.Attachment[metricsMetadata.TagProviderApp] = app
+       event.Attachment[metricsMetadata.TagSource] = source
+       event.Attachment[metricsMetadata.TagStorageType] = storageType
+       if err == nil {
+               event.Attachment[metricsMetadata.TagResult] = 
metricsMetadata.ResultSuccess
+       } else {
+               event.Attachment[metricsMetadata.TagResult] = 
metricsMetadata.ResultFailure
+       }
+       metrics.Publish(event)
+}
+
 var (
        // metadataRetryInitialDelay is the first backoff delay before retrying 
a failed
        // metadata fetch. Package-level so tests can shrink it.
@@ -509,27 +549,29 @@ func getMetadataStorageType(instance 
registry.ServiceInstance) string {
        return storageType
 }
 
-func getMetadataInfoFromRPC(ctx context.Context, app string, instance 
registry.ServiceInstance, revision string, registryId string) 
(*info.MetadataInfo, error) {
+func getMetadataInfoFromRPC(ctx context.Context, app string, instance 
registry.ServiceInstance, revision string, registryId string) 
(*info.MetadataInfo, string, error) {
        metadataInfo, err := metadata.GetMetadataFromRpcWithContext(ctx, 
revision, instance)
        if err != nil {
-               return nil, perrors.Wrapf(err,
+               return nil, metricsMetadata.SourceRpc, perrors.Wrapf(err,
                        "failed app=%s registry=%s revision=%s", app, 
registryId, revision)
        }
-       return requireMetadataInfo(metadataInfo, app, registryId, revision)
+       metadataInfo, err = requireMetadataInfo(metadataInfo, app, registryId, 
revision)
+       return metadataInfo, metricsMetadata.SourceRpc, err
 }
 
-func getRemoteMetadataInfo(ctx context.Context, app string, instance 
registry.ServiceInstance, revision string, registryId string) 
(*info.MetadataInfo, error) {
+func getRemoteMetadataInfo(ctx context.Context, app string, instance 
registry.ServiceInstance, revision string, registryId string) 
(*info.MetadataInfo, string, error) {
        metadataInfo, reportErr := 
metadata.GetMetadataFromMetadataReport(revision, instance, registryId)
        if reportErr == nil && metadataInfo != nil {
-               return metadataInfo, nil
+               return metadataInfo, metricsMetadata.SourceReport, nil
        }
        logMetadataReportFallback(app, registryId, revision, reportErr)
 
        metadataInfo, rpcErr := metadata.GetMetadataFromRpcWithContext(ctx, 
revision, instance)
        if rpcErr != nil {
-               return nil, wrapMetadataRPCFallbackError(rpcErr, reportErr)
+               return nil, metricsMetadata.SourceRpc, 
wrapMetadataRPCFallbackError(rpcErr, reportErr)
        }
-       return requireMetadataInfo(metadataInfo, app, registryId, revision)
+       metadataInfo, rpcErr = requireMetadataInfo(metadataInfo, app, 
registryId, revision)
+       return metadataInfo, metricsMetadata.SourceRpc, rpcErr
 }
 
 func logMetadataReportFallback(app, registryId, revision string, reportErr 
error) {

Reply via email to