Skip to content

Commit

Permalink
Update log admin to support configure log level per logger
Browse files Browse the repository at this point in the history
  • Loading branch information
pnoltes committed Sep 29, 2024
1 parent bba867a commit f0835a5
Show file tree
Hide file tree
Showing 8 changed files with 537 additions and 134 deletions.
8 changes: 5 additions & 3 deletions bundles/logging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ log messages will be printed on stdout/stderr.


## Logging Properties
Properties shared among the logging bundles
Config Properties used in the Celix Log Admina and framework (fallback to stdout/stderr, when no log admin is present).

CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL The default active log level for created log services. Default is "info".
CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL The default active log level for created log services. Default is "info". The value should be one of the log levels: "trace", "debug", "info", "error", "fatal".

## Log Admin Properties
Properties specific for the Celix Log Admin (`Celix::log_admin` bundle)
Config Properties specific for the Celix Log Admin (`Celix::log_admin` bundle)

CELIX_LOG_ADMIN_FALLBACK_TO_STDOUT If set to true, the log admin will log to stdout/stderr if no celix log writers are available. Default is true
CELIX_LOG_ADMIN_ALWAYS_USE_STDOUT If set to true, the log admin will always log to stdout/stderr after forwaring log statements to the available celix log writers. Default is false.
CELIX_LOG_ADMIN_LOG_SINKS_DEFAULT_ENABLED Whether discovered log sink are default enabled. Default is true.
CELIX_LOG_ADMIN_LOG_SINK_<log_sink_name>_ENABLED If present, the log sink with the name <log_sink_name> is enabled/disabled. The <log_sink_name> is the name of the log sink in all caps. For example, the log sink with the name "celix_syslog" can be enabled/disabled with the property CELIX_LOG_ADMIN_LOG_SINK_CELIX_SYSLOG_ENABLED.
CELIX_LOG_ADMIN_LOGGER_<logger_name>_ACTIVE_LOG_LEVEL If present, the active log level for the log service with the name <logger_name>. The <logger_name> is the name of the logger in all caps. For example, the active log level for the log service with the name "celix_framework" can be set with the property CELIX_LOGGING_CELIX_FRAMEWORK_ACTIVE_LOG_LEVEL.

## CMake option
BUILD_LOG_SERVICE=ON
Expand Down
15 changes: 11 additions & 4 deletions bundles/logging/log_admin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,22 @@
# specific language governing permissions and limitations
# under the License.

set(LOG_ADMIN_SRC
src/celix_log_admin.c
src/celix_log_admin_activator.c
)

set(LOG_ADDMIN_PRIVATE_DEPS Celix::log_service_api Celix::shell_api)

add_celix_bundle(log_admin
SYMBOLIC_NAME "apache_celix_log_admin"
NAME "Apache Celix Log Admin"
GROUP "Celix/Logging"
VERSION "1.1.0"
SOURCES
src/celix_log_admin.c
src/celix_log_admin_activator.c
SOURCES ${LOG_ADMIN_SRC}
FILENAME celix_log_admin
)
target_link_libraries(log_admin PRIVATE Celix::log_service_api Celix::shell_api)
target_link_libraries(log_admin PRIVATE ${LOG_ADDMIN_PRIVATE_DEPS})
target_include_directories(log_admin PRIVATE src)
celix_deprecated_utils_headers(log_admin)
install_celix_bundle(log_admin EXPORT celix COMPONENT logging)
Expand All @@ -34,5 +39,7 @@ install_celix_bundle(log_admin EXPORT celix COMPONENT logging)
add_library(Celix::log_admin ALIAS log_admin)

if (ENABLE_TESTING)
add_library(log_admin_cut STATIC ${LOG_ADMIN_SRC})
target_link_libraries(log_admin_cut PRIVATE ${LOG_ADDMIN_PRIVATE_DEPS} Celix::framework)
add_subdirectory(gtest)
endif()
15 changes: 14 additions & 1 deletion bundles/logging/log_admin/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,20 @@ target_link_libraries(test_log_admin PRIVATE Celix::framework Celix::log_service
add_celix_bundle_dependencies(test_log_admin Celix::log_admin)
target_compile_definitions(test_log_admin PRIVATE -DLOG_ADMIN_BUNDLE=\"$<TARGET_PROPERTY:log_admin,BUNDLE_FILE>\")


add_test(NAME test_log_admin COMMAND test_log_admin)
setup_target_for_coverage(test_log_admin SCAN_DIR ..)


if (EI_TESTS)
add_executable(test_log_admin_with_ei src/LogAdminWithErrorInjectionTestSuite.cc)
target_link_libraries(test_log_admin_with_ei PRIVATE
log_admin_cut
Celix::framework Celix::log_service_api Celix::shell_api
Celix::malloc_ei Celix::asprintf_ei Celix::properties_ei Celix::string_hash_map_ei
GTest::gtest GTest::gtest_main
)
target_include_directories(test_log_admin_with_ei PRIVATE ../src)
add_test(NAME test_log_admin_with_ei COMMAND test_log_admin_with_ei)
setup_target_for_coverage(test_log_admin_with_ei SCAN_DIR ..)
endif ()

124 changes: 110 additions & 14 deletions bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@

#include <gtest/gtest.h>

#include <thread>
#include <atomic>
#include <memory>

#include "celix/FrameworkFactory.h"
#include "celix_log_sink.h"
#include "celix_log_control.h"
#include "celix_bundle_context.h"
Expand All @@ -30,11 +31,12 @@
#include "celix_shell_command.h"
#include "celix_constants.h"

class LogBundleTestSuite : public ::testing::Test {

class LogAdminTestSuite : public ::testing::Test {
public:
LogBundleTestSuite() {
LogAdminTestSuite() {
auto* properties = celix_properties_create();
celix_properties_set(properties, CELIX_FRAMEWORK_CACHE_DIR, ".cacheLogBundleTestSuite");
celix_properties_set(properties, CELIX_FRAMEWORK_CACHE_DIR, ".cacheLogAdminTestSuite");


auto* fwPtr = celix_frameworkFactory_createFramework(properties);
Expand All @@ -50,14 +52,14 @@ class LogBundleTestSuite : public ::testing::Test {
opts.filter.versionRange = CELIX_LOG_CONTROL_USE_RANGE;
opts.callbackHandle = (void*)this;
opts.set = [](void *handle, void *svc) {
auto* self = (LogBundleTestSuite*)handle;
auto* self = (LogAdminTestSuite*)handle;
self->control = std::shared_ptr<celix_log_control_t>{(celix_log_control_t*)svc, [](celix_log_control_t *){/*nop*/}};
};
trkId = celix_bundleContext_trackServicesWithOptions(ctx.get(), &opts);
EXPECT_TRUE(trkId >= 0);
}

~LogBundleTestSuite() override {
~LogAdminTestSuite() override {
celix_bundleContext_stopTracker(ctx.get(), trkId);
}

Expand All @@ -70,7 +72,7 @@ class LogBundleTestSuite : public ::testing::Test {



TEST_F(LogBundleTestSuite, StartStop) {
TEST_F(LogAdminTestSuite, StartStop) {
auto *list = celix_bundleContext_listBundles(ctx.get());
EXPECT_EQ(1, celix_arrayList_size(list));
celix_arrayList_destroy(list);
Expand All @@ -79,7 +81,7 @@ TEST_F(LogBundleTestSuite, StartStop) {
EXPECT_TRUE(svcId >= 0);
}

TEST_F(LogBundleTestSuite, NrOfLogServices) {
TEST_F(LogAdminTestSuite, NrOfLogServices) {
ASSERT_TRUE(control);
EXPECT_EQ(1, control->nrOfLogServices(control->handle, nullptr)); //default the framework log services is available

Expand Down Expand Up @@ -118,7 +120,7 @@ TEST_F(LogBundleTestSuite, NrOfLogServices) {
celix_bundleContext_stopTracker(ctx.get(), trkId4);
}

TEST_F(LogBundleTestSuite, NrOfLogSinks) {
TEST_F(LogAdminTestSuite, NrOfLogSinks) {
ASSERT_TRUE(control);
EXPECT_EQ(0, control->nrOfSinks(control->handle, nullptr));

Expand Down Expand Up @@ -152,7 +154,7 @@ TEST_F(LogBundleTestSuite, NrOfLogSinks) {
celix_bundleContext_unregisterService(ctx.get(), svcId3);
}

TEST_F(LogBundleTestSuite, SinkLogControl) {
TEST_F(LogAdminTestSuite, SinkLogControl) {
celix_log_sink_t logSink;
logSink.handle = nullptr;
logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
Expand Down Expand Up @@ -219,7 +221,7 @@ TEST_F(LogBundleTestSuite, SinkLogControl) {
celix_bundleContext_unregisterService(ctx.get(), svcId3);
}

TEST_F(LogBundleTestSuite, LogServiceControl) {
TEST_F(LogAdminTestSuite, LogServiceControl) {
//request "default" log service
long trkId1 = celix_bundleContext_trackServices(ctx.get(), CELIX_LOG_SERVICE_NAME);
celix_framework_waitForEmptyEventQueue(fw.get());
Expand Down Expand Up @@ -295,7 +297,7 @@ static void logSinkFunction(void *handle, celix_log_level_e level, long logServi
fprintf(stdout, "\n");
}

TEST_F(LogBundleTestSuite, LogServiceAndSink) {
TEST_F(LogAdminTestSuite, LogServiceAndSink) {
celix_log_sink_t logSink;
logSink.handle = nullptr;
logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
Expand Down Expand Up @@ -431,7 +433,7 @@ TEST_F(LogBundleTestSuite, LogServiceAndSink) {
celix_bundleContext_stopTracker(ctx.get(), trkId);
}

TEST_F(LogBundleTestSuite, LogAdminCmd) {
TEST_F(LogAdminTestSuite, LogAdminCmd) {
celix_log_sink_t logSink;
logSink.handle = nullptr;
logSink.sinkLog = [](void */*handle*/, celix_log_level_e /*level*/, long /*logServiceId*/, const char* /*logServiceName*/, const char* /*file*/, const char* /*function*/, int /*line*/, const char */*format*/, va_list /*formatArgs*/) {
Expand Down Expand Up @@ -590,4 +592,98 @@ TEST_F(LogBundleTestSuite, LogAdminCmd) {
};
called = celix_bundleContext_useServiceWithOptions(ctx.get(), &opts);
EXPECT_TRUE(called);
}
}

TEST_F(LogAdminTestSuite, LogServiceWithConfigPropertyTest) {
// Given a fw with a config property that set the active log level of a log service to fatal
auto fw = celix::createFramework({
{CELIX_FRAMEWORK_CACHE_DIR, ".cacheLogAdminTestSuiteWithConfig"},
{"CELIX_LOG_ADMIN_LOGGER_FOO_ACTIVE_LOG_LEVEL", "fatal"},
{"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "debug"}});
EXPECT_NE(fw.get(), nullptr);

// And a log admin
auto bndId = fw->getFrameworkBundleContext()->installBundle(LOG_ADMIN_BUNDLE, true);
EXPECT_GE(bndId, 0);

// When the log service bar is requested
auto trk1 = fw->getFrameworkBundleContext()
->trackServices<celix_log_service_t>(CELIX_LOG_SERVICE_NAME)
.setFilter("(name=bar)")
.build();

// Then the active log level of the log service bar is debug (default);
auto count = fw->getFrameworkBundleContext()
->useService<celix_log_control_t>(CELIX_LOG_CONTROL_NAME)
.addUseCallback([](auto& svc) {
celix_log_level_e level;
svc.logServiceInfo(svc.handle, "bar", &level);
EXPECT_EQ(CELIX_LOG_LEVEL_DEBUG, level);
})
.build();
EXPECT_EQ(1, count);

// When the log service foo is requested
auto trk2 = fw->getFrameworkBundleContext()
->trackServices<celix_log_service_t>(CELIX_LOG_SERVICE_NAME)
.setFilter("(name=foo)")
.build();

// Then the active log level of the log service foo is fatal (set by config property);
count = fw->getFrameworkBundleContext()
->useService<celix_log_control_t>(CELIX_LOG_CONTROL_NAME)
.addUseCallback([](auto& svc) {
celix_log_level_e level;
svc.logServiceInfo(svc.handle, "foo", &level);
EXPECT_EQ(CELIX_LOG_LEVEL_FATAL, level);
})
.build();
EXPECT_EQ(1, count);
}

TEST_F(LogAdminTestSuite, LogSinkWithConfigPropertyTest) {
// Given a fw with a config property that set the active log level of a log service to fatal
auto fw = celix::createFramework({
{CELIX_FRAMEWORK_CACHE_DIR, ".cacheLogAdminTestSuiteWithConfig"},
{"CELIX_LOG_ADMIN_LOG_SINKS_DEFAULT_ENABLED", "false"},
{"CELIX_LOG_ADMIN_LOG_SINK_FOO_ENABLED", "true"}});
EXPECT_NE(fw.get(), nullptr);

// And a log admin
auto bndId = fw->getFrameworkBundleContext()->installBundle(LOG_ADMIN_BUNDLE, true);
EXPECT_GE(bndId, 0);

// When a log sink bar is registered
auto reg1 = fw->getFrameworkBundleContext()->registerService<celix_log_sink_t>(std::make_shared<celix_log_sink_t>(), CELIX_LOG_SINK_NAME)
.setVersion(CELIX_LOG_SINK_VERSION)
.addProperty("name", "bar")
.build();

// Then the log sink bar will be disabled (default for all sinks)
auto count = fw->getFrameworkBundleContext()
->useService<celix_log_control_t>(CELIX_LOG_CONTROL_NAME)
.addUseCallback([](auto& svc) {
bool enabled;
svc.sinkInfo(svc.handle, "bar", &enabled);
EXPECT_FALSE(enabled);
})
.build();
EXPECT_EQ(1, count);

// When a log sink foo is registered
auto reg2 = fw->getFrameworkBundleContext()->registerService<celix_log_sink_t>(std::make_shared<celix_log_sink_t>(), CELIX_LOG_SINK_NAME)
.setVersion(CELIX_LOG_SINK_VERSION)
.addProperty("name", "foo")
.build();

// Then the log sink fpp will be enabled (CELIX_LOG_ADMIN_LOG_SINK_FOO_ENABLED=true)
count = fw->getFrameworkBundleContext()
->useService<celix_log_control_t>(CELIX_LOG_CONTROL_NAME)
.addUseCallback([](auto& svc) {
bool enabled;
svc.sinkInfo(svc.handle, "foo", &enabled);
EXPECT_TRUE(enabled);
})
.build();
EXPECT_EQ(1, count);
}
Loading

0 comments on commit f0835a5

Please sign in to comment.