lookupprocessor

package module
v0.155.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

README

Lookup Processor

The lookup processor enriches telemetry signals by performing external lookups to retrieve additional data. It evaluates an OTTL value expression to extract a lookup key, queries a lookup source, and writes the results as new attributes.

Status
Stability development: logs, traces, metrics
Distributions []
Issues Open issues Closed issues
Code coverage codecov
Code Owners @jsvd, @dehaansa, @VihasMakwana

Configuration

processors:
  lookup:
    source:
      type: yaml
      path: /etc/otel/mappings.yaml
    lookups:
      - key: log.attributes["user.id"]
        attributes:
          - destination: user.name
            default: "Unknown User"
Full Configuration
Field Description Default
source.type The source type identifier (noop, yaml, dns) noop
lookups List of lookup rules (required, at least one) -
Lookup Configuration

Each entry in lookups defines a lookup rule:

Field Description Default
key OTTL value expression for extracting the lookup key (required) -
context Default context for destination attributes: record, resource record
attributes List of attribute mappings for writing results (required, at least one) -

The key field supports any OTTL value expression, including paths across contexts and converters. The path prefix depends on the signal type:

Signal OTTL context Record-level path prefix
Logs ottllog log.attributes["..."]
Traces ottlspan span.attributes["..."]
Metrics ottldatapoint datapoint.attributes["..."]

Resource attributes use resource.attributes["..."] for all signals. A context prefix is always required; bare attributes["..."] paths are rejected at configuration time.

Examples:

  • attributes["user.id"] - record attribute (works for all signals)
  • log.attributes["user.id"] - log record attribute (logs only)
  • span.attributes["user.id"] - span attribute (traces only)
  • datapoint.attributes["host.id"] - datapoint attribute (metrics only)
  • resource.attributes["service.name"] - resource attribute (all signals)
  • Trim(attributes["raw.id"]) - apply a converter
Attribute Mapping

Each entry in attributes defines where to write a lookup result:

Field Description Default
source Field name in the lookup result (for map results, leave empty for scalar) -
destination Attribute key to write the result to (required) -
default Value to use when the lookup returns no result -
context Override the key's context for this attribute inherited from key's context (record if unset)

Examples

Scalar Lookup (1:1)

When the source returns a single value per key, leave the source field empty:

processors:
  lookup:
    source:
      type: yaml
      path: /etc/otel/mappings.yaml
    lookups:
      - key: log.attributes["user.id"]
        attributes:
          - destination: user.name
            default: "Unknown User"
Map Lookup (1:N)

When the source returns a map of fields per key, use the source field to extract individual values:

processors:
  lookup:
    source:
      type: yaml
      path: /etc/otel/user-details.yaml
    lookups:
      - key: log.attributes["user.id"]
        attributes:
          - source: name
            destination: user.name
            default: "Unknown"
          - source: email
            destination: user.email
          - source: role
            destination: user.role
            context: resource
OTTL Converter on Key

The key field supports OTTL converters for transforming the lookup key before querying the source:

processors:
  lookup:
    source:
      type: yaml
      path: /etc/otel/mappings.yaml
    lookups:
      - key: Trim(log.attributes["raw.id"])
        attributes:
          - destination: display.name
Context
  • record: Write to the signal's record-level attributes (default). This maps to log record attributes for logs, span attributes for traces, and datapoint attributes for metrics. For metrics, lookups are evaluated for each datapoint across all metric types (Gauge, Sum, Histogram, ExponentialHistogram, Summary).
  • resource: Write to resource attributes.

The context field on a key sets the default for all its destination attributes. Each attribute mapping can override this with its own context field.

Lookups are evaluated per record. When writing to resource attributes, later records in the same resource may overwrite values written by earlier records.

Built-in Sources

  • noop - No-operation source for testing
  • yaml - Key-value mappings from YAML files
  • dns - DNS lookups with caching

Caching

Sources that support external lookups (like DNS) can use the built-in LRU caching system to reduce latency and external queries. The cache uses a doubly-linked list with a hash map for O(1) lookups, insertions, and evictions.

Cache Configuration
Field Description Default
cache.enabled Enable caching varies by source
cache.size Maximum number of entries (LRU eviction, must be > 0 when enabled) varies by source
cache.ttl Time-to-live for successful lookups 0 (no expiration)
cache.negative_ttl TTL for "not found" results 0 (disabled)
Cache Performance

Run with go test -bench=BenchmarkCache -run=^$ ./lookupsource/

Single-threaded (Apple M4 Pro):

Operation ns/op B/op allocs/op
Get (hit) 36 0 0
Get (negative hit) 36 0 0
Get (miss) 5 0 0
Set (new entry) 281 151 3
Set (update existing) 42 0 0
Set (with eviction) 152 152 4

Concurrent (12 goroutines):

Operation ns/op B/op allocs/op
Get (parallel) 131 13 1
Mixed read/write (parallel) 145 25 1
Using Cache in Custom Sources

Custom sources can use the cache by wrapping their lookup function:

func createSource(ctx context.Context, settings lookupsource.CreateSettings, cfg lookupsource.SourceConfig) (lookupsource.Source, error) {
    myCfg := cfg.(*Config)

    // Create base lookup function
    lookupFn := func(ctx context.Context, key string) (any, bool, error) {
        // Perform actual lookup
        return result, true, nil
    }

    // Wrap with cache if enabled
    if myCfg.Cache.Enabled {
        cache := lookupsource.NewCache(myCfg.Cache)
        lookupFn = lookupsource.WrapWithCache(cache, lookupFn)
    }

    return lookupsource.NewSource(lookupFn, ...), nil
}

Custom Sources

Custom lookup sources can be added using WithSources:

import (
    "github.com/open-telemetry/opentelemetry-collector-contrib/processor/lookupprocessor"
    "github.com/example/httplookup"
)

factories.Processors[lookupprocessor.Type] = lookupprocessor.NewFactoryWithOptions(
    lookupprocessor.WithSources(httplookup.NewFactory()),
)
Source contract
  • Concurrency: Lookup is called concurrently from multiple goroutines. Implementations must be safe for concurrent use.
  • Keys are strings: The OTTL expression result is converted to a string before calling Lookup.
  • Return values: For scalar (1:1) lookups, return any single value. For map (1:N) lookups, return map[string]any. Values are written to attributes via pcommon.Value.FromRaw. Unsupported types are stringified via fmt.Sprintf.
  • Errors are non-fatal: When Lookup returns an error the processor logs it at Debug level and skips the lookup. It does not fail the batch.
  • Lifecycle: Start is called once before any Lookup; Shutdown is called once after all processing stops. Both are optional (pass nil to NewSource).
  • Config tags: Source config structs must use mapstructure struct tags. The processor decodes source configuration from a raw map using mapstructure.
Implementing a Source
package mysource

import (
    "context"
    "errors"
    "time"

    "github.com/open-telemetry/opentelemetry-collector-contrib/processor/lookupprocessor/lookupsource"
)

type Config struct {
    Endpoint string        `mapstructure:"endpoint"`
    Timeout  time.Duration `mapstructure:"timeout"`
}

func (c *Config) Validate() error {
    if c.Endpoint == "" {
        return errors.New("endpoint is required")
    }
    return nil
}

func NewFactory() lookupsource.SourceFactory {
    return lookupsource.NewSourceFactory(
        "mysource",
        func() lookupsource.SourceConfig {
            return &Config{Timeout: 5 * time.Second}
        },
        createSource,
    )
}

func createSource(
    ctx context.Context,
    settings lookupsource.CreateSettings,
    cfg lookupsource.SourceConfig,
) (lookupsource.Source, error) {
    c := cfg.(*Config)

    return lookupsource.NewSource(
        func(ctx context.Context, key string) (any, bool, error) {
            // Perform lookup - return (value, found, error)
            return "result", true, nil
        },
        func() string { return "mysource" },
        nil, // start function (optional)
        nil, // shutdown function (optional)
    ), nil
}

Benchmarks

Run benchmarks with:

make benchmark
Processor Performance

Measures the full processing pipeline including OTTL key evaluation, source lookup, value conversion, and attribute writes. Uses noop source to isolate processor overhead from source implementation (Apple M4 Pro):

Scenario ns/op B/op allocs/op
1 log, 1 lookup 396 728 22
10 logs, 1 lookup 1,829 3,538 94
100 logs, 1 lookup 17,577 31,732 814
100 logs, 3 lookups 34,993 63,752 1,614
1000 logs, 1 lookup 183,558 312,844 8,014
YAML Source Performance

Measures only the source lookup operation (map access), isolated from processor overhead:

Map Size ns/op allocs/op
10 entries 1,462 0
100 entries 1,415 0
1,000 entries 1,388 0
10,000 entries 1,368 0

Documentation

Overview

Package lookupprocessor contains a processor that enriches telemetry data by performing lookups from various sources and adding the results as attributes.

Built-in Sources

Sources are configured via the processor's configuration using the source.type field.

Third-party Sources

Custom sources can be registered using NewFactoryWithOptions with WithSources. The public API for creating custom sources is in the lookupsource subpackage. See lookupsource.NewSourceFactory and lookupsource.NewSource for details.

Index

Constants

This section is empty.

Variables

Functions

func NewFactory

func NewFactory() processor.Factory

func NewFactoryWithOptions

func NewFactoryWithOptions(options ...FactoryOption) processor.Factory

NewFactoryWithOptions creates a lookup processor factory with custom sources.

