From f0835a5facc2a21b6aaf9a61b14887d051f17677 Mon Sep 17 00:00:00 2001 From: Pepijn Noltes Date: Sun, 29 Sep 2024 22:02:00 +0200 Subject: [PATCH] Update log admin to support configure log level per logger --- bundles/logging/README.md | 8 +- bundles/logging/log_admin/CMakeLists.txt | 15 +- .../logging/log_admin/gtest/CMakeLists.txt | 15 +- .../log_admin/gtest/src/LogAdminTestSuite.cc | 124 ++++++- .../LogAdminWithErrorInjectionTestSuite.cc | 141 ++++++++ .../logging/log_admin/src/celix_log_admin.c | 308 +++++++++++------- .../logging/log_admin/src/celix_log_admin.h | 12 + .../include/celix_log_control.h | 48 ++- 8 files changed, 537 insertions(+), 134 deletions(-) create mode 100644 bundles/logging/log_admin/gtest/src/LogAdminWithErrorInjectionTestSuite.cc diff --git a/bundles/logging/README.md b/bundles/logging/README.md index 511c556e4..ab2d55680 100644 --- a/bundles/logging/README.md +++ b/bundles/logging/README.md @@ -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__ENABLED If present, the log sink with the name is enabled/disabled. The 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__ACTIVE_LOG_LEVEL If present, the active log level for the log service with the name . The 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 diff --git a/bundles/logging/log_admin/CMakeLists.txt b/bundles/logging/log_admin/CMakeLists.txt index 049206792..541c35e19 100644 --- a/bundles/logging/log_admin/CMakeLists.txt +++ b/bundles/logging/log_admin/CMakeLists.txt @@ -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) @@ -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() diff --git a/bundles/logging/log_admin/gtest/CMakeLists.txt b/bundles/logging/log_admin/gtest/CMakeLists.txt index 41f3b9d8b..1178dea87 100644 --- a/bundles/logging/log_admin/gtest/CMakeLists.txt +++ b/bundles/logging/log_admin/gtest/CMakeLists.txt @@ -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=\"$\") - 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 () + diff --git a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc index 4e94ff11f..79fef9a82 100644 --- a/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc +++ b/bundles/logging/log_admin/gtest/src/LogAdminTestSuite.cc @@ -19,9 +19,10 @@ #include -#include #include +#include +#include "celix/FrameworkFactory.h" #include "celix_log_sink.h" #include "celix_log_control.h" #include "celix_bundle_context.h" @@ -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); @@ -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*)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); } @@ -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); @@ -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 @@ -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)); @@ -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*/) { @@ -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()); @@ -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*/) { @@ -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*/) { @@ -590,4 +592,98 @@ TEST_F(LogBundleTestSuite, LogAdminCmd) { }; called = celix_bundleContext_useServiceWithOptions(ctx.get(), &opts); EXPECT_TRUE(called); -} \ No newline at end of file +} + +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_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_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_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_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(std::make_shared(), 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_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(std::make_shared(), 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_NAME) + .addUseCallback([](auto& svc) { + bool enabled; + svc.sinkInfo(svc.handle, "foo", &enabled); + EXPECT_TRUE(enabled); + }) + .build(); + EXPECT_EQ(1, count); +} diff --git a/bundles/logging/log_admin/gtest/src/LogAdminWithErrorInjectionTestSuite.cc b/bundles/logging/log_admin/gtest/src/LogAdminWithErrorInjectionTestSuite.cc new file mode 100644 index 000000000..fdf393d1b --- /dev/null +++ b/bundles/logging/log_admin/gtest/src/LogAdminWithErrorInjectionTestSuite.cc @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#include + +#include "asprintf_ei.h" +#include "celix/FrameworkFactory.h" +#include "celix/Constants.h" +#include "celix_log_admin.h" +#include "celix_properties_ei.h" +#include "celix_string_hash_map_ei.h" +#include "malloc_ei.h" + +#include + +class LogAdminWithErrorInjectionTestSuite : public ::testing::Test { + public: + LogAdminWithErrorInjectionTestSuite() { + fw = celix::createFramework(); + + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_ei_expect_asprintf(nullptr, 0, -1); + celix_ei_expect_celix_stringHashMap_put(nullptr, 0, 0); + celix_ei_expect_celix_properties_create(nullptr, 0, nullptr); + } + + ~LogAdminWithErrorInjectionTestSuite() noexcept override {} + + std::shared_ptr fw{}; +}; + +TEST_F(LogAdminWithErrorInjectionTestSuite, CreateWithErrorTest) { + //Given an error injection for calloc + celix_ei_expect_calloc((void*)celix_logAdmin_create, 0, nullptr); + + //When creating a log admin + auto* admin = celix_logAdmin_create(nullptr); + + //Then the admin is nullptr + ASSERT_EQ(nullptr, admin); +} + +TEST_F(LogAdminWithErrorInjectionTestSuite, AddLogSvcWithErrorTest) { + //Given a log admin + auto* logAdmin = celix_logAdmin_create(fw->getFrameworkBundleContext()->getCBundleContext()); + ASSERT_NE(logAdmin, nullptr); + + //And an error injection for calloc is expected + celix_ei_expect_asprintf((void*)celix_logAdmin_addLogSvcForName, 0, -1); + + //When adding a log service for a name + celix_logAdmin_addLogSvcForName(logAdmin, "test"); + + //Then the loggers count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfLogServices(logAdmin, "test")); + + //When an error injection for calloc is expected + celix_ei_expect_calloc( (void*)celix_logAdmin_addLogSvcForName, 0, nullptr); + + //And adding a log service for a name + celix_logAdmin_addLogSvcForName(logAdmin, "test"); + + //Then the loggers count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfLogServices(logAdmin, "test")); + + //When an error injection for celix_stringHashMap_put is expected + celix_ei_expect_celix_stringHashMap_put( (void*)celix_logAdmin_addLogSvcForName, 0, ENOMEM); + + //And adding a log service for a name + celix_logAdmin_addLogSvcForName(logAdmin, "test"); + + //Then the loggers count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfLogServices(logAdmin, "test")); + + //When an error injection for celix_properties_create is expected + celix_ei_expect_celix_properties_create( (void*)celix_logAdmin_addLogSvcForName, 0, nullptr); + + //And adding a log service for a name + celix_logAdmin_addLogSvcForName(logAdmin, "test"); + + //Then the loggers count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfLogServices(logAdmin, "test")); + + celix_logAdmin_destroy(logAdmin); +} + +TEST_F(LogAdminWithErrorInjectionTestSuite, AddLogSinkWithErrorTest) { + //Given a log admin + auto* logAdmin = celix_logAdmin_create(fw->getFrameworkBundleContext()->getCBundleContext()); + ASSERT_NE(logAdmin, nullptr); + + //And a service properties + celix_autoptr(celix_properties_t) props = celix_properties_create(); + celix_properties_set(props, "name", "test"); + celix_properties_setLong(props, celix::SERVICE_ID, 1L); + + //And an error injection for asprintf is expected + celix_ei_expect_asprintf((void*)celix_logAdmin_addSink, 0, -1); + + //When adding a log sink + celix_logAdmin_addSink(logAdmin, nullptr, props); + + //Then the sinks count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfSinks(logAdmin, "test")); + + //When an error injection for calloc is expected + celix_ei_expect_calloc( (void*)celix_logAdmin_addSink, 0, nullptr); + + //And adding a log sink + celix_logAdmin_addSink(logAdmin, nullptr, props); + + //Then the sinks count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfSinks(logAdmin, "test")); + + //When an error injection for celix_stringHashMap_put is expected + celix_ei_expect_celix_stringHashMap_put( (void*)celix_logAdmin_addSink, 0, ENOMEM); + + //And adding a log sink + celix_logAdmin_addSink(logAdmin, nullptr, props); + + //Then the sinks count is not increased + EXPECT_EQ(0, celix_logAdmin_nrOfSinks(logAdmin, "test")); + + celix_logAdmin_destroy(logAdmin); +} diff --git a/bundles/logging/log_admin/src/celix_log_admin.c b/bundles/logging/log_admin/src/celix_log_admin.c index 5ed38d53d..14381ed5c 100644 --- a/bundles/logging/log_admin/src/celix_log_admin.c +++ b/bundles/logging/log_admin/src/celix_log_admin.c @@ -22,21 +22,34 @@ #include #include #include +#include #include #include #include +#include +#include "celix_bundle_context.h" +#include "celix_bundle_context_type.h" +#include "celix_cleanup.h" #include "celix_compiler.h" +#include "celix_errno.h" +#include "celix_filter.h" +#include "celix_framework.h" +#include "celix_log_constants.h" +#include "celix_log_level.h" #include "celix_log_service.h" #include "celix_log_sink.h" -#include "celix_utils.h" #include "celix_log_utils.h" -#include "celix_log_constants.h" +#include "celix_properties_type.h" +#include "celix_properties.h" #include "celix_shell_command.h" +#include "celix_string_hash_map.h" #include "celix_threads.h" -#include "hash_map.h" -#include "celix_framework.h" +#include "celix_utils.h" + +#include +#include #define CELIX_LOG_ADMIN_DEFAULT_LOG_NAME "default" #define CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME "celix_framework" @@ -57,8 +70,8 @@ struct celix_log_admin { long cmdSvcId; celix_thread_rwlock_t lock; //protects below - hash_map_t *loggers; //key = name, value = celix_log_service_instance_t - hash_map_t* sinks; //key = name, value = celix_log_sink_t + celix_string_hash_map_t *loggers; //key = name, value = celix_log_service_instance_t + celix_string_hash_map_t* sinks; //key = name, value = celix_log_sink_t }; typedef struct celix_log_service_entry { @@ -82,6 +95,14 @@ typedef struct celix_log_sink_entry { bool enabled; } celix_log_sink_entry_t; +static char* celix_logAdmin_allCaps(const char* str) { + char* result = strdup(str); + for (int i = 0; result && i < strlen(result); ++i) { + result[i] = (char)toupper(result[i]); + } + return result; +} + static void celix_logAdmin_vlogDetails(void *handle, celix_log_level_e level, const char* file, const char* function, int line, const char *format, va_list formatArgs) { celix_log_service_entry_t* entry = handle; @@ -92,19 +113,18 @@ static void celix_logAdmin_vlogDetails(void *handle, celix_log_level_e level, co celixThreadRwlock_readLock(&entry->admin->lock); if (level >= entry->activeLogLevel) { - int nrOfLogWriters = hashMap_size(entry->admin->sinks); - hash_map_iterator_t iter = hashMapIterator_construct(entry->admin->sinks); - while (hashMapIterator_hasNext(&iter)) { - celix_log_sink_entry_t *sinkEntry = hashMapIterator_nextValue(&iter); - if (sinkEntry->enabled) { - celix_log_sink_t *sink = sinkEntry->sink; - va_list argCopy; - va_copy(argCopy, formatArgs); - sink->sinkLog(sink->handle, level, entry->logSvcId, entry->name, - entry->detailed ? file : NULL, entry->detailed ? function : NULL, entry->detailed ? line : 0, - format, argCopy); - va_end(argCopy); - } + size_t nrOfLogWriters = celix_stringHashMap_size(entry->admin->sinks); + CELIX_STRING_HASH_MAP_ITERATE(entry->admin->sinks, iter) { + celix_log_sink_entry_t *sinkEntry = iter.value.ptrValue; + if (sinkEntry->enabled) { + celix_log_sink_t *sink = sinkEntry->sink; + va_list argCopy; + va_copy(argCopy, formatArgs); + sink->sinkLog(sink->handle, level, entry->logSvcId, entry->name, + entry->detailed ? file : NULL, entry->detailed ? function : NULL, entry->detailed ? line : 0, + format, argCopy); + va_end(argCopy); + } } if (entry->admin->alwaysLogToStdOut || (nrOfLogWriters == 0 && entry->admin->fallbackToStdOut)) { @@ -184,18 +204,44 @@ static void celix_logAdmin_frameworkLogFunction(void* handle, celix_log_level_e entry->logSvc.vlogDetails(entry->logSvc.handle, level, file, function, line, format, formatArgs); } -static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char* name) { +void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char* name) { celix_log_service_entry_t* newEntry = NULL; - celixThreadRwlock_writeLock(&admin->lock); - celix_log_service_entry_t* found = hashMap_get(admin->loggers, name); + celix_autofree char* logNameInCaps = celix_logAdmin_allCaps(name); + celix_autofree char* configKey = NULL; + int rc = -1; + if (logNameInCaps) { + rc = asprintf(&configKey, "CELIX_LOG_ADMIN_LOGGER_%s_ACTIVE_LOG_LEVEL", logNameInCaps); + } + if (rc < 0) { + (void)fprintf(stderr, "Error creating config key for log service %s. ENOMEM\n", name); + return; + } + + const char* configuredLogLevel = celix_bundleContext_getProperty(admin->ctx, configKey, NULL); + celix_log_level_e activeLogLevel = admin->logServicesDefaultActiveLogLevel; + if (configuredLogLevel != NULL) { + activeLogLevel = celix_logLevel_fromString(configuredLogLevel, activeLogLevel); + } + + + celix_auto(celix_rwlock_wlock_guard_t) lock = celixRwlockWlockGuard_init(&admin->lock); + celix_log_service_entry_t* found = celix_stringHashMap_get(admin->loggers, name); if (found == NULL) { //new newEntry = calloc(1, sizeof(*newEntry)); + char* entryName = celix_utils_strdup(name); + if (!newEntry || !entryName) { + free(newEntry); + free(entryName); + (void)fprintf(stderr, "Error creating log service entry for log service %s. ENOMEM\n", name); + return; + } + newEntry->logSvcId = -1L; newEntry->admin = admin; - newEntry->name = celix_utils_strdup(name); + newEntry->name = entryName; newEntry->count = 1; - newEntry->activeLogLevel = admin->logServicesDefaultActiveLogLevel; + newEntry->activeLogLevel = activeLogLevel; newEntry->detailed = true; newEntry->logSvc.handle = newEntry; newEntry->logSvc.trace = celix_logAdmin_trace; @@ -208,16 +254,32 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char newEntry->logSvc.logDetails = celix_logAdmin_logDetails; newEntry->logSvc.vlog = celix_logAdmin_vlog; newEntry->logSvc.vlogDetails = celix_logAdmin_vlogDetails; - hashMap_put(admin->loggers, (void*)newEntry->name, newEntry); - celixThreadRwlock_unlock(&admin->lock); + celix_status_t addHashmapStatus = celix_stringHashMap_put(admin->loggers, (void*)newEntry->name, newEntry); + if (addHashmapStatus != CELIX_SUCCESS) { + (void)fprintf(stderr, "Error adding log service entry for log service %s. ENOMEM\n", name); + free(newEntry->name); + free(newEntry); + return; + } { - //register new log service async + // register new log service async celix_properties_t* props = celix_properties_create(); - celix_properties_set(props, CELIX_LOG_SERVICE_PROPERTY_NAME, newEntry->name); - if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_DEFAULT_LOG_NAME) == 0) { - //ensure that the default log service is found when no name filter is used. - celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, 100); + celix_status_t status = CELIX_SUCCESS; + if (props) { + status = celix_properties_set(props, CELIX_LOG_SERVICE_PROPERTY_NAME, newEntry->name); + if (celix_utils_stringEquals(newEntry->name, CELIX_LOG_ADMIN_DEFAULT_LOG_NAME) == 0) { + // ensure that the default log service is found when no name filter is used. + status = CELIX_DO_IF(status, celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, 100)); + } + } + + if (!props || status != CELIX_SUCCESS) { + (void)fprintf(stderr, "Error creating properties for log service %s. ENOMEM\n", name); + celix_stringHashMap_remove(admin->loggers, name); + free(newEntry->name); + free(newEntry); + return; } celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS; @@ -234,7 +296,6 @@ static void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char } } else { found->count += 1; - celixThreadRwlock_unlock(&admin->lock); } } @@ -259,12 +320,12 @@ static void celix_logAdmin_freeLogEntry(void *data) { static void celix_logAdmin_remLogSvcForName(celix_log_admin_t* admin, const char* name) { celixThreadRwlock_writeLock(&admin->lock); - celix_log_service_entry_t* found = hashMap_get(admin->loggers, name); + celix_log_service_entry_t* found = celix_stringHashMap_get(admin->loggers, name); if (found != NULL) { found->count -= 1; if (found->count == 0) { //remove - hashMap_remove(admin->loggers, name); + celix_stringHashMap_remove(admin->loggers, name); celix_bundleContext_unregisterServiceAsync(admin->ctx, found->logSvcId, found, celix_logAdmin_freeLogEntry); } } @@ -281,33 +342,60 @@ static void celix_logAdmin_trackerRem(void *handle, const celix_service_tracker_ celix_logAdmin_remLogSvcForName(admin, name); } - -static void celix_logAdmin_addSink(void *handle, void *svc, const celix_properties_t* props) { +void celix_logAdmin_addSink(void* handle, void* svc, const celix_properties_t* props) { celix_log_admin_t* admin = handle; celix_log_sink_t* sink = svc; - long svcId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1L); + const long svcId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1L); const char* sinkName = celix_properties_get(props, CELIX_LOG_SINK_PROPERTY_NAME, NULL); - char nameBuf[16]; + char nameBuf[24]; if (sinkName == NULL) { - snprintf(nameBuf, 16, "LogSink-%li", svcId); + (void)snprintf(nameBuf, 24, "LogSink-%li", svcId); sinkName = nameBuf; } - celixThreadRwlock_writeLock(&admin->lock); - celix_log_sink_entry_t* found = hashMap_get(admin->sinks, sinkName); + celix_autofree char* sinkNameCaps = celix_logAdmin_allCaps(sinkName); + celix_autofree char* configKey = NULL; + int rc = -1; + if (sinkNameCaps) { + rc = asprintf(&configKey, "CELIX_LOG_ADMIN_LOG_SINK_%s_ENABLED", sinkNameCaps); + } + if (rc < 0) { + (void)fprintf(stderr, "Error creating config key for sink %s. ENOMEM\n", sinkName); + return; + } + + bool enabled = celix_bundleContext_getPropertyAsBool(admin->ctx, configKey, admin->sinksDefaultEnabled); + + celix_auto(celix_rwlock_wlock_guard_t) lock = celixRwlockWlockGuard_init(&admin->lock); + celix_log_sink_entry_t* found = celix_stringHashMap_get(admin->sinks, sinkName); if (found == NULL) { - celix_log_sink_entry_t *entry = calloc(1, sizeof(*entry)); - entry->name = celix_utils_strdup(sinkName); - entry->svcId = svcId; - entry->enabled = admin->sinksDefaultEnabled; - entry->sink = sink; - hashMap_put(admin->sinks, entry->name, entry); + celix_log_sink_entry_t* entry = calloc(1, sizeof(*entry)); + char* entryName = celix_utils_strdup(sinkName); + if (entry && entryName) { + entry->name = entryName; + entry->svcId = svcId; + entry->enabled = enabled; + entry->sink = sink; + celix_status_t status = celix_stringHashMap_put(admin->sinks, entry->name, entry); + if (status != CELIX_SUCCESS) { + (void)fprintf(stderr, "Error creating log sink entry for sink %s. ENOMEM\n", sinkName); + free(entry->name); + free(entry); + }; + } else { + free(entry); + free(entryName); + (void)fprintf(stderr, "Error creating log sink entry for sink %s. ENOMEM\n", sinkName); + return; + } } - celixThreadRwlock_unlock(&admin->lock); if (found != NULL) { - celix_logUtils_logToStdout(CELIX_LOG_ADMIN_DEFAULT_LOG_NAME, CELIX_LOG_LEVEL_ERROR, "Cannot add log sink, Log sink with name '%s' already present.", sinkName); + celix_logUtils_logToStdout(CELIX_LOG_ADMIN_DEFAULT_LOG_NAME, + CELIX_LOG_LEVEL_ERROR, + "Cannot add log sink, Log sink with name '%s' already present.", + sinkName); } } @@ -315,20 +403,20 @@ static void celix_logAdmin_remSink(void *handle, void *svc CELIX_UNUSED, const c celix_log_admin_t* admin = handle; long svcId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1L); const char* sinkName = celix_properties_get(props, CELIX_LOG_SINK_PROPERTY_NAME, NULL); - char nameBuf[16]; + char nameBuf[24]; if (sinkName == NULL) { - snprintf(nameBuf, 16, "LogSink-%li", svcId); + (void)snprintf(nameBuf, 24, "LogSink-%li", svcId); sinkName = nameBuf; } celixThreadRwlock_writeLock(&admin->lock); - celix_log_sink_entry_t* entry = hashMap_get(admin->sinks, sinkName); + celix_log_sink_entry_t* entry = celix_stringHashMap_get(admin->sinks, sinkName); if (entry != NULL && entry->svcId != svcId) { //no match (note there can be invalid log sinks with the same name, but different svc ids. entry = NULL; } if (entry != NULL) { - hashMap_remove(admin->sinks, sinkName); + celix_stringHashMap_remove(admin->sinks, sinkName); } celixThreadRwlock_unlock(&admin->lock); @@ -338,19 +426,18 @@ static void celix_logAdmin_remSink(void *handle, void *svc CELIX_UNUSED, const c } } -static size_t celix_logAdmin_nrOfLogServices(void *handle, const char* select) { +size_t celix_logAdmin_nrOfLogServices(void* handle, const char* select) { celix_log_admin_t* admin = handle; size_t count = 0; celixThreadRwlock_readLock(&admin->lock); if (select == NULL) { - count = hashMap_size(admin->loggers); + count = celix_stringHashMap_size(admin->loggers); } else { - hash_map_iterator_t iter = hashMapIterator_construct(admin->loggers); - while (hashMapIterator_hasNext(&iter)) { - celix_log_service_entry_t *visit = hashMapIterator_nextValue(&iter); - char *match = strcasestr(visit->name, select); + CELIX_STRING_HASH_MAP_ITERATE(admin->loggers, iter) { + const celix_log_service_entry_t* visit = iter.value.ptrValue; + const char* match = strcasestr(visit->name, select); if (match != NULL && match == visit->name) { - //note if select is found in visit->name and visit->name start with select + // note if select is found in visit->name and visit->name start with select count += 1; } } @@ -359,16 +446,15 @@ static size_t celix_logAdmin_nrOfLogServices(void *handle, const char* select) { return count; } -static size_t celix_logAdmin_nrOfSinks(void *handle, const char* select) { +size_t celix_logAdmin_nrOfSinks(void *handle, const char* select) { celix_log_admin_t* admin = handle; size_t count = 0; celixThreadRwlock_readLock(&admin->lock); if (select == NULL) { - count = hashMap_size(admin->sinks); + count = celix_stringHashMap_size(admin->sinks); } else { - hash_map_iterator_t iter = hashMapIterator_construct(admin->sinks); - while (hashMapIterator_hasNext(&iter)) { - celix_log_sink_entry_t *visit = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->sinks, iter) { + celix_log_sink_entry_t *visit = iter.value.ptrValue; char *match = strcasestr(visit->name, select); if (match != NULL && match == visit->name) { //note if select is found in visit->name and visit->name start with select @@ -384,9 +470,8 @@ static size_t celix_logAdmin_setActiveLogLevels(void *handle, const char* select celix_log_admin_t* admin = handle; size_t count = 0; celixThreadRwlock_writeLock(&admin->lock); - hash_map_iterator_t iter = hashMapIterator_construct(admin->loggers); - while (hashMapIterator_hasNext(&iter)) { - celix_log_service_entry_t* visit = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->loggers, iter) { + celix_log_service_entry_t* visit = iter.value.ptrValue; if (select == NULL) { visit->activeLogLevel = activeLogLevel; count += 1; @@ -403,20 +488,19 @@ static size_t celix_logAdmin_setActiveLogLevels(void *handle, const char* select return count; } -static size_t celix_logAdmin_setSinkEnabled(void *handle, const char* select, bool enabled) { +static size_t celix_logAdmin_setSinkEnabled(void* handle, const char* select, bool enabled) { celix_log_admin_t* admin = handle; size_t count = 0; celixThreadRwlock_writeLock(&admin->lock); - hash_map_iterator_t iter = hashMapIterator_construct(admin->sinks); - while (hashMapIterator_hasNext(&iter)) { - celix_log_sink_entry_t* visit = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->sinks, iter) { + celix_log_sink_entry_t* visit = iter.value.ptrValue; if (select == NULL) { visit->enabled = enabled; count += 1; } else { - char *match = strcasestr(visit->name, select); + char* match = strcasestr(visit->name, select); if (match != NULL && match == visit->name) { - //note if select is found in visit->name and visit->name start with select + // note if select is found in visit->name and visit->name start with select visit->enabled = enabled; count += 1; } @@ -431,9 +515,8 @@ static celix_array_list_t* celix_logAdmin_currentLogServices(void *handle) { celix_log_admin_t* admin = handle; celix_array_list_t* loggers = celix_arrayList_createStringArray(); celixThreadRwlock_readLock(&admin->lock); - hash_map_iterator_t iter = hashMapIterator_construct(admin->loggers); - while (hashMapIterator_hasNext(&iter)) { - celix_log_service_entry_t* visit = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->loggers, iter) { + const celix_log_service_entry_t* visit = iter.value.ptrValue; celix_arrayList_addString(loggers, visit->name); } celixThreadRwlock_unlock(&admin->lock); @@ -444,9 +527,8 @@ static celix_array_list_t* celix_logAdmin_currentSinks(void *handle) { celix_log_admin_t* admin = handle; celix_array_list_t* sinks = celix_arrayList_createStringArray(); celixThreadRwlock_readLock(&admin->lock); - hash_map_iterator_t iter = hashMapIterator_construct(admin->sinks); - while (hashMapIterator_hasNext(&iter)) { - celix_log_sink_entry_t* entry = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->sinks, iter) { + const celix_log_sink_entry_t* entry = iter.value.ptrValue; celix_arrayList_addString(sinks, entry->name); } celixThreadRwlock_unlock(&admin->lock); @@ -456,7 +538,7 @@ static celix_array_list_t* celix_logAdmin_currentSinks(void *handle) { static bool celix_logAdmin_sinkInfo(void *handle, const char* sinkName, bool* outEnabled) { celix_log_admin_t* admin = handle; celixThreadRwlock_readLock(&admin->lock); - celix_log_sink_entry_t* found = hashMap_get(admin->sinks, sinkName); + celix_log_sink_entry_t* found = celix_stringHashMap_get(admin->sinks, sinkName); if (found != NULL && outEnabled != NULL) { *outEnabled = found->enabled; } @@ -468,9 +550,8 @@ static size_t celix_logAdmin_setDetailed(void *handle, const char* select, bool celix_log_admin_t* admin = handle; size_t count = 0; celixThreadRwlock_writeLock(&admin->lock); - hash_map_iterator_t iter = hashMapIterator_construct(admin->loggers); - while (hashMapIterator_hasNext(&iter)) { - celix_log_service_entry_t* visit = hashMapIterator_nextValue(&iter); + CELIX_STRING_HASH_MAP_ITERATE(admin->loggers, iter) { + celix_log_service_entry_t* visit = iter.value.ptrValue; if (select == NULL) { visit->detailed = detailed; count += 1; @@ -490,7 +571,7 @@ static size_t celix_logAdmin_setDetailed(void *handle, const char* select, bool static bool celix_logAdmin_logServiceInfoEx(void* handle, const char* logServiceName, celix_log_level_e* outActiveLogLevel, bool* outDetailed) { celix_log_admin_t* admin = handle; celixThreadRwlock_readLock(&admin->lock); - celix_log_service_entry_t* found = hashMap_get(admin->loggers, logServiceName); + celix_log_service_entry_t* found = celix_stringHashMap_get(admin->loggers, logServiceName); if (found != NULL) { if (outActiveLogLevel != NULL) { *outActiveLogLevel = found->activeLogLevel; @@ -509,12 +590,12 @@ static bool celix_logAdmin_logServiceInfo(void *handle, const char* logServiceNa static void celix_logAdmin_setLogLevelCmd(celix_log_admin_t* admin, const char* select, const char* level, FILE* outStream, FILE* errorStream) { bool converted; - celix_log_level_e logLevel = celix_logUtils_logLevelFromStringWithCheck(level, CELIX_LOG_LEVEL_TRACE, &converted); + celix_log_level_e logLevel = celix_logLevel_fromStringWithCheck(level, CELIX_LOG_LEVEL_TRACE, &converted); if (converted) { size_t count = celix_logAdmin_setActiveLogLevels(admin, select, logLevel); - fprintf(outStream, "Updated %zu log services to log level %s\n", count, celix_logUtils_logLevelToString(logLevel)); + (void)fprintf(outStream, "Updated %zu log services to log level %s\n", count, celix_logLevel_toString(logLevel)); } else { - fprintf(errorStream, "Cannot convert '%s' to a valid celix log level.\n", level); + (void)fprintf(errorStream, "Cannot convert '%s' to a valid celix log level.\n", level); } } @@ -530,9 +611,9 @@ static void celix_logAdmin_setSinkEnabledCmd(celix_log_admin_t* admin, const cha } if (valid) { size_t count = celix_logAdmin_setSinkEnabled(admin, select, enableSink); - fprintf(outStream, "Updated %zu log sinks to %s.\n", count, enableSink ? "enabled" : "disabled"); + (void)fprintf(outStream, "Updated %zu log sinks to %s.\n", count, enableSink ? "enabled" : "disabled"); } else { - fprintf(errorStream, "Cannot convert '%s' to a boolean value.\n", enabled); + (void)fprintf(errorStream, "Cannot convert '%s' to a boolean value.\n", enabled); } } @@ -542,31 +623,31 @@ static void celix_logAdmin_InfoCmd(celix_log_admin_t* admin, FILE* outStream, FI celix_arrayList_sort(logServices); celix_arrayList_sort(sinks); - fprintf(outStream, "Log Admin provided log services:\n"); + (void)fprintf(outStream, "Log Admin provided log services:\n"); for (int i = 0 ; i < celix_arrayList_size(logServices); ++i) { const char *name = celix_arrayList_getString(logServices, i); celix_log_level_e level; bool detailed; bool found = celix_logAdmin_logServiceInfoEx(admin, name, &level, &detailed); if (found) { - fprintf(outStream, " |- %i) Log Service %20s, active log level %s, %s\n", + (void)fprintf(outStream, " |- %i) Log Service %20s, active log level %s, %s\n", i+1, name, celix_logUtils_logLevelToString(level), detailed ? "detailed" : "brief"); } } celix_arrayList_destroy(logServices); if (celix_arrayList_size(sinks) > 0) { - fprintf(outStream, "Log Admin found log sinks:\n"); + (void)fprintf(outStream, "Log Admin found log sinks:\n"); for (int i = 0 ; i < celix_arrayList_size(sinks); ++i) { const char *name = celix_arrayList_getString(sinks, i); bool enabled; bool found = celix_logAdmin_sinkInfo(admin, name, &enabled); if (found) { - fprintf(outStream, " |- %i) Log Sink %20s, %s\n", i+1, name, enabled ? "enabled" : "disabled"); + (void)fprintf(outStream, " |- %i) Log Sink %20s, %s\n", i+1, name, enabled ? "enabled" : "disabled"); } } } else { - fprintf(outStream, "Log Admin has found 0 log sinks\n"); + (void)fprintf(outStream, "Log Admin has found 0 log sinks\n"); } celix_arrayList_destroy(sinks); } @@ -583,9 +664,9 @@ static void celix_logAdmin_setLogDetailedCmd(celix_log_admin_t* admin, const cha } if (valid) { size_t count = celix_logAdmin_setDetailed(admin, select, enableDetailed); - fprintf(outStream, "Updated %zu log services to %s.\n", count, enableDetailed ? "detailed" : "brief"); + (void)fprintf(outStream, "Updated %zu log services to %s.\n", count, enableDetailed ? "detailed" : "brief"); } else { - fprintf(errorStream, "Cannot convert '%s' to a boolean value.\n", detailed); + (void)fprintf(errorStream, "Cannot convert '%s' to a boolean value.\n", detailed); } } @@ -608,7 +689,7 @@ static bool celix_logAdmin_executeCommand(void *handle, const char *commandLine, } else if (arg1 != NULL) { celix_logAdmin_setLogLevelCmd(admin, NULL, arg1, outStream, errorStream); } else { - fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); + (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); } } else if (strncmp("sink", subCmd, 64) == 0) { const char* arg1 = strtok_r(NULL, " ", &savePtr); @@ -618,7 +699,7 @@ static bool celix_logAdmin_executeCommand(void *handle, const char *commandLine, } else if (arg1 != NULL) { celix_logAdmin_setSinkEnabledCmd(admin, NULL, arg1, outStream, errorStream); } else { - fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); + (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); } } else if (strncmp("detail", subCmd, 64) == 0) { const char* arg1 = strtok_r(NULL, " ", &savePtr); @@ -628,10 +709,10 @@ static bool celix_logAdmin_executeCommand(void *handle, const char *commandLine, } else if (arg1 != NULL) { celix_logAdmin_setLogDetailedCmd(admin, NULL, arg1, outStream, errorStream); } else { - fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); + (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. ( or "); } } else { - fprintf(errorStream, "Unexpected sub command '%s'. Expected empty, log or sink command.'n", subCmd); + (void)fprintf(errorStream, "Unexpected sub command '%s'. Expected empty, log or sink command.'n", subCmd); } } else { celix_logAdmin_InfoCmd(admin, outStream, errorStream); @@ -644,14 +725,23 @@ static bool celix_logAdmin_executeCommand(void *handle, const char *commandLine, celix_log_admin_t* celix_logAdmin_create(celix_bundle_context_t *ctx) { celix_log_admin_t* admin = calloc(1, sizeof(*admin)); + celix_autoptr(celix_string_hash_map_t) loggers = celix_stringHashMap_create(); + celix_autoptr(celix_string_hash_map_t) sinks = celix_stringHashMap_create(); + + if (!admin || !loggers || !sinks) { + (void)fprintf(stderr, "Error creating log admin. Out of memory.\n"); + free(admin); + return NULL; + } + admin->ctx = ctx; - admin->loggers = hashMap_create((void*)celix_utils_stringHash, NULL, (void*)celix_utils_stringEquals, NULL); - admin->sinks = hashMap_create((void*)celix_utils_stringHash, NULL, (void*)celix_utils_stringEquals, NULL); + admin->loggers = celix_steal_ptr(loggers); + admin->sinks = celix_steal_ptr(sinks); admin->fallbackToStdOut = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOG_ADMIN_FALLBACK_TO_STDOUT_CONFIG_NAME, CELIX_LOG_ADMIN_FALLBACK_TO_STDOUT_DEFAULT_VALUE); admin->alwaysLogToStdOut = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOG_ADMIN_ALWAYS_USE_STDOUT_CONFIG_NAME, CELIX_LOG_ADMIN_ALWAYS_USE_STDOUT_DEFAULT_VALUE); const char* logLevelStr = celix_bundleContext_getProperty(ctx, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_CONFIG_NAME, CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL_DEFAULT_VALUE); - admin->logServicesDefaultActiveLogLevel = celix_logUtils_logLevelFromString(logLevelStr, CELIX_LOG_LEVEL_INFO); + admin->logServicesDefaultActiveLogLevel = celix_logLevel_fromString(logLevelStr, CELIX_LOG_LEVEL_INFO); admin->sinksDefaultEnabled = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOG_ADMIN_LOG_SINKS_DEFAULT_ENABLED_CONFIG_NAME, CELIX_LOG_ADMIN_SINKS_DEFAULT_ENABLED_DEFAULT_VALUE); celixThreadRwlock_create(&admin->lock, NULL); @@ -718,7 +808,7 @@ celix_log_admin_t* celix_logAdmin_create(celix_bundle_context_t *ctx) { } void celix_logAdmin_destroy(celix_log_admin_t *admin) { - if (admin != NULL) { + if (admin) { celix_logAdmin_remLogSvcForName(admin, CELIX_LOG_ADMIN_FRAMEWORK_LOG_NAME); celix_bundleContext_unregisterServiceAsync(admin->ctx, admin->cmdSvcId, NULL, NULL); @@ -727,11 +817,11 @@ void celix_logAdmin_destroy(celix_log_admin_t *admin) { celix_bundleContext_stopTrackerAsync(admin->ctx, admin->logWriterTrackerId, NULL, NULL); celix_bundleContext_waitForEvents(admin->ctx); - assert(hashMap_size(admin->loggers) == 0); //note stopping service tracker tracker should triggered all needed remove events - hashMap_destroy(admin->loggers, false, false); + assert(celix_stringHashMap_size(admin->loggers) == 0); //note stopping service tracker tracker should triggered all needed remove events + celix_stringHashMap_destroy(admin->loggers); - assert(hashMap_size(admin->sinks) == 0); //note stopping service tracker should triggered all needed remove events - hashMap_destroy(admin->sinks, false, false); + assert(celix_stringHashMap_size(admin->sinks) == 0); //note stopping service tracker should triggered all needed remove events + celix_stringHashMap_destroy(admin->sinks); celixThreadRwlock_destroy(&admin->lock); free(admin); diff --git a/bundles/logging/log_admin/src/celix_log_admin.h b/bundles/logging/log_admin/src/celix_log_admin.h index b4ed5d41a..5a53e46c1 100644 --- a/bundles/logging/log_admin/src/celix_log_admin.h +++ b/bundles/logging/log_admin/src/celix_log_admin.h @@ -69,6 +69,18 @@ celix_log_admin_t* celix_logAdmin_create(celix_bundle_context_t* ctx); */ void celix_logAdmin_destroy(celix_log_admin_t* admin); +//Note exposed for testing purposes +void celix_logAdmin_addLogSvcForName(celix_log_admin_t* admin, const char* name); + +//Note exposed for testing purposes +void celix_logAdmin_addSink(void* handle, void* svc, const celix_properties_t* props); + +//Note exposed for testing purposes +size_t celix_logAdmin_nrOfLogServices(void* handle, const char* select); + +//Note exposed for testing purposes +size_t celix_logAdmin_nrOfSinks(void *handle, const char* select); + #ifdef __cplusplus }; #endif diff --git a/bundles/logging/log_service_api/include/celix_log_control.h b/bundles/logging/log_service_api/include/celix_log_control.h index 98c9a31da..61f04e4aa 100644 --- a/bundles/logging/log_service_api/include/celix_log_control.h +++ b/bundles/logging/log_service_api/include/celix_log_control.h @@ -23,7 +23,6 @@ #include #include #include "celix_log_level.h" -#include "celix_array_list.h" #ifdef __cplusplus extern "C" { @@ -36,13 +35,42 @@ extern "C" { typedef struct celix_log_control { void *handle; + /** + * @brief Get the number of provided log services. + */ size_t (*nrOfLogServices)(void *handle, const char* select); + /** + * @brief Get the number of tracked log sinks. + */ size_t (*nrOfSinks)(void *handle, const char* select); - size_t (*setActiveLogLevels)(void *handle, const char* select, celix_log_level_e activeLogLevel); + /** + * @brief Set the active log level for the selected loggers. + * + * Selection is done by using strcasestr on the logger name, so the select string can be a part of the logger name. + * For example, if the select string is "foo" all loggers with "foo" in the name will be selected, like "foo.bar" + * and "bar.foo". + * + * @param[in] handle The service handle. + * @param[in] select The select string that specifies the case-insensitive string part of target loggers. + * @param[in] activeLogLevel The active log level to set. + * @return The number of loggers selected. + */ + size_t (*setActiveLogLevels)(void* handle, const char* select, celix_log_level_e activeLogLevel); - size_t (*setSinkEnabled)(void *handle, const char* select, bool enabled); + /** + * @brief Set the active log level for the selected loggers. + * + * Selection is done by using strcasestr on the sink name, so the select string can be a part of the sink name. + * For example, if the select string is "foo" all sinks with "foo" in the name will be selected, like "foo.bar" + * and "bar.foo". + * + * @param[in] handle The service handle. + * @param[in] select The select string that specifies the case-insensitive string part of target sinks. + * @param[in] activeLogLevel The active log level to set. + */ + size_t (*setSinkEnabled)(void* handle, const char* select, bool enabled); /** * @brief Get a list of names for the log service provided by the log service. @@ -60,8 +88,22 @@ typedef struct celix_log_control { */ celix_array_list_t* (*currentSinks)(void *handle); + /** + * @brief Get the active log level for the selected logger. + * @param[in] handle The service handle. + * @param[in] loggerName The name of the target logger. + * @param[out[ outActiveLogLevel The active log level of the target logger. Cannot be NULL. + * @return True if the target logger is found, false otherwise. + */ bool (*logServiceInfo)(void *handle, const char* loggerName, celix_log_level_e* outActiveLogLevel); + /** + * @brief Get the enabled state of the selected sink. + * @param[in] handle The service handle. + * @param[in] sinkName The name of the target sink. + * @param[out] outEnabled The enabled state of the target sink. Cannot be NULL. + * @return True if the target sink is found, false otherwise. + */ bool (*sinkInfo)(void *handle, const char* sinkName, bool *outEnabled); /**