Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LOG-4095: fix loki labelKeys that contain slash #2031

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 7 additions & 8 deletions apis/logging/v1/output_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,18 @@ type Loki struct {
// +optional
TenantKey string `json:"tenantKey,omitempty"`

// LabelKeys is a list of meta-data field keys to replace the default Loki labels.
//
// Loki label names must match the regular expression "[a-zA-Z_:][a-zA-Z0-9_:]*".
// Illegal characters in meta-data keys are replaced with "_" to form the label name.
// For example meta-data key "kubernetes.labels.foo" becomes Loki label "kubernetes_labels_foo".
// LabelKeys is a list of log record keys that will be used as Loki labels with the corresponding log record value.
//
// If LabelKeys is not set, the default keys are `[log_type, kubernetes.namespace_name, kubernetes.pod_name, kubernetes_host]`
// These keys are translated to Loki labels by replacing '.' with '_' as: `log_type`, `kubernetes_namespace_name`, `kubernetes_pod_name`, `kubernetes_host`
// Note that not all logs will include all of these keys: audit logs and infrastructure journal logs do not have namespace or pod name.
//
// Note: Loki label names must match the regular expression "[a-zA-Z_:][a-zA-Z0-9_:]*"
// Log record keys may contain characters like "." and "/" that are not allowed in Loki labels.
// Log record keys are translated to Loki labels by replacing any illegal characters with '_'.
// For example the default log record keys translate to these Loki labels: `log_type`, `kubernetes_namespace_name`, `kubernetes_pod_name`, `kubernetes_host`
//
// Note: the set of labels should be small, Loki imposes limits on the size and number of labels allowed.
// See https://grafana.com/docs/loki/latest/configuration/#limits_config for more.
// You can still query based on any log record field using query filters.
// Loki queries can also query based on any log record field (not just labels) using query filters.
//
// +optional
LabelKeys []string `json:"labelKeys,omitempty"`
Expand Down
34 changes: 16 additions & 18 deletions bundle/manifests/logging.openshift.io_clusterlogforwarders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -269,24 +269,22 @@ spec:
loki`'
properties:
labelKeys:
description: "LabelKeys is a list of meta-data field keys
to replace the default Loki labels. \n Loki label names
must match the regular expression \"[a-zA-Z_:][a-zA-Z0-9_:]*\".
Illegal characters in meta-data keys are replaced with
\"_\" to form the label name. For example meta-data key
\"kubernetes.labels.foo\" becomes Loki label \"kubernetes_labels_foo\".
\n If LabelKeys is not set, the default keys are `[log_type,
kubernetes.namespace_name, kubernetes.pod_name, kubernetes_host]`
These keys are translated to Loki labels by replacing
'.' with '_' as: `log_type`, `kubernetes_namespace_name`,
`kubernetes_pod_name`, `kubernetes_host` Note that not
all logs will include all of these keys: audit logs and
infrastructure journal logs do not have namespace or pod
name. \n Note: the set of labels should be small, Loki
imposes limits on the size and number of labels allowed.
See https://grafana.com/docs/loki/latest/configuration/#limits_config
for more. You can still query based on any log record
field using query filters."
description: "LabelKeys is a list of log record keys that
will be used as Loki labels with the corresponding log
record value. \n If LabelKeys is not set, the default
keys are `[log_type, kubernetes.namespace_name, kubernetes.pod_name,
kubernetes_host]` \n Note: Loki label names must match
the regular expression \"[a-zA-Z_:][a-zA-Z0-9_:]*\" Log
record keys may contain characters like \".\" and \"/\"
that are not allowed in Loki labels. Log record keys are
translated to Loki labels by replacing any illegal characters
with '_'. For example the default log record keys translate
to these Loki labels: `log_type`, `kubernetes_namespace_name`,
`kubernetes_pod_name`, `kubernetes_host` \n Note: the
set of labels should be small, Loki imposes limits on
the size and number of labels allowed. See https://grafana.com/docs/loki/latest/configuration/#limits_config
for more. Loki queries can also query based on any log
record field (not just labels) using query filters."
items:
type: string
type: array
Expand Down
34 changes: 16 additions & 18 deletions config/crd/bases/logging.openshift.io_clusterlogforwarders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -265,24 +265,22 @@ spec:
loki`'
properties:
labelKeys:
description: "LabelKeys is a list of meta-data field keys
to replace the default Loki labels. \n Loki label names
must match the regular expression \"[a-zA-Z_:][a-zA-Z0-9_:]*\".
Illegal characters in meta-data keys are replaced with
\"_\" to form the label name. For example meta-data key
\"kubernetes.labels.foo\" becomes Loki label \"kubernetes_labels_foo\".
\n If LabelKeys is not set, the default keys are `[log_type,
kubernetes.namespace_name, kubernetes.pod_name, kubernetes_host]`
These keys are translated to Loki labels by replacing
'.' with '_' as: `log_type`, `kubernetes_namespace_name`,
`kubernetes_pod_name`, `kubernetes_host` Note that not
all logs will include all of these keys: audit logs and
infrastructure journal logs do not have namespace or pod
name. \n Note: the set of labels should be small, Loki
imposes limits on the size and number of labels allowed.
See https://grafana.com/docs/loki/latest/configuration/#limits_config
for more. You can still query based on any log record
field using query filters."
description: "LabelKeys is a list of log record keys that
will be used as Loki labels with the corresponding log
record value. \n If LabelKeys is not set, the default
keys are `[log_type, kubernetes.namespace_name, kubernetes.pod_name,
kubernetes_host]` \n Note: Loki label names must match
the regular expression \"[a-zA-Z_:][a-zA-Z0-9_:]*\" Log
record keys may contain characters like \".\" and \"/\"
that are not allowed in Loki labels. Log record keys are
translated to Loki labels by replacing any illegal characters
with '_'. For example the default log record keys translate
to these Loki labels: `log_type`, `kubernetes_namespace_name`,
`kubernetes_pod_name`, `kubernetes_host` \n Note: the
set of labels should be small, Loki imposes limits on
the size and number of labels allowed. See https://grafana.com/docs/loki/latest/configuration/#limits_config
for more. Loki queries can also query based on any log
record field (not just labels) using query filters."
items:
type: string
type: array
Expand Down
35 changes: 33 additions & 2 deletions docs/reference/operator/api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,9 @@ This is the struct that will contain information pertinent to Log visualization
|Property|Type|Description

|kibana|object| **(DEPRECATED)** *(optional)* Specification of the Kibana Visualization component
|nodeSelector|object| Define which Nodes the Pods are scheduled on.
|ocpConsole|object| *(optional)* OCPConsole is the specification for the OCP console plugin
|tolerations|array| *(optional)* Define the tolerations the Pods will accept
|type|string| The type of Visualization to configure
|======================

Expand All @@ -1050,11 +1052,11 @@ This is the struct that will contain information pertinent to Log visualization
|======================
|Property|Type|Description

|nodeSelector|object| Define which Nodes the Pods are scheduled on.
|nodeSelector|object| **(DEPRECATED)** Define which Nodes the Pods are scheduled on.
|proxy|object| Specification of the Kibana Proxy component
|replicas|int| *(optional)* Number of instances to deploy for a Kibana deployment
|resources|object| *(optional)* The resource requirements for Kibana
|tolerations|array|
|tolerations|array| **(DEPRECATED)** Define the tolerations the Pods will accept
|======================

=== .spec.visualization.kibana.nodeSelector
Expand Down Expand Up @@ -1157,6 +1159,12 @@ This is the struct that will contain information pertinent to Log visualization
===== Type
* int

=== .spec.visualization.nodeSelector
===== Description

===== Type
* object

=== .spec.visualization.ocpConsole
===== Description

Expand All @@ -1171,6 +1179,29 @@ This is the struct that will contain information pertinent to Log visualization
|timeout|string| *(optional)* Timeout is the max duration before a query timeout
|======================

=== .spec.visualization.tolerations[]
===== Description

===== Type
* array

[options="header"]
|======================
|Property|Type|Description

|effect|string| *(optional)* Effect indicates the taint effect to match. Empty means match all taint effects.
|key|string| *(optional)* Key is the taint key that the toleration applies to. Empty means match all taint keys.
|operator|string| *(optional)* Operator represents a key's relationship to the value.
|tolerationSeconds|int| *(optional)* TolerationSeconds represents the period of time the toleration (which must be
|value|string| *(optional)* Value is the taint value the toleration matches to.
|======================

=== .spec.visualization.tolerations[].tolerationSeconds
===== Description

===== Type
* int

=== .status
===== Description
ClusterLoggingStatus defines the observed state of ClusterLogging
Expand Down
15 changes: 13 additions & 2 deletions internal/generator/vector/output/loki/loki.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,11 @@ func lokiLabelKeys(l *logging.Loki) []string {
func lokiLabels(lo *logging.Loki) []Label {
ls := []Label{}
for _, k := range lokiLabelKeys(lo) {
name := strings.ReplaceAll(k, ".", "_")
name = strings.ReplaceAll(name, "/", "_")
l := Label{
Name: strings.ReplaceAll(k, ".", "_"),
Value: fmt.Sprintf("{{%s}}", k),
Name: name,
Value: formatLokiLabelValue(k),
}
if k == lokiLabelKubernetesHost {
l.Value = "${VECTOR_SELF_NODE_NAME}"
Expand All @@ -175,6 +177,15 @@ func lokiLabels(lo *logging.Loki) []Label {
return ls
}

func formatLokiLabelValue(value string) string {
if strings.HasPrefix(value, "kubernetes.labels.") || strings.HasPrefix(value, "kubernetes.namespace_labels.") {
parts := strings.SplitAfterN(value, ".", 2)
key := strings.ReplaceAll(parts[1], "/", "_")
value = fmt.Sprintf("%s%s", parts[0], key)
}
return fmt.Sprintf("{{%s}}", value)
}

func Labels(o logging.OutputSpec) Element {
return LokiLabels{
ComponentID: strings.ToLower(vectorhelpers.Replacer.Replace(o.Name)),
Expand Down
30 changes: 29 additions & 1 deletion test/functional/outputs/loki/application_logs_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ var _ = Describe("[Functional][Outputs][Loki] Forwarding to Loki", func() {
Labels: map[string]string{"logging": "logging-value"},
})

Expect(f.Deploy()).To(BeNil())
})

AfterEach(func() {
f.Cleanup()
})

Context("with vector not ordered events", func() {
BeforeEach(func() {
Expect(f.Deploy()).To(BeNil())
})
It("should accept not ordered event", func() {
now := time.Now()
tsNow := functional.CRIOTime(now)
Expand All @@ -79,5 +81,31 @@ var _ = Describe("[Functional][Outputs][Loki] Forwarding to Loki", func() {
Expect(strings.Contains(lines[2], "Present days")).To(BeTrue())
})
})
Context("when label keys are defined that include slashes. Ref LOG-4095", func() {
const myValue = "foobarvalue"
BeforeEach(func() {
f.Labels["foo/bar"] = myValue
f.Forwarder.Spec.Outputs[0].Loki.LabelKeys = []string{
"kubernetes.namespace_name",
"kubernetes.pod_name",
"kubernetes.labels.foo/bar",
}
Expect(f.Deploy()).To(BeNil())
})
It("should handle the configuration so the collector starts", func() {
now := time.Now()
tsNow := functional.CRIOTime(now)
msg := functional.NewFullCRIOLogMessage(tsNow, "Present days")
Expect(f.WriteMessagesToApplicationLog(msg, 1)).To(Succeed())

query := fmt.Sprintf(`{kubernetes_labels_foo_bar=%q}`, myValue)
result, err := l.QueryUntil(query, "", 1)
Expect(err).To(BeNil())
Expect(result).NotTo(BeNil())
Expect(len(result)).To(Equal(1))
lines := result[0].Lines()
Expect(len(lines)).To(Equal(1))
})
})

})