Skip to content
This repository has been archived by the owner on Aug 30, 2022. It is now read-only.

Support plugin configuration via environment variables #192

Merged
merged 1 commit into from
Mar 3, 2020

Conversation

johanneswuerbach
Copy link
Contributor

Which problem is this PR solving?

Support using the environment variables added in #181 when using the plugin. E.g. as part of https://github.com/opentracing-contrib/nginx-opentracing/

Short description of the changes

The plugin entry point now tries to parse the yaml file and loads environment variables if they are defined. Once all configuration methods performed, validate that the service name is not empty.

@AppVeyorBot
Copy link

"Failed to construct tracer: Jaeger was not build with yaml support.";
return opentracing::make_unexpected(
std::make_error_code(std::errc::not_supported));
auto tracerConfig = jaegertracing::Config();
Copy link
Member

Choose a reason for hiding this comment

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

this feels unintuitive - the configuration is silently ignored in favor of default config? Maybe it should at least have a null check and still error our if not null?

Copy link
Member

Choose a reason for hiding this comment

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

also, does C++ have some standard way to document functions? This one could use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this feels unintuitive - the configuration is silently ignored in favor of default config?

I reverted now to the old behaviour of erroring out when no config is provided as it feels out of scope of this PR to change that.

does C++ have some standard way to document functions?

I haven't done C++ in a long time so I would need on your knowledge, but looking at the other files I don't see any documentation.

@AppVeyorBot
Copy link

src/jaegertracing/testutils/EnvVariable.cpp Outdated Show resolved Hide resolved
src/jaegertracing/testutils/EnvVariable.h Outdated Show resolved Hide resolved
errorMessage = "`service_name` not provided";
return opentracing::make_unexpected(
opentracing::invalid_configuration_error);
}
if (!serviceNameNode.IsScalar()) {
Copy link
Member

Choose a reason for hiding this comment

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

not sure what this was testing, why is it no longer needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was manually verifying the integrity of the yaml, which should be done by the parsing itself

@johanneswuerbach
Copy link
Contributor Author

@yurishkuro thanks for the feedback, I addressed your comments.

@AppVeyorBot
Copy link

@yurishkuro
Copy link
Member

getting build errors

In file included from /home/travis/.hunter/_Base/29f1068/a9c2d7f/74005d6/Install/include/gtest/gtest.h:1874:0,
                 from /home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp:20:
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp: In member function ‘virtual void jaegertracing::TracerFactory_testConfigAndEnvironment_Test::TestBody()’:
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp:102:27: error: ‘class opentracing::v2::expected<std::shared_ptr<opentracing::v2::Tracer> >’ has no member named ‘serviceName’
     ASSERT_EQ(tracerMaybe.serviceName(), "test")
                           ^
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp:102:5: error: template argument 1 is invalid
     ASSERT_EQ(tracerMaybe.serviceName(), "test")
     ^
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp:102:27: error: ‘class opentracing::v2::expected<std::shared_ptr<opentracing::v2::Tracer> >’ has no member named ‘serviceName’
     ASSERT_EQ(tracerMaybe.serviceName(), "test")
                           ^
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerFactoryTest.cpp:103:1: error: expected ‘;’ before ‘}’ token
 }
 ^
/home/travis/build/jaegertracing/jaeger-client-cpp/src/jaegertracing/TracerTest.cpp: In member function ‘virtual void jaegertracing::Tracer_testTracer_Test::TestBody()’:

@AppVeyorBot
Copy link

@johanneswuerbach johanneswuerbach force-pushed the plugin-from-env branch 2 times, most recently from 5663937 to ca95345 Compare December 5, 2019 22:10
@johanneswuerbach
Copy link
Contributor Author

@yurishkuro sorry my C++ knowledge is really rusty, any pointers how to properly access the returned tracer instance in the last test to get the service name?

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@johanneswuerbach johanneswuerbach force-pushed the plugin-from-env branch 2 times, most recently from 7d78e8b to 86f9fca Compare December 6, 2019 16:40
@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

ASSERT_TRUE(tracerMaybe);

