Skip to content

Commit 09b4aa9

Browse files
committed
feat(backend): refactor
1 parent 9866134 commit 09b4aa9

6 files changed

Lines changed: 207 additions & 30 deletions

File tree

backend/modules/observability/domain/component/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ type MetricQueryConfig struct {
118118
OfflineCriticalPoint int `mapstructure:"offline_critical_point" json:"offline_critical_point"`
119119
}
120120

121+
type MetricOfflineCalculateConfig struct {
122+
SkipSpaceIds []string `mapstructure:"skip_space_ids" json:"skip_space_ids"`
123+
}
124+
121125
//go:generate mockgen -destination=mocks/config.go -package=mocks . ITraceConfig
122126
type ITraceConfig interface {
123127
GetSystemViews(ctx context.Context) ([]*SystemView, error)
@@ -138,6 +142,7 @@ type ITraceConfig interface {
138142
GetSpanWithAnnotationMqProducerCfg(ctx context.Context) (*MqProducerCfg, error)
139143
GetMetricPlatformTenants(ctx context.Context) (*PlatformTenantsCfg, error)
140144
GetMetricQueryConfig(ctx context.Context) *MetricQueryConfig
145+
GetMetricOfflineCalculateConfig(ctx context.Context) MetricOfflineCalculateConfig
141146

142147
conf.IConfigLoader
143148
}

backend/modules/observability/domain/component/config/mocks/config.go

Lines changed: 44 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/modules/observability/domain/metric/service/metric.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,25 @@ func (m *MetricsService) buildOnlineMetricQuery(ctx context.Context, req *QueryM
442442
return nil
443443
})
444444
mBuilder.mRepoReq.Source = repo.MetricSourceOffline
445+
// add more filter
446+
cfg := m.traceConfig.GetMetricOfflineCalculateConfig(ctx)
447+
if len(cfg.SkipSpaceIds) > 0 {
448+
param.Filters = &loop_span.FilterFields{
449+
QueryAndOr: ptr.Of(loop_span.QueryAndOrEnumAnd),
450+
FilterFields: []*loop_span.FilterField{
451+
{
452+
FieldName: loop_span.SpanFieldSpaceId,
453+
FieldType: loop_span.FieldTypeString,
454+
Values: cfg.SkipSpaceIds,
455+
QueryType: ptr.Of(loop_span.QueryTypeEnumNotIn),
456+
},
457+
{
458+
QueryAndOr: ptr.Of(loop_span.QueryAndOrEnumAnd),
459+
SubFilter: param.Filters,
460+
},
461+
},
462+
}
463+
}
445464
}
446465
return mBuilder, nil
447466
}

backend/modules/observability/domain/metric/service/metric_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package service
55

