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

Add jaeger-v2 single binary based on OTEL collector #4766

Merged
merged 15 commits into from
Sep 27, 2023
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,18 @@ jaeger-ui/packages/jaeger-ui/build/index.html:
.PHONY: rebuild-ui
rebuild-ui:
bash ./scripts/rebuild-ui.sh

.PHONY: build-all-in-one-linux
build-all-in-one-linux:
GOOS=linux $(MAKE) build-all-in-one

build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug build-remote-storage-debug: DISABLE_OPTIMIZATIONS = -gcflags="all=-N -l"
build-all-in-one-debug build-agent-debug build-query-debug build-collector-debug build-ingester-debug build-remote-storage-debug: SUFFIX = -debug

.PHONY: build-jaeger-v2
build-jaeger-v2:
$(GOBUILD) $(DISABLE_OPTIMIZATIONS) -tags ui -o ./cmd/jaeger-v2/jaeger-v2$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/jaeger-v2/main.go

.PHONY: build-all-in-one build-all-in-one-debug
build-all-in-one build-all-in-one-debug: build-ui
$(GOBUILD) $(DISABLE_OPTIMIZATIONS) -tags ui -o ./cmd/all-in-one/all-in-one$(SUFFIX)-$(GOOS)-$(GOARCH) $(BUILD_INFO) ./cmd/all-in-one/main.go
Expand Down
2 changes: 2 additions & 0 deletions cmd/jaeger-v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
jaeger-v2-*-*
jaeger-v2
45 changes: 45 additions & 0 deletions cmd/jaeger-v2/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
service:
extensions: [jaeger_storage, jaeger_query]
pipelines:
traces:
receivers: [otlp, jaeger, zipkin]
yurishkuro marked this conversation as resolved.
Show resolved Hide resolved
processors: [batch]
exporters: [jaeger_storage_exporter]

extensions:
# health_check:
# pprof:
# endpoint: 0.0.0.0:1777
# zpages:
# endpoint: 0.0.0.0:55679

jaeger_query:
trace_storage: memstore

jaeger_storage:
memory:
memstore:
max_traces: 100000


receivers:
otlp:
protocols:
grpc:
http:

jaeger:
protocols:
grpc:
thrift_binary:
thrift_compact:
thrift_http:

zipkin:

processors:
batch:

exporters:
jaeger_storage_exporter:
trace_storage: memstore
2 changes: 2 additions & 0 deletions cmd/jaeger-v2/internal/.nocover
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FIXME

43 changes: 43 additions & 0 deletions cmd/jaeger-v2/internal/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package internal

import (
"log"

"github.com/spf13/cobra"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/otelcol"

"github.com/jaegertracing/jaeger/pkg/version"
)

const description = "Jaeger backend v2"

func Command() *cobra.Command {
factories, err := components()
if err != nil {
log.Fatalf("failed to build components: %v", err)
}

versionInfo := version.Get()

info := component.BuildInfo{
Command: "jaeger-v2",
Description: description,
Version: versionInfo.GitVersion,
}

cmd := otelcol.NewCommand(
otelcol.CollectorSettings{
BuildInfo: info,
Factories: factories,
},
)

cmd.Short = description
cmd.Long = description

return cmd
}
98 changes: 98 additions & 0 deletions cmd/jaeger-v2/internal/components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) 2023 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package internal

import (
"github.com/open-telemetry/opentelemetry-collector-contrib/connector/spanmetricsconnector"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/kafkaexporter"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kafkareceiver"
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver"
"go.opentelemetry.io/collector/connector"
"go.opentelemetry.io/collector/connector/forwardconnector"
"go.opentelemetry.io/collector/exporter"
"go.opentelemetry.io/collector/exporter/loggingexporter"
"go.opentelemetry.io/collector/exporter/otlpexporter"
"go.opentelemetry.io/collector/exporter/otlphttpexporter"
"go.opentelemetry.io/collector/extension"
"go.opentelemetry.io/collector/extension/ballastextension"
"go.opentelemetry.io/collector/extension/zpagesextension"
"go.opentelemetry.io/collector/otelcol"
"go.opentelemetry.io/collector/processor"
"go.opentelemetry.io/collector/processor/batchprocessor"
"go.opentelemetry.io/collector/processor/memorylimiterprocessor"
"go.opentelemetry.io/collector/receiver"
"go.opentelemetry.io/collector/receiver/otlpreceiver"

"github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/exporters/storageexporter"
"github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerquery"
"github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerstorage"
)

