Repository: clouditor/clouditor
Pull request #969 (2023-02-15 13:58:02)

More debugging info for Store*/Create*/Update*/Remove* methods

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> Merging [#969](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (10ade58) into [main](https://codecov.io/gh/clouditor/clouditor/commit/7016bbdd2da2925319d10e7b99fee8ab4fac787d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (7016bbd) will **increase** coverage by `0.13%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 10ade58 differs from pull request most recent head 4936c60. Consider uploading reports for the commit 4936c60 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #969      +/-   ##
==========================================
+ Coverage   81.76%   81.90%   +0.13%     
==========================================
  Files          87       87              
  Lines        7327     7367      +40     
==========================================
+ Hits         5991     6034      +43     
+ Misses       1027     1025       -2     
+ Partials      309      308       -1     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `81.90% <100.00%> (+0.13%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [service/orchestrator/catalogs.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvY2F0YWxvZ3MuZ28=) | `76.16% <100.00%> (+1.31%)` | :arrow_up: |
| [service/orchestrator/cloud\_service.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvY2xvdWRfc2VydmljZS5nbw==) | `78.41% <100.00%> (+1.49%)` | :arrow_up: |
| [service/orchestrator/metrics.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvbWV0cmljcy5nbw==) | `80.05% <100.00%> (+0.47%)` | :arrow_up: |
| [service/orchestrator/orchestrator.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3Ivb3JjaGVzdHJhdG9yLmdv) | `78.66% <100.00%> (+1.04%)` | :arrow_up: |
| [service/orchestrator/toe.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9vcmNoZXN0cmF0b3IvdG9lLmdv) | `82.64% <100.00%> (+0.57%)` | :arrow_up: |
| [service/assessment/assessment.go](https://codecov.io/gh/clouditor/clouditor/pull/969?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9hc3Nlc3NtZW50L2Fzc2Vzc21lbnQuZ28=) | `89.58% <0.00%> (+0.78%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)


Patch:
diff --git a/api/assessment/assessment.go b/api/assessment/assessment.go
index eac3d967..5bb163af 100644
--- a/api/assessment/assessment.go
+++ b/api/assessment/assessment.go
@@ -27,6 +27,8 @@ package assessment
 
 import (
 	"errors"
+
+	"google.golang.org/protobuf/proto"
 )
 
 type ResultHookFunc func(result *AssessmentResult, err error)
@@ -36,3 +38,7 @@ var (
 	ErrMetricConfigurationOperatorMissing    = errors.New("operator in metric data is missing")
 	ErrMetricConfigurationTargetValueMissing = errors.New("target value in metric data is missing")
 )
+
+func (req *AssessEvidenceRequest) GetPayload() proto.Message {
+	return req.Evidence
+}
diff --git a/api/evidence/evidence.go b/api/evidence/evidence.go
index be2e7ac0..f7a0a8cd 100644
--- a/api/evidence/evidence.go
+++ b/api/evidence/evidence.go
@@ -29,6 +29,7 @@ import (
 	"errors"
 	"fmt"
 
+	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/types/known/structpb"
 )
 
@@ -130,3 +131,7 @@ func (evidence *Evidence) ResourceTypes() (types []string, err error) {
 
 	return
 }
+
+func (req *StoreEvidenceRequest) GetPayload() proto.Message {
+	return req.Evidence
+}
diff --git a/api/orchestrator/orchestrator.go b/api/orchestrator/orchestrator.go
index 50078bd2..d8211dc7 100644
--- a/api/orchestrator/orchestrator.go
+++ b/api/orchestrator/orchestrator.go
@@ -34,44 +34,28 @@ import (
 type CloudServiceHookFunc func(ctx context.Context, cld *CloudService, err error)
 type TargetOfEvaluationHookFunc func(ctx context.Context, event *TargetOfEvaluationChangeEvent, err error)
 
-// CloudServiceRequest represents any kind of RPC request, that contains a
-// reference to a cloud service.
-//
-// Note: GetCloudServiceId() is already implemented by the generated protobuf
-// code for the following messages because they directly have a cloud_service id
-// field:
-//   - RemoveControlFromScopeRequest
-//   - ListControlsInScopeRequest
-//   - GetCloudServiceRequest
-//   - RemoveCloudServiceRequest
-//   - UpdateMetricConfigurationRequest
-//   - GetMetricConfigurationRequest
-//   - ListMetricConfigurationRequest
-//   - MetricChangeEvent
-//   - TargetOfEvaluation
-//   - RemoveTargetOfEvaluationRequest
-//   - GetTargetOfEvaluationRequest
-//   - ListTargetsOfEvaluationRequest
-//   - Certificate
-//
-// All other requests, especially in cases where the cloud service ID is
-// embedded in a sub-field need to explicitly implement this interface in order.
-// This interface is for example used by authorization checks.
-type CloudServiceRequest interface {
-	GetCloudServiceId() string
-	proto.Message
+// GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
+// the cloud service ID of the inner object.
+func (req *StoreAssessmentResultRequest) GetCloudServiceId() string {
+	return req.GetResult().GetCloudServiceId()
 }
 
 // GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
 // the cloud service ID of the inner object.
-func (req *AddControlToScopeRequest) GetCloudServiceId() string {
-	return req.Scope.GetTargetOfEvaluationCloudServiceId()
+func (req *CreateCertificateRequest) GetCloudServiceId() string {
+	return req.GetCertificate().GetCloudServiceId()
 }
 
 // GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
 // the cloud service ID of the inner object.
-func (req *UpdateControlInScopeRequest) GetCloudServiceId() string {
-	return req.Scope.GetTargetOfEvaluationCloudServiceId()
+func (req *UpdateCertificateRequest) GetCloudServiceId() string {
+	return req.GetCertificate().GetCloudServiceId()
+}
+
+// GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
+// the cloud service ID of the inner object.
+func (req *RegisterCloudServiceRequest) GetCloudServiceId() string {
+	return req.GetCloudService().GetId()
 }
 
 // GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
@@ -82,8 +66,14 @@ func (req *UpdateCloudServiceRequest) GetCloudServiceId() string {
 
 // GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
 // the cloud service ID of the inner object.
-func (req *StoreAssessmentResultRequest) GetCloudServiceId() string {
-	return req.GetResult().GetCloudServiceId()
+func (req *AddControlToScopeRequest) GetCloudServiceId() string {
+	return req.Scope.GetTargetOfEvaluationCloudServiceId()
+}
+
+// GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
+// the cloud service ID of the inner object.
+func (req *UpdateControlInScopeRequest) GetCloudServiceId() string {
+	return req.Scope.GetTargetOfEvaluationCloudServiceId()
 }
 
 // GetCloudServiceId is a shortcut to implement CloudServiceRequest. It returns
@@ -98,6 +88,98 @@ func (req *UpdateTargetOfEvaluationRequest) GetCloudServiceId() string {
 	return req.GetTargetOfEvaluation().GetCloudServiceId()
 }
 
+func (req *StoreAssessmentResultRequest) GetPayload() proto.Message {
+	return req.Result
+}
+
+func (req *RegisterAssessmentToolRequest) GetPayload() proto.Message {
+	return req.Tool
+}
+
+func (req *UpdateAssessmentToolRequest) GetPayload() proto.Message {
+	return req.Tool
+}
+
+func (req *DeregisterAssessmentToolRequest) GetPayload() proto.Message {
+	return &AssessmentTool{Id: req.ToolId}
+}
+
+func (req *CreateCatalogRequest) GetPayload() proto.Message {
+	return req.Catalog
+}
+
+func (req *UpdateCatalogRequest) GetPayload() proto.Message {
+	return req.Catalog
+}
+
+func (req *RemoveCatalogRequest) GetPayload() proto.Message {
+	return &Catalog{Id: req.CatalogId}
+}
+
+func (req *CreateCertificateRequest) GetPayload() proto.Message {
+	return req.Certificate
+}
+
+func (req *UpdateCertificateRequest) GetPayload() proto.Message {
+	return req.Certificate
+}
+
+func (req *RemoveCertificateRequest) GetPayload() proto.Message {
+	return &Certificate{Id: req.CertificateId}
+}
+
+func (req *RegisterCloudServiceRequest) GetPayload() proto.Message {
+	return req.CloudService
+}
+
+func (req *UpdateCloudServiceRequest) GetPayload() proto.Message {
+	return req.CloudService
+}
+
+func (req *RemoveCloudServiceRequest) GetPayload() proto.Message {
+	return &CloudService{Id: req.CloudServiceId}
+}
+
+func (req *AddControlToScopeRequest) GetPayload() proto.Message {
+	return req.Scope
+}
+
+func (req *UpdateControlInScopeRequest) GetPayload() proto.Message {
+	return req.Scope
+}
+
+func (req *RemoveControlFromScopeRequest) GetPayload() proto.Message {
+	return &ControlInScope{TargetOfEvaluationCloudServiceId: req.CloudServiceId, ControlId: req.ControlId}
+}
+
+func (req *CreateMetricRequest) GetPayload() proto.Message {
+	return req.Metric
+}
+
+func (req *UpdateMetricRequest) GetPayload() proto.Message {
+	return req.Metric
+}
+
+func (req *UpdateMetricConfigurationRequest) GetPayload() proto.Message {
+	return req.Configuration
+}
+
+func (req *UpdateMetricImplementationRequest) GetPayload() proto.Message {
+	return req.Implementation
+}
+
+func (req *CreateTargetOfEvaluationRequest) GetPayload() proto.Message {
+	return req.TargetOfEvaluation
+}
+
+func (req *UpdateTargetOfEvaluationRequest) GetPayload() proto.Message {
+	return req.TargetOfEvaluation
+}
+
+func (req *RemoveTargetOfEvaluationRequest) GetPayload() proto.Message {
+	return &TargetOfEvaluation{CloudServiceId: req.CloudServiceId, CatalogId: req.CatalogId}
+}
+
 // TableName overrides the table name used by ControlInScope to `controls_in_scope`
 func (*ControlInScope) TableName() string {
 	return "controls_in_scope"
diff --git a/api/request.go b/api/request.go
new file mode 100644
index 00000000..7f24d1e3
--- /dev/null
+++ b/api/request.go
@@ -0,0 +1,60 @@
+// Copyright 2023 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package api
+
+import (
+	"clouditor.io/clouditor/internal/api"
+)
+
+// PayloadRequest describes any kind of requests that carries a certain payload.
+// This is for example a Create/Update request carrying an embedded message,
+// which should be updated or created.
+type PayloadRequest = api.PayloadRequest
+
+// CloudServiceRequest represents any kind of RPC request, that contains a
+// reference to a cloud service.
+//
+// Note: GetCloudServiceId() is already implemented by the generated protobuf
+// code for the following messages because they directly have a cloud_service id
+// field:
+//   - orchestrator.RemoveControlFromScopeRequest
+//   - orchestrator.ListControlsInScopeRequest
+//   - orchestrator.GetCloudServiceRequest
+//   - orchestrator.RemoveCloudServiceRequest
+//   - orchestrator.UpdateMetricConfigurationRequest
+//   - orchestrator.GetMetricConfigurationRequest
+//   - orchestrator.ListMetricConfigurationRequest
+//   - orchestrator.MetricChangeEvent
+//   - orchestrator.TargetOfEvaluation
+//   - orchestrator.RemoveTargetOfEvaluationRequest
+//   - orchestrator.GetTargetOfEvaluationRequest
+//   - orchestrator.ListTargetsOfEvaluationRequest
+//   - orchestrator.Certificate
+//
+// All other requests, especially in cases where the cloud service ID is
+// embedded in a sub-field need to explicitly implement this interface in order.
+// This interface is for example used by authorization checks.
+type CloudServiceRequest = api.CloudServiceRequest
diff --git a/api/streams.go b/api/streams.go
index 59b8606a..448b54ae 100644
--- a/api/streams.go
+++ b/api/streams.go
@@ -31,10 +31,10 @@ import (
 	"io"
 	"sync"
 
+	"clouditor.io/clouditor/internal/logging"
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/reflect/protoreflect"
 	"google.golang.org/protobuf/types/known/emptypb"
 )
 
@@ -206,16 +206,13 @@ func (c *StreamChannelOf[StreamType, MsgType]) sendLoop(s *StreamsOf[StreamType,
 			return
 		}
 
-		// Some protobuf refletion magic to print more meaningful debug messages. Since this causes some performance
-		// drawbacks, we are only doing this if DEBUG level is enabled.
-		if logrus.IsLevelEnabled(logrus.DebugLevel) {
-			id := extractID(m)
-			if id != "" {
-				s.log.Debugf("%T containing id %s sent to %s (%s)", m, id, c.component, c.target)
-			} else {
-				s.log.Debugf("%T sent to %s (%s)", m, c.component, c.target)
-			}
+		// We want to log some additional information about this stream and its payload
+		preq, ok := any(m).(PayloadRequest)
+		if !ok {
+			return
 		}
+
+		logging.LogRequest(s.log, logrus.DebugLevel, logging.Send, preq, fmt.Sprintf("to %s (%s)", c.component, c.target))
 	}
 }
 
@@ -252,39 +249,3 @@ func (c *StreamChannelOf[StreamType, MsgType]) Send(msg MsgType) {
 func defaultLog() *logrus.Entry {
 	return logrus.NewEntry(logrus.StandardLogger())
 }
-
-// extractID uses a simple heuristic to extract an ID field out of a protobuf message. It assumes that the protobuf
-// message either directly has an ID field or that the last field of the message contains another protobuf message that
-// contains ID. The latter is a typical scenario for request and response messages.
-//
-// This function uses the protoreflect package and the caller needs to be aware of potential performance drawbacks of
-// using reflection.
-func extractID(msg proto.Message) string {
-	var (
-		inner any
-		ok    bool
-		r     protoreflect.Message
-	)
-	// Try the message directly
-	r = msg.ProtoReflect()
-	fd := r.Descriptor().Fields().ByName(IDField)
-	if fd != nil {
-		return r.Get(fd).String()
-	}
-
-	// Otherwise try to go one level "deeper", e.g., to retrieve the inner message in a request/response message.
-	fields := r.Descriptor().Fields()
-	fd = fields.Get(fields.Len() - 1)
-	if fd == nil {
-		return ""
-	}
-
-	// Retrieve the value of the inner message
-	inner = r.Get(fd).Interface()
-	if r, ok = inner.(protoreflect.Message); !ok {
-		return ""
-	}
-
-	// And try to extract the ID based on it
-	return extractID(r.Interface())
-}
diff --git a/internal/api/request.go b/internal/api/request.go
new file mode 100644
index 00000000..130d1254
--- /dev/null
+++ b/internal/api/request.go
@@ -0,0 +1,20 @@
+package api
+
+import (
+	"google.golang.org/protobuf/proto"
+)
+
+// PayloadRequest describes any kind of requests that carries a certain payload.
+// Defined in internal/api to avoid cyclic dependencies.
+type PayloadRequest interface {
+	GetPayload() proto.Message
+	proto.Message
+}
+
+// CloudServiceRequest represents any kind of RPC request, that contains a
+// reference to a cloud service. Defined in internal/api to avoid cyclic
+// dependencies.
+type CloudServiceRequest interface {
+	GetCloudServiceId() string
+	proto.Message
+}
diff --git a/internal/logging/logging.go b/internal/logging/logging.go
new file mode 100644
index 00000000..6eed4971
--- /dev/null
+++ b/internal/logging/logging.go
@@ -0,0 +1,134 @@
+// Copyright 2023 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package logging
+
+import (
+	"fmt"
+	"strings"
+
+	"clouditor.io/clouditor/internal/api"
+	"clouditor.io/clouditor/internal/util"
+	"github.com/sirupsen/logrus"
+)
+
+// RequestType specifies the type of request
+type RequestType int
+
+const (
+	Assess RequestType = iota
+	Add
+	Create
+	Register
+	Remove
+	Store
+	Send
+	Update
+)
+
+// String returns the RequestType as string.
+func (r RequestType) String() string {
+	switch r {
+	case Assess:
+		return "assessed"
+	case Add:
+		return "added"
+	case Create:
+		return "created"
+	case Register:
+		return "registered"
+	case Remove:
+		return "removed"
+	case Store:
+		return "stored"
+	case Send:
+		return "sent"
+	case Update:
+		return "updated"
+	default:
+		return "unspecified"
+	}
+}
+
+// LogRequest creates a logging message with the given parameters
+//   - log *logrus.Entry
+//   - level the message must have
+//   - reqType is the request type
+//   - Optional. params for self-created string messages to be appended to the created log message. The elements do not need a space at the beginning of the message.
+//
+// The message looks like one of the following depending on the given information
+//
+//	"*orchestrator.Catalog created with ID 'Cat1234'."
+//
+// or
+//
+//	"*orchestrator.Certificate created with ID 'Cert1234' for Cloud Service '00000000-0000-0000-0000-000000000000'."
+//
+// or
+//
+//	"*orchestrator.TargetOfEvaluation created with ID 'ToE1234' for Cloud Service '00000000-0000-0000-0000-000000000000' and Catalog 'EUCS'."
+func LogRequest(log *logrus.Entry, level logrus.Level, reqType RequestType, req api.PayloadRequest, params ...string) {
+	var (
+		message string
+	)
+
+	// Check if inputs are available
+	if log == nil || util.IsNil(req) {
+		return
+	}
+
+	// Retrieve the payload from the request. The request itself is usually
+	// a wrapper around the sent object.
+	payload := req.GetPayload()
+	if util.IsNil(payload) {
+		return
+	}
+
+	// We can retrieve the name via the proto descriptor. This should be
+	// sufficiently fast and also gives us the non-pointer type in comparison to
+	// the %T printf directive.
+	name := req.GetPayload().ProtoReflect().Descriptor().Name()
+
+	// Check, if our payload has an ID field
+	idreq, ok := payload.(interface{ GetId() string })
+	if ok && idreq.GetId() != "" {
+		message = fmt.Sprintf("%s with ID '%s' %s", name, idreq.GetId(), reqType.String())
+	} else {
+		message = fmt.Sprintf("%s %s", name, reqType.String())
+	}
+
+	// Check, if it is a cloud service request. In this case we can append the
+	// information about the target cloud service. However, we only want to do
+	// that, if the payload type is not a cloud service itself.
+	csreq, ok := req.(api.CloudServiceRequest)
+	// If params is not empty, the elements are joined and added to the message
+	if name != "CloudService" && ok && len(params) > 0 {
+		message = fmt.Sprintf("%s for Cloud Service '%s' %s", message, csreq.GetCloudServiceId(), strings.Join(params, " "))
+	} else if name != "CloudService" && ok {
+		message = fmt.Sprintf("%s for Cloud Service '%s'", message, csreq.GetCloudServiceId())
+	}
+
+	log.Logf(level, "%s.", message)
+}
diff --git a/internal/util/deref.go b/internal/util/deref.go
index f7288c22..d9af1a22 100644
--- a/internal/util/deref.go
+++ b/internal/util/deref.go
@@ -25,6 +25,8 @@
 
 package util
 
+import "reflect"
+
 // Deref dereferences pointer values
 func Deref[T any](p *T) T {
 	var result T
@@ -39,3 +41,13 @@ func Deref[T any](p *T) T {
 func Ref[T any](p T) *T {
 	return &p
 }
+
+// IsNil checks if an interface value is nil or if the value nil is assigned to it.
+func IsNil(value any) bool {
+	if value == nil || (reflect.ValueOf(value).Kind() == reflect.Pointer &&
+		reflect.ValueOf(value).IsNil()) {
+		return true
+	}
+
+	return false
+}
diff --git a/persistence/gorm/serializer.go b/persistence/gorm/serializer.go
index 2c854bcf..04c89882 100644
--- a/persistence/gorm/serializer.go
+++ b/persistence/gorm/serializer.go
@@ -37,6 +37,7 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 	"gorm.io/gorm/schema"
 
+	"clouditor.io/clouditor/internal/util"
 	"clouditor.io/clouditor/persistence"
 )
 
@@ -52,7 +53,7 @@ func (TimestampSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.V
 		ok bool
 	)
 
-	if isInterfaceNil(fieldValue) {
+	if util.IsNil(fieldValue) {
 		return nil, nil
 	}
 
@@ -94,7 +95,7 @@ func (AnySerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value,
 		ok bool
 	)
 
-	if isInterfaceNil(fieldValue) {
+	if util.IsNil(fieldValue) {
 		return nil, nil
 	}
 
@@ -145,7 +146,7 @@ func (ValueSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value
 		ok bool
 	)
 
-	if isInterfaceNil(fieldValue) {
+	if util.IsNil(fieldValue) {
 		return nil, nil
 	}
 
@@ -177,12 +178,3 @@ func (ValueSerializer) Scan(ctx context.Context, field *schema.Field, dst reflec
 
 	return
 }
-
-func isInterfaceNil(fieldValue interface{}) bool {
-	if fieldValue == nil || (reflect.ValueOf(fieldValue).Kind() == reflect.Pointer &&
-		reflect.ValueOf(fieldValue).IsNil()) {
-		return true
-	}
-
-	return false
-}
diff --git a/service/assessment/assessment.go b/service/assessment/assessment.go
index 45689fcd..1cffa605 100644
--- a/service/assessment/assessment.go
+++ b/service/assessment/assessment.go
@@ -37,6 +37,7 @@ import (
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/evidence"
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/policies"
 	"clouditor.io/clouditor/service"
 
@@ -256,7 +257,7 @@ func (svc *Service) AssessEvidence(_ context.Context, req *assessment.AssessEvid
 		return nil, status.Errorf(codes.Internal, "%v", err)
 	}
 
-	log.Debugf("Evidence assessed with id %s' for Cloud Service ID '%s'.", req.Evidence.GetId(), req.Evidence.GetCloudServiceId())
+	logging.LogRequest(log, logrus.DebugLevel, logging.Assess, req)
 
 	return resp, nil
 }
diff --git a/service/authz.go b/service/authz.go
index e5eee96d..ccb0a734 100644
--- a/service/authz.go
+++ b/service/authz.go
@@ -28,13 +28,12 @@ package service
 import (
 	"context"
 
+	"clouditor.io/clouditor/api"
 	"github.com/golang-jwt/jwt/v4"
 	grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
 	"golang.org/x/exp/slices"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
-
-	"clouditor.io/clouditor/api/orchestrator"
 )
 
 // RequestType specifies the type of request, usually CRUD.
@@ -54,7 +53,7 @@ var ErrPermissionDenied = status.Errorf(codes.PermissionDenied, "access denied")
 // checkers whether the current cloud service request can be fulfilled using the
 // supplied context (e.g., based on the authenticated user).
 type AuthorizationStrategy interface {
-	CheckAccess(ctx context.Context, typ RequestType, req orchestrator.CloudServiceRequest) bool
+	CheckAccess(ctx context.Context, typ RequestType, req api.CloudServiceRequest) bool
 	AllowedCloudServices(ctx context.Context) (all bool, IDs []string)
 }
 
@@ -65,7 +64,7 @@ type AuthorizationStrategyJWT struct {
 }
 
 // CheckAccess checks whether the current request can be fulfilled using the current access strategy.
-func (a *AuthorizationStrategyJWT) CheckAccess(ctx context.Context, _ RequestType, req orchestrator.CloudServiceRequest) bool {
+func (a *AuthorizationStrategyJWT) CheckAccess(ctx context.Context, _ RequestType, req api.CloudServiceRequest) bool {
 	var list []string
 
 	// Retrieve the list of allowed cloud services. we never allow to retrieve
@@ -124,7 +123,7 @@ type AuthorizationStrategyAllowAll struct{}
 
 // CheckAccess checks whether the current request can be fulfilled using the current access strategy. Returns true since
 // strategy is `AuthorizationStrategyAllowAll`
-func (*AuthorizationStrategyAllowAll) CheckAccess(_ context.Context, _ RequestType, _ orchestrator.CloudServiceRequest) bool {
+func (*AuthorizationStrategyAllowAll) CheckAccess(_ context.Context, _ RequestType, _ api.CloudServiceRequest) bool {
 	return true
 }
 
diff --git a/service/evidence/evidence_store.go b/service/evidence/evidence_store.go
index 74138dae..d9fc7cea 100644
--- a/service/evidence/evidence_store.go
+++ b/service/evidence/evidence_store.go
@@ -33,6 +33,7 @@ import (
 	"sync"
 
 	"clouditor.io/clouditor/api/evidence"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/persistence/inmemory"
 	"clouditor.io/clouditor/service"
@@ -120,7 +121,7 @@ func (svc *Service) StoreEvidence(ctx context.Context, req *evidence.StoreEviden
 
 	res = &evidence.StoreEvidenceResponse{}
 
-	log.Debugf("Evidence stored with id %s' for Cloud Service ID '%s'.", req.Evidence.GetId(), req.Evidence.GetCloudServiceId())
+	logging.LogRequest(log, logrus.DebugLevel, logging.Store, req)
 
 	return res, nil
 }
diff --git a/service/orchestrator/assessment_results.go b/service/orchestrator/assessment_results.go
index a0af3d69..3e8be114 100644
--- a/service/orchestrator/assessment_results.go
+++ b/service/orchestrator/assessment_results.go
@@ -32,12 +32,14 @@ import (
 	"io"
 	"strings"
 
+	"github.com/sirupsen/logrus"
 	"golang.org/x/exp/slices"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/service"
 )
 
@@ -125,7 +127,7 @@ func (svc *Service) StoreAssessmentResult(ctx context.Context, req *orchestrator
 
 	res = &orchestrator.StoreAssessmentResultResponse{}
 
-	log.Debugf("Assessment result stored with id %s' for Cloud Service ID '%s'.", req.Result.GetId(), req.Result.GetCloudServiceId())
+	logging.LogRequest(log, logrus.DebugLevel, logging.Store, req)
 
 	return res, nil
 }
diff --git a/service/orchestrator/catalogs.go b/service/orchestrator/catalogs.go
index 2a74ce47..dc0f531f 100644
--- a/service/orchestrator/catalogs.go
+++ b/service/orchestrator/catalogs.go
@@ -7,9 +7,11 @@ import (
 	"fmt"
 
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/persistence/gorm"
 	"clouditor.io/clouditor/service"
+	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/emptypb"
@@ -30,6 +32,8 @@ func (svc *Service) CreateCatalog(_ context.Context, req *orchestrator.CreateCat
 		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Create, req)
+
 	// Return catalog
 	return req.Catalog, nil
 }
@@ -94,6 +98,9 @@ func (svc *Service) UpdateCatalog(_ context.Context, req *orchestrator.UpdateCat
 	} else if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
@@ -112,6 +119,8 @@ func (svc *Service) RemoveCatalog(_ context.Context, req *orchestrator.RemoveCat
 		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Remove, req)
+
 	return &emptypb.Empty{}, nil
 }
 
@@ -208,6 +217,8 @@ func (svc *Service) loadCatalogs() (err error) {
 		log.Errorf("Error while saving catalog %v", err)
 	}
 
+	log.Debugf("Catalog loaded with id '%s'.", catalogs[0].GetId())
+
 	return
 }
 
diff --git a/service/orchestrator/cloud_service.go b/service/orchestrator/cloud_service.go
index 5ef813f9..fc2df7a6 100644
--- a/service/orchestrator/cloud_service.go
+++ b/service/orchestrator/cloud_service.go
@@ -31,10 +31,12 @@ import (
 	"fmt"
 
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/service"
 
 	"github.com/google/uuid"
+	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/types/known/emptypb"
@@ -67,6 +69,9 @@ func (s *Service) RegisterCloudService(ctx context.Context, req *orchestrator.Re
 	}
 
 	go s.informHooks(ctx, res, nil)
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Register, req)
+
 	return
 }
 
@@ -158,6 +163,9 @@ func (s *Service) UpdateCloudService(ctx context.Context, req *orchestrator.Upda
 	}
 
 	go s.informHooks(ctx, response, nil)
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
@@ -182,6 +190,9 @@ func (s *Service) RemoveCloudService(ctx context.Context, req *orchestrator.Remo
 	}
 
 	go s.informHooks(ctx, nil, nil)
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Remove, req)
+
 	return &emptypb.Empty{}, nil
 }
 
diff --git a/service/orchestrator/metrics.go b/service/orchestrator/metrics.go
index aa6649a2..e15e2e72 100644
--- a/service/orchestrator/metrics.go
+++ b/service/orchestrator/metrics.go
@@ -35,9 +35,11 @@ import (
 
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/persistence/gorm"
 	"clouditor.io/clouditor/service"
+	"github.com/sirupsen/logrus"
 
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
@@ -87,6 +89,9 @@ func (svc *Service) loadMetrics() (err error) {
 			log.Errorf("Error while saving metrics: %v", err)
 			continue
 		}
+
+		log.Debugf("Metric loaded with id '%s'.", m.GetId())
+
 	}
 
 	return
@@ -201,6 +206,8 @@ func (svc *Service) CreateMetric(_ context.Context, req *orchestrator.CreateMetr
 		}
 	}()
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Create, req)
+
 	return
 }
 
@@ -240,6 +247,8 @@ func (svc *Service) UpdateMetric(_ context.Context, req *orchestrator.UpdateMetr
 		}
 	}()
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
@@ -281,6 +290,8 @@ func (svc *Service) UpdateMetricImplementation(_ context.Context, req *orchestra
 		}
 	}()
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
@@ -404,6 +415,8 @@ func (svc *Service) UpdateMetricConfiguration(ctx context.Context, req *orchestr
 	// Update response
 	res = req.Configuration
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
diff --git a/service/orchestrator/orchestrator.go b/service/orchestrator/orchestrator.go
index b68682a5..3c40ed1e 100644
--- a/service/orchestrator/orchestrator.go
+++ b/service/orchestrator/orchestrator.go
@@ -34,6 +34,7 @@ import (
 	"clouditor.io/clouditor/api/assessment"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/api/runtime"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/persistence/inmemory"
 	"clouditor.io/clouditor/service"
@@ -217,6 +218,8 @@ func (svc *Service) CreateCertificate(_ context.Context, req *orchestrator.Creat
 			status.Errorf(codes.Internal, "could not add certificate to the database: %v", err)
 	}
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Create, req)
+
 	// Return certificate
 	return req.Certificate, nil
 }
@@ -280,6 +283,9 @@ func (svc *Service) UpdateCertificate(_ context.Context, req *orchestrator.Updat
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req)
+
 	return
 }
 
@@ -298,6 +304,8 @@ func (svc *Service) RemoveCertificate(_ context.Context, req *orchestrator.Remov
 		return nil, status.Errorf(codes.Internal, "database error: %v", err)
 	}
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Remove, req)
+
 	return &emptypb.Empty{}, nil
 }
 
diff --git a/service/orchestrator/toe.go b/service/orchestrator/toe.go
index 6b940708..9ed463c7 100644
--- a/service/orchestrator/toe.go
+++ b/service/orchestrator/toe.go
@@ -28,12 +28,15 @@ package orchestrator
 import (
 	"context"
 	"errors"
+	"fmt"
 
 	"clouditor.io/clouditor/api"
 	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/logging"
 	"clouditor.io/clouditor/persistence"
 	"clouditor.io/clouditor/persistence/gorm"
 	"clouditor.io/clouditor/service"
+	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
@@ -99,6 +102,8 @@ func (svc *Service) CreateTargetOfEvaluation(ctx context.Context, req *orchestra
 
 	res = req.TargetOfEvaluation
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Create, req, fmt.Sprintf("and Catalog '%s'", req.TargetOfEvaluation.GetCatalogId()))
+
 	return
 }
 
@@ -182,6 +187,8 @@ func (svc *Service) UpdateTargetOfEvaluation(ctx context.Context, req *orchestra
 
 	go svc.informToeHooks(ctx, &orchestrator.TargetOfEvaluationChangeEvent{Type: orchestrator.TargetOfEvaluationChangeEvent_TYPE_TARGET_OF_EVALUATION_UPDATED, TargetOfEvaluation: req.TargetOfEvaluation}, nil)
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req, fmt.Sprintf("and Catalog '%s'", req.TargetOfEvaluation.GetCatalogId()))
+
 	return
 }
 
@@ -211,6 +218,9 @@ func (svc *Service) RemoveTargetOfEvaluation(ctx context.Context, req *orchestra
 		CatalogId:      req.GetCatalogId(),
 	}
 	go svc.informToeHooks(ctx, &orchestrator.TargetOfEvaluationChangeEvent{Type: orchestrator.TargetOfEvaluationChangeEvent_TYPE_TARGET_OF_EVALUATION_REMOVED, TargetOfEvaluation: toe}, nil)
+
+	logging.LogRequest(log, logrus.DebugLevel, logging.Remove, req, fmt.Sprintf("and Catalog '%s'", req.GetCatalogId()))
+
 	return &emptypb.Empty{}, nil
 }
 
@@ -270,6 +280,8 @@ func (svc *Service) AddControlToScope(ctx context.Context, req *orchestrator.Add
 
 	go svc.informToeHooks(ctx, &orchestrator.TargetOfEvaluationChangeEvent{Type: orchestrator.TargetOfEvaluationChangeEvent_TYPE_CONTROL_IN_SCOPE_ADDED, ControlInScope: req.GetScope()}, nil)
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Add, req, fmt.Sprintf("with Control ID '%s'", req.Scope.GetControlId()))
+
 	return
 }
 
@@ -306,6 +318,8 @@ func (svc *Service) UpdateControlInScope(ctx context.Context, req *orchestrator.
 
 	go svc.informToeHooks(ctx, &orchestrator.TargetOfEvaluationChangeEvent{Type: orchestrator.TargetOfEvaluationChangeEvent_TYPE_CONTROL_IN_SCOPE_UPDATED, ControlInScope: req.GetScope()}, nil)
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Update, req, fmt.Sprintf("with Control ID '%s'", req.Scope.GetControlId()))
+
 	return
 }
 
@@ -342,6 +356,8 @@ func (svc *Service) RemoveControlFromScope(ctx context.Context, req *orchestrato
 
 	go svc.informToeHooks(ctx, &orchestrator.TargetOfEvaluationChangeEvent{Type: orchestrator.TargetOfEvaluationChangeEvent_TYPE_CONTROL_IN_SCOPE_REMOVED, ControlInScope: nil}, nil)
 
+	logging.LogRequest(log, logrus.DebugLevel, logging.Remove, req, fmt.Sprintf("with Control ID '%s'", req.GetControlId()))
+
 	return
 }
 
diff --git a/service/validate.go b/service/validate.go
index 2005cba7..f22db238 100644
--- a/service/validate.go
+++ b/service/validate.go
@@ -26,9 +26,8 @@
 package service
 
 import (
-	"reflect"
-
 	"clouditor.io/clouditor/api"
+	"clouditor.io/clouditor/internal/util"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	"google.golang.org/protobuf/proto"
@@ -47,7 +46,7 @@ func ValidateRequest(req IncomingRequest) (err error) {
 	// whether the *value* of the interface is nil. This can happen if nil is first assigned to a variable of a struct
 	// (pointer) that implements the interface. If this variable is then passed to the validate function, the req
 	// parameter is not nil, but the value of the interface representing it is.
-	if req == nil || reflect.ValueOf(req).IsNil() {
+	if util.IsNil(req) {
 		return status.Errorf(codes.InvalidArgument, "%s", api.ErrEmptyRequest)
 	}
 


Test patch:
diff --git a/api/orchestrator/orchestrator_test.go b/api/orchestrator/orchestrator_test.go
index 21f2837a..20ddad35 100644
--- a/api/orchestrator/orchestrator_test.go
+++ b/api/orchestrator/orchestrator_test.go
@@ -349,3 +349,125 @@ func TestControlInScope_TableName(t *testing.T) {
 		})
 	}
 }
