/** *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. */ /* * log.c * * \date Jun 26, 2011 * \author Apache Celix Project Team * \copyright Apache License, Version 2.0 */ #include #include "log.h" #include "linked_list_iterator.h" #include "array_list.h" struct log { linked_list_pt entries; celix_thread_mutex_t lock; array_list_pt listeners; array_list_pt listenerEntries; celix_thread_t listenerThread; bool running; celix_thread_cond_t entriesToDeliver; celix_thread_mutex_t deliverLock; celix_thread_mutex_t listenerLock; int max_size; bool store_debug; }; static celix_status_t log_startListenerThread(log_pt logger); static celix_status_t log_stopListenerThread(log_pt logger); static void *log_listenerThread(void *data); celix_status_t log_create(int max_size, bool store_debug, log_pt *logger) { celix_status_t status = CELIX_ENOMEM; *logger = calloc(1, sizeof(**logger)); if (*logger != NULL) { linkedList_create(&(*logger)->entries); status = celixThreadMutex_create(&(*logger)->lock, NULL); (*logger)->listeners = NULL; (*logger)->listenerEntries = NULL; (*logger)->listenerThread = celix_thread_default; (*logger)->running = false; (*logger)->max_size = max_size; (*logger)->store_debug = store_debug; arrayList_create(&(*logger)->listeners); arrayList_create(&(*logger)->listenerEntries); if (celixThreadCondition_init(&(*logger)->entriesToDeliver, NULL) != CELIX_SUCCESS) { status = CELIX_INVALID_SYNTAX; } else if (celixThreadMutex_create(&(*logger)->deliverLock, NULL) != CELIX_SUCCESS) { status = CELIX_INVALID_SYNTAX; } else if (celixThreadMutex_create(&(*logger)->listenerLock, NULL) != CELIX_SUCCESS) { status = CELIX_INVALID_SYNTAX; } else { status = CELIX_SUCCESS; } } return status; } celix_status_t log_destroy(log_pt logger) { celix_status_t status = CELIX_SUCCESS; celixThreadMutex_destroy(&logger->listenerLock); celixThreadMutex_destroy(&logger->deliverLock); celixThreadCondition_destroy(&logger->entriesToDeliver); arrayList_destroy(logger->listeners); linked_list_iterator_pt iter = linkedListIterator_create(logger->entries, 0); while (linkedListIterator_hasNext(iter)) { log_entry_pt entry = linkedListIterator_next(iter); if (arrayList_contains(logger->listenerEntries, entry)) { arrayList_removeElement(logger->listenerEntries, entry); } logEntry_destroy(&entry); } linkedListIterator_destroy(iter); array_list_iterator_pt entryIter = arrayListIterator_create(logger->listenerEntries); while (arrayListIterator_hasNext(entryIter)) { log_entry_pt entry = arrayListIterator_next(entryIter); logEntry_destroy(&entry); } arrayListIterator_destroy(entryIter); arrayList_destroy(logger->listenerEntries); linkedList_destroy(logger->entries); celixThreadMutex_destroy(&logger->lock); free(logger); return status; } celix_status_t log_addEntry(log_pt log, log_entry_pt entry) { celixThreadMutex_lock(&log->lock); if (log->max_size != 0) { if (log->store_debug || entry->level != OSGI_LOGSERVICE_DEBUG) { linkedList_addElement(log->entries, entry); } } celixThreadMutex_lock(&log->deliverLock); arrayList_add(log->listenerEntries, entry); celixThreadMutex_unlock(&log->deliverLock); celixThreadCondition_signal(&log->entriesToDeliver); if (log->max_size != 0) { if (log->max_size != -1) { if (linkedList_size(log->entries) > log->max_size) { log_entry_pt rentry = linkedList_removeFirst(log->entries); if (rentry) { celixThreadMutex_lock(&log->deliverLock); arrayList_removeElement(log->listenerEntries, rentry); logEntry_destroy(&rentry); celixThreadMutex_unlock(&log->deliverLock); } } } } celixThreadMutex_unlock(&log->lock); return CELIX_SUCCESS; } celix_status_t log_getEntries(log_pt log, linked_list_pt *list) { linked_list_pt entries = NULL; if (linkedList_create(&entries) == CELIX_SUCCESS) { linked_list_iterator_pt iter = NULL; celixThreadMutex_lock(&log->lock); iter = linkedListIterator_create(log->entries, 0); while (linkedListIterator_hasNext(iter)) { linkedList_addElement(entries, linkedListIterator_next(iter)); } linkedListIterator_destroy(iter); *list = entries; celixThreadMutex_unlock(&log->lock); return CELIX_SUCCESS; } else { return CELIX_ENOMEM; } } celix_status_t log_bundleChanged(void *listener, bundle_event_pt event) { celix_status_t status = CELIX_SUCCESS; log_pt logger = ((bundle_listener_pt) listener)->handle; log_entry_pt entry = NULL; int messagesLength = 10; char *messages[] = { "BUNDLE_EVENT_INSTALLED", "BUNDLE_EVENT_STARTED", "BUNDLE_EVENT_STOPPED", "BUNDLE_EVENT_UPDATED", "BUNDLE_EVENT_UNINSTALLED", "BUNDLE_EVENT_RESOLVED", "BUNDLE_EVENT_UNRESOLVED", "BUNDLE_EVENT_STARTING", "BUNDLE_EVENT_STOPPING", "BUNDLE_EVENT_LAZY_ACTIVATION" }; char *message = NULL; int i = 0; for (i = 0; i < messagesLength; i++) { if (event->type >> i == 1) { message = messages[i]; } } if (message != NULL) { status = logEntry_create(event->bundle, NULL, OSGI_LOGSERVICE_INFO, message, 0, &entry); if (status == CELIX_SUCCESS) { status = log_addEntry(logger, entry); } } return status; } celix_status_t log_frameworkEvent(void *listener, framework_event_pt event) { celix_status_t status = CELIX_SUCCESS; log_pt logger = ((framework_listener_pt) listener)->handle; log_entry_pt entry = NULL; status = logEntry_create(event->bundle, NULL, (event->type == OSGI_FRAMEWORK_EVENT_ERROR) ? OSGI_LOGSERVICE_ERROR : OSGI_LOGSERVICE_INFO, event->error, event->errorCode, &entry); if (status == CELIX_SUCCESS) { status = log_addEntry(logger, entry); } return status; } celix_status_t log_addLogListener(log_pt logger, log_listener_pt listener) { celix_status_t status = CELIX_SUCCESS; status = celixThreadMutex_lock(&logger->listenerLock); if (status == CELIX_SUCCESS) { arrayList_add(logger->listeners, listener); log_startListenerThread(logger); status = celixThreadMutex_unlock(&logger->listenerLock); } return status; } celix_status_t log_removeLogListener(log_pt logger, log_listener_pt listener) { celix_status_t status = CELIX_SUCCESS; bool last = false; status = CELIX_DO_IF(status, celixThreadMutex_lock(&logger->deliverLock)); status = CELIX_DO_IF(status, celixThreadMutex_lock(&logger->listenerLock)); if (status == CELIX_SUCCESS) { arrayList_removeElement(logger->listeners, listener); if (arrayList_size(logger->listeners) == 0) { status = log_stopListenerThread(logger); last = true; } status = CELIX_DO_IF(status, celixThreadMutex_unlock(&logger->listenerLock)); status = CELIX_DO_IF(status, celixThreadMutex_unlock(&logger->deliverLock)); if (last) { status = CELIX_DO_IF(status, celixThread_join(logger->listenerThread, NULL)); } } if (status != CELIX_SUCCESS) { status = CELIX_SERVICE_EXCEPTION; } return status; } celix_status_t log_removeAllLogListener(log_pt logger) { celix_status_t status = CELIX_SUCCESS; status = celixThreadMutex_lock(&logger->listenerLock); if (status == CELIX_SUCCESS) { arrayList_clear(logger->listeners); status = celixThreadMutex_unlock(&logger->listenerLock); } return status; } static celix_status_t log_startListenerThread(log_pt logger) { celix_status_t status = CELIX_SUCCESS; logger->running = true; logger->running = true; status = celixThread_create(&logger->listenerThread, NULL, log_listenerThread, logger); return status; } static celix_status_t log_stopListenerThread(log_pt logger) { celix_status_t status = CELIX_SUCCESS; logger->running = false; status = celixThreadCondition_signal(&logger->entriesToDeliver); return status; } static void * log_listenerThread(void *data) { celix_status_t status = CELIX_SUCCESS; log_pt logger = data; while (logger->running) { status = celixThreadMutex_lock(&logger->deliverLock); if ( status != CELIX_SUCCESS) { logger->running = false; } else { if (!arrayList_isEmpty(logger->listenerEntries)) { log_entry_pt entry = (log_entry_pt) arrayList_remove(logger->listenerEntries, 0); if (entry) { status = celixThreadMutex_lock(&logger->listenerLock); if (status != CELIX_SUCCESS) { logger->running = false; break; } else { array_list_iterator_pt it = arrayListIterator_create(logger->listeners); while (arrayListIterator_hasNext(it)) { log_listener_pt listener = arrayListIterator_next(it); listener->logged(listener, entry); } arrayListIterator_destroy(it); // destroy not-stored entries if (!(logger->store_debug || entry->level != OSGI_LOGSERVICE_DEBUG)) { logEntry_destroy(&entry); } status = celixThreadMutex_unlock(&logger->listenerLock); if (status != CELIX_SUCCESS) { logger->running = false; break; } } } } if (arrayList_isEmpty(logger->listenerEntries) && logger->running) { celixThreadCondition_wait(&logger->entriesToDeliver, &logger->deliverLock); } status = celixThreadMutex_unlock(&logger->deliverLock); if (status != CELIX_SUCCESS) { logger->running = false; break; } } } celixThread_exit(NULL); return NULL; }