Repository: sse-secure-systems/connaisseur
Pull request #1846 (2024-11-29 09:32:03)

feat: support single-line json as log format
Also introduces a structure to add more log formats should the need arise. Does not change the default of logging prettified json.

> Duplicates #1842, since pipeline can only be run by repo members (sadly).

## Checklist
<!--- Mark as done if a point is not necessary. Feel free to reach out if help on any items in the checklist is needed. -->

- [x] PR is rebased to/aimed at branch `develop`
- [x] PR follows [Contributing Guide](https://github.com/sse-secure-systems/connaisseur/blob/master/docs/CONTRIBUTING.md)
- [x] Added tests (if necessary)
- [x] Extended README/Documentation (if necessary)
- [x] Adjusted versions of image and Helm chart in `Chart.yaml` (if necessary)



Patch:
diff --git a/charts/connaisseur/Chart.yaml b/charts/connaisseur/Chart.yaml
index e35b3390..49adcf19 100644
--- a/charts/connaisseur/Chart.yaml
+++ b/charts/connaisseur/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
 name: connaisseur
 description: Helm chart for Connaisseur - a Kubernetes admission controller to integrate container image signature verification and trust pinning into a cluster.
 type: application
-version: 2.7.1
-appVersion: 3.7.1
+version: 2.8.0
+appVersion: 3.8.0
 keywords:
   - container image
   - signature
diff --git a/charts/connaisseur/templates/env.yaml b/charts/connaisseur/templates/env.yaml
index eac180b5..40780365 100644
--- a/charts/connaisseur/templates/env.yaml
+++ b/charts/connaisseur/templates/env.yaml
@@ -14,6 +14,7 @@ data:
   CACHE_EXPIRY_SECONDS: {{ dig "features" "cache" "expirySeconds" 30 . | quote }}
   CACHE_ERRORS: {{ dig "features" "cache" "cacheErrors" true . | quote }}
   LOG_LEVEL: {{ dig "logLevel" "info" . | quote }}
+  LOG_FORMAT: {{ dig "logFormat" "json-pretty" . | quote }}
   {{- end }}
 ---
 {{- if .Values.kubernetes.deployment.envs -}}
diff --git a/charts/connaisseur/values.yaml b/charts/connaisseur/values.yaml
index 232dd139..4c7770f2 100644
--- a/charts/connaisseur/values.yaml
+++ b/charts/connaisseur/values.yaml
@@ -162,6 +162,7 @@ application:
       cacheErrors: true
 
   logLevel: info
+  logFormat: json-pretty  # or json, for single line logging
 # alerting options: https://sse-secure-systems.github.io/connaisseur/latest/features/alerting/#configuration-options
 # alerting:
 #   clusterIdentifier: example-cluster-staging-europe # defaults to "not specified"
diff --git a/cmd/connaisseur/main.go b/cmd/connaisseur/main.go
index 90e20d01..f23e69cd 100644
--- a/cmd/connaisseur/main.go
+++ b/cmd/connaisseur/main.go
@@ -102,9 +102,14 @@ func loadConfigs() (*config.Config, *alerting.Config) {
 // Main function
 func main() {
 	// set logging
-	logrus.SetFormatter(&logrus.JSONFormatter{
-		PrettyPrint: true,
-	})
+	switch utils.ConnaisseurLogFormat() {
+		case constants.LogFormatJson:
+			logrus.SetFormatter(&logrus.JSONFormatter{})
+		default:
+			logrus.SetFormatter(&logrus.JSONFormatter{
+				PrettyPrint: true,
+			})
+	}
 	logrus.SetOutput(os.Stdout)
 	logrus.SetLevel(utils.ConnaisseurLogLevel())
 
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
index e4b47254..44fabdde 100644
--- a/internal/constants/constants.go
+++ b/internal/constants/constants.go
@@ -17,6 +17,7 @@ const (
 	DetectionMode              = "DETECTION_MODE"
 	ResourceValidationMode     = "RESOURCE_VALIDATION_MODE"
 	LogLevel                   = "LOG_LEVEL"
+	LogFormat                  = "LOG_FORMAT"
 	PodName                    = "POD_NAME"
 	DefaultAuthFile            = "secret.yaml"
 	DockerAuthFile             = ".dockerconfigjson"
@@ -82,3 +83,10 @@ const (
 	NotificationResultTimeout = "timeout"
 	NotificationResultInvalid = "invalid"
 )
+
+type ConnaisseurLogFormat uint32
+
+const (
+	LogFormatJson ConnaisseurLogFormat = iota
+	LogFormatJsonPretty
+)
diff --git a/internal/utils/logging.go b/internal/utils/logging.go
index 491e5b05..35f43266 100644
--- a/internal/utils/logging.go
+++ b/internal/utils/logging.go
@@ -31,6 +31,22 @@ func ConnaisseurLogLevel() logrus.Level {
 	return LogLevel(os.Getenv(constants.LogLevel))
 }
 
+func logFormat(logFormat string) constants.ConnaisseurLogFormat {
+	switch strings.ToLower(logFormat) {
+	case "json":
+		return constants.LogFormatJson
+	case "json-pretty":
+		return constants.LogFormatJsonPretty
+	default:
+		// default for backwards compatibility
+		return constants.LogFormatJsonPretty
+	}
+}
+
+func ConnaisseurLogFormat() constants.ConnaisseurLogFormat {
+	return logFormat(os.Getenv(constants.LogFormat))
+}
+
 func InitiateThirdPartyLibraryLogging() {
 	currentLevel := ConnaisseurLogLevel()
 	// Cosign uses debug logs.Debug to redirect registry request log


Test patch:
diff --git a/internal/utils/logging_test.go b/internal/utils/logging_test.go
index 2f751842..b8b6adcc 100644
--- a/internal/utils/logging_test.go
+++ b/internal/utils/logging_test.go
@@ -83,6 +83,31 @@ func TestConnaisseurLogLevel(t *testing.T) {
 	}
 }
 
+func TestConnaisseurLogFormat(t *testing.T) {
+	var testCases = []struct {
+		logFormat string
+		want      constants.ConnaisseurLogFormat
+	}{
+		{
+			logFormat: "json",
+			want:      constants.LogFormatJson,
+		},
+		{
+			logFormat: "json-pretty",
+			want:      constants.LogFormatJsonPretty,
+		},
+		{
+			logFormat: "not valid",
+			want:      constants.LogFormatJsonPretty,
+		},
+	}
+	for _, tc := range testCases {
+		(*testing.T).Setenv(t, constants.LogFormat, tc.logFormat)
+		logFormat := ConnaisseurLogFormat()
+		assert.Equal(t, tc.want, logFormat)
+	}
+}
+
 func TestCompare(t *testing.T) {
 	var testCases = []struct {
 		level1   logrus.Level