OpenTelemetry/collector

[exporter] 분석 - 1

justbagmeg 2023. 8. 20. 17:08

먼저 exporter의 Factory를 살펴보자.

// Factory is factory interface for exporters.
//
// This interface cannot be directly implemented. Implementations must
// use the NewFactory to implement it.
// 해당 인터페이스를 직접 사용할 수 없고 반드시 NewFactory()를 이용해 생성해야한다.
type Factory interface {
    component.Factory // 모든 컴포넌트 팩토리가 구현하는 인터페이스. Type()과 CreateDefaultConfig() 메서드 필요.

    // CreateTracesExporter creates a TracesExporter based on this config.
    // If the exporter type does not support tracing or if the config is not valid,
    // an error will be returned instead.
    CreateTracesExporter(ctx context.Context, set CreateSettings, cfg component.Config) (Traces, error)

    // TracesExporterStability gets the stability level of the TracesExporter.
    TracesExporterStability() component.StabilityLevel

    // CreateMetricsExporter creates a MetricsExporter based on this config.
    // If the exporter type does not support metrics or if the config is not valid,
    // an error will be returned instead.
    CreateMetricsExporter(ctx context.Context, set CreateSettings, cfg component.Config) (Metrics, error)

    // MetricsExporterStability gets the stability level of the MetricsExporter.
    MetricsExporterStability() component.StabilityLevel

    // CreateLogsExporter creates a LogsExporter based on the config.
    // If the exporter type does not support logs or if the config is not valid,
    // an error will be returned instead.
    CreateLogsExporter(ctx context.Context, set CreateSettings, cfg component.Config) (Logs, error)

    // LogsExporterStability gets the stability level of the LogsExporter.
    LogsExporterStability() component.StabilityLevel

    unexportedFactoryFunc()
}
// FactoryOption apply changes to Factory.
type FactoryOption interface {
    // applyExporterFactoryOption applies the option.
    applyExporterFactoryOption(o *factory)
}

FactoryOption 인터페이스로 팩토리에 옵션을 적용한다.

 

NewFactory 함수. 이 함수를 이용해 Factory를 생성한다.
매개변수로는 생성되는 컴포터는의 Type, 컴포넌트의 default config를 생성하는 createDefaultConfig 함수, 그리고 팩토리에 적용할 FactoryOption들을 받는다.

// NewFactory returns a Factory.
func NewFactory(cfgType component.Type, createDefaultConfig component.CreateDefaultConfigFunc, options ...FactoryOption) Factory {
    f := &factory{
        cfgType:                 cfgType,
        CreateDefaultConfigFunc: createDefaultConfig,
    }
    for _, opt := range options {
        opt.applyExporterFactoryOption(f)
    }
    return f
}

options는 factoryOptionFunc 타입이다.

var _ FactoryOption = (*factoryOptionFunc)(nil)

// factoryOptionFunc is an ExporterFactoryOption created through a function.
type factoryOptionFunc func(*factory)

func (f factoryOptionFunc) applyExporterFactoryOption(o *factory) {
	f(o)
}

먼저 첫 번째 라인은, factoryOptionFunc 타입이 FactoryOption 인터페이스를 만족하는지 확인하는 용도로 사용된다. 만약 구현되지 않았다면 빌드 과정에서 에러가 발생한다.

factoryOptionFunc은 factory를 반환하는 빌더라고 생각할 수 있다. factoryOptionFunc은 applyExporterFactoryOption을 구현했기 때문에 FactoryOption 인터페이스를 만족한다.

 

다시 아래 코드로 돌아와서...

NewFactory() 호출 시 매개변수로 받은 FactoryOption 인터페이스를 만족하는 함수들을 실행시킨다.

	for _, opt := range options {
		opt.applyExporterFactoryOption(f)
	}

여기서 재밌는게, options로 넘겨주는 함수들을 보면 아래처럼 생겼다.

func WithTraces(createTraces CreateTracesFunc, sl component.StabilityLevel) FactoryOption {
	return factoryOptionFunc(func(o *factory) {
		o.tracesStabilityLevel = sl
		o.CreateTracesFunc = createTraces
	})
}

넘겨주는 타입(?) 함수들이 각각 applyExporterFactoryOption()을 구현하지 않고, factoryOptionFunc 타입을 사용해서 처리한다. 😮

 

NewFactory 함수가 반환하는 타입은 factory struct로,

  1. 컴포넌트 타입
  2. 컴포넌트 default config 생성 함수
  3. Trace Factory 생성 함수
  4. Metric Factory 생성 함수
  5. Log Factory 생성 함수
    그리고 Trace, Metric, Log 컴포넌트의 Stability를 나타내는 필드를 갖는다.
    type factory struct {
    	cfgType component.Type
    	component.CreateDefaultConfigFunc
    	CreateTracesFunc
    	tracesStabilityLevel component.StabilityLevel
    	CreateMetricsFunc
    	metricsStabilityLevel component.StabilityLevel
    	CreateLogsFunc
    	logsStabilityLevel component.StabilityLevel
    }

그런데, 구현된 factory struct 메서드를 보면, CreateTracesExporter, CreateMetricsExporter, CreateLogsExporter가 없는 것을 볼 수 있다. 왜 그럴까? 이유는 코드 아래 나온다.

func (f *factory) Type() component.Type {
    return f.cfgType
}

func (f *factory) unexportedFactoryFunc() {}

func (f *factory) TracesExporterStability() component.StabilityLevel {
    return f.tracesStabilityLevel
}

func (f *factory) MetricsExporterStability() component.StabilityLevel {
    return f.metricsStabilityLevel
}

func (f *factory) LogsExporterStability() component.StabilityLevel {
    return f.logsStabilityLevel
}

아래 코드는 factory struct에 임베드된 CreateTracesFunc 타입이다. 이 함수의 메서드로 CreateTracesExporter가 구현되어 있다. factory가 CreateTracesFunc를 임베딩하고 있기 때문에 factory가 CreateTracesExporter메서드를 갖고 있는 것처럼 사용할 수 있는 것이다.

// CreateTracesFunc is the equivalent of Factory.CreateTraces.
type CreateTracesFunc func(context.Context, CreateSettings, component.Config) (Traces, error)

// CreateTracesExporter implements ExporterFactory.CreateTracesExporter().
func (f CreateTracesFunc) CreateTracesExporter(ctx context.Context, set CreateSettings, cfg component.Config) (Traces, error) {
    if f == nil {
        return nil, component.ErrDataTypeIsNotSupported
    }
    return f(ctx, set, cfg)
}