func components() (otelcol.Factories, error) {
var err error
factories := otelcol.Factories{}

factories.Extensions, err = extension.MakeFactoryMap(
// standard
ballastextension.NewFactory(),
zpagesextension.NewFactory(),
// add-ons
jaegerquery.NewFactory(),
jaegerstorage.NewFactory(),
// TODO add adaptive sampling
)
if err != nil {
return otelcol.Factories{}, err
}

factories.Receivers, err = receiver.MakeFactoryMap(
// standard
otlpreceiver.NewFactory(),
// add-ons
jaegerreceiver.NewFactory(),
kafkareceiver.NewFactory(),
zipkinreceiver.NewFactory(),
)
if err != nil {
return otelcol.Factories{}, err
}

factories.Exporters, err = exporter.MakeFactoryMap(
// standard
loggingexporter.NewFactory(),
otlpexporter.NewFactory(),
otlphttpexporter.NewFactory(),
// add-ons
storageexporter.NewFactory(), // generic exporter to Jaeger v1 spanstore.SpanWriter
kafkaexporter.NewFactory(),
// elasticsearch.NewFactory(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why exclude the elasticsearch exporter? Is it because storageexporter should already cover the ability to write to elasticsearch using jaeger's native elasticsearch span writer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, because there isn't one, it's just the code I copied from Pavol's repo where es exporter module was defined, but was not implemented.

Initially yes, the generic support for existing storage backends is done via storageexporter, which converts OTLP into Jaeger model and sends to SpanWriter one span at a time. As we build OTLP-native exporters people can migrate to those.

)
if err != nil {
return otelcol.Factories{}, err
}

factories.Processors, err = processor.MakeFactoryMap(
// standard
batchprocessor.NewFactory(),
memorylimiterprocessor.NewFactory(),
// add-ons
// TODO add adaptive sampling
)
if err != nil {
return otelcol.Factories{}, err
}

factories.Connectors, err = connector.MakeFactoryMap(
// standard
forwardconnector.NewFactory(),
// add-ons
spanmetricsconnector.NewFactory(),
)
if err != nil {
return otelcol.Factories{}, err
}

return factories, nil
}
2 changes: 2 additions & 0 deletions cmd/jaeger-v2/internal/exporters/storageexporter/.nocover
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FIXME

17 changes: 17 additions & 0 deletions cmd/jaeger-v2/internal/exporters/storageexporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# jaeger_storage_exporter

This module implements `exporter.Traces` and writes spans into Jaeger native `spanstore.SpanWriter`, that it obtains from the [jaeger_storage](../../extension/jaegerstorage/) extension. This is primarily needed to wire a memory storage into the exporter pipeline (used for all-in-one), but the design of the exporter is such that it can do this for any V1 storage implementation.

## Configuration

```yaml
exporters:
jaeger_storage_exporter:
trace_storage: memstore

extensions:
jaeger_storage:
memory:
memstore:
max_traces: 100000
```
24 changes: 24 additions & 0 deletions cmd/jaeger-v2/internal/exporters/storageexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2023 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package storageexporter

import (
"github.com/asaskevich/govalidator"
"go.opentelemetry.io/collector/component"
)

var (
_ component.Config = (*Config)(nil)
_ component.ConfigValidator = (*Config)(nil)
)

// Config defines configuration for jaeger_storage_exporter.
type Config struct {
TraceStorage string `valid:"required" mapstructure:"trace_storage"`
}