66
import (
77
"context"
8+
"fmt"
89
"testing"
910

1011
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/config"
@@ -17,6 +18,7 @@ import (
1718
traceServicemocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/trace/service/mocks"
1819
spanfilter "github.com/coze-dev/coze-loop/backend/modules/observability/domain/trace/service/trace/span_filter"
1920
spanfiltermocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/trace/service/trace/span_filter/mocks"
21+
"github.com/coze-dev/coze-loop/backend/pkg/json"
2022
"github.com/stretchr/testify/assert"
2123
"go.uber.org/mock/gomock"
2224
)
@@ -642,3 +644,129 @@ func TestGetMetricGroupBy(t *testing.T) {
642644
assert.Contains(t, err.Error(), "groupby dimension has no alias")
643645
})
644646
}
647+
648+
func TestQueryMetrics_GroupBySpaceID_SkipSpaceIds(t *testing.T) {
649+
t.Parallel()
650+
ctrl := gomock.NewController(t)
651+
defer ctrl.Finish()
652+
653+
repoMock := repomocks.NewMockIMetricRepo(ctrl)
654+
tenantMock := tenantmocks.NewMockITenantProvider(ctrl)
655+
builderMock := traceServicemocks.NewMockTraceFilterProcessorBuilder(ctrl)
656+
filterMock := spanfiltermocks.NewMockFilter(ctrl)
657+
traceConfigMock := configmocks.NewMockITraceConfig(ctrl)
658+
659+
// Setup mocks
660+
tenantMock.EXPECT().GetMetricTenantsByPlatformType(gomock.Any(), gomock.Any()).Return([]string{"tenant-1"}, nil)
661+
builderMock.EXPECT().BuildPlatformRelatedFilter(gomock.Any(), gomock.Any()).Return(filterMock, nil)
662+
663+
// BuildBasicSpanFilter returns filter with SpaceId=0
664+
filterMock.EXPECT().BuildBasicSpanFilter(gomock.Any(), gomock.Any()).DoAndReturn(
665+
func(_ context.Context, env *spanfilter.SpanEnv) ([]*loop_span.FilterField, bool, error) {
666+
return []*loop_span.FilterField{
667+
{
668+
FieldName: loop_span.SpanFieldSpaceId,
669+
Values: []string{"0"},
670+
},
671+
}, true, nil
672+
},
673+
)
674+
675+
// TraceConfig returns SkipSpaceIds
676+
traceConfigMock.EXPECT().GetMetricOfflineCalculateConfig(gomock.Any()).Return(config.MetricOfflineCalculateConfig{
677+
SkipSpaceIds: []string{"space_skip_1", "space_skip_2"},
678+
})
679+
traceConfigMock.EXPECT().GetMetricQueryConfig(gomock.Any()).Return(&config.MetricQueryConfig{
680+
SupportOffline: false,
681+
}).AnyTimes()
682+
683+
requestFilter := &loop_span.FilterFields{
684+
FilterFields: []*loop_span.FilterField{
685+
{FieldName: "some_req_field", Values: []string{"val"}},
686+
},
687+
}
688+
689+
// Assert that filters are modified
690+
repoMock.EXPECT().GetMetrics(gomock.Any(), gomock.Any()).DoAndReturn(
691+
func(_ context.Context, param *repo.GetMetricsParam) (*repo.GetMetricsResult, error) {
692+
d, _ := json.Marshal(param.Filters)
693+
fmt.Println(string(d))
694+
assert.NotNil(t, param.Filters)
695+
assert.Equal(t, loop_span.QueryAndOrEnumAnd, *param.Filters.QueryAndOr)
696+
// Should have 2 fields: space_id not in, and requestFilter
697+
assert.Len(t, param.Filters.FilterFields, 2)
698+
699+
// Check first field: space_id not in skip_ids
700+
f1 := param.Filters.FilterFields[0]
701+
assert.Equal(t, loop_span.SpanFieldSpaceId, f1.FieldName)
702+
assert.NotNil(t, f1.QueryType)
703+
assert.Equal(t, loop_span.QueryTypeEnumNotIn, *f1.QueryType)
704+
assert.ElementsMatch(t, []string{"space_skip_1", "space_skip_2"}, f1.Values)
705+
706+
// Check second field: original filters (basic + request)
707+
f2 := param.Filters.FilterFields[1]
708+
assert.Equal(t, loop_span.QueryAndOrEnumAnd, *f2.QueryAndOr)
709+
710+
// Verify that original filters are preserved
711+
originalFilters := f2.SubFilter
712+
assert.NotNil(t, originalFilters)
713+
assert.Len(t, originalFilters.FilterFields, 2)
714+
715+
// 1. Basic filter (should contain space_id which was modified to Exist)
716+
basicWrapper := originalFilters.FilterFields[0]
717+
assert.NotNil(t, basicWrapper.SubFilter)
718+
719+
foundSpaceId := false
720+
for _, f := range basicWrapper.SubFilter.FilterFields {
721+
if f.FieldName == loop_span.SpanFieldSpaceId {
722+
foundSpaceId = true
723+
assert.NotNil(t, f.QueryType)
724+
assert.Equal(t, loop_span.QueryTypeEnumExist, *f.QueryType)
725+
}
726+
}
727+
assert.True(t, foundSpaceId, "Modified SpaceId filter not found in preserved filters")
728+
729+
// 2. Request filter
730+
reqWrapper := originalFilters.FilterFields[1]
731+
assert.Equal(t, requestFilter, reqWrapper.SubFilter)
732+
733+
return &repo.GetMetricsResult{
734+
Data: []map[string]any{},
735+
}, nil
736+
},
737+
)
738+
739+
metricDef := &testMetricDefinition{
740+
name: "metric_a",
741+
metricType: entity.MetricTypeTimeSeries,
742+
where: []*loop_span.FilterField{},
743+
}
744+
pMetrics := &entity.PlatformMetrics{
745+
MetricGroups: map[string]*entity.MetricGroup{
746+
"test_group": {
747+
MetricDefinitions: []entity.IMetricDefinition{metricDef},
748+
},
749+
},
750+
DrillDownObjects: map[string]*loop_span.FilterField{},
751+
PlatformMetricDefs: map[loop_span.PlatformType]*entity.PlatformMetricDef{
752+
loop_span.PlatformType("loop"): {
753+
MetricGroups: []string{"test_group"},
754+
},
755+
},
756+
}
757+
758+
svc, err := NewMetricsService(repoMock, nil, tenantMock, builderMock, traceConfigMock, pMetrics)
759+
assert.NoError(t, err)
760+
761+
_, err = svc.QueryMetrics(context.Background(), &QueryMetricsReq{
762+
PlatformType: loop_span.PlatformType("loop"),
763+
WorkspaceID: 1,
764+
MetricsNames: []string{"metric_a"},
765+
Granularity: entity.MetricGranularity1Hour,
766+
StartTime: 0,
767+
EndTime: 0,
768+
GroupBySpaceID: true,
769+
FilterFields: requestFilter,
770+
})
771+
assert.NoError(t, err)
772+
}

