Skip to content

Commit f39eeec

Browse files
committed
ROX-33584: Add verbosity setting to FSS and use the glog log adapter to forward client-go logs to glog
1 parent ccf1708 commit f39eeec

File tree

5 files changed

+127
-28
lines changed

5 files changed

+127
-28
lines changed

deploy/charts/fleetshard-sync/templates/deployment.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ spec:
5656
value: {{ .Values.clusterName }}
5757
- name: ENVIRONMENT
5858
value: {{ .Values.environment }}
59+
- name: GLOG_V
60+
value: {{ .Values.glogVerbosity | quote }}
5961
- name: CREATE_AUTH_PROVIDER
6062
value: "{{ .Values.createAuthProvider }}"
6163
- name: AUTH_TYPE

deploy/charts/fleetshard-sync/values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ createAuthProvider: true
1818
# Static token can be issued by the kubernetes issuer with the following command:
1919
# $ kubectl create token -n rhacs fleetshard-sync --audience acs-fleet-manager-private-api
2020
staticToken: ""
21+
# glog verbosity level (see CONTRIBUTING.md for guidelines)
22+
# 1: Production level logging - no unnecessary spam, no sensitive information
23+
# 5: Stage/test level logging - useful debugging information, not spammy
24+
# 10: Local/debug level logging - useful for tracing transactions during development
25+
glogVerbosity: "1"
2126
auditLogs:
2227
enabled: true
2328
skipTLSVerify: true

fleetshard/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type Config struct {
3030
ServiceAccountTokenFile string `env:"FLEET_MANAGER_TOKEN_FILE"`
3131
CreateAuthProvider bool `env:"CREATE_AUTH_PROVIDER" envDefault:"false"`
3232
MetricsAddress string `env:"FLEETSHARD_METRICS_ADDRESS" envDefault:":8080"`
33+
LogVerbosity string `env:"GLOG_V" envDefault:"1"`
3334
DefaultBaseCRDURL string `env:"DEFAULT_BASE_CRD_URL" envDefault:"https://raw.githubusercontent.com/stackrox/stackrox/%s/operator/bundle/manifests/"`
3435
// TenantImagePullSecret can be used to inject a Kubernetes image pull secret into tenant namespaces.
3536
// If it is empty, nothing is injected (for example, it is not required when running on OpenShift).

fleetshard/main.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import (
1414
"k8s.io/client-go/informers"
1515

1616
"github.com/golang/glog"
17-
"github.com/stackrox/acs-fleet-manager/fleetshard/config"
17+
cfg "github.com/stackrox/acs-fleet-manager/fleetshard/config"
1818
"github.com/stackrox/acs-fleet-manager/fleetshard/pkg/fleetshardmetrics"
1919
"github.com/stackrox/acs-fleet-manager/fleetshard/pkg/k8s"
2020
"github.com/stackrox/acs-fleet-manager/fleetshard/pkg/runtime"
2121
"github.com/stackrox/acs-fleet-manager/pkg/logger"
2222
"github.com/stackrox/acs-fleet-manager/pkg/server/profiler"
2323
"golang.org/x/sys/unix"
24+
"k8s.io/klog/v2"
2425
ctrl "sigs.k8s.io/controller-runtime"
2526
)
2627

@@ -35,18 +36,30 @@ func main() {
3536
glog.Info("Unable to set logtostderr to true")
3637
}
3738

38-
config, err := config.GetConfig()
39+
config, err := cfg.GetConfig()
3940
if err != nil {
4041
glog.Fatalf("Failed to load configuration: %v", err)
4142
}
4243

44+
if err := flag.Set("v", config.LogVerbosity); err != nil {
45+
glog.Errorf("Unable to set glog verbosity: %v", err)
46+
}
47+
// Set up klog with its own FlagSet to avoid conflicts with glog
48+
klogFlags := flag.NewFlagSet("klog", flag.ContinueOnError)
49+
klog.InitFlags(klogFlags)
50+
// Set klog's verbosity to match glog's
51+
if err := klogFlags.Set("v", config.LogVerbosity); err != nil {
52+
glog.Errorf("Unable to set klog verbosity: %v", err)
53+
}
54+
4355
ctx, cancel := context.WithTimeout(context.Background(), config.StartupTimeout)
4456
defer cancel()
4557
glog.Infof("Starting application, timeout=%s", config.StartupTimeout)
4658
glog.Infof("FleetManagerEndpoint: %s", config.FleetManagerEndpoint)
4759
glog.Infof("ClusterID: %s", config.ClusterID)
4860
glog.Infof("RuntimePollPeriod: %s", config.RuntimePollPeriod.String())
4961
glog.Infof("AuthType: %s", config.AuthType)
62+
glog.Infof("LogVerbosity: %s", config.LogVerbosity)
5063
glog.Infof("ManagedDB.Enabled: %t", config.ManagedDB.Enabled)
5164
glog.Infof("ManagedDB.SecurityGroup: %s", config.ManagedDB.SecurityGroup)
5265
glog.Infof("ManagedDB.SubnetGroup: %s", config.ManagedDB.SubnetGroup)
@@ -56,7 +69,11 @@ func main() {
5669
}
5770
glog.Info("Creating k8s client...")
5871
k8sClient := k8s.CreateClientOrDie()
59-
ctrl.SetLogger(logger.NewKubeAPILogger())
72+
73+
kubeLogger := logger.NewKubeAPILogger()
74+
ctrl.SetLogger(kubeLogger) // forward controller-runtime logs
75+
klog.SetLogger(kubeLogger) // forward client-go logs
76+
6077
glog.Info("Creating runtime...")
6178
runtime, err := runtime.NewRuntime(ctx, config, k8sClient)
6279
if err != nil {

pkg/logger/kubeapi_logger.go

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,112 @@
1-
// Kube controller-runtime client uses structured logr.Logger under the hood.
2-
// ACSCS services use glog unstructured logging.
3-
// This file contains a wrapper which helps to send structured logr.Logger logs to glog logSink.
4-
// This wrapper can be removed once ACSCS service move to structured logging instead of glog
1+
// Kube controller-runtime and client-go clients use different logger libraries (zap and klog respectively).
2+
// This is a logr.Logger (common interface) adapter for glog to forward the logs from kubernetes libraries to glog.
53

64
package logger
75

86
import (
7+
"fmt"
8+
"strings"
9+
910
"github.com/go-logr/logr"
10-
"github.com/go-logr/logr/funcr"
1111
"github.com/golang/glog"
12-
"strings"
1312
)
1413

15-
const msgKeyPattern = "msg\"="
14+
// glogSink implements logr.LogSink and forwards logs to glog with appropriate levels.
15+
type glogSink struct {
16+
name string
17+
keysAndValues []interface{}
18+
}
19+
20+
var _ logr.LogSink = &glogSink{}
1621

17-
// NewKubeAPILogger creates a new logr.Logger instance which uses a glog.Warning as log message sink.
18-
// This logger should be passed to controller-runtime client. The client will use it to print log messages.
19-
func NewKubeAPILogger() logr.Logger {
20-
return funcr.New(func(prefix, args string) {
21-
logMsg := sanitizeLog(args)
22-
if prefix != "" {
23-
glog.Warningf("%s: %s\n", prefix, logMsg)
24-
} else {
25-
glog.Warningln(logMsg)
26-
}
27-
}, funcr.Options{})
22+
// Init implements logr.LogSink.
23+
func (g *glogSink) Init(_ logr.RuntimeInfo) {
24+
// No initialization needed
25+
}
26+
27+
// Enabled implements logr.LogSink.
28+
// Always returns true and lets glog do the verbosity filtering in Info().
29+
func (g *glogSink) Enabled(_ int) bool {
30+
// Always return true - let glog.V() filter in the Info() method
31+
return true
32+
}
33+
34+
// Info implements logr.LogSink.
35+
// Maps to glog.Info() for level 0, and glog.V() for higher verbosity levels.
36+
func (g *glogSink) Info(level int, msg string, keysAndValues ...interface{}) {
37+
allKVs := make([]interface{}, 0, len(g.keysAndValues)+len(keysAndValues))
38+
allKVs = append(allKVs, g.keysAndValues...)
39+
allKVs = append(allKVs, keysAndValues...)
40+
logMsg := formatMessage(g.name, msg, allKVs)
41+
if level == 0 {
42+
glog.InfoDepth(1, logMsg)
43+
} else {
44+
glog.V(glog.Level(level)).InfoDepth(1, logMsg)
45+
}
2846
}
2947

30-
// sanitizeLog removes redundant builtin logr.Logger log keys from the log message.
31-
// Only `msg` value is worth to log eventually
32-
func sanitizeLog(log string) string {
33-
at := strings.Index(log, msgKeyPattern)
34-
if at > 0 {
35-
return log[at+len(msgKeyPattern):]
48+
// Error implements logr.LogSink.
49+
// Maps to glog.Error().
50+
func (g *glogSink) Error(err error, msg string, keysAndValues ...interface{}) {
51+
allKVs := make([]interface{}, 0, len(g.keysAndValues)+len(keysAndValues)+2)
52+
allKVs = append(allKVs, g.keysAndValues...)
53+
allKVs = append(allKVs, keysAndValues...)
54+
if err != nil {
55+
allKVs = append(allKVs, "error", err)
3656
}
37-
return log
57+
logMsg := formatMessage(g.name, msg, allKVs)
58+
glog.ErrorDepth(1, logMsg)
59+
}
60+
61+
// WithValues implements logr.LogSink.
62+
func (g *glogSink) WithValues(keysAndValues ...interface{}) logr.LogSink {
63+
newKVs := make([]interface{}, 0, len(g.keysAndValues)+len(keysAndValues))
64+
newKVs = append(newKVs, g.keysAndValues...)
65+
newKVs = append(newKVs, keysAndValues...)
66+
return &glogSink{
67+
name: g.name,
68+
keysAndValues: newKVs,
69+
}
70+
}
71+
72+
// WithName implements logr.LogSink.
73+
func (g *glogSink) WithName(name string) logr.LogSink {
74+
newName := name
75+
if g.name != "" {
76+
newName = g.name + "." + name
77+
}
78+
return &glogSink{
79+
name: newName,
80+
keysAndValues: g.keysAndValues,
81+
}
82+
}
83+
84+
// formatMessage formats the log message with name and key-value pairs.
85+
func formatMessage(name, msg string, keysAndValues []interface{}) string {
86+
var builder strings.Builder
87+
88+
if name != "" {
89+
builder.WriteString(name)
90+
builder.WriteString(": ")
91+
}
92+
93+
builder.WriteString(msg)
94+
95+
// Format key-value pairs
96+
for i := 0; i < len(keysAndValues); i += 2 {
97+
if i+1 < len(keysAndValues) {
98+
builder.WriteString(fmt.Sprintf(" %v=%v", keysAndValues[i], keysAndValues[i+1]))
99+
}
100+
}
101+
102+
return builder.String()
103+
}
104+
105+
// NewKubeAPILogger creates a new logr.Logger instance that forwards logs to glog.
106+
// Log levels are mapped as follows:
107+
// - Info(level=0) -> glog.Info()
108+
// - Info(level>0) -> glog.V(level).Info()
109+
// - Error() -> glog.Error()
110+
func NewKubeAPILogger() logr.Logger {
111+
return logr.New(&glogSink{})
38112
}

0 commit comments

Comments
 (0)