Add tracer configuration
The tracer can be configured with environment variables.
By default a no-op tracer is still create.
Change-Id: I508b69fdc62ff46f3978c6204824c4900a5c6e3c
Signed-off-by: Roni Riska <roni.riska@nokia.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a8d101..e44fb55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,7 +42,7 @@
set (tracelibcpp_VERSION_MAJOR "0")
set (tracelibcpp_VERSION_MINOR "0")
-set (tracelibcpp_VERSION_MICRO "3")
+set (tracelibcpp_VERSION_MICRO "4")
set (tracelibcpp_VERSION_STRING
"${tracelibcpp_VERSION_MAJOR}.${tracelibcpp_VERSION_MINOR}.${tracelibcpp_VERSION_MICRO}")
@@ -83,6 +83,7 @@
option(WITH_TESTING "Include using testing support" OFF)
include_directories ("${PROJECT_SOURCE_DIR}/include/tracelibcpp")
+include_directories ("${PROJECT_SOURCE_DIR}/src")
add_library(tracelibcpp SHARED
src/tracelib.cpp
diff --git a/README.md b/README.md
index 2acd2dd..4f44ce3 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
# RIC tracing helper library
-The library includes a function for creating a tracer instance.
-
-ToDo: tracer configuration options
+The library includes a function for creating a configured tracer instance.
+It hides the underlaying tracer implementation from the application.
## Usage
@@ -20,6 +19,22 @@
how span context **inject** and **extract** with textmap can be done.
Serialization to JSON can be done with any JSON library.
+## Configuration
+
+The trace library currently supports only [Jaeger](https://www.jaegertracing.io/) [C++ client](https://github.com/jaegertracing/jaeger-client-cpp) tracer implementation.
+The configuration is done using environment variables:
+
+| environment variable | values | default |
+| ---------------------------- |------------------------------------ | -------------- |
+| TRACING_ENABLED | 1, true, 0, false | false |
+| TRACING_JAEGER_SAMPLER_TYPE | const, propabilistic, ratelimiting | const |
+| TRACING_JAEGER_SAMPLER_PARAM | float | 0.001 |
+| TRACING_JAEGER_AGENT_ADDR | IP addr + port | 127.0.0.1:6831 |
+| TRACING_JAEGER_LOG_LEVEL | all, error, none | none |
+
+Meaning of the configuration variables is described in Jaeger web pages.
+By default a no-op tracer is created.
+
## Requires
cmake
@@ -46,6 +61,21 @@
genhtml cov.info
```
+## Binary package support
+Binary packages of the libary can be created with `make package` target, or with
+the Dockerfile in the `ci` directory.
+
+The Docker build executes unit tests and compiles binary packages which can then be
+exported from the container by running it and giving the target directory as a command line
+argument. The target directory must mounted to the container.
+
+```shell
+# Build the container
+docker build -t tracelibcpp -f ci/Dockerfile .
+# Export binary packages to /tmp
+docker run -v /tmp:/tmp tracelibcpp /tmp
+```
+
## License
See [LICENSES.txt](LICENSES.txt) file.
diff --git a/src/config.hpp b/src/config.hpp
new file mode 100644
index 0000000..5fb4ab8
--- /dev/null
+++ b/src/config.hpp
@@ -0,0 +1,46 @@
+#ifndef _TRACELIB_CONFIG_HPP_
+#define _TRACELIB_CONFIG_HPP_
+
+#include <boost/algorithm/string.hpp>
+#include <jaegertracing/Tracer.h>
+
+#define TRACING_ENABLED_ENV "TRACING_ENABLED"
+#define JAEGER_SAMPLER_TYPE_ENV "TRACING_JAEGER_SAMPLER_TYPE"
+#define JAEGER_SAMPLER_PARAM_ENV "TRACING_JAEGER_SAMPLER_PARAM"
+#define JAEGER_AGENT_ADDR_ENV "TRACING_JAEGER_AGENT_ADDR"
+#define JAEGER_LOG_LEVEL_ENV "TRACING_JAEGER_LOG_LEVEL"
+
+namespace tracelibcpp
+{
+ typedef enum {
+ LOG_ALL,
+ LOG_ERR,
+ LOG_NONE
+ } LogLevel;
+ class ConfMaker {
+ public:
+ ConfMaker(std::string serviceName):
+ name(serviceName) {}
+
+ std::string getEnv(const char* envName, std::string defVal);
+
+ bool isTracingEnabled(void);
+
+ jaegertracing::Config makeNopTraceConfig(void);
+
+ jaegertracing::samplers::Config getSamplerConfig(void);
+
+ jaegertracing::reporters::Config getReporterConfig(void);
+
+ LogLevel getLoggingLevel(void);
+
+ std::unique_ptr<jaegertracing::logging::Logger> getLogger(void);
+
+ jaegertracing::Config getTraceConfig(void);
+
+ private:
+ std::string name;
+ };
+}
+
+#endif
diff --git a/src/tracelib.cpp b/src/tracelib.cpp
index 2ab6941..ae8717d 100644
--- a/src/tracelib.cpp
+++ b/src/tracelib.cpp
@@ -16,13 +16,100 @@
*/
#include "tracelibcpp.hpp"
+#include "config.hpp"
-#include <jaegertracing/Tracer.h>
+using namespace tracelibcpp;
+
+std::string ConfMaker::getEnv(const char* envName, std::string defVal)
+{
+ const char *ev = getenv(envName);
+ if (!ev)
+ return defVal;
+ return std::string(ev);
+}
+
+bool ConfMaker::isTracingEnabled()
+{
+ std::string envValue = getEnv(TRACING_ENABLED_ENV, "false");
+ if (envValue == "1" || boost::iequals(envValue, "true"))
+ return true;
+ else
+ return false;
+}
+
+jaegertracing::Config ConfMaker::makeNopTraceConfig()
+{
+ return jaegertracing::Config(true,
+ jaegertracing::samplers::Config("const", 0));
+}
+
+jaegertracing::samplers::Config ConfMaker::getSamplerConfig()
+{
+ std::string samplerType = getEnv(JAEGER_SAMPLER_TYPE_ENV, "const");
+ // Use value 0.001 as default param, same way as jaeger does it
+ double param = atof(getEnv(JAEGER_SAMPLER_PARAM_ENV, "0.001").c_str());
+ return jaegertracing::samplers::Config(samplerType, param);
+}
+
+jaegertracing::reporters::Config ConfMaker::getReporterConfig()
+{
+ std::string agentHostPort = getEnv(JAEGER_AGENT_ADDR_ENV, jaegertracing::reporters::Config::kDefaultLocalAgentHostPort);
+
+ if (agentHostPort.find(':') == std::string::npos)
+ agentHostPort += ":6831";
+
+ return jaegertracing::reporters::Config(
+ 0, std::chrono::seconds(0), // use jaeger defaults
+ getLoggingLevel() == LOG_ALL, // log spans
+ agentHostPort
+ );
+}
+
+LogLevel ConfMaker::getLoggingLevel()
+{
+ std::string logLevel = getEnv(JAEGER_LOG_LEVEL_ENV, "error");
+ if (boost::iequals(logLevel, "all"))
+ return LOG_ALL;
+ else if (boost::iequals(logLevel, "error"))
+ return LOG_ERR;
+ else
+ return LOG_NONE;
+}
+
+std::unique_ptr<jaegertracing::logging::Logger> ConfMaker::getLogger()
+{
+ switch (getLoggingLevel())
+ {
+ case LOG_ALL:
+ case LOG_ERR:
+ return jaegertracing::logging::consoleLogger();
+ break;
+ default:
+ return jaegertracing::logging::nullLogger();
+ }
+}
+
+jaegertracing::Config ConfMaker::getTraceConfig()
+{
+ if (!isTracingEnabled())
+ return makeNopTraceConfig();
+ auto sampler = getSamplerConfig();
+ auto reporter = getReporterConfig();
+ return jaegertracing::Config(false, sampler, reporter);
+}
std::shared_ptr<opentracing::Tracer> tracelibcpp::createTracer(std::string serviceName)
{
- auto config = jaegertracing::Config(true,
- jaegertracing::samplers::Config("const", 0));
- return jaegertracing::Tracer::make(serviceName, config, jaegertracing::logging::consoleLogger());
+ auto cm = ConfMaker(serviceName);
+ auto config = cm.getTraceConfig();
+ try {
+ return jaegertracing::Tracer::make(serviceName, config, cm.getLogger());
+ } catch (std::exception& e)
+ {
+ if (cm.getLoggingLevel() != LOG_NONE)
+ std::cerr << "Cannot create tracer: " << e.what() << std::endl;
+ return jaegertracing::Tracer::make(serviceName, cm.makeNopTraceConfig());
+ }
}
+
diff --git a/tst/testcreate.cpp b/tst/testcreate.cpp
index 6529c18..7ab5a94 100644
--- a/tst/testcreate.cpp
+++ b/tst/testcreate.cpp
@@ -19,11 +19,145 @@
#include <gmock/gmock.h>
#include "tracelibcpp.hpp"
+#include "config.hpp"
using namespace testing;
using namespace tracelibcpp;
+class ConfMakerTest: public ::testing::Test
+{
+public:
+ ConfMaker cm;
+ ConfMakerTest():
+ cm(ConfMaker("testservice"))
+ {
+ }
+ ~ConfMakerTest()
+ {
+ unsetenv(JAEGER_SAMPLER_TYPE_ENV);
+ unsetenv(TRACING_ENABLED_ENV);
+ unsetenv(JAEGER_SAMPLER_PARAM_ENV);
+ unsetenv(JAEGER_AGENT_ADDR_ENV);
+ unsetenv(JAEGER_LOG_LEVEL_ENV);
+ }
+};
+
+TEST_F(ConfMakerTest, TestEnvReadingNonExisingReturnsDefaultValue)
+{
+ EXPECT_THAT("foobar", StrEq(cm.getEnv("nonexistent", "foobar")));
+}
+
+TEST_F(ConfMakerTest, TestEnvReadingReturnsEnvironmentValValue)
+{
+ setenv("FOO", "BAR", 1);
+ auto val = cm.getEnv("FOO", "foobar");
+ unsetenv("FOO");
+ EXPECT_THAT("BAR", StrEq(val));
+}
+
+TEST_F(ConfMakerTest, TestTracingIsDisabledByDefault)
+{
+ EXPECT_FALSE(cm.isTracingEnabled());
+}
+
+TEST_F(ConfMakerTest, TestTracingCanBeEnabledWithEnvValTrue)
+{
+ setenv(TRACING_ENABLED_ENV, "true", 1);
+ EXPECT_TRUE(cm.isTracingEnabled());
+ setenv(TRACING_ENABLED_ENV, "TRUE", 1);
+ EXPECT_TRUE(cm.isTracingEnabled());
+}
+
+TEST_F(ConfMakerTest, TestTracingCanBeEnabledWithEnvValOne)
+{
+ setenv(TRACING_ENABLED_ENV, "1", 1);
+ EXPECT_TRUE(cm.isTracingEnabled());
+}
+
+TEST_F(ConfMakerTest, TestTracingEnabledWithUnknownValuesResultsDisabled)
+{
+ setenv(TRACING_ENABLED_ENV, "0", 1);
+ EXPECT_FALSE(cm.isTracingEnabled());
+ setenv(TRACING_ENABLED_ENV, "off", 1);
+ EXPECT_FALSE(cm.isTracingEnabled());
+}
+
+TEST_F(ConfMakerTest, TestThatByDefaultConstSamplerIsCreated)
+{
+ auto samplerconf = cm.getSamplerConfig();
+ EXPECT_THAT("const", StrEq(samplerconf.type()));
+ EXPECT_EQ(0.001, samplerconf.param());
+}
+
+TEST_F(ConfMakerTest, TestThatSamplerTypeCanBeDefined)
+{
+ setenv(JAEGER_SAMPLER_TYPE_ENV, "probabilistic", 1);
+ auto samplerconf = cm.getSamplerConfig();
+ EXPECT_THAT("probabilistic", StrEq(samplerconf.type()));
+ EXPECT_EQ(0.001, samplerconf.param());
+}
+
+TEST_F(ConfMakerTest, TestThatSamplerParamCanBeDefined)
+{
+ setenv(JAEGER_SAMPLER_PARAM_ENV, "0.01", 1);
+ setenv(JAEGER_SAMPLER_TYPE_ENV, "probabilistic", 1);
+ auto samplerconf = cm.getSamplerConfig();
+ EXPECT_EQ(0.01, samplerconf.param());
+}
+
+TEST_F(ConfMakerTest, TestThatReporterAgentAddrDefaultsToLocalhost)
+{
+ auto reporterConf = cm.getReporterConfig();
+ EXPECT_THAT(reporterConf.localAgentHostPort(), StrEq("127.0.0.1:6831"));
+}
+
+TEST_F(ConfMakerTest, TestThatReporterAgentAddrCanBeDefined)
+{
+ setenv(JAEGER_AGENT_ADDR_ENV, "1.1.1.1:1111", 1);
+ auto reporterConf = cm.getReporterConfig();
+ EXPECT_THAT(reporterConf.localAgentHostPort(), StrEq("1.1.1.1:1111"));
+}
+
+TEST_F(ConfMakerTest, TestThatIfAgentPortIsNotGivenDefaultIsUsed)
+{
+ setenv(JAEGER_AGENT_ADDR_ENV, "1.1.1.1", 1);
+ auto reporterConf = cm.getReporterConfig();
+ EXPECT_THAT(reporterConf.localAgentHostPort(), StrEq("1.1.1.1:6831"));
+}
+
+TEST_F(ConfMakerTest, TestThatLoggingDefaultIsErr)
+{
+ EXPECT_EQ(LOG_ERR, cm.getLoggingLevel());
+}
+
+TEST_F(ConfMakerTest, TestThatLoggingLevelCanBeConfigured)
+{
+ setenv(JAEGER_LOG_LEVEL_ENV, "all", 1);
+ EXPECT_EQ(LOG_ALL, cm.getLoggingLevel());
+
+ setenv(JAEGER_LOG_LEVEL_ENV, "error", 1);
+ EXPECT_EQ(LOG_ERR, cm.getLoggingLevel());
+
+ setenv(JAEGER_LOG_LEVEL_ENV, "none", 1);
+ EXPECT_EQ(LOG_NONE, cm.getLoggingLevel());
+}
+
+TEST_F(ConfMakerTest, TestThatByDefaultDisabledConfigIsCreated)
+{
+ auto conf = cm.getTraceConfig();
+ EXPECT_TRUE(conf.disabled());
+}
+
+TEST_F(ConfMakerTest, TestThatIfTracerCreationFailsNoopTracerIsReturned)
+{
+ setenv(TRACING_ENABLED_ENV, "1", 1);
+ setenv(JAEGER_AGENT_ADDR_ENV, "foobar.invalid", 1); // invalid address causes an exception
+ auto tracer = createTracer("foo");
+ EXPECT_THAT(tracer, NotNull());
+}
+
TEST(CreateTest, TestThatTraceCreateReturnsANewInstance) {
auto tracer = createTracer("foo");
EXPECT_THAT(tracer, NotNull());
}
+