c738542201
This PR adds opentelemetry and chi wrapper to have basic instrumentation <!--start release-notes-assistant--> ## Draft release notes <!--URL:https://codeberg.org/forgejo/forgejo--> - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/3972): <!--number 3972 --><!--line 0 --><!--description YWRkIHN1cHBvcnQgZm9yIGJhc2ljIHJlcXVlc3QgdHJhY2luZyB3aXRoIG9wZW50ZWxlbWV0cnk=-->add support for basic request tracing with opentelemetry<!--description--> <!--end release-notes-assistant--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3972 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: TheFox0x7 <thefox0x7@gmail.com> Co-committed-by: TheFox0x7 <thefox0x7@gmail.com>
239 lines
6.6 KiB
Go
239 lines
6.6 KiB
Go
// Copyright 2024 TheFox0x7. All rights reserved.
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package setting
|
|
|
|
import (
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/test"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
)
|
|
|
|
func TestExporterLoad(t *testing.T) {
|
|
globalSetting := `
|
|
[opentelemetry.exporter.otlp]
|
|
ENDPOINT=http://example.org:4318/
|
|
CERTIFICATE=/boo/bar
|
|
CLIENT_CERTIFICATE=/foo/bar
|
|
CLIENT_KEY=/bar/bar
|
|
COMPRESSION=
|
|
HEADERS=key=val,val=key
|
|
PROTOCOL=http/protobuf
|
|
TIMEOUT=20s
|
|
`
|
|
endpoint, err := url.Parse("http://example.org:4318/")
|
|
require.NoError(t, err)
|
|
expected := &OtelExporter{
|
|
Endpoint: endpoint,
|
|
Certificate: "/boo/bar",
|
|
ClientCertificate: "/foo/bar",
|
|
ClientKey: "/bar/bar",
|
|
Headers: map[string]string{
|
|
"key": "val", "val": "key",
|
|
},
|
|
Timeout: 20 * time.Second,
|
|
Protocol: "http/protobuf",
|
|
}
|
|
cfg, err := NewConfigProviderFromData(globalSetting)
|
|
require.NoError(t, err)
|
|
exp := createOtlpExporterConfig(cfg, ".traces")
|
|
assert.Equal(t, expected, exp)
|
|
localSetting := `
|
|
[opentelemetry.exporter.otlp.traces]
|
|
ENDPOINT=http://example.com:4318/
|
|
CERTIFICATE=/boo
|
|
CLIENT_CERTIFICATE=/foo
|
|
CLIENT_KEY=/bar
|
|
COMPRESSION=gzip
|
|
HEADERS=key=val2,val1=key
|
|
PROTOCOL=grpc
|
|
TIMEOUT=5s
|
|
`
|
|
endpoint, err = url.Parse("http://example.com:4318/")
|
|
require.NoError(t, err)
|
|
expected = &OtelExporter{
|
|
Endpoint: endpoint,
|
|
Certificate: "/boo",
|
|
ClientCertificate: "/foo",
|
|
ClientKey: "/bar",
|
|
Compression: "gzip",
|
|
Headers: map[string]string{
|
|
"key": "val2", "val1": "key", "val": "key",
|
|
},
|
|
Timeout: 5 * time.Second,
|
|
Protocol: "grpc",
|
|
}
|
|
|
|
cfg, err = NewConfigProviderFromData(globalSetting + localSetting)
|
|
require.NoError(t, err)
|
|
exp = createOtlpExporterConfig(cfg, ".traces")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, expected, exp)
|
|
}
|
|
|
|
func TestOpenTelemetryConfiguration(t *testing.T) {
|
|
defer test.MockProtect(&OpenTelemetry)()
|
|
iniStr := ``
|
|
cfg, err := NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
assert.Nil(t, OpenTelemetry.OtelTraces)
|
|
assert.False(t, IsOpenTelemetryEnabled())
|
|
|
|
iniStr = `
|
|
[opentelemetry]
|
|
ENABLED=true
|
|
SERVICE_NAME = test service
|
|
RESOURCE_ATTRIBUTES = foo=bar
|
|
TRACES_SAMPLER = always_on
|
|
|
|
[opentelemetry.exporter.otlp]
|
|
ENDPOINT = http://jaeger:4317/
|
|
TIMEOUT = 30s
|
|
COMPRESSION = gzip
|
|
INSECURE = TRUE
|
|
HEADERS=foo=bar,overwrite=false
|
|
`
|
|
cfg, err = NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
|
|
assert.True(t, IsOpenTelemetryEnabled())
|
|
assert.Equal(t, "test service", OpenTelemetry.ServiceName)
|
|
assert.Equal(t, "foo=bar", OpenTelemetry.ResourceAttributes)
|
|
assert.Equal(t, 30*time.Second, OpenTelemetry.OtelTraces.Timeout)
|
|
assert.Equal(t, "gzip", OpenTelemetry.OtelTraces.Compression)
|
|
assert.Equal(t, sdktrace.AlwaysSample(), OpenTelemetry.Sampler)
|
|
assert.Equal(t, "http://jaeger:4317/", OpenTelemetry.OtelTraces.Endpoint.String())
|
|
assert.Contains(t, OpenTelemetry.OtelTraces.Headers, "foo")
|
|
assert.Equal(t, "bar", OpenTelemetry.OtelTraces.Headers["foo"])
|
|
assert.Contains(t, OpenTelemetry.OtelTraces.Headers, "overwrite")
|
|
assert.Equal(t, "false", OpenTelemetry.OtelTraces.Headers["overwrite"])
|
|
}
|
|
|
|
func TestOpenTelemetryTraceDisable(t *testing.T) {
|
|
defer test.MockProtect(&OpenTelemetry)()
|
|
iniStr := ``
|
|
cfg, err := NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
assert.False(t, OpenTelemetry.Enabled)
|
|
assert.False(t, IsOpenTelemetryEnabled())
|
|
|
|
iniStr = `
|
|
[opentelemetry]
|
|
ENABLED=true
|
|
EXPORTER_OTLP_ENDPOINT =
|
|
`
|
|
cfg, err = NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
|
|
assert.True(t, IsOpenTelemetryEnabled())
|
|
endpoint, _ := url.Parse("http://localhost:4318/")
|
|
assert.Equal(t, endpoint, OpenTelemetry.OtelTraces.Endpoint)
|
|
}
|
|
|
|
func TestSamplerCombinations(t *testing.T) {
|
|
defer test.MockProtect(&OpenTelemetry)()
|
|
type config struct {
|
|
IniCfg string
|
|
Expected sdktrace.Sampler
|
|
}
|
|
testSamplers := []config{
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = always_on
|
|
TRACES_SAMPLER_ARG = nothing`, sdktrace.AlwaysSample()},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = always_off`, sdktrace.NeverSample()},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = traceidratio
|
|
TRACES_SAMPLER_ARG = 0.7`, sdktrace.TraceIDRatioBased(0.7)},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = traceidratio
|
|
TRACES_SAMPLER_ARG = badarg`, sdktrace.TraceIDRatioBased(1)},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = parentbased_always_off`, sdktrace.ParentBased(sdktrace.NeverSample())},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = parentbased_always_of`, sdktrace.ParentBased(sdktrace.AlwaysSample())},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = parentbased_traceidratio
|
|
TRACES_SAMPLER_ARG = 0.3`, sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.3))},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = parentbased_traceidratio
|
|
TRACES_SAMPLER_ARG = badarg`, sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1))},
|
|
{`[opentelemetry]
|
|
ENABLED=true
|
|
TRACES_SAMPLER = not existing
|
|
TRACES_SAMPLER_ARG = badarg`, sdktrace.ParentBased(sdktrace.AlwaysSample())},
|
|
}
|
|
|
|
for _, sampler := range testSamplers {
|
|
cfg, err := NewConfigProviderFromData(sampler.IniCfg)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
assert.Equal(t, sampler.Expected, OpenTelemetry.Sampler)
|
|
}
|
|
}
|
|
|
|
func TestOpentelemetryBadConfigs(t *testing.T) {
|
|
defer test.MockProtect(&OpenTelemetry)()
|
|
iniStr := `
|
|
[opentelemetry]
|
|
ENABLED=true
|
|
|
|
[opentelemetry.exporter.otlp]
|
|
ENDPOINT = jaeger:4317/
|
|
`
|
|
cfg, err := NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
|
|
assert.True(t, IsOpenTelemetryEnabled())
|
|
assert.Equal(t, "jaeger:4317/", OpenTelemetry.OtelTraces.Endpoint.String())
|
|
|
|
iniStr = ``
|
|
cfg, err = NewConfigProviderFromData(iniStr)
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
assert.False(t, IsOpenTelemetryEnabled())
|
|
|
|
iniStr = `
|
|
[opentelemetry]
|
|
ENABLED=true
|
|
SERVICE_NAME =
|
|
TRACES_SAMPLER = not existing one
|
|
[opentelemetry.exporter.otlp]
|
|
ENDPOINT = http://jaeger:4317/
|
|
|
|
TIMEOUT = abc
|
|
COMPRESSION = foo
|
|
HEADERS=%s=bar,foo=%h,foo
|
|
|
|
`
|
|
|
|
cfg, err = NewConfigProviderFromData(iniStr)
|
|
|
|
require.NoError(t, err)
|
|
loadOpenTelemetryFrom(cfg)
|
|
assert.True(t, IsOpenTelemetryEnabled())
|
|
assert.Equal(t, "forgejo", OpenTelemetry.ServiceName)
|
|
assert.Equal(t, 10*time.Second, OpenTelemetry.OtelTraces.Timeout)
|
|
assert.Equal(t, sdktrace.ParentBased(sdktrace.AlwaysSample()), OpenTelemetry.Sampler)
|
|
assert.Equal(t, "http://jaeger:4317/", OpenTelemetry.OtelTraces.Endpoint.String())
|
|
assert.Empty(t, OpenTelemetry.OtelTraces.Headers)
|
|
}
|