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

trace.NewTracerProvider logs configuration before storing spanProcessors #3568

Open
kmai opened this issue Jan 6, 2023 · 4 comments
Open
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@kmai
Copy link

kmai commented Jan 6, 2023

Description

TracerProvider creation logs incomplete configuration, leaving out defined SpanProcessor structs.

Environment

  • OS: MacOS
  • Architecture: x86_64
  • Go Version: 1.19
  • opentelemetry-go version: v1.11.2

Steps To Reproduce

  1. Configure a logr compatible logger with verbosity set higher than 5 (as per OTEL documentation, to enable debug logging).
  2. Create a TraceProvider by using go.opentelemetry.io/otel/sdk/trace.NewTracerProvider(), with a configured SpanProcessor
  3. The log emitted after the provider is created will be incorrect because it will display SpanProcessors as empty.

Expected behavior

The log message should promptly display the full configuration of the TraceProvider struct.

Comments

I believe the offending block of code is the following:

// NewTracerProvider returns a new and configured TracerProvider.
//
// By default the returned TracerProvider is configured with:
//   - a ParentBased(AlwaysSample) Sampler
//   - a random number IDGenerator
//   - the resource.Default() Resource
//   - the default SpanLimits.
//
// The passed opts are used to override these default values and configure the
// returned TracerProvider appropriately.
func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
	o := tracerProviderConfig{
		spanLimits: NewSpanLimits(),
	}
	o = applyTracerProviderEnvConfigs(o)

	for _, opt := range opts {
		o = opt.apply(o)
	}

	o = ensureValidTracerProviderConfig(o)

	tp := &TracerProvider{
		namedTracer: make(map[instrumentation.Scope]*tracer),
		sampler:     o.sampler,
		idGenerator: o.idGenerator,
		spanLimits:  o.spanLimits,
		resource:    o.resource,
	}
	global.Info("TracerProvider created", "config", o)

	spss := spanProcessorStates{}
	for _, sp := range o.processors {
		spss = append(spss, newSpanProcessorState(sp))
	}
	tp.spanProcessors.Store(spss)

	return tp
}

As you can see, tp.spanProcessors.Store(spss) is called after the log entry has been created. For that reason, the spanProcessors attribute is empty on the log message.

@kmai kmai added the bug Something isn't working label Jan 6, 2023
@MrAlias MrAlias added the help wanted Extra attention is needed label Jan 6, 2023
@PalanQu
Copy link

PalanQu commented Jan 8, 2023

May I help with this? @MrAlias

@hanyuancheung
Copy link
Member

hanyuancheung commented Jan 8, 2023

Thanks for ur advice! @kmai

I've tested the issue as you mentioned above. All of the config msg(such as spanProcessor, sampler) are stored in tracerProviderConfig, it seems like the spanProcessors wouldn't be left out in the log message.

It's also possible that I misunderstood. Could you please offer more info to point out where the problem is? Thanks.

@kmai
Copy link
Author

kmai commented Jan 9, 2023

Hi @hanyuancheung! When I start my application with the following snippet (just to try out logging when dealing with some troubleshooting):

	// Initialize Subsystems
	logRus := providers.LoggingInit()

	var logger logr.Logger

	if zapLogger, err := zap.NewDevelopment(); err != nil {
		logRus.Panic(fmt.Errorf("error while instantiating logger for OTEL: %w", err))
	} else {
		logger = zapr.NewLogger(zapLogger)
	}

	otel.SetLogger(logger)

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	tracerProvider, err := providers.TracerInit()
	if err != nil {
		logRus.Warnf("error while initializing tracing subsystem: %v", err)
	}

	defer middleware.TracingShutdownSignal(ctx, tracerProvider)

	// Register our TracerProvider as the global so any imported
	// instrumentation in the future will default to using it.
	otel.SetTracerProvider(tracerProvider)
	otel.SetTextMapPropagator(b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)))

I instantiate the TracerProvider with a Prometheus Exporter. The debug log dumps the struct configuration, but it shows the spanProcessors empty, while I should see a single one with my configured exporter:

{
  "config": {
    "SpanProcessors": [
      {}
    ],
    "SamplerType": "trace.alwaysOnSampler",
    "IDGeneratorType": "*trace.randomIDGenerator",
    "SpanLimits": {
      "AttributeValueLengthLimit": -1,
      "AttributeCountLimit": 128,
      "EventCountLimit": 128,
      "LinkCountLimit": 128,
      "AttributePerEventCountLimit": 128,
      "AttributePerLinkCountLimit": 128
    },
    "Resource": [
      {
        "Key": "deployment.environment",
        "Value": {
          "Type": "STRING",
          "Value": "development"
        }
      },
      {
        "Key": "host.name",
        "Value": {
          "Type": "STRING",
          "Value": "192-168-0-74.static.private.lan"
        }
      },
      {
        "Key": "process.command_args",
        "Value": {
          "Type": "STRINGSLICE",
          "Value": [
            "/private/var/folders/p5/n87t8_wj24q7jg6r6dqjb2hr0000gn/T/GoLand/___Build_Server"
          ]
        }
      },
      {
        "Key": "process.executable.name",
        "Value": {
          "Type": "STRING",
          "Value": "___Build_Server"
        }
      },
      {
        "Key": "process.executable.path",
        "Value": {
          "Type": "STRING",
          "Value": "/private/var/folders/p5/n87t8_wj24q7jg6r6dqjb2hr0000gn/T/GoLand/___Build_Server"
        }
      },
      {
        "Key": "process.owner",
        "Value": {
          "Type": "STRING",
          "Value": "kmai"
        }
      },
      {
        "Key": "process.pid",
        "Value": {
          "Type": "INT64",
          "Value": 42386
        }
      },
      {
        "Key": "process.runtime.description",
        "Value": {
          "Type": "STRING",
          "Value": "go version go1.19.4 darwin/arm64"
        }
      },
      {
        "Key": "process.runtime.name",
        "Value": {
          "Type": "STRING",
          "Value": "go"
        }
      },
      {
        "Key": "process.runtime.version",
        "Value": {
          "Type": "STRING",
          "Value": "go1.19.4"
        }
      },
      {
        "Key": "service.name",
        "Value": {
          "Type": "STRING",
          "Value": "example-service"
        }
      }
    ]
  }
}

I expected to see my exporter configured, but I don't! Let me know if you need the full codebase.

@MadVikingGod
Copy link
Contributor

This looks to be zapr not respecting the MarshalLog interface for slices. Here is a playground that shows how stdr does show the appropriate data, but zapr only will display {} for span processors: https://go.dev/play/p/BmKiLbhfNRY

I think the fix would be:
A) Add a work around for zapr, probably something akin to BatchSpanProcessor.MarshalLog() for the config, or for the []SpanProcessor type
B) Upstream a patch that fixes this behavior. It has been a while but I recall the first time I looked into this there is a closed issue with zapr around this behavior.
C) Add a workaround for zapr, find zap's MarshalLog equivalent and add that so it marshals correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants