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 ability to auto-instrument with the OTel Java SDK #1704

Merged
merged 23 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ COPY Makefile Makefile
COPY LICENSE LICENSE
COPY NOTICE NOTICE
COPY third_party_licenses.csv third_party_licenses.csv
COPY javaotel/opentelemetry-javaagent.jar opentelemetry-javaagent.jar

# Build
RUN make compile
Expand All @@ -39,6 +40,7 @@ COPY --from=builder /opt/app-root/bin/beyla .
COPY --from=builder /opt/app-root/LICENSE .
COPY --from=builder /opt/app-root/NOTICE .
COPY --from=builder /opt/app-root/third_party_licenses.csv .
COPY --from=builder /opt/app-root/opentelemetry-javaagent.jar .
Copy link
Contributor

Choose a reason for hiding this comment

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

As something to improve in the future, we should find a way track the javaagent version and include it in our vulnerability scans.


COPY --from=builder /etc/ssl/certs /etc/ssl/certs

Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ artifact: compile
cp LICENSE ./bin
cp NOTICE ./bin
cp third_party_licenses.csv ./bin
tar -C ./bin -cvzf bin/beyla.tar.gz beyla LICENSE NOTICE third_party_licenses.csv
cp javaotel/opentelemetry-javaagent.jar ./bin
tar -C ./bin -cvzf bin/beyla.tar.gz beyla LICENSE NOTICE third_party_licenses.csv opentelemetry-javaagent.jar

.PHONY: clean-testoutput
clean-testoutput:
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/grafana/beyla/v2

go 1.23.0
go 1.23.2

require (
github.com/AlessandroPomponio/go-gibberish v0.0.0-20191004143433-a2d4156f0396
Expand All @@ -17,6 +17,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/grafana/go-offsets-tracker v0.1.7
github.com/grafana/jvmtools v0.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/go-offsets-tracker v0.1.7 h1:2zBQ7iiGzvyXY7LA8kaaSiEqH/Yx82UcfRabbY5aOG4=
github.com/grafana/go-offsets-tracker v0.1.7/go.mod h1:qcQdu7zlUKIFNUdBJlLyNHuJGW0SKWKjkrN6jtt+jds=
github.com/grafana/jvmtools v0.0.3 h1:4uPquep5v+54Z04OQYOCldrv2SR42IRagHQXrNHsc4g=
github.com/grafana/jvmtools v0.0.3/go.mod h1:b0tiPI5zNyIuPUz2DwvxUCI+5bFoG7A4i47F9sc8w98=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
Expand Down
Binary file added javaotel/opentelemetry-javaagent.jar
Binary file not shown.
3 changes: 3 additions & 0 deletions pkg/config/ebpf_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ type EBPFTracer struct {

// Enables debug printing of the protocol data
ProtocolDebug bool `yaml:"protocol_debug_print" env:"BEYLA_PROTOCOL_DEBUG_PRINT"`

// Enables Java instrumentation with the OpenTelemetry JDK Agent
UseOTelSDKForJava bool `yaml:"use_otel_sdk_for_java" env:"BEYLA_USE_OTEL_SDK_FOR_JAVA"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Reading the code above, I assume that enabling this would only generate OTEL metrics or traces. I think we should validate that if Prometheus is enabled this option can't be enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this happens right now:
https://github.com/grafana/beyla/pull/1704/files#diff-d6000537195fb5cd7aa9e0b6bea705c332ce53d5db9e070e39dffcdc191562e3R65

With prometheus scrape Metrics will be false, so we'll program it to send traces only.

}
22 changes: 11 additions & 11 deletions pkg/export/otel/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const (
envProtocol = "OTEL_EXPORTER_OTLP_PROTOCOL"
envHeaders = "OTEL_EXPORTER_OTLP_HEADERS"
envTracesHeaders = "OTEL_EXPORTER_OTLP_TRACES_HEADERS"
envMetricsHeaders = "OTEL_EXPORTER_OTLP_METRICS_HEADERS"
envResourceAttrs = "OTEL_RESOURCE_ATTRIBUTES"
)

Expand Down Expand Up @@ -229,8 +230,7 @@ type otlpOptions struct {
BaseURLPath string
URLPath string
SkipTLSVerify bool
HTTPHeaders map[string]string
GRPCHeaders map[string]string
Headers map[string]string
}

func (o *otlpOptions) AsMetricHTTP() []otlpmetrichttp.Option {
Expand All @@ -246,8 +246,8 @@ func (o *otlpOptions) AsMetricHTTP() []otlpmetrichttp.Option {
if o.SkipTLSVerify {
opts = append(opts, otlpmetrichttp.WithTLSClientConfig(&tls.Config{InsecureSkipVerify: true}))
}
if len(o.HTTPHeaders) > 0 {
opts = append(opts, otlpmetrichttp.WithHeaders(o.HTTPHeaders))
if len(o.Headers) > 0 {
opts = append(opts, otlpmetrichttp.WithHeaders(o.Headers))
}
return opts
}
Expand All @@ -262,8 +262,8 @@ func (o *otlpOptions) AsMetricGRPC() []otlpmetricgrpc.Option {
if o.SkipTLSVerify {
opts = append(opts, otlpmetricgrpc.WithTLSCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
}
if len(o.GRPCHeaders) > 0 {
opts = append(opts, otlpmetricgrpc.WithHeaders(o.GRPCHeaders))
if len(o.Headers) > 0 {
opts = append(opts, otlpmetricgrpc.WithHeaders(o.Headers))
}
return opts
}
Expand All @@ -281,8 +281,8 @@ func (o *otlpOptions) AsTraceHTTP() []otlptracehttp.Option {
if o.SkipTLSVerify {
opts = append(opts, otlptracehttp.WithTLSClientConfig(&tls.Config{InsecureSkipVerify: true}))
}
if len(o.HTTPHeaders) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(o.HTTPHeaders))
if len(o.Headers) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(o.Headers))
}
return opts
}
Expand All @@ -297,8 +297,8 @@ func (o *otlpOptions) AsTraceGRPC() []otlptracegrpc.Option {
if o.SkipTLSVerify {
opts = append(opts, otlptracegrpc.WithTLSCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
}
if len(o.GRPCHeaders) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(o.GRPCHeaders))
if len(o.Headers) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(o.Headers))
}
return opts
}
Expand Down Expand Up @@ -359,7 +359,7 @@ func (l *LogrAdaptor) WithName(name string) logr.LogSink {
return &LogrAdaptor{inner: l.inner.With("name", name)}
}

func headersFromEnv(varName string) map[string]string {
func HeadersFromEnv(varName string) map[string]string {
headers := map[string]string{}

addToMap := func(k string, v string) {
Expand Down
16 changes: 8 additions & 8 deletions pkg/export/otel/grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,18 @@ func (cfg *GrafanaOTLP) AuthHeader() string {
return "Basic " + base64.StdEncoding.EncodeToString([]byte(cfg.InstanceID+":"+cfg.APIKey))
}

func (cfg *GrafanaOTLP) HasAuth() bool {
return cfg.InstanceID != "" && cfg.APIKey != ""
}

func (cfg *GrafanaOTLP) setupOptions(opt *otlpOptions) {
if cfg == nil {
return
}
if cfg.InstanceID != "" && cfg.APIKey != "" {
if opt.HTTPHeaders == nil {
opt.HTTPHeaders = map[string]string{}
}
opt.HTTPHeaders["Authorization"] = cfg.AuthHeader()
if opt.GRPCHeaders == nil {
opt.GRPCHeaders = map[string]string{}
if cfg.HasAuth() {
if opt.Headers == nil {
opt.Headers = map[string]string{}
}
opt.GRPCHeaders["Authorization"] = cfg.AuthHeader()
opt.Headers["Authorization"] = cfg.AuthHeader()
}
}
38 changes: 27 additions & 11 deletions pkg/export/otel/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log/slog"
"maps"
"net/url"
"os"
"slices"
Expand Down Expand Up @@ -144,6 +145,22 @@ func (m *MetricsConfig) GuessProtocol() Protocol {
return ProtocolHTTPProtobuf
}

func (m *MetricsConfig) OTLPMetricsEndpoint() (string, bool) {
isCommon := false
endpoint := m.MetricsEndpoint
if endpoint == "" {
endpoint = m.CommonEndpoint
if endpoint == "" && m.Grafana != nil && m.Grafana.CloudZone != "" {
endpoint = m.Grafana.Endpoint()
}
if endpoint != "" {
isCommon = true
}
}

return endpoint, isCommon
}

// EndpointEnabled specifies that the OTEL metrics node is enabled if and only if
// either the OTEL endpoint and OTEL metrics endpoint is defined.
// If not enabled, this node won't be instantiated
Expand Down Expand Up @@ -963,7 +980,7 @@ func (mr *MetricsReporter) reportMetrics(input <-chan []request.Span) {
}

func getHTTPMetricEndpointOptions(cfg *MetricsConfig) (otlpOptions, error) {
opts := otlpOptions{}
opts := otlpOptions{Headers: map[string]string{}}
log := mlog().With("transport", "http")
murl, isCommon, err := parseMetricsEndpoint(cfg)
if err != nil {
Expand Down Expand Up @@ -996,12 +1013,14 @@ func getHTTPMetricEndpointOptions(cfg *MetricsConfig) (otlpOptions, error) {
}

cfg.Grafana.setupOptions(&opts)
maps.Copy(opts.Headers, HeadersFromEnv(envHeaders))
maps.Copy(opts.Headers, HeadersFromEnv(envMetricsHeaders))

return opts, nil
}

func getGRPCMetricEndpointOptions(cfg *MetricsConfig) (otlpOptions, error) {
opts := otlpOptions{}
opts := otlpOptions{Headers: map[string]string{}}
log := mlog().With("transport", "grpc")
murl, _, err := parseMetricsEndpoint(cfg)
if err != nil {
Expand All @@ -1020,6 +1039,11 @@ func getGRPCMetricEndpointOptions(cfg *MetricsConfig) (otlpOptions, error) {
log.Debug("Setting InsecureSkipVerify")
opts.SkipTLSVerify = true
}

cfg.Grafana.setupOptions(&opts)
maps.Copy(opts.Headers, HeadersFromEnv(envHeaders))
maps.Copy(opts.Headers, HeadersFromEnv(envMetricsHeaders))

return opts, nil
}

Expand All @@ -1030,15 +1054,7 @@ func getGRPCMetricEndpointOptions(cfg *MetricsConfig) (otlpOptions, error) {
// If, by some reason, Grafana changes its OTLP Gateway URL in a distant future, you can still point to the
// correct URL with the OTLP_EXPORTER_... variables.
func parseMetricsEndpoint(cfg *MetricsConfig) (*url.URL, bool, error) {
isCommon := false
endpoint := cfg.MetricsEndpoint
if endpoint == "" {
isCommon = true
endpoint = cfg.CommonEndpoint
if endpoint == "" && cfg.Grafana != nil && cfg.Grafana.CloudZone != "" {
endpoint = cfg.Grafana.Endpoint()
}
}
endpoint, isCommon := cfg.OTLPMetricsEndpoint()

murl, err := url.Parse(endpoint)
if err != nil {
Expand Down
28 changes: 10 additions & 18 deletions pkg/export/otel/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestHTTPMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with two endpoints", func(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", URLPath: "/v1/metrics"}, &mcfg)
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", URLPath: "/v1/metrics", Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -49,7 +49,7 @@ func TestHTTPMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with only common endpoint", func(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3131", URLPath: "/otlp/v1/metrics"}, &mcfg)
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3131", URLPath: "/otlp/v1/metrics", Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -60,7 +60,7 @@ func TestHTTPMetricsEndpointOptions(t *testing.T) {
},
}
t.Run("testing with insecure endpoint", func(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", Insecure: true}, &mcfg)
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", Insecure: true, Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -72,7 +72,7 @@ func TestHTTPMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with skip TLS verification", func(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", URLPath: "/v1/metrics", SkipTLSVerify: true}, &mcfg)
testMetricsHTTPOptions(t, otlpOptions{Endpoint: "localhost:3232", URLPath: "/v1/metrics", SkipTLSVerify: true, Headers: map[string]string{}}, &mcfg)
})
}

Expand All @@ -89,11 +89,7 @@ func TestHTTPMetricsWithGrafanaOptions(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{
Endpoint: "otlp-gateway-eu-west-23.grafana.net",
URLPath: "/otlp/v1/metrics",
HTTPHeaders: map[string]string{
// Basic + output of: echo -n 12345:affafafaafkd | gbase64 -w 0
"Authorization": "Basic MTIzNDU6YWZmYWZhZmFhZmtk",
},
GRPCHeaders: map[string]string{
Headers: map[string]string{
// Basic + output of: echo -n 12345:affafafaafkd | gbase64 -w 0
"Authorization": "Basic MTIzNDU6YWZmYWZhZmFhZmtk",
},
Expand All @@ -104,11 +100,7 @@ func TestHTTPMetricsWithGrafanaOptions(t *testing.T) {
testMetricsHTTPOptions(t, otlpOptions{
Endpoint: "localhost:3939",
URLPath: "/v1/metrics",
HTTPHeaders: map[string]string{
// Basic + output of: echo -n 12345:affafafaafkd | gbase64 -w 0
"Authorization": "Basic MTIzNDU6YWZmYWZhZmFhZmtk",
},
GRPCHeaders: map[string]string{
Headers: map[string]string{
// Basic + output of: echo -n 12345:affafafaafkd | gbase64 -w 0
"Authorization": "Basic MTIzNDU6YWZmYWZhZmFhZmtk",
},
Expand Down Expand Up @@ -259,7 +251,7 @@ func TestGRPCMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with two endpoints", func(t *testing.T) {
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232"}, &mcfg)
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232", Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -268,7 +260,7 @@ func TestGRPCMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with only common endpoint", func(t *testing.T) {
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3131"}, &mcfg)
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3131", Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -277,7 +269,7 @@ func TestGRPCMetricsEndpointOptions(t *testing.T) {
Instrumentations: []string{instrumentations.InstrumentationHTTP},
}
t.Run("testing with insecure endpoint", func(t *testing.T) {
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232", Insecure: true}, &mcfg)
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232", Insecure: true, Headers: map[string]string{}}, &mcfg)
})

mcfg = MetricsConfig{
Expand All @@ -287,7 +279,7 @@ func TestGRPCMetricsEndpointOptions(t *testing.T) {
}

t.Run("testing with skip TLS verification", func(t *testing.T) {
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232", SkipTLSVerify: true}, &mcfg)
testMetricsGRPCOptions(t, otlpOptions{Endpoint: "localhost:3232", SkipTLSVerify: true, Headers: map[string]string{}}, &mcfg)
})
}

Expand Down
Loading
Loading