Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions collector/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ package: build

.PHONY: publish
publish:
aws lambda publish-layer-version --layer-name $(LAYER_NAME) --zip-file fileb://$(BUILD_SPACE)/opentelemetry-collector-layer-$(ARCH).zip --compatible-runtimes nodejs20.x nodejs22.x nodejs24.x java11 java17 java21 java25 python3.9 python3.10 python3.11 python3.12 python3.13 python3.14 --compatible-architectures $(ARCH) --query 'LayerVersionArn' --output text
aws lambda publish-layer-version --layer-name $(LAYER_NAME) --zip-file fileb://$(BUILD_SPACE)/opentelemetry-collector-layer-$(ARCH).zip --compatible-runtimes nodejs20.x nodejs22.x nodejs24.x java11 java17 java21 java25 python3.10 python3.11 python3.12 python3.13 python3.14 --compatible-architectures $(ARCH) --query 'LayerVersionArn' --output text

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This is acknowledged already in APM Python relnotes


.PHONY: publish-layer
publish-layer: package
@echo Publishing collector extension layer...
aws lambda publish-layer-version --layer-name $(LAYER_NAME) --zip-file fileb://$(BUILD_SPACE)/opentelemetry-collector-layer-$(ARCH).zip --compatible-runtimes nodejs20.x nodejs22.x nodejs24.x java11 java17 java21 java25 python3.9 python3.10 python3.11 python3.12 python3.13 python3.14 --compatible-architectures $(ARCH) --query 'LayerVersionArn' --output text
aws lambda publish-layer-version --layer-name $(LAYER_NAME) --zip-file fileb://$(BUILD_SPACE)/opentelemetry-collector-layer-$(ARCH).zip --compatible-runtimes nodejs20.x nodejs22.x nodejs24.x java11 java17 java21 java25 python3.10 python3.11 python3.12 python3.13 python3.14 --compatible-architectures $(ARCH) --query 'LayerVersionArn' --output text
@echo OpenTelemetry Collector layer published.

.PHONY: set-otelcol-version
Expand Down
8 changes: 0 additions & 8 deletions collector/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,3 @@ service:
receivers: [otlp,telemetryapi]
processors: [resource,resourcedetection,batch]
exporters: [otlp,debug]
telemetry:
metrics:
readers:
- pull:
exporter:
prometheus:
host: "localhost"
port: 8888
358 changes: 180 additions & 178 deletions collector/go.mod

Large diffs are not rendered by default.

844 changes: 424 additions & 420 deletions collector/go.sum

Large diffs are not rendered by default.

15 changes: 4 additions & 11 deletions collector/internal/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"go.uber.org/zap/zapcore"

"github.com/open-telemetry/opentelemetry-lambda/collector/internal/confmap/converter/disablequeuedretryconverter"
"github.com/open-telemetry/opentelemetry-lambda/collector/internal/logging"
)

// Collector runs a single otelcol as a go routine within the
Expand All @@ -45,6 +46,7 @@ type Collector struct {
stopped bool
logger *zap.Logger
version string
coreFunc func(zapcore.LevelEnabler) zapcore.Core
}

func getConfig(logger *zap.Logger) string {
Expand Down Expand Up @@ -88,6 +90,7 @@ func NewCollector(logger *zap.Logger, factories otelcol.Factories, version strin
cfgProSet: cfgSet,
logger: logger,
version: version,
coreFunc: logging.NewCore,
}
return col
}
Expand All @@ -103,18 +106,8 @@ func (c *Collector) Start(ctx context.Context) error {
Factories: func() (otelcol.Factories, error) {
return c.factories, nil
},
// TODO: fully decouple extension and collector log levels so that
// OPENTELEMETRY_EXTENSION_LOG_LEVEL only affects extension logs.
LoggingOptions: []zap.Option{zap.WrapCore(func(collectorCore zapcore.Core) zapcore.Core {
extensionCore := c.logger.Core()
if zapcore.LevelOf(collectorCore) == zapcore.InfoLevel {
return extensionCore
}
increased, err := zapcore.NewIncreaseLevelCore(extensionCore, collectorCore)
if err != nil {
return extensionCore
}
return increased
return c.coreFunc(collectorCore)
})},
}
var err error
Expand Down
85 changes: 68 additions & 17 deletions collector/internal/collector/collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,19 @@ import (
"go.uber.org/zap/zaptest/observer"
)

func TestCollectorConfigLogLevelIsOverridden(t *testing.T) {
func TestCollectorConfigLogLevelSuppressesCollectorInfoLogs(t *testing.T) {
t.Setenv("OPENTELEMETRY_COLLECTOR_CONFIG_URI", "file:testdata/config-error-level.yaml")

receivers, err := otelcol.MakeFactoryMap(receivertest.NewNopFactory())
require.NoError(t, err)
exporters, err := otelcol.MakeFactoryMap(exportertest.NewNopFactory())
require.NoError(t, err)

factories := otelcol.Factories{
Receivers: receivers,
Exporters: exporters,
Telemetry: otelconftelemetry.NewFactory(),
collectorLogs := &observer.ObservedLogs{}
collector := NewCollector(zap.NewNop(), testFactories(t), "test")
collector.coreFunc = func(levelEnabler zapcore.LevelEnabler) zapcore.Core {
var collectorObservedCore zapcore.Core
collectorObservedCore, collectorLogs = observer.New(levelEnabler)
return collectorObservedCore
}

// Use a nop logger so extension logs don't end up in our observer
collector := NewCollector(zap.NewNop(), factories, "test")
// Replace collector logger with an observed core at INFO level.
collectorObservedCore, collectorLogs := observer.New(zapcore.InfoLevel)
collector.logger = zap.New(collectorObservedCore)

ctx := context.Background()
err = collector.Start(ctx)
err := collector.Start(ctx)
require.NoError(t, err)

err = collector.Stop()
Expand All @@ -61,3 +52,63 @@ func TestCollectorConfigLogLevelIsOverridden(t *testing.T) {
assert.Empty(t, infoLogs,
"INFO logs from the collector should be suppressed when config sets level: error")
}

func TestExtensionLogLevelDoesNotSuppressCollectorLogs(t *testing.T) {
t.Setenv("OPENTELEMETRY_COLLECTOR_CONFIG_URI", "file:testdata/config-info-level.yaml")

extensionObservedCore, extensionLogs := observer.New(zapcore.ErrorLevel)
collectorLogs := &observer.ObservedLogs{}
collector := NewCollector(zap.New(extensionObservedCore), testFactories(t), "test")
collector.coreFunc = func(levelEnabler zapcore.LevelEnabler) zapcore.Core {
var collectorObservedCore zapcore.Core
collectorObservedCore, collectorLogs = observer.New(levelEnabler)
return collectorObservedCore
}

ctx := context.Background()
err := collector.Start(ctx)
require.NoError(t, err)

err = collector.Stop()
require.NoError(t, err)

assert.NotEmpty(t, collectorLogs.FilterLevelExact(zapcore.InfoLevel).All(),
"INFO logs from the collector should be emitted when collector config sets level: info")
assert.Empty(t, extensionLogs.All(), "collector logs should not be written through the extension logger core")
}

func TestCollectorLogLevelDoesNotSuppressExtensionLogs(t *testing.T) {
t.Setenv("OPENTELEMETRY_COLLECTOR_CONFIG_URI", "file:testdata/config-error-level.yaml")

extensionObservedCore, extensionLogs := observer.New(zapcore.InfoLevel)
collector := NewCollector(zap.New(extensionObservedCore), testFactories(t), "test")
collector.coreFunc = func(levelEnabler zapcore.LevelEnabler) zapcore.Core {
collectorObservedCore, _ := observer.New(levelEnabler)
return collectorObservedCore
}

ctx := context.Background()
err := collector.Start(ctx)
require.NoError(t, err)

collector.logger.Info("extension log")

err = collector.Stop()
require.NoError(t, err)

assert.Len(t, extensionLogs.FilterMessage("extension log").All(), 1,
"extension logs should be controlled by the extension logger, not collector config")
}

func testFactories(t *testing.T) otelcol.Factories {
receivers, err := otelcol.MakeFactoryMap(receivertest.NewNopFactory())
require.NoError(t, err)
exporters, err := otelcol.MakeFactoryMap(exportertest.NewNopFactory())
require.NoError(t, err)

return otelcol.Factories{
Receivers: receivers,
Exporters: exporters,
Telemetry: otelconftelemetry.NewFactory(),
}
}
14 changes: 14 additions & 0 deletions collector/internal/collector/testdata/config-info-level.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
receivers:
nop:

exporters:
nop:

service:
telemetry:
logs:
level: info
pipelines:
traces:
receivers: [nop]
exporters: [nop]
57 changes: 57 additions & 0 deletions collector/internal/logging/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package logging

import (
"os"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

const extensionLogLevelEnvVar = "OPENTELEMETRY_EXTENSION_LOG_LEVEL"

var (
encoderConfig = zap.NewProductionEncoderConfig()
stdoutSyncer = zapcore.Lock(zapcore.AddSync(os.Stdout))
)

func NewLogger() *zap.Logger {
lvl, err := parseLevel(os.Getenv(extensionLogLevelEnvVar))

l := zap.New(NewCore(lvl))

if err != nil {
l.Warn("unable to parse log level from environment, falling back to default log level", zap.Error(err), zap.Stringer("default_level", lvl))
}
return l
}

func NewCore(levelEnabler zapcore.LevelEnabler) zapcore.Core {
return zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), stdoutSyncer, levelEnabler)
}

// parseLevel resolves the extension log level from the env var value,
// falling back to INFO and returning an error if the value is invalid.
func parseLevel(envLvl string) (zap.AtomicLevel, error) {
Comment on lines +46 to +48
if envLvl == "" {
return zap.NewAtomicLevelAt(zapcore.WarnLevel), nil
}
userLvl, err := zap.ParseAtomicLevel(envLvl)
if err != nil {
return zap.NewAtomicLevelAt(zapcore.WarnLevel), err
}
return userLvl, nil
}
41 changes: 41 additions & 0 deletions collector/internal/logging/logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package logging

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
)

func TestParseLevelDefaultsToInfoWhenUnset(t *testing.T) {
lvl, err := parseLevel("")
require.NoError(t, err)
assert.Equal(t, zapcore.WarnLevel, lvl.Level())
}

func TestParseLevelAppliesValidLevel(t *testing.T) {
lvl, err := parseLevel("error")
require.NoError(t, err)
assert.Equal(t, zapcore.ErrorLevel, lvl.Level())
}

func TestParseLevelFallsBackToInfoAndErrorsOnInvalid(t *testing.T) {
lvl, err := parseLevel("not-a-level")
require.Error(t, err)
assert.Equal(t, zapcore.WarnLevel, lvl.Level(), "should fall back to INFO")
}
Comment on lines +37 to +41
20 changes: 10 additions & 10 deletions collector/internal/tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ require (
go.opentelemetry.io/build-tools/crosslink v0.30.0
go.opentelemetry.io/build-tools/multimod v0.30.0
go.opentelemetry.io/build-tools/semconvgen v0.24.0
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/tools v0.44.0
golang.org/x/exp v0.0.0-20260709172345-9ea1abe57597
golang.org/x/tools v0.48.0
)

require (
Expand Down Expand Up @@ -74,7 +74,7 @@ require (
github.com/go-critic/go-critic v0.12.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.9.0 // indirect
github.com/go-git/go-git/v5 v5.19.0 // indirect
github.com/go-git/go-git/v5 v5.19.1 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
Expand Down Expand Up @@ -215,15 +215,15 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.28.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/mod v0.38.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/telemetry v0.0.0-20260708182218-49f421fb7959 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
Loading
Loading