You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2024/09/30 15:01:50 UTC
(celix) branch feature/improve-configurability-log-admin created (now f0835a5fa)
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a change to branch feature/improve-configurability-log-admin
in repository https://gitbox.apache.org/repos/asf/celix.git
at f0835a5fa Update log admin to support configure log level per logger
This branch includes the following new commits:
new f0835a5fa Update log admin to support configure log level per logger
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(celix) 01/01: Update log admin to support configure log level per logger
Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/improve-configurability-log-admin
in repository https://gitbox.apache.org/repos/asf/celix.git
commit f0835a5facc2a21b6aaf9a61b14887d051f17677
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Sep 29 22:02:00 2024 +0200
Update log admin to support configure log level per logger
---
bundles/logging/README.md | 8 +-
bundles/logging/log_admin/CMakeLists.txt | 15 +-
bundles/logging/log_admin/gtest/CMakeLists.txt | 15 +-
.../log_admin/gtest/src/LogAdminTestSuite.cc | 124 ++++++++-
.../src/LogAdminWithErrorInjectionTestSuite.cc | 141 ++++++++++
bundles/logging/log_admin/src/celix_log_admin.c | 308 +++++++++++++--------
bundles/logging/log_admin/src/celix_log_admin.h | 12 +
.../log_service_api/include/celix_log_control.h | 48 +++-
8 files changed, 537 insertions(+), 134 deletions(-)
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_<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
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=\"$<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 ()
+
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 <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"
@@ -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 @@ public:
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);
}
@@ -70,7 +72,7 @@ public:
-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_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);
+}
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 <gtest/gtest.h>
+
+#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 <celix_log_service.h>
+
+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<celix::Framework> 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 <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#include <stdio.h>
#include <celix_constants.h>
#include <celix_log_control.h>
#include <assert.h>
+#include <strings.h>
+#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 <celix_stdlib_cleanup.h>
+#include <ctype.h>
#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. (<log_level> or <log_service_selection> <log_level>");
+ (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. (<log_level> or <log_service_selection> <log_level>");
}
} 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. (<true|false> or <log_service_selection> <true|false>");
+ (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. (<true|false> or <log_service_selection> <true|false>");
}
} 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. (<true|false> or <log_service_selection> <true|false>");
+ (void)fprintf(errorStream, "Invalid arguments. For log command expected 1 or 2 args. (<true|false> or <log_service_selection> <true|false>");
}
} 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 <stdbool.h>
#include <stddef.h>
#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);
/**