func (cfg *Config) Validate() error {
_, err := govalidator.ValidateStruct(cfg)
return err
}
66 changes: 66 additions & 0 deletions cmd/jaeger-v2/internal/exporters/storageexporter/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2023 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package storageexporter

import (
"context"
"errors"
"fmt"

otlp2jaeger "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/pdata/ptrace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerstorage"
"github.com/jaegertracing/jaeger/storage/spanstore"
)

type storageExporter struct {
config *Config
logger *zap.Logger
spanWriter spanstore.Writer
}

func newExporter(config *Config, otel component.TelemetrySettings) *storageExporter {
return &storageExporter{
config: config,
logger: otel.Logger,
}
}

func (exp *storageExporter) start(_ context.Context, host component.Host) error {
f, err := jaegerstorage.GetStorageFactory(exp.config.TraceStorage, host)
if err != nil {
return fmt.Errorf("cannot find storage factory: %w", err)
}

if exp.spanWriter, err = f.CreateSpanWriter(); err != nil {
return fmt.Errorf("cannot create span writer: %w", err)
}

return nil
}

func (exp *storageExporter) close(_ context.Context) error {
// span writer is not closable
return nil
}

func (exp *storageExporter) pushTraces(ctx context.Context, td ptrace.Traces) error {
batches, err := otlp2jaeger.ProtoFromTraces(td)
if err != nil {
return fmt.Errorf("cannot transform OTLP traces to Jaeger format: %w", err)
}
var errs []error
for _, batch := range batches {
for _, span := range batch.Spans {
if span.Process == nil {
span.Process = batch.Process
}
errs = append(errs, exp.spanWriter.WriteSpan(ctx, span))
}
}
return errors.Join(errs...)
}
44 changes: 44 additions & 0 deletions cmd/jaeger-v2/internal/exporters/storageexporter/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2023 The Jaeger Authors.
// SPDX-License-Identifier: Apache-2.0

package storageexporter

import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/exporter"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

// componentType is the name of this extension in configuration.
const componentType = component.Type("jaeger_storage_exporter")

// NewFactory creates a factory for jaeger_storage_exporter.
func NewFactory() exporter.Factory {
return exporter.NewFactory(
componentType,
createDefaultConfig,
exporter.WithTraces(createTracesExporter, component.StabilityLevelDevelopment),
)
}

func createDefaultConfig() component.Config {
return &Config{}
}

func createTracesExporter(ctx context.Context, set exporter.CreateSettings, config component.Config) (exporter.Traces, error) {
cfg := config.(*Config)
ex := newExporter(cfg, set.TelemetrySettings)
return exporterhelper.NewTracesExporter(ctx, set, cfg,
ex.pushTraces,
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
// Disable Timeout/RetryOnFailure and SendingQueue
exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}),
exporterhelper.WithRetry(exporterhelper.RetrySettings{Enabled: false}),
exporterhelper.WithQueue(exporterhelper.QueueSettings{Enabled: false}),
exporterhelper.WithStart(ex.start),
exporterhelper.WithShutdown(ex.close),
)
}
2 changes: 2 additions & 0 deletions cmd/jaeger-v2/internal/extension/jaegerquery/.nocover
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FIXME

15 changes: 15 additions & 0 deletions cmd/jaeger-v2/internal/extension/jaegerquery/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# jaeger_query

This extension implements traditional `jaeger-query` service (including Jaeger UI). Because it is not a part of the collector pipeline, it needs access to a storage backend, which can be potentially shared with an exporter (e.g. in order to implement the `all-in-one` functionality). For this reason it depends on the [jaeger_storage](../jaegerstorage/) extension.

## Configuration

This is work in progress, most of the usual settings of `jaeger-query` are not supported yet.

```yaml
jaeger_query:
trace_storage: cassandra_primary
trace_archive: cassandra_archive
dependencies: memstore
metrics_store: prometheus_store
```
Loading