auto tracer = tracerMaybe.value();
const auto jaegerTracer = std::dynamic_pointer_cast<Tracer>(tracer);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@yurishkuro any hints how I could the jaeger tracer instance here instead of the opentracing::tracer?

The dynamic pointer cast seems not to work, but I don't understand why :-(

Copy link
Member

Choose a reason for hiding this comment

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

not really, but maybe @mdouaihy could suggest something

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mdouaihy I tried:

TEST(TracerFactory, testConfigAndEnvironment)
{
    testutils::EnvVariable::setEnv("JAEGER_SERVICE_NAME", "");
    const char* config = R"(
  {
    "service_name": "test"
  })";
    testutils::EnvVariable::setEnv("JAEGER_SERVICE_NAME", "AService");
    TracerFactory tracerFactory;
    std::string errorMessage;
    auto tracerMaybe = tracerFactory.MakeTracer(config, errorMessage);
    ASSERT_EQ(errorMessage, "");
    ASSERT_TRUE(tracerMaybe);

    const auto tracer =
        std::static_pointer_cast<jaegertracing::Tracer>(tracerMaybe.value());

    ASSERT_EQ(std::string("test"), tracer->serviceName());
}

but this segfaults. Any pointers?

Copy link
Contributor

Choose a reason for hiding this comment

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

this is failing because the the returned tracer is not a Jaeger Tracer but a NoopTracer.
the reason is the environment variable JAEGER_DISABLED being set to true in ConfigTest.
I think that we should clear the set variables in those tests at the end of the test to avoid polluting other tests executions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also note that you'll have to change the configuration of the Sampler and the Reporter to avoid trying to reach a remote address that doesn't exist.

@AppVeyorBot
Copy link

@mdouaihy
Copy link
Contributor

mdouaihy commented Dec 8, 2019

Hello,

I find the change a bit unintuitive: the factory is not respecting the configuration the caller provides. Instead, it's reading a global configuration (env variable) instead of just using the configuration provided.

Any reason why not filling the configuration as need before calling the factory?

@johanneswuerbach
Copy link
Contributor Author

johanneswuerbach commented Dec 8, 2019

Most other jaeger clients are already configurable using environment variables https://www.jaegertracing.io/docs/1.15/client-features/ and we use JAEGER_AGENT_HOST inside kubernetes to inject the agent IP into each pod https://www.jaegertracing.io/docs/1.15/operator/#installing-the-agent-as-daemonset

While the cpp client itself allows to be configured via environment variables, we only use the plugin via
https://github.com/opentracing-contrib/nginx-opentracing/blob/master/README.md.

To inject now the IP we could generate the config, or change the plugin to also respect the common environment variables. I found the second option cleaner and opened this PR, but happy for any other recommendations.

@johanneswuerbach
Copy link
Contributor Author

@mdouaihy, the TracerFactory implements opentracing::TracerFactory so if I understand correctly adding an additional argument wouldn’t be easily possible.
https://github.com/opentracing/opentracing-cpp/blob/4bb431f7728eaf383a07e86f9754a5b67575dab0/include/opentracing/tracer_factory.h#L50-L51

If I understand opentracing/opentracing-cpp#45 (comment) correctly the factory seems also be mainly used for dynamic loading, so I'm not sure modifying only dynamic load would be sufficient?

@AppVeyorBot
Copy link

@mdouaihy
Copy link
Contributor

@johanneswuerbach, there is no need to change the makeTracer method. You can add a property to TracerFactory and an argument to the constructor (in DynamicLoad.cpp/makeTracerFactory)

@johanneswuerbach
Copy link
Contributor Author

@mdouaihy is something like e0fd882 what you had in mind?

It looks like environment variable now take always precedent over the file, is that desired or should I add checks to https://github.com/jaegertracing/jaeger-client-cpp/blob/master/src/jaegertracing/Config.cpp#L37 to validate they are only used when nothing is configured yet? The downside is obviously that this would change behaviour of the current, but unreleased Config::fromEnv().

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@mdouaihy
Copy link
Contributor

mdouaihy commented Jan 3, 2020