Example (third-party HTTP source):

import (
    "github.com/open-telemetry/opentelemetry-collector-contrib/processor/lookupprocessor"
    "github.com/user/otel-lookup-http/httplookup"
)

factories.Processors[lookupprocessor.Type] = lookupprocessor.NewFactoryWithOptions(
    lookupprocessor.WithSources(httplookup.NewFactory()),
)

Types

type AttributeMapping added in v0.146.0

type AttributeMapping struct {
	// Source is the field name in the lookup result for map results.
	// Leave empty for 1:1 scalar lookups.
	// Optional.
	Source string `mapstructure:"source"`

	// Destination is the attribute key to write the result to.
	// Required.
	Destination string `mapstructure:"destination"`

	// Default is the value to use when the lookup returns no result.
	// If not set and the lookup fails, no attribute is written.
	// Optional.
	Default string `mapstructure:"default"`

	// Context overrides the parent LookupConfig context for this mapping.
	// Valid values: "record", "resource".
	// Default: inherits from parent LookupConfig.
	Context ContextID `mapstructure:"context"`
}

AttributeMapping defines where to write a lookup result.

func (*AttributeMapping) GetContext added in v0.146.0

func (m *AttributeMapping) GetContext(parent ContextID) ContextID

GetContext returns the context for this mapping, falling back to the parent context.

type Config

type Config struct {
	Source SourceConfig `mapstructure:"source"`

	// Lookups defines the lookup rules.
	// Each rule specifies a key expression to extract the lookup value,
	// and one or more attribute mappings for where to write the results.
	Lookups []LookupConfig `mapstructure:"lookups"`
}

func (*Config) Validate

func (cfg *Config) Validate() error

type ContextID added in v0.146.0

type ContextID string

ContextID specifies where to apply the lookup.

const (
	ContextRecord   ContextID = "record"
	ContextResource ContextID = "resource"
)

func (*ContextID) UnmarshalText added in v0.146.0

func (c *ContextID) UnmarshalText(text []byte) error

type FactoryOption

type FactoryOption func(*lookupProcessorFactory)

func WithSources

func WithSources(factories ...lookupsource.SourceFactory) FactoryOption

WithSources adds custom source factories to the processor. This REPLACES the default sources on first call, then MERGES on subsequent calls. (Same pattern as transform processor's WithXxxFunctions)

Example:

lookupprocessor.NewFactoryWithOptions(
    lookupprocessor.WithSources(httplookup.NewFactory()),
)

type LookupConfig added in v0.146.0

type LookupConfig struct {
	// Key is an OTTL value expression for extracting the lookup key.
	// Examples: attributes["user.id"], Trim(attributes["raw.id"]),
	//           resource.attributes["service.name"]
	// Required.
	Key string `mapstructure:"key"`

	// Context is the default context for destination attributes.
	// Valid values: "record", "resource".
	// Default: "record"
	Context ContextID `mapstructure:"context"`

	// Attributes defines where to write lookup results.
	// Required: at least one mapping.
	Attributes []AttributeMapping `mapstructure:"attributes"`
}

LookupConfig defines a single lookup rule.

func (*LookupConfig) GetContext added in v0.146.0

func (l *LookupConfig) GetContext() ContextID

GetContext returns the context for this lookup, defaulting to ContextRecord.

type SourceConfig

type SourceConfig struct {
	// Type is the source type identifier (e.g., "noop", "yaml").
	Type string `mapstructure:"type"`

	// Config holds the source-specific configuration as raw map.
	// It is decoded into the source's typed config struct by the factory
	// at processor creation time.
	Config map[string]any `mapstructure:",remain"`
}

SourceConfig captures the source type and its opaque settings.

Source-specific fields are collected into Config via mapstructure's ",remain" tag and decoded later in the factory's createSource using a mapstructure decoder. An alternative would be to implement confmap.Unmarshaler on Config (like geoipprocessor does) to resolve the source config at unmarshal time. We defer decoding to the factory because the set of available source factories is not known until the factory is constructed (custom sources can be injected via WithSources), so the config layer cannot look up the correct factory. Both paths run during collector startup, so validation timing is equivalent in practice.

Directories

Path Synopsis
internal
metadata
Package metadata contains the autogenerated telemetry and build information for the processor/lookup component.
Package metadata contains the autogenerated telemetry and build information for the processor/lookup component.
source/dns
Package dns provides a DNS-based lookup source.
Package dns provides a DNS-based lookup source.
source/noop
Package noop provides a no-operation lookup source for testing.
Package noop provides a no-operation lookup source for testing.
source/yaml
Package yaml provides a YAML file-based lookup source.
Package yaml provides a YAML file-based lookup source.
Package lookupsource provides the public API for creating lookup sources.
Package lookupsource provides the public API for creating lookup sources.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL