Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ boot/Makefile.common	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -74,5 +74,6 @@
 	$(USPACE_PATH)/srv/locsrv/locsrv \
 	$(USPACE_PATH)/srv/bd/rd/rd \
-	$(USPACE_PATH)/srv/vfs/vfs
+	$(USPACE_PATH)/srv/vfs/vfs \
+	$(USPACE_PATH)/srv/logger/logger
 
 ifeq ($(RDFMT),tmpfs)
@@ -165,4 +166,5 @@
 	$(USPACE_PATH)/app/killall/killall \
 	$(USPACE_PATH)/app/loc/loc \
+	$(USPACE_PATH)/app/logset/logset \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/mkexfat/mkexfat \
Index: kernel/genarch/include/multiboot/multiboot.h
===================================================================
--- kernel/genarch/include/multiboot/multiboot.h	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ kernel/genarch/include/multiboot/multiboot.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -99,4 +99,5 @@
 
 extern void multiboot_extract_command(char *, size_t, const char *);
+extern void multiboot_extract_argument(char *, size_t, const char *);
 extern void multiboot_info_parse(uint32_t, const multiboot_info_t *);
 
Index: kernel/genarch/src/multiboot/multiboot.c
===================================================================
--- kernel/genarch/src/multiboot/multiboot.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ kernel/genarch/src/multiboot/multiboot.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -71,4 +71,33 @@
 }
 
+/** Extract arguments from the multiboot module command line.
+ *
+ * @param buf      Destination buffer (will be always NULL-terminated).
+ * @param size     Size of destination buffer (in bytes).
+ * @param cmd_line Input string (the command line).
+ *
+ */
+void multiboot_extract_argument(char *buf, size_t size, const char *cmd_line)
+{
+	/* Start after first space. */
+	const char *start = str_chr(cmd_line, ' ');
+	if (start == NULL) {
+		str_cpy(buf, size, "");
+		return;
+	}
+
+	const char *end = cmd_line + str_size(cmd_line);
+
+	/* Skip the space(s). */
+	while (start != end) {
+		if (start[0] == ' ')
+			start++;
+		else
+			break;
+	}
+
+	str_ncpy(buf, size, start, (size_t) (end - start));
+}
+
 static void multiboot_modules(uint32_t count, multiboot_module_t *mods)
 {
@@ -84,6 +113,10 @@
 			multiboot_extract_command(init.tasks[init.cnt].name,
 			    CONFIG_TASK_NAME_BUFLEN, MULTIBOOT_PTR(mods[i].string));
-		} else
+			multiboot_extract_argument(init.tasks[init.cnt].arguments,
+			    CONFIG_TASK_ARGUMENTS_BUFLEN, MULTIBOOT_PTR(mods[i].string));
+		} else {
 			init.tasks[init.cnt].name[0] = 0;
+			init.tasks[init.cnt].arguments[0] = 0;
+		}
 		
 		init.cnt++;
Index: kernel/genarch/src/multiboot/multiboot2.c
===================================================================
--- kernel/genarch/src/multiboot/multiboot2.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ kernel/genarch/src/multiboot/multiboot2.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -49,4 +49,6 @@
 		multiboot_extract_command(init.tasks[init.cnt].name,
 		    CONFIG_TASK_NAME_BUFLEN, module->string);
+		multiboot_extract_argument(init.tasks[init.cnt].arguments,
+		    CONFIG_TASK_ARGUMENTS_BUFLEN, module->string);
 		
 		init.cnt++;
Index: kernel/generic/include/config.h
===================================================================
--- kernel/generic/include/config.h	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ kernel/generic/include/config.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -47,4 +47,5 @@
 #define CONFIG_INIT_TASKS        32
 #define CONFIG_TASK_NAME_BUFLEN  32
+#define CONFIG_TASK_ARGUMENTS_BUFLEN 64
 
 #ifndef __ASM__
@@ -56,4 +57,5 @@
 	size_t size;
 	char name[CONFIG_TASK_NAME_BUFLEN];
+	char arguments[CONFIG_TASK_ARGUMENTS_BUFLEN];
 } init_task_t;
 
Index: kernel/generic/src/main/kinit.c
===================================================================
--- kernel/generic/src/main/kinit.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ kernel/generic/src/main/kinit.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -69,4 +69,5 @@
 #include <str.h>
 #include <sysinfo/stats.h>
+#include <sysinfo/sysinfo.h>
 #include <align.h>
 
@@ -179,4 +180,26 @@
 	program_t programs[CONFIG_INIT_TASKS];
 	
+	// FIXME: do not propagate arguments through sysinfo
+	// but pass them directly to the tasks
+	for (i = 0; i < init.cnt; i++) {
+		const char *arguments = init.tasks[i].arguments;
+		if (str_length(arguments) == 0)
+			continue;
+		if (str_length(init.tasks[i].name) == 0)
+			continue;
+		size_t arguments_size = str_size(arguments);
+
+		void *arguments_copy = malloc(arguments_size, 0);
+		if (arguments_copy == NULL)
+			continue;
+		memcpy(arguments_copy, arguments, arguments_size);
+
+		char item_name[CONFIG_TASK_NAME_BUFLEN + 15];
+		snprintf(item_name, CONFIG_TASK_NAME_BUFLEN + 15,
+		    "init_args.%s", init.tasks[i].name);
+
+		sysinfo_set_item_data(item_name, NULL, arguments_copy, arguments_size);
+	}
+
 	for (i = 0; i < init.cnt; i++) {
 		if (init.tasks[i].paddr % FRAME_SIZE) {
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -47,4 +47,5 @@
 	app/klog \
 	app/loc \
+	app/logset \
 	app/mkfat \
 	app/mkexfat \
@@ -74,4 +75,5 @@
 	srv/clipboard \
 	srv/locsrv \
+	srv/logger \
 	srv/devman \
 	srv/loader \
Index: uspace/app/logset/Makefile
===================================================================
--- uspace/app/logset/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/logset/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2012 Vojtech Horky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = logset
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/logset/main.c
===================================================================
--- uspace/app/logset/main.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/logset/main.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup logset
+ * @{
+ */
+/** @file Change logger behavior.
+ */
+#include <stdio.h>
+#include <async.h>
+#include <errno.h>
+#include <str_error.h>
+#include <io/logctl.h>
+
+static log_level_t parse_log_level_or_die(const char *log_level)
+{
+	log_level_t result;
+	int rc = log_level_from_str(log_level, &result);
+	if (rc != EOK) {
+		fprintf(stderr, "Unrecognised log level '%s': %s.\n",
+		    log_level, str_error(rc));
+		exit(2);
+	}
+	return result;
+}
+
+static void usage(const char *progname)
+{
+	fprintf(stderr, "Usage:\n");
+	fprintf(stderr, "  %s <default-logging-level>\n", progname);
+	fprintf(stderr, "  %s <namespace> <logging-level>\n", progname);
+	fprintf(stderr, "  %s <namespace> <context> <logging-level>\n", progname);
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc == 2) {
+		log_level_t new_default_level = parse_log_level_or_die(argv[1]);
+		int rc = logctl_set_default_level(new_default_level);
+
+		if (rc != EOK) {
+			fprintf(stderr, "Failed to change default logging level: %s.\n",
+			    str_error(rc));
+			return 2;
+		}
+	} else if (argc == 3) {
+		log_level_t new_level = parse_log_level_or_die(argv[2]);
+		const char *namespace = argv[1];
+		int rc = logctl_set_namespace_level(namespace, new_level);
+
+		if (rc != EOK) {
+			fprintf(stderr, "Failed to change logging level: %s.\n",
+			    str_error(rc));
+			return 2;
+		}
+	} else if (argc == 4) {
+		log_level_t new_level = parse_log_level_or_die(argv[3]);
+		const char *namespace = argv[1];
+		const char *context = argv[2];
+		int rc = logctl_set_context_level(namespace, context, new_level);
+		if (rc != EOK) {
+			fprintf(stderr, "Failed to change logging level: %s.\n",
+			    str_error(rc));
+			return 2;
+		}
+	} else {
+		usage(argv[0]);
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/app/tester/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -45,4 +45,6 @@
 	stdio/stdio1.c \
 	stdio/stdio2.c \
+	stdio/logger1.c \
+	stdio/logger2.c \
 	fault/fault1.c \
 	fault/fault2.c \
Index: uspace/app/tester/stdio/logger1.c
===================================================================
--- uspace/app/tester/stdio/logger1.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/tester/stdio/logger1.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <io/log.h>
+#include "../tester.h"
+
+const char *test_logger1(void)
+{
+	for (log_level_t level = 0; level < LVL_LIMIT; level++) {
+		log_msg(level, "Testing logger, level %d.", (int) level);
+	}
+
+	return NULL;
+}
Index: uspace/app/tester/stdio/logger1.def
===================================================================
--- uspace/app/tester/stdio/logger1.def	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/tester/stdio/logger1.def	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,6 @@
+{
+	"logger1",
+	"Logger printing test",
+	&test_logger1,
+	true
+},
Index: uspace/app/tester/stdio/logger2.c
===================================================================
--- uspace/app/tester/stdio/logger2.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/tester/stdio/logger2.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <io/log.h>
+#include <async.h>
+#include "../tester.h"
+
+const char *test_logger2(void)
+{
+	log_context_t ctx_alpha = log_context_create("alpha");
+	log_context_t ctx_bravo = log_context_create("bravo");
+
+	TPRINTF("Alpha context is %" PRIlogctx ".\n", ctx_alpha);
+	TPRINTF("Bravo context is %" PRIlogctx ".\n", ctx_bravo);
+
+	while (true) {
+		/*
+		 * Intentionally skipping FATAL to allow muting
+		 * the output completely by setting visible level to FATAL.
+		 */
+		for (log_level_t level = LVL_ERROR; level < LVL_LIMIT; level++) {
+			log_msg(level, "Printing level %d (%s).",
+			    (int) level, log_level_str(level));
+			log_ctx_msg(ctx_alpha, level,
+			    "Printing level %d (%s) in alpha context.",
+			    (int) level, log_level_str(level));
+			log_ctx_msg(ctx_bravo, level,
+			    "Printing level %d (%s) in bravo context.",
+			    (int) level, log_level_str(level));
+			async_usleep(1000 * 100);
+		}
+	}
+
+	return NULL;
+}
Index: uspace/app/tester/stdio/logger2.def
===================================================================
--- uspace/app/tester/stdio/logger2.def	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/app/tester/stdio/logger2.def	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,6 @@
+{
+	"logger2",
+	"Logger endless test",
+	&test_logger2,
+	false
+},
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/app/tester/tester.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -39,4 +39,5 @@
 #include <stdio.h>
 #include <str.h>
+#include <io/log.h>
 #include "tester.h"
 
@@ -55,4 +56,6 @@
 #include "stdio/stdio1.def"
 #include "stdio/stdio2.def"
+#include "stdio/logger1.def"
+#include "stdio/logger2.def"
 #include "fault/fault1.def"
 #include "fault/fault2.def"
@@ -138,4 +141,6 @@
 	}
 	
+	log_init("tester", LVL_NOTE);
+
 	test_quiet = false;
 	test_argc = argc - 2;
Index: uspace/app/tester/tester.h
===================================================================
--- uspace/app/tester/tester.h	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/app/tester/tester.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -88,4 +88,6 @@
 extern const char *test_stdio1(void);
 extern const char *test_stdio2(void);
+extern const char *test_logger1(void);
+extern const char *test_logger2(void);
 extern const char *test_fault1(void);
 extern const char *test_fault2(void);
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/lib/c/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -97,4 +97,5 @@
 	generic/io/printf.c \
 	generic/io/log.c \
+	generic/io/logctl.c \
 	generic/io/klog.c \
 	generic/io/snprintf.c \
Index: uspace/lib/c/generic/io/log.c
===================================================================
--- uspace/lib/c/generic/io/log.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/lib/c/generic/io/log.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -38,26 +38,175 @@
 #include <stdlib.h>
 #include <stdio.h>
-
+#include <async.h>
 #include <io/log.h>
-
-/** Serialization mutex for logging functions. */
-static FIBRIL_MUTEX_INITIALIZE(log_serializer);
-
-/** Current log level. */
-static log_level_t log_level;
-
-static FILE *log_stream;
-
+#include <ipc/logger.h>
+#include <ns.h>
+
+/** Log messages are printed under this name. */
 static const char *log_prog_name;
 
-/** Prefixes for individual logging levels. */
 static const char *log_level_names[] = {
-	[LVL_FATAL] = "Fatal error",
-	[LVL_ERROR] = "Error",
-	[LVL_WARN] = "Warning",
-	[LVL_NOTE] = "Note",
-	[LVL_DEBUG] = "Debug",
-	[LVL_DEBUG2] = "Debug2"
+	"fatal",
+	"error",
+	"warn",
+	"note",
+	"debug",
+	"debug2",
+	NULL
 };
+
+/** IPC session with the logger service. */
+static async_sess_t *logger_session;
+
+/** Maximum length of a single log message (in bytes). */
+#define MESSAGE_BUFFER_SIZE 4096
+
+FIBRIL_RWLOCK_INITIALIZE(current_observed_level_lock);
+log_level_t current_observed_level;
+
+static int logger_register(async_sess_t *session, const char *prog_name)
+{
+	async_exch_t *exchange = async_exchange_begin(session);
+	if (exchange == NULL) {
+		return ENOMEM;
+	}
+
+	aid_t reg_msg = async_send_0(exchange, LOGGER_REGISTER, NULL);
+	int rc = async_data_write_start(exchange, prog_name, str_size(prog_name));
+	sysarg_t reg_msg_rc;
+	async_wait_for(reg_msg, &reg_msg_rc);
+
+	async_exchange_end(exchange);
+
+	if (rc != EOK) {
+		return rc;
+	}
+
+	return reg_msg_rc;
+}
+
+static int logger_message(async_sess_t *session, log_context_t ctx, log_level_t level, const char *message)
+{
+	async_exch_t *exchange = async_exchange_begin(session);
+	if (exchange == NULL) {
+		return ENOMEM;
+	}
+
+	aid_t reg_msg = async_send_2(exchange, LOGGER_MESSAGE,
+	    ctx, level, NULL);
+	int rc = async_data_write_start(exchange, message, str_size(message));
+	sysarg_t reg_msg_rc;
+	async_wait_for(reg_msg, &reg_msg_rc);
+
+	async_exchange_end(exchange);
+
+	/*
+	 * Getting ENAK means no-one wants our message. That is not an
+	 * error at all.
+	 */
+	if (rc == ENAK)
+		rc = EOK;
+
+	if (rc != EOK) {
+		return rc;
+	}
+
+	return reg_msg_rc;
+}
+
+static void cannot_use_level_changed_monitor(void)
+{
+	fibril_rwlock_write_lock(&current_observed_level_lock);
+	current_observed_level = LVL_LIMIT;
+	fibril_rwlock_write_unlock(&current_observed_level_lock);
+}
+
+static int observed_level_changed_monitor(void *arg)
+{
+	async_sess_t *monitor_session = service_connect_blocking(EXCHANGE_SERIALIZE, SERVICE_LOGGER, LOGGER_INTERFACE_SINK, 0);
+	if (monitor_session == NULL) {
+		cannot_use_level_changed_monitor();
+		return ENOMEM;
+	}
+
+	int rc = logger_register(monitor_session, log_prog_name);
+	if (rc != EOK) {
+		cannot_use_level_changed_monitor();
+		return rc;
+	}
+
+	async_exch_t *exchange = async_exchange_begin(monitor_session);
+	if (exchange == NULL) {
+		cannot_use_level_changed_monitor();
+		return ENOMEM;
+	}
+
+	while (true) {
+		sysarg_t has_reader;
+		sysarg_t msg_rc = async_req_0_1(exchange,
+		    LOGGER_BLOCK_UNTIL_READER_CHANGED, &has_reader);
+		if (msg_rc != EOK) {
+			cannot_use_level_changed_monitor();
+			break;
+		}
+
+		fibril_rwlock_write_lock(&current_observed_level_lock);
+		if ((bool) has_reader) {
+			current_observed_level = LVL_LIMIT;
+		} else {
+			current_observed_level = LVL_NOTE;
+		}
+		fibril_rwlock_write_unlock(&current_observed_level_lock);
+	}
+
+	async_exchange_end(exchange);
+
+	return EOK;
+}
+
+static log_level_t get_current_observed_level(void)
+{
+	fibril_rwlock_read_lock(&current_observed_level_lock);
+	log_level_t level = current_observed_level;
+	fibril_rwlock_read_unlock(&current_observed_level_lock);
+	return level;
+}
+
+const char *log_level_str(log_level_t level)
+{
+	if (level >= LVL_LIMIT)
+		return "unknown";
+	else
+		return log_level_names[level];
+}
+
+int log_level_from_str(const char *name, log_level_t *level_out)
+{
+	log_level_t level = LVL_FATAL;
+
+	while (log_level_names[level] != NULL) {
+		if (str_cmp(name, log_level_names[level]) == 0) {
+			if (level_out != NULL)
+				*level_out = level;
+			return EOK;
+		}
+		level++;
+	}
+
+	/* Maybe user specified number directly. */
+	char *end_ptr;
+	int level_int = strtol(name, &end_ptr, 0);
+	if ((end_ptr == name) || (str_length(end_ptr) != 0))
+		return EINVAL;
+	if (level_int < 0)
+		return ERANGE;
+	if (level_int >= (int) LVL_LIMIT)
+		return ERANGE;
+
+	if (level_out != NULL)
+		*level_out = (log_level_t) level_int;
+
+	return EOK;
+}
 
 /** Initialize the logging system.
@@ -69,12 +218,55 @@
 {
 	assert(level < LVL_LIMIT);
-	log_level = level;
-
-	log_stream = stdout;
+
 	log_prog_name = str_dup(prog_name);
 	if (log_prog_name == NULL)
 		return ENOMEM;
 
-	return EOK;
+	logger_session = service_connect_blocking(EXCHANGE_SERIALIZE, SERVICE_LOGGER, LOGGER_INTERFACE_SINK, 0);
+	if (logger_session == NULL) {
+		return ENOMEM;
+	}
+
+	int rc = logger_register(logger_session, log_prog_name);
+
+	current_observed_level = LVL_NOTE;
+
+	fid_t observed_level_changed_fibril = fibril_create(observed_level_changed_monitor, NULL);
+	if (observed_level_changed_fibril == 0) {
+		cannot_use_level_changed_monitor();
+	} else {
+		fibril_add_ready(observed_level_changed_fibril);
+	}
+
+	return rc;
+}
+
+/** Create logging context.
+ *
+ * This function always returns a valid context.
+ */
+log_context_t log_context_create(const char *name)
+{
+	async_exch_t *exchange = async_exchange_begin(logger_session);
+	if (exchange == NULL)
+		return LOG_CONTEXT_DEFAULT;
+
+	ipc_call_t answer;
+	aid_t reg_msg = async_send_0(exchange, LOGGER_CREATE_CONTEXT, &answer);
+	int rc = async_data_write_start(exchange, name, str_size(name));
+	sysarg_t reg_msg_rc;
+	async_wait_for(reg_msg, &reg_msg_rc);
+
+	async_exchange_end(exchange);
+
+	if ((rc != EOK) || (reg_msg_rc != EOK))
+		return LOG_CONTEXT_DEFAULT;
+
+	return IPC_GET_ARG1(answer);
+}
+
+bool _log_shall_record(log_context_t context, log_level_t level)
+{
+	return get_current_observed_level() >= level;
 }
 
@@ -86,10 +278,10 @@
  * @param fmt		Format string (no traling newline).
  */
-void log_msg(log_level_t level, const char *fmt, ...)
+void _log_ctx_msg(log_context_t ctx, log_level_t level, const char *fmt, ...)
 {
 	va_list args;
 
 	va_start(args, fmt);
-	log_msgv(level, fmt, args);
+	_log_ctx_msgv(ctx, level, fmt, args);
 	va_end(args);
 }
@@ -102,20 +294,19 @@
  * @param fmt		Format string (no trailing newline)
  */
-void log_msgv(log_level_t level, const char *fmt, va_list args)
+void _log_ctx_msgv(log_context_t ctx, log_level_t level, const char *fmt, va_list args)
 {
 	assert(level < LVL_LIMIT);
 
-	/* Higher number means higher verbosity. */
-	if (level <= log_level) {
-		fibril_mutex_lock(&log_serializer);
-
-		fprintf(log_stream, "%s: %s: ", log_prog_name,
-		    log_level_names[level]);
-		vfprintf(log_stream, fmt, args);
-		fputc('\n', log_stream);
-		fflush(log_stream);
-
-		fibril_mutex_unlock(&log_serializer);
-	}
+	if (get_current_observed_level() < level) {
+		return;
+	}
+
+	char *message_buffer = malloc(MESSAGE_BUFFER_SIZE);
+	if (message_buffer == NULL) {
+		return;
+	}
+
+	vsnprintf(message_buffer, MESSAGE_BUFFER_SIZE, fmt, args);
+	logger_message(logger_session, ctx, level, message_buffer);
 }
 
Index: uspace/lib/c/generic/io/logctl.c
===================================================================
--- uspace/lib/c/generic/io/logctl.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/lib/c/generic/io/logctl.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <io/logctl.h>
+#include <ipc/logger.h>
+#include <sysinfo.h>
+#include <ns.h>
+#include <str.h>
+
+#define SYSINFO_LOGGGER_BOOT_ARGUMENT "init_args.logger"
+
+/** IPC session with the logger service. */
+static async_sess_t *logger_session = NULL;
+
+static int start_logger_exchange(async_exch_t **exchange_out)
+{
+	assert(exchange_out != NULL);
+
+	if (logger_session == NULL) {
+		logger_session = service_connect_blocking(EXCHANGE_SERIALIZE,
+		    SERVICE_LOGGER, LOGGER_INTERFACE_CONTROL, 0);
+		if (logger_session == NULL)
+			return ENOMEM;
+	}
+
+	assert(logger_session != NULL);
+
+	async_exch_t *exchange = async_exchange_begin(logger_session);
+	if (exchange == NULL)
+		return ENOMEM;
+
+	*exchange_out = exchange;
+
+	return EOK;
+}
+
+
+int logctl_set_default_level(log_level_t new_level)
+{
+	async_exch_t *exchange = NULL;
+	int rc = start_logger_exchange(&exchange);
+	if (rc != EOK)
+		return rc;
+
+	rc = (int) async_req_1_0(exchange,
+	    LOGGER_CTL_SET_DEFAULT_LEVEL, new_level);
+
+	async_exchange_end(exchange);
+
+	return rc;
+}
+
+int logctl_set_namespace_level(const char *namespace, log_level_t new_level)
+{
+	async_exch_t *exchange = NULL;
+	int rc = start_logger_exchange(&exchange);
+	if (rc != EOK)
+		return rc;
+
+	aid_t reg_msg = async_send_1(exchange, LOGGER_CTL_SET_NAMESPACE_LEVEL,
+	    new_level, NULL);
+	rc = async_data_write_start(exchange, namespace, str_size(namespace));
+	sysarg_t reg_msg_rc;
+	async_wait_for(reg_msg, &reg_msg_rc);
+
+	async_exchange_end(exchange);
+
+	if (rc != EOK) {
+		return rc;
+	}
+
+	return (int) reg_msg_rc;
+}
+
+int logctl_set_context_level(const char *namespace, const char *context, log_level_t new_level)
+{
+	async_exch_t *exchange = NULL;
+	int rc = start_logger_exchange(&exchange);
+	if (rc != EOK)
+		return rc;
+
+	aid_t reg_msg = async_send_1(exchange, LOGGER_CTL_SET_CONTEXT_LEVEL,
+	    new_level, NULL);
+	rc = async_data_write_start(exchange, namespace, str_size(namespace));
+	int rc2 = async_data_write_start(exchange, context, str_size(context));
+	sysarg_t reg_msg_rc;
+	async_wait_for(reg_msg, &reg_msg_rc);
+
+	async_exchange_end(exchange);
+
+	if (rc != EOK)
+		return rc;
+
+	if (rc2 != EOK)
+		return rc2;
+
+	return (int) reg_msg_rc;
+}
+
+
+int logctl_get_boot_level(log_level_t *level)
+{
+	size_t argument_size;
+	void *argument = sysinfo_get_data(SYSINFO_LOGGGER_BOOT_ARGUMENT, &argument_size);
+	if (argument == NULL)
+		return EINVAL;
+
+	char level_str[20];
+	str_cpy(level_str, 20, (const char *) argument);
+
+	log_level_t boot_level;
+	int rc = log_level_from_str(level_str, &boot_level);
+	if (rc != EOK)
+		return rc;
+
+	if (level != NULL)
+		*level = (log_level_t) boot_level;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/include/io/log.h
===================================================================
--- uspace/lib/c/include/io/log.h	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/lib/c/include/io/log.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -36,4 +36,6 @@
 
 #include <stdarg.h>
+#include <inttypes.h>
+#include <bool.h>
 
 typedef enum {
@@ -49,7 +51,37 @@
 } log_level_t;
 
+typedef sysarg_t log_context_t;
+#define PRIlogctx PRIxn
+#define LOG_CONTEXT_DEFAULT 0
+
+extern const char *log_level_str(log_level_t);
+extern int log_level_from_str(const char *, log_level_t *);
+
+extern bool _log_shall_record(log_context_t, log_level_t);
 extern int log_init(const char *, log_level_t);
-extern void log_msg(log_level_t, const char *, ...);
-extern void log_msgv(log_level_t, const char *, va_list);
+
+extern log_context_t log_context_create(const char *);
+
+#define log_ctx_msg(context, level, format, ...) \
+	do { \
+		if (_log_shall_record((context), (level))) { \
+			_log_ctx_msg((context), (level), format, ##__VA_ARGS__); \
+		} \
+	} while (false)
+
+#define log_ctx_msgv(context, level, format, args) \
+	do { \
+		if (_log_shall_record((context), (level))) { \
+			_log_ctx_msgv((context), (level), format, args); \
+		} \
+	} while (false)
+
+#define log_msg(level, format, ...) \
+	log_ctx_msg(LOG_CONTEXT_DEFAULT, (level), (format), ##__VA_ARGS__)
+#define log_msgv(level, format, args) \
+	log_ctx_msgv(LOG_CONTEXT_DEFAULT, (level), (format), (args))
+
+extern void _log_ctx_msg(log_context_t, log_level_t, const char *, ...);
+extern void _log_ctx_msgv(log_context_t, log_level_t, const char *, va_list);
 
 #endif
Index: uspace/lib/c/include/io/logctl.h
===================================================================
--- uspace/lib/c/include/io/logctl.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/lib/c/include/io/logctl.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011-2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+
+#ifndef LIBC_IO_LOGCTL_H_
+#define LIBC_IO_LOGCTL_H_
+
+#include <io/log.h>
+
+extern int logctl_set_default_level(log_level_t);
+extern int logctl_set_namespace_level(const char *, log_level_t);
+extern int logctl_set_context_level(const char *, const char *, log_level_t);
+extern int logctl_get_boot_level(log_level_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/logger.h
===================================================================
--- uspace/lib/c/include/ipc/logger.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/lib/c/include/ipc/logger.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+
+#ifndef LIBC_IPC_LOGGER_H_
+#define LIBC_IPC_LOGGER_H_
+
+#include <ipc/common.h>
+
+typedef enum {
+	LOGGER_CTL_GET_DEFAULT_LEVEL = IPC_FIRST_USER_METHOD,
+	LOGGER_CTL_SET_DEFAULT_LEVEL,
+	LOGGER_CTL_SET_NAMESPACE_LEVEL,
+	LOGGER_CTL_SET_CONTEXT_LEVEL
+} logger_control_request_t;
+
+typedef enum {
+	LOGGER_REGISTER = IPC_FIRST_USER_METHOD,
+	LOGGER_CREATE_CONTEXT,
+	LOGGER_MESSAGE,
+	LOGGER_BLOCK_UNTIL_READER_CHANGED
+} logger_sink_request_t;
+
+typedef enum {
+	 LOGGER_CONNECT = IPC_FIRST_USER_METHOD,
+	 LOGGER_GET_MESSAGE
+} logger_source_request_t;
+
+typedef enum {
+	/** Interface for controlling logger behavior. */
+	LOGGER_INTERFACE_CONTROL,
+	/** Interface for servers writing to the log. */
+	LOGGER_INTERFACE_SINK,
+	/** Interface for clients displaying the log. */
+	LOGGER_INTERFACE_SOURCE
+} logger_interface_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/services.h
===================================================================
--- uspace/lib/c/include/ipc/services.h	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/lib/c/include/ipc/services.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -45,4 +45,5 @@
 	SERVICE_VFS        = FOURCC('v', 'f', 's', ' '),
 	SERVICE_LOC        = FOURCC('l', 'o', 'c', ' '),
+	SERVICE_LOGGER     = FOURCC('l', 'o', 'g', 'g'),
 	SERVICE_DEVMAN     = FOURCC('d', 'e', 'v', 'n'),
 	SERVICE_IRC        = FOURCC('i', 'r', 'c', ' '),
Index: uspace/lib/usb/src/debug.c
===================================================================
--- uspace/lib/usb/src/debug.c	(revision 13c4fe0f11335fa9bae9ef702016fe2e3a748030)
+++ uspace/lib/usb/src/debug.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -74,4 +74,5 @@
 		}
 	}
+	log_init(message_prefix, LVL_DEBUG);
 }
 
@@ -148,4 +149,8 @@
 	}
 
+	va_start(args, format);
+	log_msgv(level, format, args);
+	va_end(args);
+
 	fibril_mutex_unlock(&log_serializer);
 }
Index: uspace/srv/logger/Makefile
===================================================================
--- uspace/srv/logger/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/srv/logger/Makefile	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2012 Vojtech Horky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = logger
+STATIC_NEEDED = y
+
+SOURCES = \
+	level.c \
+	main.c \
+	namespace.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/logger/level.c
===================================================================
--- uspace/srv/logger/level.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/srv/logger/level.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @addtogroup logger
+ * @{
+ */
+
+/** @file
+ */
+
+#include <errno.h>
+#include "logger.h"
+
+log_level_t default_logging_level = LVL_NOTE;
+static FIBRIL_MUTEX_INITIALIZE(default_logging_level_guard);
+
+log_level_t get_default_logging_level(void)
+{
+	fibril_mutex_lock(&default_logging_level_guard);
+	log_level_t result = default_logging_level;
+	fibril_mutex_unlock(&default_logging_level_guard);
+	return result;
+}
+
+int set_default_logging_level(log_level_t new_level)
+{
+	if (new_level >= LVL_LIMIT)
+		return EINVAL;
+	fibril_mutex_lock(&default_logging_level_guard);
+	default_logging_level = new_level;
+	fibril_mutex_unlock(&default_logging_level_guard);
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/logger/logger.h
===================================================================
--- uspace/srv/logger/logger.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/srv/logger/logger.h	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup logger
+ * @{
+ */
+/** @file Common logger service definitions.
+ */
+
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include <adt/list.h>
+#include <adt/prodcons.h>
+#include <io/log.h>
+#include <bool.h>
+#include <fibril_synch.h>
+
+#define NAME "logger"
+#define MAX_NAMESPACE_LENGTH 256
+#define LOG_LEVEL_USE_DEFAULT (LVL_LIMIT + 1)
+
+typedef struct logging_namespace logging_namespace_t;
+
+logging_namespace_t *namespace_create(const char *);
+const char *namespace_get_name(logging_namespace_t *);
+void namespace_destroy(logging_namespace_t *);
+logging_namespace_t *namespace_writer_attach(const char *);
+void namespace_writer_detach(logging_namespace_t *);
+
+int namespace_change_level(logging_namespace_t *, log_level_t);
+
+void namespace_wait_for_reader_change(logging_namespace_t *, bool *);
+bool namespace_has_reader(logging_namespace_t *, sysarg_t, log_level_t);
+void namespace_add_message(logging_namespace_t *, const char *, sysarg_t, log_level_t);
+
+int namespace_create_context(logging_namespace_t *, const char *);
+int namespace_change_context_level(logging_namespace_t *, const char *, log_level_t);
+
+log_level_t get_default_logging_level(void);
+int set_default_logging_level(log_level_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/logger/main.c
===================================================================
--- uspace/srv/logger/main.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/srv/logger/main.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup logger Logging service.
+ * @brief HelenOS logging service.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <ipc/services.h>
+#include <ipc/logger.h>
+#include <io/log.h>
+#include <io/logctl.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <malloc.h>
+#include "logger.h"
+
+static int handle_namespace_level_change(sysarg_t new_level)
+{
+	void *namespace_name;
+	int rc = async_data_write_accept(&namespace_name, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	logging_namespace_t *namespace = namespace_writer_attach((const char *) namespace_name);
+	free(namespace_name);
+	if (namespace == NULL)
+		return ENOENT;
+
+	rc = namespace_change_level(namespace, (log_level_t) new_level);
+	namespace_writer_detach(namespace);
+
+	return rc;
+}
+
+static int handle_context_level_change(sysarg_t new_level)
+{
+	void *namespace_name;
+	int rc = async_data_write_accept(&namespace_name, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	logging_namespace_t *namespace = namespace_writer_attach((const char *) namespace_name);
+	free(namespace_name);
+	if (namespace == NULL)
+		return ENOENT;
+
+	void *context_name;
+	rc = async_data_write_accept(&context_name, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		namespace_writer_detach(namespace);
+		return rc;
+	}
+
+	rc = namespace_change_context_level(namespace, context_name, new_level);
+	free(context_name);
+	namespace_writer_detach(namespace);
+
+	return rc;
+}
+
+static void connection_handler_control(void)
+{
+	printf(NAME "/control: new client.\n");
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call))
+			break;
+
+		int rc;
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOGGER_CTL_GET_DEFAULT_LEVEL:
+			async_answer_1(callid, EOK, get_default_logging_level());
+			break;
+		case LOGGER_CTL_SET_DEFAULT_LEVEL:
+			rc = set_default_logging_level(IPC_GET_ARG1(call));
+			async_answer_0(callid, rc);
+			break;
+		case LOGGER_CTL_SET_NAMESPACE_LEVEL:
+			rc = handle_namespace_level_change(IPC_GET_ARG1(call));
+			async_answer_0(callid, rc);
+			break;
+		case LOGGER_CTL_SET_CONTEXT_LEVEL:
+			rc = handle_context_level_change(IPC_GET_ARG1(call));
+			async_answer_0(callid, rc);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+			break;
+		}
+	}
+
+	printf(NAME "/control: client terminated.\n");
+}
+
+static logging_namespace_t *find_namespace_and_attach_writer(void)
+{
+	ipc_call_t call;
+	ipc_callid_t callid = async_get_call(&call);
+
+	if (IPC_GET_IMETHOD(call) != LOGGER_REGISTER) {
+		async_answer_0(callid, EINVAL);
+		return NULL;
+	}
+
+	void *name;
+	int rc = async_data_write_accept(&name, true, 1, MAX_NAMESPACE_LENGTH, 0, NULL);
+	async_answer_0(callid, rc);
+
+	if (rc != EOK) {
+		return NULL;
+	}
+
+	logging_namespace_t *result = namespace_writer_attach((const char *) name);
+
+	free(name);
+
+	return result;
+}
+
+static int handle_receive_message(logging_namespace_t *namespace, sysarg_t context, int level)
+{
+	bool skip_message = !namespace_has_reader(namespace, context, level);
+	if (skip_message) {
+		/* Abort the actual message buffer transfer. */
+		ipc_callid_t callid;
+		size_t size;
+		int rc = ENAK;
+		if (!async_data_write_receive(&callid, &size))
+			rc = EINVAL;
+
+		async_answer_0(callid, rc);
+		return rc;
+	}
+
+	void *message;
+	int rc = async_data_write_accept(&message, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	namespace_add_message(namespace, message, context, level);
+
+	free(message);
+
+	return EOK;
+}
+
+static int handle_create_context(logging_namespace_t *namespace, sysarg_t *idx)
+{
+	void *name;
+	int rc = async_data_write_accept(&name, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	rc = namespace_create_context(namespace, name);
+
+	free(name);
+
+	if (rc < 0)
+		return rc;
+
+	*idx = (sysarg_t) rc;
+	return EOK;
+}
+
+static void connection_handler_sink(logging_namespace_t *namespace)
+{
+	printf(NAME "/sink: new client %s.\n", namespace_get_name(namespace));
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		if (!IPC_GET_IMETHOD(call))
+			break;
+
+		int rc;
+		sysarg_t arg = 0;
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOGGER_CREATE_CONTEXT:
+			rc = handle_create_context(namespace, &arg);
+			async_answer_1(callid, rc, arg);
+			break;
+		case LOGGER_MESSAGE:
+			rc = handle_receive_message(namespace, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
+			async_answer_0(callid, rc);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+			break;
+		}
+	}
+
+	printf(NAME "/sink: client %s terminated.\n", namespace_get_name(namespace));
+	namespace_writer_detach(namespace);
+}
+
+static void connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	logger_interface_t iface = IPC_GET_ARG1(*icall);
+
+	logging_namespace_t *namespace = NULL;
+
+	switch (iface) {
+	case LOGGER_INTERFACE_CONTROL:
+		async_answer_0(iid, EOK);
+		connection_handler_control();
+		break;
+	case LOGGER_INTERFACE_SINK:
+		/* First call has to be the registration. */
+		async_answer_0(iid, EOK);
+		namespace = find_namespace_and_attach_writer();
+		if (namespace == NULL) {
+			fprintf(stderr, NAME ": failed to register namespace.\n");
+			break;
+		}
+		connection_handler_sink(namespace);
+		break;
+	default:
+		async_answer_0(iid, EINVAL);
+		break;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS Logging Service\n");
+	
+	/* Get default logging level from sysinfo (if available). */
+	log_level_t boot_logging_level = LVL_NOTE;
+	int rc = logctl_get_boot_level(&boot_logging_level);
+	if (rc == EOK)
+		set_default_logging_level(boot_logging_level);
+	else
+		printf(NAME ": Warn: failed to get logging level from sysinfo: %s.\n",
+		    str_error(rc));
+
+	async_set_client_connection(connection_handler);
+	
+	rc = service_register(SERVICE_LOGGER);
+	if (rc != EOK) {
+		printf(NAME ": failed to register: %s.\n", str_error(rc));
+		return -1;
+	}
+	
+	printf(NAME ": Accepting connections\n");
+	async_manager();
+	
+	/* Never reached */
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/logger/namespace.c
===================================================================
--- uspace/srv/logger/namespace.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
+++ uspace/srv/logger/namespace.c	(revision dc5aa568a4c8addb5f59d28700a384a1570b43d0)
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2012 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup logger
+ * @{
+ */
+#include <assert.h>
+#include <malloc.h>
+#include <str.h>
+#include <stdio.h>
+#include <errno.h>
+#include "logger.h"
+
+/** @file
+ * Logging namespaces.
+ */
+
+#define CONTEXT_SIZE 16
+
+typedef struct {
+	const char *name;
+	log_level_t level;
+} logging_context_t;
+
+struct logging_namespace {
+	fibril_mutex_t guard;
+	size_t writers_count;
+	fibril_condvar_t level_changed_cv;
+	FILE *logfile;
+	log_level_t level;
+	const char *name;
+
+	// FIXME: make dynamic
+	size_t context_count;
+	logging_context_t context[CONTEXT_SIZE];
+
+	link_t link;
+};
+
+static FIBRIL_MUTEX_INITIALIZE(namespace_list_guard);
+static LIST_INITIALIZE(namespace_list);
+
+static log_level_t namespace_get_actual_log_level(logging_namespace_t *namespace, sysarg_t context)
+{
+	fibril_mutex_lock(&namespace->guard);
+	if (context >= namespace->context_count) {
+		fibril_mutex_unlock(&namespace->guard);
+		fprintf(stderr, "Invalid context!\n");
+		return LVL_FATAL;
+	}
+	log_level_t level = namespace->context[context].level;
+	fibril_mutex_unlock(&namespace->guard);
+
+	if (level == LOG_LEVEL_USE_DEFAULT)
+		level = get_default_logging_level();
+
+	return level;
+}
+
+static logging_namespace_t *namespace_find_no_lock(const char *name)
+{
+	list_foreach(namespace_list, it) {
+		logging_namespace_t *namespace = list_get_instance(it, logging_namespace_t, link);
+		if (str_cmp(namespace->name, name) == 0) {
+			return namespace;
+		}
+	}
+
+	return NULL;
+}
+
+static logging_namespace_t *namespace_create_no_lock(const char *name)
+{
+	logging_namespace_t *existing = namespace_find_no_lock(name);
+	if (existing != NULL) {
+		return NULL;
+	}
+
+	logging_namespace_t *namespace = malloc(sizeof(logging_namespace_t));
+	if (namespace == NULL) {
+		return NULL;
+	}
+
+	namespace->name = str_dup(name);
+	if (namespace->name == NULL) {
+		free(namespace);
+		return NULL;
+	}
+
+	char *logfilename;
+	int rc = asprintf(&logfilename, "/log/%s", name);
+	if (rc < 0) {
+		free(namespace->name);
+		free(namespace);
+		return NULL;
+	}
+	namespace->logfile = fopen(logfilename, "a");
+	free(logfilename);
+	if (namespace->logfile == NULL) {
+		free(namespace->name);
+		free(namespace);
+		return NULL;
+	}
+
+	namespace->level = LOG_LEVEL_USE_DEFAULT;
+
+	namespace->context_count = 1;
+	namespace->context[0].name = "";
+	namespace->context[0].level = LOG_LEVEL_USE_DEFAULT;
+
+	fibril_mutex_initialize(&namespace->guard);
+	fibril_condvar_initialize(&namespace->level_changed_cv);
+	namespace->writers_count = 0;
+	link_initialize(&namespace->link);
+
+	list_append(&namespace->link, &namespace_list);
+
+	return namespace;
+}
+
+
+logging_namespace_t *namespace_create(const char *name)
+{
+	fibril_mutex_lock(&namespace_list_guard);
+	logging_namespace_t *result = namespace_create_no_lock(name);
+	fibril_mutex_unlock(&namespace_list_guard);
+	return result;
+}
+
+const char *namespace_get_name(logging_namespace_t *namespace)
+{
+	assert(namespace);
+	return namespace->name;
+}
+
+static void namespace_destroy_careful(logging_namespace_t *namespace)
+{
+	assert(namespace);
+	fibril_mutex_lock(&namespace_list_guard);
+
+	fibril_mutex_lock(&namespace->guard);
+	if (namespace->writers_count > 0) {
+		fibril_mutex_unlock(&namespace->guard);
+		fibril_mutex_unlock(&namespace_list_guard);
+		return;
+	}
+
+	list_remove(&namespace->link);
+
+	fibril_mutex_unlock(&namespace->guard);
+	fibril_mutex_unlock(&namespace_list_guard);
+
+	// TODO - destroy pending messages
+	fclose(namespace->logfile);
+	free(namespace->name);
+	free(namespace);
+}
+
+void namespace_destroy(logging_namespace_t *namespace)
+{
+	namespace_destroy_careful(namespace);
+}
+
+logging_namespace_t *namespace_writer_attach(const char *name)
+{
+	logging_namespace_t *namespace = NULL;
+
+	fibril_mutex_lock(&namespace_list_guard);
+
+	namespace = namespace_find_no_lock(name);
+
+	if (namespace == NULL) {
+		namespace = namespace_create_no_lock(name);
+	}
+
+	fibril_mutex_lock(&namespace->guard);
+	namespace->writers_count++;
+	fibril_mutex_unlock(&namespace->guard);
+
+	fibril_mutex_unlock(&namespace_list_guard);
+
+	return namespace;
+}
+
+void namespace_writer_detach(logging_namespace_t *namespace)
+{
+	fibril_mutex_lock(&namespace->guard);
+	assert(namespace->writers_count > 0);
+	namespace->writers_count--;
+	fibril_mutex_unlock(&namespace->guard);
+
+	namespace_destroy_careful(namespace);
+}
+
+int namespace_change_level(logging_namespace_t *namespace, log_level_t level)
+{
+	if (level >= LVL_LIMIT)
+		return ERANGE;
+
+	fibril_mutex_lock(&namespace->guard);
+	namespace->level = level;
+	for (size_t i = 0; i < namespace->context_count; i++) {
+		namespace->context[i].level = level;
+	}
+	fibril_condvar_broadcast(&namespace->level_changed_cv);
+	fibril_mutex_unlock(&namespace->guard);
+
+	return EOK;
+}
+
+
+bool namespace_has_reader(logging_namespace_t *namespace, sysarg_t context, log_level_t level)
+{
+	return level <= namespace_get_actual_log_level(namespace, context);
+}
+
+int namespace_create_context(logging_namespace_t *namespace, const char *name)
+{
+	int rc;
+	fibril_mutex_lock(&namespace->guard);
+	if (namespace->context_count >= CONTEXT_SIZE) {
+		rc = ELIMIT;
+		goto leave;
+	}
+
+	namespace->context[namespace->context_count].level
+	    = LOG_LEVEL_USE_DEFAULT;
+	namespace->context[namespace->context_count].name
+	    = str_dup(name);
+	if (namespace->context[namespace->context_count].name == NULL) {
+		rc = ENOMEM;
+		goto leave;
+	}
+	rc = (int) namespace->context_count;
+	namespace->context_count++;
+
+
+leave:
+	fibril_mutex_unlock(&namespace->guard);
+	return rc;
+}
+
+int namespace_change_context_level(logging_namespace_t *namespace, const char *context, log_level_t new_level)
+{
+	if (new_level >= LVL_LIMIT)
+		return ERANGE;
+
+	int rc;
+	fibril_mutex_lock(&namespace->guard);
+	for (size_t i = 0; i < namespace->context_count; i++) {
+		if (str_cmp(namespace->context[i].name, context) == 0) {
+			namespace->context[i].level = new_level;
+			rc = EOK;
+			fibril_condvar_broadcast(&namespace->level_changed_cv);
+			goto leave;
+		}
+	}
+	rc =  ENOENT;
+
+leave:
+	fibril_mutex_unlock(&namespace->guard);
+	return rc;
+}
+
+void namespace_wait_for_reader_change(logging_namespace_t *namespace, bool *has_reader_now)
+{
+	fibril_mutex_lock(&namespace->guard);
+	// FIXME: properly watch for state change
+	fibril_condvar_wait(&namespace->level_changed_cv, &namespace->guard);
+	*has_reader_now = true;
+	fibril_mutex_unlock(&namespace->guard);
+}
+
+
+void namespace_add_message(logging_namespace_t *namespace, const char *message, sysarg_t context, log_level_t level)
+{
+	if (level <= namespace_get_actual_log_level(namespace, context)) {
+		const char *level_name = log_level_str(level);
+		if (context == 0) {
+			printf("[%s %s]: %s\n",
+			    namespace->name, level_name, message);
+			fprintf(namespace->logfile, "%s: %s\n",
+			    level_name, message);
+		} else {
+			const char *context_name = namespace->context[context].name;
+			printf("[%s/%s %s]: %s\n",
+			    namespace->name, context_name, level_name, message);
+			fprintf(namespace->logfile, "[%s] %s: %s\n",
+			    context_name, level_name, message);
+		}
+		fflush(namespace->logfile);
+		fflush(stdout);
+	}
+}
+
+
+/**
+ * @}
+ */