+
+func TestCreateCertificateRequest_GetCloudServiceId(t *testing.T) {
+	type fields struct {
+		Certificate *Certificate
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   string
+	}{
+		{
+			name: "Happy path",
+			fields: fields{
+				Certificate: &Certificate{
+					CloudServiceId: testdata.MockCloudServiceID,
+				},
+			},
+			want: testdata.MockCloudServiceID,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := &CreateCertificateRequest{
+				Certificate: tt.fields.Certificate,
+			}
+			if got := req.GetCloudServiceId(); got != tt.want {
+				t.Errorf("CreateCertificateRequest.GetCloudServiceId() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestUpdateCertificateRequest_GetCloudServiceId(t *testing.T) {
+	type fields struct {
+		Certificate *Certificate
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   string
+	}{
+		{
+			name: "Happy path",
+			fields: fields{
+				Certificate: &Certificate{
+					CloudServiceId: testdata.MockCloudServiceID,
+				},
+			},
+			want: testdata.MockCloudServiceID,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := &UpdateCertificateRequest{
+				Certificate: tt.fields.Certificate,
+			}
+			if got := req.GetCloudServiceId(); got != tt.want {
+				t.Errorf("UpdateCertificateRequest.GetCloudServiceId() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestRegisterCloudServiceRequest_GetCloudServiceId(t *testing.T) {
+	type fields struct {
+		CloudService *CloudService
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   string
+	}{
+		{
+			name: "Happy path",
+			fields: fields{
+				CloudService: &CloudService{},
+			},
+			want: "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := &RegisterCloudServiceRequest{
+				CloudService: tt.fields.CloudService,
+			}
+			if got := req.GetCloudServiceId(); got != tt.want {
+				t.Errorf("RegisterCloudServiceRequest.GetCloudServiceId() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestUpdateTargetOfEvaluationRequest_GetCloudServiceId(t *testing.T) {
+	type fields struct {
+		TargetOfEvaluation *TargetOfEvaluation
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   string
+	}{
+		{
+			name: "Happy path",
+			fields: fields{
+				TargetOfEvaluation: &TargetOfEvaluation{
+					CloudServiceId: testdata.MockCloudServiceID,
+				},
+			},
+			want: testdata.MockCloudServiceID,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			req := &UpdateTargetOfEvaluationRequest{
+				TargetOfEvaluation: tt.fields.TargetOfEvaluation,
+			}
+			if got := req.GetCloudServiceId(); got != tt.want {
+				t.Errorf("UpdateTargetOfEvaluationRequest.GetCloudServiceId() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/api/streams_test.go b/api/streams_test.go
index 086f6f76..fc2ff0fd 100644
--- a/api/streams_test.go
+++ b/api/streams_test.go
@@ -38,7 +38,6 @@ import (
 	"google.golang.org/protobuf/reflect/protoreflect"
 
 	"clouditor.io/clouditor/api/assessment"
-	"clouditor.io/clouditor/api/evidence"
 )
 
 var ErrSomeError = errors.New("some error")
@@ -246,44 +245,3 @@ func (m mockClientStream) SendMsg(interface{}) error {
 func (mockClientStream) RecvMsg(interface{}) error {
 	return nil
 }
-
-func Test_extractID(t *testing.T) {
-	type args struct {
-		msg proto.Message
-	}
-	tests := []struct {
-		name string
-		args args
-		want string
-	}{
-		{
-			name: "ID in message",
-			args: args{
-				msg: &evidence.Evidence{Id: "MyID"},
-			},
-			want: "MyID",
-		},
-		{
-			name: "ID in response",
-			args: args{
-				msg: &assessment.AssessEvidenceRequest{Evidence: &evidence.Evidence{Id: "MyID"}},
-			},
-			want: "MyID",
-		},
-		{
-			name: "No ID",
-			args: args{
-				msg: &assessment.MinMax{},
-			},
-			want: "",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := extractID(tt.args.msg); got != tt.want {
-				t.Errorf("extractID() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/internal/logging/logging_test.go b/internal/logging/logging_test.go
new file mode 100644
index 00000000..000bf3ef
--- /dev/null
+++ b/internal/logging/logging_test.go
@@ -0,0 +1,187 @@
+// Copyright 2023 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package logging
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+
+	"clouditor.io/clouditor/api/orchestrator"
+	"clouditor.io/clouditor/internal/api"
+	"clouditor.io/clouditor/internal/testdata"
+	"github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRequestType_String(t *testing.T) {
+	tests := []struct {
+		name string
+		r    RequestType
+		want string
+	}{
+		{
+			name: "Happy path: Assess",
+			r:    Assess,
+			want: "assessed",
+		},
+		{
+			name: "Happy path: Add",
+			r:    Add,
+			want: "added",
+		},
+		{
+			name: "Happy path: Create",
+			r:    Create,
+			want: "created",
+		},
+		{
+			name: "Happy path: Register",
+			r:    Register,
+			want: "registered",
+		},
+		{
+			name: "Happy path: Remove",
+			r:    Remove,
+			want: "removed",
+		},
+		{
+			name: "Happy path: Store",
+			r:    Store,
+			want: "stored",
+		},
+		{
+			name: "Happy path: Send",
+			r:    Send,
+			want: "sent",
+		},
+		{
+			name: "Happy path: Update",
+			r:    Update,
+			want: "updated",
+		},
+		{
+			name: "Happy path: Create",
+			r:    RequestType(20),
+			want: "unspecified",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := tt.r.String(); got != tt.want {
+				t.Errorf("RequestType.String() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestLogRequest(t *testing.T) {
+	type args struct {
+		level   logrus.Level
+		reqType RequestType
+		req     api.PayloadRequest
+		params  []string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "Request missing",
+			args: args{
+				level:   logrus.DebugLevel,
+				reqType: Register,
+			},
+			want: "",
+		},
+		{
+			name: "Register cloud service",
+			args: args{
+				level:   logrus.DebugLevel,
+				reqType: Register,
+				req: &orchestrator.RegisterCloudServiceRequest{
+					CloudService: &orchestrator.CloudService{},
+				},
+			},
+			want: "level=debug msg=CloudService registered.\n",
+		},
+
+		{
+			name: "Register cloud service",
+			args: args{
+				level:   logrus.DebugLevel,
+				reqType: Register,
+				req: &orchestrator.RegisterCloudServiceRequest{
+					CloudService: &orchestrator.CloudService{Id: testdata.MockCloudServiceID},
+				},
+			},
+			want: "level=debug msg=CloudService with ID '11111111-1111-1111-1111-111111111111' registered.\n",
+		},
+		{
+			name: "Create TargetOfEvaluation",
+			args: args{
+				level:   logrus.DebugLevel,
+				reqType: Update,
+				req: &orchestrator.UpdateTargetOfEvaluationRequest{
+					TargetOfEvaluation: &orchestrator.TargetOfEvaluation{
+						CloudServiceId: testdata.MockCloudServiceID,
+						CatalogId:      testdata.MockCatalogID,
+					},
+				},
+			},
+			want: "level=debug msg=TargetOfEvaluation updated for Cloud Service '11111111-1111-1111-1111-111111111111'.\n",
+		},
+		{
+			name: "Create TargetOfEvaluation with params",
+			args: args{
+				level:   logrus.DebugLevel,
+				reqType: Update,
+				req: &orchestrator.UpdateTargetOfEvaluationRequest{
+					TargetOfEvaluation: &orchestrator.TargetOfEvaluation{
+						CloudServiceId: testdata.MockCloudServiceID,
+						CatalogId:      testdata.MockCatalogID,
+					},
+				},
+				params: []string{fmt.Sprintf("and Catalog '%s'", testdata.MockCatalogID)},
+			},
+			want: "level=debug msg=TargetOfEvaluation updated for Cloud Service '11111111-1111-1111-1111-111111111111' and Catalog 'Cat1234'.\n",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var buffer bytes.Buffer
+			var log = &logrus.Entry{Logger: &logrus.Logger{Out: &buffer, Formatter: &logrus.TextFormatter{
+				DisableColors:    true,
+				DisableTimestamp: true,
+				DisableQuote:     true,
+			}, Level: logrus.DebugLevel}}
+			LogRequest(log, tt.args.level, tt.args.reqType, tt.args.req, tt.args.params...)
+
+			assert.Equal(t, tt.want, buffer.String())
+		})
+	}
+}
diff --git a/service/authz_test.go b/service/authz_test.go
index df26ce24..7b727857 100644
--- a/service/authz_test.go
+++ b/service/authz_test.go
@@ -30,6 +30,7 @@ import (
 	"reflect"
 	"testing"
 
+	"clouditor.io/clouditor/api"
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/api/orchestrator"
 	"clouditor.io/clouditor/internal/testutil"
@@ -39,7 +40,7 @@ func TestAuthorizationStrategyAllowAll_CheckAccess(t *testing.T) {
 	type args struct {
 		ctx context.Context
 		typ RequestType
-		req orchestrator.CloudServiceRequest
+		req api.CloudServiceRequest
 	}
 	tests := []struct {
 		name string
@@ -108,7 +109,7 @@ func TestAuthorizationStrategyJWT_CheckAccess(t *testing.T) {
 	type args struct {
 		ctx context.Context
 		typ RequestType
-		req orchestrator.CloudServiceRequest
+		req api.CloudServiceRequest
 	}
 	tests := []struct {
 		name   string
diff --git a/service/orchestrator/metrics_test.go b/service/orchestrator/metrics_test.go
index 020252be..1366b239 100644
--- a/service/orchestrator/metrics_test.go
+++ b/service/orchestrator/metrics_test.go
@@ -1185,7 +1185,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 					return false
 				}
 
-				return assert.Equal(t, config, i1)
+				return assert.True(t, proto.Equal(config, i1.(proto.Message)))
 			},
 		},
 		{
@@ -1226,7 +1226,7 @@ func TestService_UpdateMetricConfiguration(t *testing.T) {
 					return false
 				}
 
-				return assert.Equal(t, config, i1)
+				return assert.True(t, proto.Equal(config, i1.(proto.Message)))
 			},
 		},
 	}
diff --git a/service/orchestrator/orchestrator_test.go b/service/orchestrator/orchestrator_test.go
index bae8ee45..cbdac646 100644
--- a/service/orchestrator/orchestrator_test.go
+++ b/service/orchestrator/orchestrator_test.go
@@ -502,7 +502,7 @@ func TestCloudServiceHooks(t *testing.T) {
 				t.Errorf("UpdateCloudService() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(gotResp, tt.wantResp) {
+			if !proto.Equal(gotResp, tt.wantResp) {
 				t.Errorf("UpdateCloudService() gotResp = %v, want %v", gotResp, tt.wantResp)
 			}
 
diff --git a/service/orchestrator/toe_test.go b/service/orchestrator/toe_test.go
index de441630..42d9d946 100644
--- a/service/orchestrator/toe_test.go
+++ b/service/orchestrator/toe_test.go
@@ -550,9 +550,8 @@ func TestToeHook(t *testing.T) {
 				t.Errorf("UpdateTargetOfEvaluation() error = %v, wantErrMessage %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(gotResp, tt.wantResp) {
+			if !proto.Equal(gotResp, tt.wantResp) {
 				t.Errorf("UpdateTargetOfEvaluation() gotResp = %v, want %v", gotResp, tt.wantResp)
-
 			}
 			assert.Equal(t, 2, hookCallCounter)
 		})
@@ -851,7 +850,7 @@ func TestService_AddControlToScope(t *testing.T) {
 			gotRes, err := svc.AddControlToScope(tt.args.in0, tt.args.req)
 			tt.wantErr(t, err, tt.args)
 
-			if !reflect.DeepEqual(gotRes, tt.wantRes) {
+			if !proto.Equal(gotRes, tt.wantRes) {
 				t.Errorf("Service.UpdateControlInScope() = %v, want %v", gotRes, tt.wantRes)
 			}
 		})
@@ -973,7 +972,7 @@ func TestService_UpdateControlInScope(t *testing.T) {
 			assert.NoError(t, gotRes.Validate())
 			tt.wantErr(t, err, tt.args)
 
-			if !reflect.DeepEqual(gotRes, tt.wantRes) {
+			if !proto.Equal(gotRes, tt.wantRes) {
 				t.Errorf("Service.UpdateControlInScope() = %v, want %v", gotRes, tt.wantRes)
 			}
 		})