/**
*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;
}