backend/modules/observability/domain/metric/service/offline_metric_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,7 @@ func TestMetricsService_TraverseMetrics(t *testing.T) {
11051105
traceCfgMock.EXPECT().GetMetricQueryConfig(gomock.Any()).Return(&config.MetricQueryConfig{
11061106
SupportOffline: false,
11071107
}).AnyTimes()
1108+
traceCfgMock.EXPECT().GetMetricOfflineCalculateConfig(gomock.Any()).Return(config.MetricOfflineCalculateConfig{}).AnyTimes()
11081109

11091110
// 配置 filter 与租户
11101111
tenantMock.EXPECT().GetMetricTenantsByPlatformType(gomock.Any(), gomock.Any()).Return([]string{"tenant-1"}, nil)

backend/modules/observability/infra/config/trace.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
consumerListeningCfgKey = "consumer_listening"
3131
metricPlatformTenantCfgKey = "metric_platform_tenants"
3232
metricQueryConfigKey = "metric_query_config"
33+
metricOfflineCalculateCfgKey = "metric_offline_calculate_config"
3334
)
3435

3536
type TraceConfigCenter struct {
@@ -210,6 +211,15 @@ func (t *TraceConfigCenter) GetMetricQueryConfig(ctx context.Context) *config.Me
210211
return cfg
211212
}
212213

214+
func (t *TraceConfigCenter) GetMetricOfflineCalculateConfig(ctx context.Context) config.MetricOfflineCalculateConfig {
215+
cfg := new(config.MetricOfflineCalculateConfig)
216+
if err := t.UnmarshalKey(ctx, metricOfflineCalculateCfgKey, cfg); err != nil {
217+
logs.CtxWarn(ctx, "fail to get metric query cfg, %v", err)
218+
return config.MetricOfflineCalculateConfig{}
219+
}
220+
return *cfg
221+
}
222+
213223
func NewTraceConfigCenter(confP conf.IConfigLoader) config.ITraceConfig {
214224
ret := &TraceConfigCenter{
215225
IConfigLoader: confP,

0 commit comments

Comments
 (0)