This is an automated email from the ASF dual-hosted git repository.
github-actions[bot] pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git
The following commit(s) were added to refs/heads/master by this push:
new 6f807d51 feat: configure access logs with Telemetry API (#1021)
6f807d51 is described below
commit 6f807d5175cdfe6a831e29ffeb6326d352aef36a
Author: mfordjody <[email protected]>
AuthorDate: Thu Aug 20 22:00:35 2026 +0800
feat: configure access logs with Telemetry API (#1021)
---
.../pkg/bootstrap/inherent_grpc_controller.go | 66 ++++++++++++++++------
.../pkg/bootstrap/inherent_grpc_controller_test.go | 17 ++++++
.../config/kube/gateway/deployment_controller.go | 48 ++++++++++++++--
.../kube/gateway/deployment_controller_test.go | 11 ++++
go.mod | 10 ++--
go.sum | 18 +++---
manifests/charts/base/files/crd-all.gen.yaml | 54 ++++++++++++++++++
manifests/charts/dubbod/files/kube-gateway.yaml | 16 ++++++
pkg/config/telemetry/telemetry.go | 37 ++++++++++++
pkg/config/telemetry/telemetry_test.go | 39 +++++++++++++
pkg/config/validation/validators.go | 44 +++++++++++++++
11 files changed, 326 insertions(+), 34 deletions(-)
diff --git a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller.go
b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller.go
index 7c57f583..231c3393 100644
--- a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller.go
+++ b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller.go
@@ -372,7 +372,17 @@ type inherentGRPCRuntimeConfig struct {
}
type inherentGRPCTelemetryRuntimeConfig struct {
- Metrics *inherentGRPCMetricsRuntimeConfig `json:"metrics,omitempty"`
+ Metrics *inherentGRPCMetricsRuntimeConfig `json:"metrics,omitempty"`
+ Logging []inherentGRPCLoggingRuntimeConfig `json:"logging,omitempty"`
+}
+
+type inherentGRPCLoggingRuntimeConfig struct {
+ Providers []string `json:"providers,omitempty"`
+ Disabled bool `json:"disabled"`
+ Mode string `json:"mode,omitempty"`
+ FilterExpression string `json:"filterExpression,omitempty"`
+ Tags map[string]string `json:"tags,omitempty"`
+ Endpoint string `json:"endpoint,omitempty"`
}
type inherentGRPCMetricsRuntimeConfig struct {
@@ -771,28 +781,52 @@ func buildRuntimeConfigJSON(
}
func inherentGRPCTelemetryConfig(effective telemetryconfig.EffectiveTracing)
*inherentGRPCTelemetryRuntimeConfig {
- if !effective.MetricsConfigured {
+ if !effective.MetricsConfigured && !effective.LoggingConfigured {
return nil
}
- metrics := &inherentGRPCMetricsRuntimeConfig{
- Enabled: effective.MetricsEnabled(),
- Providers: append([]string(nil), effective.MetricProviders...),
- Rules: make([]inherentGRPCMetricRuleRuntimeConfig, 0,
len(effective.MetricRules)),
+ config := &inherentGRPCTelemetryRuntimeConfig{
+ Logging: make([]inherentGRPCLoggingRuntimeConfig, 0,
len(effective.Logging)),
}
- for _, rule := range effective.MetricRules {
- runtimeRule := inherentGRPCMetricRuleRuntimeConfig{
- Metric: rule.Metric.String(),
- Scope: rule.Scope.String(),
+ if effective.MetricsConfigured {
+ config.Metrics = &inherentGRPCMetricsRuntimeConfig{
+ Enabled: effective.MetricsEnabled(),
+ Providers: append([]string(nil),
effective.MetricProviders...),
+ Rules: make([]inherentGRPCMetricRuleRuntimeConfig,
0, len(effective.MetricRules)),
}
- if len(rule.Tags) > 0 {
- runtimeRule.Tags =
make(map[string]inherentGRPCTagOverrideRuntimeConfig, len(rule.Tags))
- for _, tag := range rule.Tags {
- runtimeRule.Tags[tag.Name] =
inherentGRPCTagOverrideRuntimeConfig{Action: tag.Action.String()}
+ for _, rule := range effective.MetricRules {
+ runtimeRule := inherentGRPCMetricRuleRuntimeConfig{
+ Metric: rule.Metric.String(),
+ Scope: rule.Scope.String(),
}
+ if len(rule.Tags) > 0 {
+ runtimeRule.Tags =
make(map[string]inherentGRPCTagOverrideRuntimeConfig, len(rule.Tags))
+ for _, tag := range rule.Tags {
+ runtimeRule.Tags[tag.Name] =
inherentGRPCTagOverrideRuntimeConfig{Action: tag.Action.String()}
+ }
+ }
+ config.Metrics.Rules = append(config.Metrics.Rules,
runtimeRule)
+ }
+ }
+ for _, rule := range effective.Logging {
+ runtimeRule := inherentGRPCLoggingRuntimeConfig{
+ Providers: append([]string(nil),
rule.Providers...),
+ Disabled: rule.Disabled,
+ Mode: rule.Mode.String(),
+ FilterExpression: rule.FilterExpression,
+ Tags: make(map[string]string,
len(rule.Tags)),
+ }
+ if runtimeRule.Mode == "MODE_UNSPECIFIED" {
+ runtimeRule.Mode = "CLIENT_AND_SERVER"
+ }
+ if len(rule.Providers) > 0 {
+ runtimeRule.Endpoint =
telemetryconfig.ProviderEndpoint(rule.Providers[0],
constants.DubboSystemNamespace)
+ }
+ for _, tag := range rule.Tags {
+ runtimeRule.Tags[tag.Name] = tag.Value
}
- metrics.Rules = append(metrics.Rules, runtimeRule)
+ config.Logging = append(config.Logging, runtimeRule)
}
- return &inherentGRPCTelemetryRuntimeConfig{Metrics: metrics}
+ return config
}
func (c *inherentGRPCWorkloadController) buildRuntimeTrafficConfig()
([]inherentGRPCServiceRuntimeConfig, []inherentGRPCRouteRuntimeConfig) {
diff --git a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
index c965b697..073c3c28 100644
--- a/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
+++ b/dubbod/discovery/pkg/bootstrap/inherent_grpc_controller_test.go
@@ -162,6 +162,13 @@ func TestBuildRuntimeConfigJSON(t *testing.T) {
Action: telemetryapi.TagOverride_REMOVE,
}},
}},
+ LoggingConfigured: true,
+ Logging: []telemetryconfig.LoggingRule{{
+ Providers:
[]string{telemetryconfig.OTELLogProvider},
+ Mode: telemetryapi.Logging_Match_SERVER,
+ FilterExpression: "response.code >= 500",
+ Tags: []telemetryconfig.Tag{{Name:
"environment", Value: "test"}},
+ }},
}
data, err := buildRuntimeConfigJSON(workload, nil, nil,
effectiveTelemetry)
if err != nil {
@@ -249,6 +256,16 @@ func TestBuildRuntimeConfigJSON(t *testing.T) {
if tag := rule.Tags["grpc_response_status"]; tag.Action != "REMOVE" {
t.Fatalf("grpc_response_status override = %#v", tag)
}
+ if len(got.Telemetry.Logging) != 1 {
+ t.Fatalf("telemetry logging = %#v, want one",
got.Telemetry.Logging)
+ }
+ logging := got.Telemetry.Logging[0]
+ if logging.Mode != "SERVER" ||
+ logging.Endpoint !=
"http://opentelemetry-collector.dubbo-system.svc:4317" ||
+ logging.FilterExpression != "response.code >= 500" ||
+ logging.Tags["environment"] != "test" {
+ t.Fatalf("telemetry logging = %#v", logging)
+ }
}
func TestInherentTelemetrySerializesAllStandardMetrics(t *testing.T) {
diff --git a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
index a91606c1..beabb6b8 100644
--- a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
+++ b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller.go
@@ -424,11 +424,15 @@ func (d *DeploymentController) configureGateway(log
*dubbolog.Logger, gw gateway
ClusterID: string(d.clusterID),
DomainSuffix: d.domainSuffix(),
OtelEndpoint: observability.OtelEndpoint,
+ OtelLogsEndpoint: observability.OtelLogsEndpoint,
OtelServiceName: observability.OtelServiceName,
OtelSampling: observability.OtelSampling,
OtelTags: observability.OtelTags,
AccessLog: observability.AccessLog,
AccessLogFormat: observability.AccessLogFormat,
+ AccessLogMode: observability.AccessLogMode,
+ AccessLogFilter: observability.AccessLogFilter,
+ AccessLogTags: observability.AccessLogTags,
ActivationControlPlane: d.activationControlPlane(),
ActivationHoldTimeout: features.ActivationHoldTimeout,
@@ -506,11 +510,15 @@ type TemplateInput struct {
ClusterID string
DomainSuffix string
OtelEndpoint string
+ OtelLogsEndpoint string
OtelServiceName string
OtelSampling string
OtelTags string
AccessLog string
AccessLogFormat string
+ AccessLogMode string
+ AccessLogFilter string
+ AccessLogTags string
// ActivationControlPlane is the address a gateway reports pending
requests
// to so that scaled-to-zero targets get activated. Empty turns the
feature
// off in the data plane; the gateway then fails such a request
outright, as
@@ -520,12 +528,16 @@ type TemplateInput struct {
}
type gatewayObservabilityConfig struct {
- OtelEndpoint string
- OtelServiceName string
- OtelSampling string
- OtelTags string
- AccessLog string
- AccessLogFormat string
+ OtelEndpoint string
+ OtelLogsEndpoint string
+ OtelServiceName string
+ OtelSampling string
+ OtelTags string
+ AccessLog string
+ AccessLogFormat string
+ AccessLogMode string
+ AccessLogFilter string
+ AccessLogTags string
}
// observabilityConfigForGateway resolves the meshlevel, namespace, and
@@ -554,6 +566,30 @@ func resolveGatewayObservability(gw gateway.Gateway,
meshNamespace string, resou
if effective.Configured && !effective.Disabled() {
cfg.OtelEndpoint =
telemetryconfig.ProviderEndpoint(effective.Provider(), meshNamespace)
}
+ for _, logging := range effective.Logging {
+ mode := logging.Mode.String()
+ if mode == "MODE_UNSPECIFIED" {
+ mode = "CLIENT_AND_SERVER"
+ }
+ if mode != "SERVER" && mode != "CLIENT_AND_SERVER" {
+ continue
+ }
+ cfg.AccessLog = strconv.FormatBool(!logging.Disabled)
+ cfg.AccessLogMode = mode
+ cfg.AccessLogFilter = logging.FilterExpression
+ tags := make(map[string]string, len(logging.Tags))
+ for _, tag := range logging.Tags {
+ tags[tag.Name] = tag.Value
+ }
+ if len(tags) > 0 {
+ data, _ := json.Marshal(tags)
+ cfg.AccessLogTags = string(data)
+ }
+ if len(logging.Providers) > 0 && !logging.Disabled {
+ cfg.OtelLogsEndpoint =
telemetryconfig.ProviderEndpoint(logging.Providers[0], meshNamespace)
+ }
+ break
+ }
return cfg
}
diff --git
a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
index e429444e..e51b587d 100644
--- a/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
+++ b/dubbod/discovery/pkg/config/kube/gateway/deployment_controller_test.go
@@ -856,6 +856,11 @@ func TestResolveGatewayObservabilityTelemetryHierarchy(t
*testing.T) {
Providers:
[]*apitelemetry.Tracing_TracingProvider{{Name: "localtrace"}},
Tags:
[]*apitelemetry.Tracing_Tag{{Name: "foo", Value: "bar"}},
RandomSamplingPercentage:
wrapperspb.Double(100),
+ }}, Logging: []*apitelemetry.Logging{{
+ Providers:
[]*apitelemetry.Logging_LoggingProvider{{Name:
telemetryconfig.OTELLogProvider}},
+ Match: &apitelemetry.Logging_Match{Mode:
apitelemetry.Logging_Match_SERVER},
+ Filter:
&apitelemetry.Logging_Filter{Expression: "response.code >= 500"},
+ Tags: []*apitelemetry.Logging_Tag{{Name:
"environment", Value: "test"}},
}}},
},
{
@@ -880,6 +885,12 @@ func TestResolveGatewayObservabilityTelemetryHierarchy(t
*testing.T) {
if cfg.OtelTags != `{"userId":"unknown"}` {
t.Fatalf("otel tags = %q", cfg.OtelTags)
}
+ if cfg.OtelLogsEndpoint !=
"http://opentelemetry-collector.dubbo-system.svc:4317" ||
+ cfg.AccessLog != "true" || cfg.AccessLogMode != "SERVER" ||
+ cfg.AccessLogFilter != "response.code >= 500" ||
+ cfg.AccessLogTags != `{"environment":"test"}` {
+ t.Fatalf("access logging = %#v", cfg)
+ }
resources = append(resources, telemetryconfig.Resource{
Name: "workload-override", Namespace: "app", CreationTimestamp:
time.Unix(3, 0),
Spec: &apitelemetry.Telemetry{
diff --git a/go.mod b/go.mod
index 39c7967e..0f4f09f1 100644
--- a/go.mod
+++ b/go.mod
@@ -41,9 +41,9 @@ require (
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru/v2 v2.0.7
- github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f
- github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3
- github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3
+ github.com/kdubbo/api v0.0.0-20260820123851-c3a7c138547d
+ github.com/kdubbo/client-go v0.0.0-20260820124012-0079f2cf2b1d
+ github.com/kdubbo/xds-api v0.0.0-20260820125224-2e2719c54121
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_model v0.6.2
github.com/spf13/cobra v1.10.2
@@ -51,7 +51,7 @@ require (
github.com/stoewer/go-strcase v1.3.1
go.uber.org/atomic v1.11.0
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
- golang.org/x/net v0.56.0
+ golang.org/x/net v0.57.0
golang.org/x/sys v0.47.0
golang.org/x/term v0.45.0
golang.org/x/time v0.15.0
@@ -106,6 +106,7 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 //
indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 //
indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -136,6 +137,7 @@ require (
github.com/spf13/cast v1.8.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.54.0 // indirect
diff --git a/go.sum b/go.sum
index 24e59654..b54475b4 100644
--- a/go.sum
+++ b/go.sum
@@ -1007,6 +1007,7 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod
h1:o//XUCC/F+yRGJoPO/VU
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod
h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod
h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod
h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2
h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod
h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hamba/avro/v2 v2.17.2/go.mod
h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo=
github.com/hashicorp/consul/api v1.3.0/go.mod
h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
@@ -1071,12 +1072,12 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod
h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/jung-kurt/gofpdf v1.0.0/go.mod
h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod
h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod
h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f
h1:T1jlRZ+ulFTUGziUnTjMvNEtiknaHQr4vQYviz4U/kQ=
-github.com/kdubbo/api v0.0.0-20260814141555-d9b670d33d9f/go.mod
h1:8BtJiIovg7QCPsCxXcw3gDf922VcvYq5ihOSvj49Rq8=
-github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3
h1:vTpsq7IAuUqKz05NoCWpDu0MY5SdjpH5NXZpj+l4I9Q=
-github.com/kdubbo/client-go v0.0.0-20260814141742-c761bafa7cf3/go.mod
h1:wCARoHJuh9ccSRQYlGXzPyMfUMYavnRot05dKh1N4KY=
-github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3
h1:ypir1ZNYdAKOuWokpObAdUtCDXLD3yqP1AOqCURe3WU=
-github.com/kdubbo/xds-api v0.0.0-20260814172110-c45be7c324a3/go.mod
h1:o2HDUgL1ntaDbWomZ4cD2tt8jBamuG2qRtjXOa1zZ0Q=
+github.com/kdubbo/api v0.0.0-20260820123851-c3a7c138547d
h1:FCV7OMp3FZmQzmJAM75SSI2SSVHsgbDxHz/pyCQGWVw=
+github.com/kdubbo/api v0.0.0-20260820123851-c3a7c138547d/go.mod
h1:8BtJiIovg7QCPsCxXcw3gDf922VcvYq5ihOSvj49Rq8=
+github.com/kdubbo/client-go v0.0.0-20260820124012-0079f2cf2b1d
h1:bfMl7kxZq9Gp83MNh/4btIjK4FiGzUNbHNJQNDcZL+w=
+github.com/kdubbo/client-go v0.0.0-20260820124012-0079f2cf2b1d/go.mod
h1:XiHz2FHeoNOavRxt2EQLR9WC6Jl69TzM5MwQrS4ToqU=
+github.com/kdubbo/xds-api v0.0.0-20260820125224-2e2719c54121
h1:4yzXRhnMrCm6236P3dqU2f7CJpz7vzIQQkl5ye44HnQ=
+github.com/kdubbo/xds-api v0.0.0-20260820125224-2e2719c54121/go.mod
h1:TilAt91qTzM3pArPae5S/1uRTdH2adhGnFIUkTBaSkk=
github.com/kisielk/errcheck v1.1.0/go.mod
h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod
h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod
h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -1557,6 +1558,7 @@ go.opentelemetry.io/proto/otlp v0.19.0/go.mod
h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI
go.opentelemetry.io/proto/otlp v1.0.0/go.mod
h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod
h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod
h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
+go.opentelemetry.io/proto/otlp v1.9.0
h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod
h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/atomic v1.3.2/go.mod
h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod
h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -1858,8 +1860,8 @@ golang.org/x/net v0.49.0/go.mod
h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
-golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
-golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/manifests/charts/base/files/crd-all.gen.yaml
b/manifests/charts/base/files/crd-all.gen.yaml
index 95713abb..1e88e4f1 100644
--- a/manifests/charts/base/files/crd-all.gen.yaml
+++ b/manifests/charts/base/files/crd-all.gen.yaml
@@ -1601,6 +1601,60 @@ spec:
description: 'Telemetry configuration for Inherent workloads. See
more
details at: '
properties:
+ logging:
+ description: Logging configures access logging for selected
workloads.
+ items:
+ properties:
+ disabled:
+ description: Disables access logging for selected
workloads.
+ nullable: true
+ type: boolean
+ filter:
+ description: Filters access logs using a CEL expression.
+ properties:
+ expression:
+ description: REQUIRED.
+ type: string
+ type: object
+ match:
+ description: Restricts access logs to a reporter side.
+ properties:
+ mode:
+ description: |-
+ Reporter side selected for access logging.
+
+ Valid Options: CLIENT, SERVER, CLIENT_AND_SERVER
+ enum:
+ - MODE_UNSPECIFIED
+ - CLIENT
+ - SERVER
+ - CLIENT_AND_SERVER
+ type: string
+ type: object
+ providers:
+ description: Providers used for access log reporting.
+ items:
+ properties:
+ name:
+ description: REQUIRED.
+ type: string
+ type: object
+ type: array
+ tags:
+ description: Static custom attributes added to generated
access
+ logs.
+ items:
+ properties:
+ name:
+ description: REQUIRED.
+ type: string
+ value:
+ description: Static access log attribute value.
+ type: string
+ type: object
+ type: array
+ type: object
+ type: array
metrics:
description: Metrics configures metric generation for selected
workloads.
items:
diff --git a/manifests/charts/dubbod/files/kube-gateway.yaml
b/manifests/charts/dubbod/files/kube-gateway.yaml
index 4d478c68..ce28a8d4 100644
--- a/manifests/charts/dubbod/files/kube-gateway.yaml
+++ b/manifests/charts/dubbod/files/kube-gateway.yaml
@@ -160,11 +160,27 @@ spec:
{{- if .OtelEndpoint }}
- name: DXGATE_OTEL_ENDPOINT
value: {{ .OtelEndpoint | quote }}
+{{- end }}
+{{- if .OtelLogsEndpoint }}
+ - name: DXGATE_OTEL_LOGS_ENDPOINT
+ value: {{ .OtelLogsEndpoint | quote }}
{{- end }}
- name: DXGATE_ACCESS_LOG
value: {{ .AccessLog | quote }}
- name: DXGATE_ACCESS_LOG_FORMAT
value: {{ .AccessLogFormat | quote }}
+{{- if .AccessLogMode }}
+ - name: DXGATE_ACCESS_LOG_MODE
+ value: {{ .AccessLogMode | quote }}
+{{- end }}
+{{- if .AccessLogFilter }}
+ - name: DXGATE_ACCESS_LOG_FILTER
+ value: {{ .AccessLogFilter | quote }}
+{{- end }}
+{{- if .AccessLogTags }}
+ - name: DXGATE_ACCESS_LOG_TAGS
+ value: {{ .AccessLogTags | quote }}
+{{- end }}
{{- if .ActivationControlPlane }}
# On-demand activation. Absent, the gateway fails a request for a
# scaled-to-zero backend immediately, which is the behaviour every
diff --git a/pkg/config/telemetry/telemetry.go
b/pkg/config/telemetry/telemetry.go
index c243c930..9ae7bee6 100644
--- a/pkg/config/telemetry/telemetry.go
+++ b/pkg/config/telemetry/telemetry.go
@@ -33,6 +33,7 @@ import (
const (
LocalTraceProvider = "localtrace"
PrometheusProvider = "prometheus"
+ OTELLogProvider = "otel"
OTLPPort = 4317
)
@@ -59,6 +60,14 @@ type MetricRule struct {
Tags []MetricTagOverride
}
+type LoggingRule struct {
+ Providers []string
+ Disabled bool
+ Mode api.Logging_Match_Mode
+ FilterExpression string
+ Tags []Tag
+}
+
type EffectiveTracing struct {
Configured bool
Providers []string
@@ -69,6 +78,8 @@ type EffectiveTracing struct {
MetricProviders []string
EnableMetrics *bool
MetricRules []MetricRule
+ LoggingConfigured bool
+ Logging []LoggingRule
}
func ResourcesFromConfigs(configs []config.Config) []Resource {
@@ -163,6 +174,29 @@ func matches(selector, labels map[string]string) bool {
}
func apply(result *EffectiveTracing, spec *api.Telemetry) {
+ if len(spec.GetLogging()) > 0 {
+ result.LoggingConfigured = true
+ result.Logging = make([]LoggingRule, 0, len(spec.GetLogging()))
+ for _, logging := range spec.GetLogging() {
+ if logging == nil {
+ continue
+ }
+ effective := LoggingRule{
+ Disabled:
logging.GetDisabled().GetValue(),
+ Mode: logging.GetMatch().GetMode(),
+ FilterExpression:
logging.GetFilter().GetExpression(),
+ Providers: make([]string, 0,
len(logging.GetProviders())),
+ Tags: make([]Tag, 0,
len(logging.GetTags())),
+ }
+ for _, provider := range logging.GetProviders() {
+ effective.Providers =
append(effective.Providers, provider.GetName())
+ }
+ for _, tag := range logging.GetTags() {
+ effective.Tags = append(effective.Tags,
Tag{Name: tag.GetName(), Value: tag.GetValue()})
+ }
+ result.Logging = append(result.Logging, effective)
+ }
+ }
for _, metrics := range spec.GetMetrics() {
if metrics == nil {
continue
@@ -300,6 +334,9 @@ func ProviderEndpoint(provider, meshNamespace string)
string {
if strings.EqualFold(service, LocalTraceProvider) {
service = "tracing"
}
+ if strings.EqualFold(service, OTELLogProvider) {
+ service = "opentelemetry-collector"
+ }
if service == "" {
return ""
}
diff --git a/pkg/config/telemetry/telemetry_test.go
b/pkg/config/telemetry/telemetry_test.go
index b96c754b..95efde25 100644
--- a/pkg/config/telemetry/telemetry_test.go
+++ b/pkg/config/telemetry/telemetry_test.go
@@ -139,6 +139,42 @@ func TestResolveMetricsRulesOverride(t *testing.T) {
}
}
+func TestResolveLoggingOverride(t *testing.T) {
+ resources := []Resource{
+ {
+ Name: "mesh-default", Namespace: "dubbo-system",
+ Spec: &api.Telemetry{Logging: []*api.Logging{{
+ Providers:
[]*api.Logging_LoggingProvider{{Name: OTELLogProvider}},
+ Tags: []*api.Logging_Tag{{Name: "mesh",
Value: "default"}},
+ }}},
+ },
+ {
+ Name: "workload-override", Namespace: "myapp",
+ Spec: &api.Telemetry{
+ Selector:
&typeapi.WorkloadSelector{MatchLabels: map[string]string{"app": "frontend"}},
+ Logging: []*api.Logging{{
+ Providers:
[]*api.Logging_LoggingProvider{{Name: OTELLogProvider}},
+ Match: &api.Logging_Match{Mode:
api.Logging_Match_SERVER},
+ Filter:
&api.Logging_Filter{Expression: "response.code >= 500"},
+ Tags: []*api.Logging_Tag{{Name:
"environment", Value: "test"}},
+ }},
+ },
+ },
+ }
+
+ got := Resolve(resources, "dubbo-system", "myapp",
map[string]string{"app": "frontend"})
+ if !got.LoggingConfigured || len(got.Logging) != 1 {
+ t.Fatalf("logging = %#v", got.Logging)
+ }
+ rule := got.Logging[0]
+ if len(rule.Providers) != 1 || rule.Providers[0] != OTELLogProvider ||
+ rule.Mode != api.Logging_Match_SERVER ||
+ rule.FilterExpression != "response.code >= 500" ||
+ len(rule.Tags) != 1 || rule.Tags[0] != (Tag{Name:
"environment", Value: "test"}) {
+ t.Fatalf("logging rule = %#v", rule)
+ }
+}
+
func TestMeshlevelSelectorIsIgnored(t *testing.T) {
resources := []Resource{{
Name: "invalid", Namespace: "dubbo-system",
@@ -157,4 +193,7 @@ func TestProviderEndpoint(t *testing.T) {
if got, want := ProviderEndpoint("localtrace", "dubbo-system"),
"http://tracing.dubbo-system.svc:4317"; got != want {
t.Fatalf("endpoint = %q, want %q", got, want)
}
+ if got, want := ProviderEndpoint("otel", "dubbo-system"),
"http://opentelemetry-collector.dubbo-system.svc:4317"; got != want {
+ t.Fatalf("logging endpoint = %q, want %q", got, want)
+ }
}
diff --git a/pkg/config/validation/validators.go
b/pkg/config/validation/validators.go
index 750bde73..92933ccd 100644
--- a/pkg/config/validation/validators.go
+++ b/pkg/config/validation/validators.go
@@ -593,6 +593,50 @@ var ValidateTelemetry =
RegisterValidateFunc("ValidateTelemetry",
if cfg.Namespace == constants.DubboSystemNamespace &&
spec.GetSelector() != nil {
v = appendValidation(v, fmt.Errorf("selector is not
allowed on meshlevel Telemetry in namespace %q",
constants.DubboSystemNamespace))
}
+ for i, logging := range spec.GetLogging() {
+ if logging == nil {
+ v = appendValidation(v, fmt.Errorf("logging[%d]
must not be null", i))
+ continue
+ }
+ providers := map[string]struct{}{}
+ for j, provider := range logging.GetProviders() {
+ name := strings.TrimSpace(provider.GetName())
+ if name == "" {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].providers[%d].name must be set", i, j))
+ continue
+ }
+ if name != telemetryconfig.OTELLogProvider {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].providers[%d].name %q is unsupported", i, j, name))
+ }
+ if _, found := providers[name]; found {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].providers[%d].name %q is duplicated", i, j, name))
+ }
+ providers[name] = struct{}{}
+ }
+ switch logging.GetMatch().GetMode() {
+ case telemetry.Logging_Match_MODE_UNSPECIFIED,
+ telemetry.Logging_Match_CLIENT,
+ telemetry.Logging_Match_SERVER,
+ telemetry.Logging_Match_CLIENT_AND_SERVER:
+ default:
+ v = appendValidation(v,
fmt.Errorf("logging[%d].match.mode is invalid", i))
+ }
+ if logging.GetFilter() != nil &&
strings.TrimSpace(logging.GetFilter().GetExpression()) == "" {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].filter.expression must be set", i))
+ }
+ tagNames := map[string]struct{}{}
+ for j, tag := range logging.GetTags() {
+ name := strings.TrimSpace(tag.GetName())
+ if name == "" {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].tags[%d].name must be set", i, j))
+ continue
+ }
+ if _, found := tagNames[name]; found {
+ v = appendValidation(v,
fmt.Errorf("logging[%d].tags[%d].name %q is duplicated", i, j, name))
+ }
+ tagNames[name] = struct{}{}
+ }
+ }
for i, m := range spec.GetMetrics() {
if m == nil {
v = appendValidation(v, fmt.Errorf("metrics[%d]
must not be null", i))