默认的interface可以是NOOP,以兼容(避免)或者说最大程度较少代码的侵入性
在编程中,NOOP(无操作)是一个常用的概念,它表示一个不执行任何操作的操作或函数。这在很多场景中都很有用。以下是关于 Go 语言中 NOOP 的一些要点:
接口默认实现:NOOP 可以作为一个接口的默认实现。例如,在 OpenTelemetry 中,当没有配置 TracerProvider 时,会使用一个 NOOP tracer。这样,如果用户没有提供他们自己的实现,代码仍然可以正常运行,而不会崩溃或抛出错误。
测试和调试:NOOP 也可以在测试和调试中使用。例如,你可能想要在测试中使用一个 NOOP 实现,以便你可以专注于测试其他部分的代码,而不必担心接口的实现。
可选的功能:NOOP 可以用于可选的功能。例如,你可能有一个可以记录详细日志的接口,但在某些情况下,你可能不希望记录这些日志。在这种情况下,你可以使用一个 NOOP 实现。
在 Go 语言中实现一个 NOOP 接口通常很简单。你只需定义一个结构体,并为它实现接口的所有方法,但这些方法什么都不做。例如,以下是一个 NOOP tracer 的简单实现:
type NoopTracer struct{}
func (t NoopTracer) Start(ctx context.Context, name string) (context.Context, trace.Span) {
return ctx, trace.NoopSpan{}
}
func (t NoopTracer) WithSpan(ctx context.Context, name string, body func(context.Context) error) error {
return body(ctx)
}
在这个例子中,NoopTracer
实现了一个 tracer 接口,但它的所有方法都没有做任何事情。这就是一个 NOOP 接口的简单实现。
以OpenTelemetr为例子,yOpenTelemetry 是一个用于观察、收集和分析软件的运行时数据,如指标、日志和追踪,以支持开发、观察、诊断和修复复杂的软件系统的开源项目。
在 OpenTelemetry Go 实现中,有许多地方使用了 Noop 接口。例如,当你创建一个新的 trace,但没有配置 tracer 提供者时,OpenTelemetry 会使用一个 Noop tracer。
以下是一个简化的示例,展示了在 OpenTelemetry 中如何使用 Noop tracer:
package main
import (
"context"
"fmt"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
func main() {
ctx := context.Background()
// Get a tracer. Since no tracer provider is configured, this will be a Noop tracer.
tracer := otel.Tracer("example.com/HelloWorld")
// Start a new trace. Since the tracer is a Noop tracer, this won't actually do anything.
ctx, span := tracer.Start(ctx, "operation")
span.End()
// Verify that the span is a Valid span.
spanCtx := trace.SpanContextFromContext(c.Request.Context())
fmt.Printf("traceID: %+v spanID %v\n", spanCtx.TraceID().String(), spanCtx.SpanID().String())
spanCtx.IsValid()
}
在这个例子中,我们使用 otel.Tracer
获取一个 tracer,但因为我们没有配置 tracer 提供者,所以返回的是一个 Noop tracer。当我们使用这个 Noop tracer 开始一个新的 trace 时,实际上并没有做任何事情。这就是 Noop 接口在 OpenTelemetry 中的一个应用示例。
那么如何配置 tracer 提供者(provider)
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
)
type OpenTelemetry struct {
Enable bool `yaml:"enable"`
CollectorEndpointHttpUrl string `yaml:"collectorEndpointHttpUrl"`
}
// tracerProvider returns an OpenTelemetry TracerProvider configured to use
// the Jaeger exporter that will send spans to the provided url. The returned
// TracerProvider will also use a Resource configured with all the information
// about the application.
func InitNewJaegerTracerProvider(disable bool, url, serviceName, id string) (*sdktrace.TracerProvider, error) {
if disable {
//不初始化,使用默认的noop provider,直接返回,所有trace的操作当作无效操作,不会panic,no operation
return nil, nil
}
// Create the Jaeger exporter
// WithAgentEndpoint(WithAgentHost(host), WithAgentPort(port)),
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
// Always be sure to batch in production.
sdktrace.WithBatcher(exp),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
// Record information about this application in an Resource.
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(serviceName),
// attribute.String("environment", environment),
// attribute.String("ID", id),
)),
)
// Register our TracerProvider as the global so any imported
// instrumentation in the future will default to using it.
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
return tp, nil
}