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

feat(loki/src/k8s): configurable default instance label for scraped pod logs #6739

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ v0.41.0 (2024-05-31)

- Propagate request metadata for `faro.receiver` to downstream components. (@hainenber)

- Configurable default `instance` label for logs scraped by `loki.source.kubernetes`. (@hainenber)

### Features

- A new `loki.rules.kubernetes` component that discovers `PrometheusRule` Kubernetes resources and loads them into a Loki Ruler instance. (@EStork09)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Name | Type | Description | Default | Required
`targets` | `list(map(string))` | List of files to read from. | | yes
`forward_to` | `list(LogsReceiver)` | List of receivers to send log entries to. | | yes


Each target in `targets` must have the following labels:

* `__meta_kubernetes_namespace` or `__pod_namespace__` to specify the namespace
Expand Down Expand Up @@ -95,6 +96,7 @@ inside a `client` block.
[oauth2]: #oauth2-block
[tls_config]: #tls_config-block
[clustering]: #clustering-block
[default_instance_label]: #default_instance_label-block

### client block

Expand Down Expand Up @@ -159,6 +161,19 @@ arguments.

[using clustering]: {{< relref "../../concepts/clustering.md" >}}

### default_instance_label block

The `default_instance_label` block configures default "instance" label to be assigned with logs
scraped from inputted targets.
hainenber marked this conversation as resolved.
Show resolved Hide resolved
If `default_instance_label` block is not provided, the target's address is used as the default `instance` label.
hainenber marked this conversation as resolved.
Show resolved Hide resolved

The following arguments are supported:

Name | Type | Description | Default | Required
------------------------ | ------------------- | ------------------------------------------------------------- | ------- | --------
`enabled` | `bool` | Enable setting default "instance" label value. | true | no
hainenber marked this conversation as resolved.
Show resolved Hide resolved
`value` | `string` | Value for the label. | | no

## Exported fields

`loki.source.kubernetes` does not export any fields.
Expand Down
8 changes: 6 additions & 2 deletions internal/component/loki/source/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ type Arguments struct {
Client commonk8s.ClientArguments `river:"client,block,optional"`

Clustering cluster.ComponentBlock `river:"clustering,block,optional"`

// DefaultInstanceLabel configures "instance" label
DefaultInstanceLabel kubetail.DefaultInstanceLabel `river:"default_instance_label,block,optional"`
}

// DefaultArguments holds default settings for loki.source.kubernetes.
var DefaultArguments = Arguments{
Client: commonk8s.DefaultClientArguments,
Client: commonk8s.DefaultClientArguments,
DefaultInstanceLabel: kubetail.DefaultInstanceLabel{Enabled: true},
}

// SetToDefault implements river.Defaulter.
Expand Down Expand Up @@ -193,7 +197,7 @@ func (c *Component) resyncTargets(targets []discovery.Target) {
tailTargets := make([]*kubetail.Target, 0, len(targets))
for _, target := range targets {
lset := target.Labels()
processed, err := kubetail.PrepareLabels(lset, c.opts.ID)
processed, err := kubetail.PrepareLabels(lset, c.opts.ID, c.args.DefaultInstanceLabel)
if err != nil {
// TODO(rfratto): should this set the health of the component?
level.Error(c.log).Log("msg", "failed to process input target", "target", lset.String(), "err", err)
Expand Down
4 changes: 4 additions & 0 deletions internal/component/loki/source/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func TestRiverConfig(t *testing.T) {
{"__address__" = "localhost:9090", "foo" = "bar"},
{"__address__" = "localhost:8080", "foo" = "buzz"},
]
default_instance_label {
enabled = true
value = "test"
}
forward_to = []
client {
api_server = "localhost:9091"
Expand Down
12 changes: 10 additions & 2 deletions internal/component/loki/source/kubernetes/kubetail/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ type Target struct {
lastEntry time.Time
}

type DefaultInstanceLabel struct {
Enabled bool `river:"enabled,attr,optional"`
Value string `river:"value,attr,optional"`
}

// NewTarget creates a new Target which can be passed to a tailer.
func NewTarget(origLabels labels.Labels, lset labels.Labels) *Target {
// Precompute some values based on labels so we don't have to continually
Expand Down Expand Up @@ -156,7 +161,7 @@ func (t *Target) LastEntry() time.Time {
//
// Validation of lset fails if there is no label indicating the pod namespace,
// pod name, or container name.
func PrepareLabels(lset labels.Labels, defaultJob string) (res labels.Labels, err error) {
func PrepareLabels(lset labels.Labels, defaultJob string, defaultInstanceLabel DefaultInstanceLabel) (res labels.Labels, err error) {
tailLabels := []labels.Label{
{Name: model.JobLabel, Value: defaultJob},
}
Expand Down Expand Up @@ -220,8 +225,11 @@ func PrepareLabels(lset labels.Labels, defaultJob string) (res labels.Labels, er
}

// Default the instance label to the target address.
if !lset.Has(model.InstanceLabel) {
if !lset.Has(model.InstanceLabel) && defaultInstanceLabel.Enabled {
defaultInstance := fmt.Sprintf("%s/%s:%s", podNamespace, podName, podContainerName)
if defaultInstanceLabel.Value != "" {
defaultInstance = defaultInstanceLabel.Value
}
lb.Set(model.InstanceLabel, defaultInstance)
}

Expand Down
3 changes: 2 additions & 1 deletion internal/component/loki/source/podlogs/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ func (r *reconciler) reconcilePodLogs(ctx context.Context, cli client.Client, po
processedLabels, _ := relabel.Process(targetLabels.Copy(), relabelRules...)

defaultJob := fmt.Sprintf("%s/%s:%s", podLogs.Namespace, podLogs.Name, container.Name)
finalLabels, err := kubetail.PrepareLabels(processedLabels, defaultJob)
// TODO: make default "instance" label for pod logs configurable.
finalLabels, err := kubetail.PrepareLabels(processedLabels, defaultJob, kubetail.DefaultInstanceLabel{Enabled: true})

if err != nil {
discoveredPod.Containers = append(discoveredPod.Containers, DiscoveredContainer{
Expand Down
Loading