feat: add otel

This commit is contained in:
Rogee
2025-01-07 15:50:11 +08:00
parent 374fa6d794
commit 38aa6664b8
17 changed files with 9823 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
package otel
import (
"git.ipao.vip/rogeecn/atom/container"
"git.ipao.vip/rogeecn/atom/utils/opt"
)
const DefaultPrefix = "OTEL"
func DefaultProvider() container.ProviderContainer {
return container.ProviderContainer{
Provider: Provide,
Options: []opt.Option{
opt.Prefix(DefaultPrefix),
},
}
}
type Config struct {
ServiceName string
Version string
Env string
EndpointGRPC string
EndpointHTTP string
Token string
}

View File

@@ -0,0 +1,30 @@
# Dependent images
GRAFANA_IMAGE=docker.hub.ipao.vip/grafana/grafana:11.4.0
JAEGERTRACING_IMAGE=docker.hub.ipao.vip/jaegertracing/all-in-one:1.64.0
OPENSEARCH_IMAGE=docker.hub.ipao.vip/opensearchproject/opensearch:2.18.0
COLLECTOR_CONTRIB_IMAGE=docker-ghcr.hub.ipao.vip/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.116.1
PROMETHEUS_IMAGE=docker-quay.hub.ipao.vip/prometheus/prometheus:v3.0.1
# OpenTelemetry Collector
HOST_FILESYSTEM=/
DOCKER_SOCK=/var/run/docker.sock
OTEL_COLLECTOR_HOST=otel-collector
OTEL_COLLECTOR_PORT_GRPC=4317
OTEL_COLLECTOR_PORT_HTTP=4318
OTEL_COLLECTOR_CONFIG=./otel-collector/otelcol-config.yml
OTEL_COLLECTOR_CONFIG_EXTRAS=./otel-collector/otelcol-config-extras.yml
OTEL_EXPORTER_OTLP_ENDPOINT=http://${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT_GRPC}
PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:8080/otlp-http/v1/traces
# Grafana
GRAFANA_SERVICE_PORT=3000
GRAFANA_SERVICE_HOST=grafana
# Jaeger
JAEGER_SERVICE_PORT=16686
JAEGER_SERVICE_HOST=jaeger
# Prometheus
PROMETHEUS_SERVICE_PORT=9090
PROMETHEUS_SERVICE_HOST=prometheus
PROMETHEUS_ADDR=${PROMETHEUS_SERVICE_HOST}:${PROMETHEUS_SERVICE_PORT}

View File

@@ -0,0 +1,153 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
x-default-logging: &logging
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
tag: "{{.Name}}"
networks:
default:
name: opentelemetry-demo
driver: bridge
services:
# ********************
# Telemetry Components
# ********************
# Jaeger
jaeger:
image: ${JAEGERTRACING_IMAGE}
container_name: jaeger
command:
- "--memory.max-traces=5000"
- "--query.base-path=/jaeger/ui"
- "--prometheus.server-url=http://${PROMETHEUS_ADDR}"
- "--prometheus.query.normalize-calls=true"
- "--prometheus.query.normalize-duration=true"
deploy:
resources:
limits:
memory: 400M
restart: unless-stopped
ports:
- "${JAEGER_SERVICE_PORT}:${JAEGER_SERVICE_PORT}" # Jaeger UI
# - "${OTEL_COLLECTOR_PORT_GRPC}"
environment:
- METRICS_STORAGE_TYPE=prometheus
logging: *logging
# Grafana
grafana:
image: ${GRAFANA_IMAGE}
container_name: grafana
deploy:
resources:
limits:
memory: 100M
restart: unless-stopped
environment:
- "GF_INSTALL_PLUGINS=grafana-opensearch-datasource"
volumes:
- ./grafana/grafana.ini:/etc/grafana/grafana.ini
- ./grafana/provisioning/:/etc/grafana/provisioning/
ports:
- "${GRAFANA_SERVICE_PORT}:${GRAFANA_SERVICE_PORT}"
logging: *logging
# OpenTelemetry Collector
otel-collector:
image: ${COLLECTOR_CONTRIB_IMAGE}
container_name: otel-collector
deploy:
resources:
limits:
memory: 200M
restart: unless-stopped
command:
[
"--config=/etc/otelcol-config.yml",
"--config=/etc/otelcol-config-extras.yml",
]
user: 0:0
volumes:
- ${HOST_FILESYSTEM}:/hostfs:ro
- ${DOCKER_SOCK}:/var/run/docker.sock:ro
- ${OTEL_COLLECTOR_CONFIG}:/etc/otelcol-config.yml
- ${OTEL_COLLECTOR_CONFIG_EXTRAS}:/etc/otelcol-config-extras.yml
ports:
- "${OTEL_COLLECTOR_PORT_GRPC}:${OTEL_COLLECTOR_PORT_GRPC}"
- "${OTEL_COLLECTOR_PORT_HTTP}:${OTEL_COLLECTOR_PORT_HTTP}"
depends_on:
jaeger:
condition: service_started
opensearch:
condition: service_healthy
logging: *logging
environment:
- ENVOY_PORT
- HOST_FILESYSTEM
- OTEL_COLLECTOR_HOST
- OTEL_COLLECTOR_PORT_GRPC
- OTEL_COLLECTOR_PORT_HTTP
# Prometheus
prometheus:
image: ${PROMETHEUS_IMAGE}
container_name: prometheus
command:
- --web.console.templates=/etc/prometheus/consoles
- --web.console.libraries=/etc/prometheus/console_libraries
- --storage.tsdb.retention.time=1h
- --config.file=/etc/prometheus/prometheus-config.yaml
- --storage.tsdb.path=/prometheus
- --web.enable-lifecycle
- --web.route-prefix=/
- --web.enable-otlp-receiver
- --enable-feature=exemplar-storage
volumes:
- ./prometheus/prometheus-config.yaml:/etc/prometheus/prometheus-config.yaml
deploy:
resources:
limits:
memory: 300M
restart: unless-stopped
ports:
- "${PROMETHEUS_SERVICE_PORT}:${PROMETHEUS_SERVICE_PORT}"
logging: *logging
# OpenSearch
opensearch:
image: ${OPENSEARCH_IMAGE}
container_name: opensearch
deploy:
resources:
limits:
memory: 1G
restart: unless-stopped
environment:
- cluster.name=demo-cluster
- node.name=demo-node
- bootstrap.memory_lock=true
- discovery.type=single-node
- OPENSEARCH_JAVA_OPTS=-Xms300m -Xmx300m
- DISABLE_INSTALL_DEMO_CONFIG=true
- DISABLE_SECURITY_PLUGIN=true
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
ports:
- "9200:9200"
healthcheck:
test: curl -s http://localhost:9200/_cluster/health | grep -E '"status":"(green|yellow)"'
start_period: 10s
interval: 5s
timeout: 10s
retries: 10
logging: *logging

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
apiVersion: 1
providers:
- name: 'OpenTelemetry Demo'
orgId: 1
folder: 'Demo'
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards/demo

View File

@@ -0,0 +1,435 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 5,
"links": [],
"panels": [
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 4,
"panels": [],
"title": "GetCart Exemplars",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 10
},
"id": 5,
"interval": "2m",
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.3.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": true,
"expr": "histogram_quantile(0.95, sum by(le) (rate(app_cart_get_cart_latency_bucket[$__rate_interval])))",
"fullMetaSearch": false,
"includeNullMetadata": false,
"legendFormat": "p95 GetCart",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "95th Pct Cart GetCart Latency with Exemplars",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"fieldConfig": {
"defaults": {
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"scaleDistribution": {
"type": "linear"
}
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 1
},
"id": 2,
"interval": "2m",
"options": {
"calculate": false,
"cellGap": 1,
"color": {
"exponent": 0.5,
"fill": "dark-orange",
"mode": "scheme",
"reverse": false,
"scale": "exponential",
"scheme": "Spectral",
"steps": 64
},
"exemplars": {
"color": "rgba(255,0,255,0.7)"
},
"filterValues": {
"le": 1e-9
},
"legend": {
"show": true
},
"rowsFrame": {
"layout": "auto"
},
"tooltip": {
"mode": "single",
"showColorScale": false,
"yHistogram": false
},
"yAxis": {
"axisPlacement": "left",
"reverse": false
}
},
"pluginVersion": "11.3.0",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": true,
"expr": "sum by(le) (rate(app_cart_get_cart_latency_bucket[$__rate_interval]))",
"format": "heatmap",
"fullMetaSearch": false,
"includeNullMetadata": false,
"instant": true,
"legendFormat": "{{le}}",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "GetCart Latency Heatmap with Exemplars",
"type": "heatmap"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 20
},
"id": 3,
"panels": [],
"title": "AddItem Exemplars",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"fieldConfig": {
"defaults": {
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"scaleDistribution": {
"type": "linear"
}
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 21
},
"id": 6,
"interval": "2m",
"options": {
"calculate": false,
"cellGap": 1,
"color": {
"exponent": 0.5,
"fill": "dark-orange",
"mode": "scheme",
"reverse": false,
"scale": "exponential",
"scheme": "Spectral",
"steps": 64
},
"exemplars": {
"color": "rgba(255,0,255,0.7)"
},
"filterValues": {
"le": 1e-9
},
"legend": {
"show": true
},
"rowsFrame": {
"layout": "auto"
},
"tooltip": {
"mode": "single",
"showColorScale": false,
"yHistogram": false
},
"yAxis": {
"axisPlacement": "left",
"reverse": false
}
},
"pluginVersion": "11.3.0",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": true,
"expr": "sum by(le) (rate(app_cart_add_item_latency_bucket[$__rate_interval]))",
"format": "heatmap",
"fullMetaSearch": false,
"includeNullMetadata": false,
"instant": true,
"legendFormat": "{{le}}",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "AddItem Latency Heatmap with Exemplars",
"type": "heatmap"
},
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 30
},
"id": 1,
"interval": "2m",
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.3.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "webstore-metrics"
},
"disableTextWrap": false,
"editorMode": "builder",
"exemplar": true,
"expr": "histogram_quantile(0.95, sum by(le) (rate(app_cart_add_item_latency_bucket[$__rate_interval])))",
"fullMetaSearch": false,
"includeNullMetadata": false,
"legendFormat": "p95 AddItem",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "95th Pct Cart AddItem Latency with Exemplars",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 40,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Cart Service Exemplars",
"uid": "ce6sd46kfkglca",
"version": 1,
"weekStart": ""
}

View File

@@ -0,0 +1,21 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
apiVersion: 1
datasources:
- name: Prometheus
uid: webstore-metrics
type: prometheus
url: http://prometheus:9090
editable: true
isDefault: true
jsonData:
exemplarTraceIdDestinations:
- datasourceUid: webstore-traces
name: trace_id
- url: http://localhost:8080/jaeger/ui/trace/$${__value.raw}
name: trace_id
urlDisplayLabel: View in Jaeger UI

View File

@@ -0,0 +1,13 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
apiVersion: 1
datasources:
- name: Jaeger
uid: webstore-traces
type: jaeger
url: http://jaeger:16686/jaeger/ui
editable: true
isDefault: false

View File

@@ -0,0 +1,20 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
apiVersion: 1
datasources:
- name: OpenSearch
type: grafana-opensearch-datasource
url: http://opensearch:9200/
access: proxy
editable: true
isDefault: false
jsonData:
database: otel
flavor: opensearch
logLevelField: severity
logMessageField: body
pplEnabled: true
timeField: observedTimestamp
version: 2.18.0

View File

@@ -0,0 +1,18 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
# extra settings to be merged into OpenTelemetry Collector configuration
# do not delete this file
## Example configuration for sending data to your own OTLP HTTP backend
## Note: the spanmetrics exporter must be included in the exporters array
## if overriding the traces pipeline.
##
# exporters:
# otlphttp/example:
# endpoint: <your-endpoint-url>
#
# service:
# pipelines:
# traces:
# exporters: [spanmetrics, otlphttp/example]

View File

@@ -0,0 +1,128 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:OTEL_COLLECTOR_HOST}:${env:OTEL_COLLECTOR_PORT_GRPC}
http:
endpoint: ${env:OTEL_COLLECTOR_HOST}:${env:OTEL_COLLECTOR_PORT_HTTP}
cors:
allowed_origins:
- "http://*"
- "https://*"
docker_stats:
endpoint: unix:///var/run/docker.sock
# Host metrics
hostmetrics:
root_path: /hostfs
scrapers:
cpu:
metrics:
system.cpu.utilization:
enabled: true
disk:
load:
filesystem:
exclude_mount_points:
mount_points:
- /dev/*
- /proc/*
- /sys/*
- /run/k3s/containerd/*
- /var/lib/docker/*
- /var/lib/kubelet/*
- /snap/*
match_type: regexp
exclude_fs_types:
fs_types:
- autofs
- binfmt_misc
- bpf
- cgroup2
- configfs
- debugfs
- devpts
- devtmpfs
- fusectl
- hugetlbfs
- iso9660
- mqueue
- nsfs
- overlay
- proc
- procfs
- pstore
- rpc_pipefs
- securityfs
- selinuxfs
- squashfs
- sysfs
- tracefs
match_type: strict
memory:
metrics:
system.memory.utilization:
enabled: true
network:
paging:
processes:
process:
mute_process_exe_error: true
mute_process_io_error: true
mute_process_user_error: true
# Collector metrics
prometheus:
config:
scrape_configs:
- job_name: "otel-collector"
scrape_interval: 10s
static_configs:
- targets: ["0.0.0.0:8888"]
exporters:
debug:
otlp:
endpoint: "jaeger:4317"
tls:
insecure: true
otlphttp/prometheus:
endpoint: "http://prometheus:9090/api/v1/otlp"
tls:
insecure: true
opensearch:
logs_index: otel
http:
endpoint: "http://opensearch:9200"
tls:
insecure: true
processors:
batch:
transform:
error_mode: ignore
trace_statements:
- context: span
statements:
# could be removed when https://github.com/vercel/next.js/pull/64852 is fixed upstream
- replace_pattern(name, "\\?.*", "")
- replace_match(name, "GET /api/products/*", "GET /api/products/{productId}")
connectors:
spanmetrics:
service:
pipelines:
traces:
receivers: [otlp]
processors: [transform, batch]
exporters: [otlp, debug, spanmetrics]
metrics:
receivers: [hostmetrics, docker_stats, otlp, prometheus, spanmetrics]
processors: [batch]
exporters: [otlphttp/prometheus, debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [opensearch, debug]

View File

@@ -0,0 +1,27 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
global:
scrape_interval: 5s
scrape_timeout: 3s
evaluation_interval: 30s
otlp:
promote_resource_attributes:
- service.instance.id
- service.name
- service.namespace
- cloud.availability_zone
- cloud.region
- container.name
- deployment.environment.name
scrape_configs:
- job_name: otel-collector
static_configs:
- targets:
- 'otel-collector:8888'
storage:
tsdb:
out_of_order_time_window: 30m

View File

@@ -0,0 +1,120 @@
package otel
import (
"context"
"os"
"time"
"git.ipao.vip/rogeecn/atom/container"
"git.ipao.vip/rogeecn/atom/utils/opt"
"github.com/pkg/errors"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"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.15.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/encoding/gzip"
)
func Provide(opts ...opt.Option) error {
o := opt.New(opts...)
var config Config
if err := o.UnmarshalConfig(&config); err != nil {
return err
}
return container.Container.Provide(func(ctx context.Context) (*OTEL, error) {
o := &OTEL{
Tracer: otel.Tracer(config.ServiceName),
}
var err error
o.Resource, err = initResource(ctx, &config)
if err != nil {
return o, errors.Wrapf(err, "Failed to create OpenTelemetry resource")
}
o.Exporter, o.SpanProcessor, err = initGrpcExporterAndSpanProcessor(ctx, &config)
if err != nil {
return o, errors.Wrapf(err, "Failed to create OpenTelemetry trace exporter")
}
traceProvider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(o.Resource),
sdktrace.WithSpanProcessor(o.SpanProcessor),
)
otel.SetTracerProvider(traceProvider)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
container.AddCloseAble(func() {
cxt, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
if err := o.Exporter.Shutdown(cxt); err != nil {
otel.Handle(err)
}
})
return o, nil
}, o.DiOptions()...)
}
type OTEL struct {
Tracer trace.Tracer
Resource *resource.Resource
Exporter *otlptrace.Exporter
SpanProcessor sdktrace.SpanProcessor
}
func initResource(ctx context.Context, conf *Config) (*resource.Resource, error) {
hostName, _ := os.Hostname()
r, err := resource.New(
ctx,
resource.WithFromEnv(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithAttributes(
semconv.ServiceNameKey.String(conf.ServiceName), //
semconv.ServiceVersionKey.String(conf.Version), //
semconv.DeploymentEnvironmentKey.String(conf.Env), //
semconv.HostNameKey.String(hostName), //
),
)
if err != nil {
return nil, err
}
return r, nil
}
func initGrpcExporterAndSpanProcessor(ctx context.Context, conf *Config) (*otlptrace.Exporter, sdktrace.SpanProcessor, error) {
opts := []otlptracegrpc.Option{
otlptracegrpc.WithCompressor(gzip.Name),
}
if conf.Token != "" {
headers := map[string]string{"Authentication": conf.Token}
opts = append(opts, otlptracegrpc.WithHeaders(headers))
}
if conf.EndpointGRPC != "" {
opts = append(opts, otlptracegrpc.WithEndpoint(conf.EndpointGRPC))
}
traceExporter, err := otlptrace.New(ctx, otlptracegrpc.NewClient(opts...))
if err != nil {
return nil, nil, err
}
batchSpanProcessor := sdktrace.NewBatchSpanProcessor(traceExporter)
return traceExporter, batchSpanProcessor, nil
}