Hello @johanneswuerbach, indeed, that's what I had in mind. One small suggestion: maybe we can pass readFromEnv as a constructor parameter (and have a default value for it for backward compatibility). What do you think?

For the precedence of the environment over the config, I dont think we should change the behavior of Config::fromEnv(). Instead, we can define it in TracerFactory::MakeTracer when readFromEnv is defined.

@mdouaihy
Copy link
Contributor

mdouaihy commented Mar 2, 2020

Hi @johanneswuerbach,

do you have any news about this PR?

@AppVeyorBot
Copy link

@johanneswuerbach
Copy link
Contributor Author

Sorry for the delay @mdouaihy, I rebased this PR, but are not sure about your suggestions.

Instead, we can define it in TracerFactory::MakeTracer when readFromEnv is defined.

I'm not sure what this means? In this PR I actually prefer the environment variable values now over the config values as fromEnv already defines some kind of a deep merge. What do you think about that?

@AppVeyorBot
Copy link

src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
src/jaegertracing/TracerFactoryTest.cpp Outdated Show resolved Hide resolved
@mdouaihy
Copy link
Contributor

mdouaihy commented Mar 3, 2020

Hi @johanneswuerbach,

Concerning my point Instead, we can define it in TracerFactory::MakeTracer when readFromEnv is defined., what I mean is exactly what you did: In makeTracer, we read the config then override with what we have in the Env Variable.

@yurishkuro, are you confortable with this behavior or shall we reverse it?

Signed-off-by: Johannes Würbach <johannes.wuerbach@googlemail.com>
@johanneswuerbach
Copy link
Contributor Author

@mdouaihy thanks for the pointers, all leaks addressed.

@AppVeyorBot
Copy link

@@ -41,7 +41,8 @@ static int makeTracerFactory(const char* opentracingVersion,
return opentracing::incompatible_library_versions_error.value();
}

*tracerFactory = new (std::nothrow) jaegertracing::TracerFactory{};
const auto jaegerTracerFactory = new (std::nothrow) jaegertracing::TracerFactory(true);
Copy link
Member

Choose a reason for hiding this comment

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

I am not a fan of this API. Without looking up the definition, what does ctor(true) mean?

In the other languages we simply provide a fromEnv() initializer. Not only it is explicit, but it also decouples factory initialization and reading env, e.g. it is very common to first initialize the factory with some default configuration, and then allow it to be overwritten by env. Here it looks like env vars are read immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was originally implemented like that, but @mdouaihy recommended the ctor approach #192 (comment)

I don’t have an opinion and happily change it to your preference

Copy link
Member

Choose a reason for hiding this comment

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

I may be mistaken about the role of TracerFactory. If it's a loading mechanism similar to Java's service loader, then it's probably ok to apply env right in the ctor. While I don't like the bool argument, it's ok for a non-user-facing class.

Copy link
Contributor

Choose a reason for hiding this comment

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

@yurishkuro, In order to create a tracer, a tracer factory is first created and then MakeTracer method is called to create a tracer.

In MakeTracer, a yaml config file sent as argument. Properties of the tracer are read and a Tracer is configured and built

We would like to read also the env variables. Now since this is a behavioral change, we would like to make this behavior activatable.

The problem is that we cannot change the signature of MakeTracer. it's a public method. the only way is activate this behavior via a parameter passed to the factory at construction time.

@yurishkuro yurishkuro merged commit 47fbf19 into jaegertracing:master Mar 3, 2020
@johanneswuerbach
Copy link
Contributor Author

@yurishkuro @mdouaihy would it be possible to get a new official release including this change? That would be amazing 🎉

@yurishkuro
Copy link
Member

waiting for the build to pass #227

@yurishkuro
Copy link
Member

released in 0.6.0

Patrick0308 pushed a commit to Patrick0308/jaeger-client-cpp that referenced this pull request Jul 21, 2021
…#192)

Signed-off-by: Johannes Würbach <johannes.wuerbach@googlemail.com>
lrouquette pushed a commit to lrouquette/jaeger-client-cpp that referenced this pull request Nov 2, 2021
…#192)

Signed-off-by: Johannes Würbach <johannes.wuerbach@googlemail.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants