diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f2269239913b1e9e77532883da8cf4e563d68f99..8f77eea4848b735b3489d93238e3335e36cf04c5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -24,7 +24,6 @@ variables:
   CPPLIB_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/src/caosdb-cpplib/testenv:$CI_COMMIT_REF_NAME
 
   CPPINTTEST_PIPELINE: https://gitlab.indiscale.com/api/v4/projects/111/trigger/pipeline
-  CPPINTTEST_BRANCHES: https://gitlab.indiscale.com/api/v4/projects/111/repository/branches
   GIT_SUBMODULE_STRATEGY: normal
 
   ## FOR DEBUGGING
@@ -50,7 +49,6 @@ info:
     - echo "Pipeline triggered by $TRIGGERED_BY_REPO@$TRIGGERED_BY_REF ($TRIGGERED_BY_HASH)"
     - echo "$CPPLIB_REGISTRY_IMAGE"
     - echo "$CPPINTTEST_PIPELINE"
-    - echo "$CPPINTTEST_BRANCHES"
     - echo "$GIT_SUBMODULE_STRATEGY"
 
 # Build a docker image in which tests for this repository can run
@@ -85,6 +83,9 @@ test:
     - mkdir build
     - cd build
     - conan install .. -s "compiler.libcxx=libstdc++11"
+    - FILE_TO_BE_PATCHED="$(grep "std::unique_ptr<ContextAllocator> context_allocator) {}" -l -r $(conan config home))"
+    - echo "FILE_TO_BE_PATCHED=$FILE_TO_BE_PATCHED"
+    - sed -e "s|std::unique_ptr<ContextAllocator> context_allocator) {}|std::unique_ptr<ContextAllocator> /*context_allocator*/) {}|" -i $FILE_TO_BE_PATCHED
     - cmake -DCMAKE_BUILD_TYPE=Debug ..
     - cmake --build .
     - cmake --build . --target unit_test_coverage
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 91c0423ade5bd19d649237dc0a4a14eec0301300..edf3cd1dd859ae648d12acadf8e06f8657946737 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,9 +18,9 @@
 # along with this program. If not, see <https://www.gnu.org/licenses/>.
 #
 
-cmake_minimum_required(VERSION 3.14)
+cmake_minimum_required(VERSION 3.13)
 
-set(libcaosdb_VERSION 0.0.5)
+set(libcaosdb_VERSION 0.0.6)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0)
@@ -31,11 +31,20 @@ project(libcaosdb
     DESCRIPTION "C and C++ client libraries for CaosDB"
     LANGUAGES CXX C)
 
+set(CMAKE_C_STANDARD 99)
 set(CMAKE_CXX_STANDARD 17)
+
+set(CMAKE_C_EXTENSIONS OFF)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 
+IF (WIN32)
+    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+ENDIF()
+
 ###########################################
 ### DEPENDENCY MANAGEMENT with CONAN
 ###########################################
@@ -74,8 +83,13 @@ set(PROTO_FILES
 set(PROTO_PATH ${PROJECT_SOURCE_DIR}/proto/proto)
 
 # compiler binaries
-set(_PROTOBUF_PROTOC "${CMAKE_BINARY_DIR}/build_tools/protoc")
-set(_GRPC_CPP_PLUGIN_EXECUTABLE "${CMAKE_BINARY_DIR}/build_tools/grpc_cpp_plugin")
+IF (WIN32)
+    set(_PROTOBUF_PROTOC "${CMAKE_BINARY_DIR}/build_tools/protoc.exe")
+    set(_GRPC_CPP_PLUGIN_EXECUTABLE "${CMAKE_BINARY_DIR}/build_tools/grpc_cpp_plugin.exe")
+ELSE()
+    set(_PROTOBUF_PROTOC "${CMAKE_BINARY_DIR}/build_tools/protoc")
+    set(_GRPC_CPP_PLUGIN_EXECUTABLE "${CMAKE_BINARY_DIR}/build_tools/grpc_cpp_plugin")
+ENDIF()
 
 # Generated sources
 list(LENGTH PROTO_FILES len_proto_files)
@@ -193,6 +207,39 @@ target_link_libraries(cxxcaosdbcli
 ### LINTING with CLANG-TIDY and INCLUDE-WHAT-YOU-USE
 #######################################################
 
+###########################################
+### PARANOID COMPILER SETTINGS
+###########################################
+option(PARANOID_COMPILER_SETTINGS "Enable extra-paranoid compiler settings
+(which may even flag errors for code in the dependencies. These only apply in
+Debug BUILD_TYPE with SKIP_LINTING=Off or when LINTING=On." OFF)
+include(CheckCXXCompilerFlag)
+include(CheckCCompilerFlag)
+
+function(add_compiler_flag flag)
+    string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" cxx_present)
+    if(cxx_present EQUAL -1)
+        check_cxx_compiler_flag("${flag}" flag_supported)
+        if(flag_supported)
+            set(PEDANTIC_CMAKE_CXX_FLAGS "${PEDANTIC_CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
+        endif()
+        unset(flag_supported CACHE)
+    endif()
+    unset(cxx_present CACHE)
+
+    string(FIND "${CMAKE_C_FLAGS}" "${flag}" c_present)
+    if(c_present EQUAL -1)
+        check_cxx_compiler_flag("${flag}" flag_supported)
+        if(flag_supported)
+            set(PEDANTIC_CMAKE_C_FLAGS "${PEDANTIC_CMAKE_C_FLAGS} ${flag}" PARENT_SCOPE)
+        endif()
+        unset(flag_supported CACHE)
+    endif()
+    unset(c_present CACHE)
+endfunction()
+
+
+
 option(LINTING "Enable linting with clang-tidy and iwyu when in non-Debug build-type" OFF)
 if("${CMAKE_BUILD_TYPE}" MATCHES "Debug" OR LINTING)
     set(_LINTING ON)
@@ -203,6 +250,28 @@ if("${CMAKE_BUILD_TYPE}" MATCHES "Debug" AND SKIP_LINTING)
     set(_LINTING OFF)
 endif()
 if(_LINTING)
+
+    ### set paranoid compiler flags
+    add_compiler_flag("-Wall")
+    add_compiler_flag("-Wextra")
+    add_compiler_flag("-pedantic")
+    add_compiler_flag("-Werror")
+
+    message(STATUS "PEDANTIC_CMAKE_CXX_FLAGS: [${PEDANTIC_CMAKE_CXX_FLAGS}]")
+    set(TARGET_CAOSDB_COMPILE_FLAGS "${TARGET_CAOSDB_COMPILE_FLAGS} ${PEDANTIC_CMAKE_CXX_FLAGS}")
+    set(TARGET_CCAOSDB_COMPILE_FLAGS "${TARGET_CCAOSDB_COMPILE_FLAGS} ${PEDANTIC_CMAKE_C_FLAGS}")
+    set(TARGET_CXXCAOSDBCLI_COMPILE_FLAGS "${TARGET_CXXCAOSDBCLI_COMPILE_FLAGS} ${PEDANTIC_CMAKE_CXX_FLAGS}")
+    set(TARGET_CCAOSDBCLI_COMPILE_FLAGS "${TARGET_CCAOSDBCLI_COMPILE_FLAGS} ${PEDANTIC_CMAKE_C_FLAGS}")
+
+    set_target_properties(caosdb PROPERTIES
+        COMPILE_FLAGS "${TARGET_CAOSDB_COMPILE_FLAGS}")
+    set_target_properties(ccaosdb PROPERTIES
+        COMPILE_FLAGS "${TARGET_CCAOSDB_COMPILE_FLAGS}")
+    set_target_properties(cxxcaosdbcli PROPERTIES
+        COMPILE_FLAGS "${TARGET_CXXCAOSDBCLI_COMPILE_FLAGS}")
+    set_target_properties(ccaosdbcli PROPERTIES
+        COMPILE_FLAGS "${TARGET_CCAOSDBCLI_COMPILE_FLAGS}")
+
     find_program(iwyu
         NAMES include-what-you-use iwyu
         PATHS ${CMAKE_SOURCE_DIR}/tools/include-what-you-use/${iwyu_os}/bin)
@@ -230,10 +299,12 @@ if(_LINTING)
     else()
         message(STATUS "clang-tidy: ${clang_tidy}")
         set(_CMAKE_CXX_CLANG_TIDY_CHECKS
-            "--checks=*,-fuchsia-*,-llvm-include-order,-llvmlibc-*,-readability-convert-member-functions-to-static,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-llvm-else-after-return,-readability-else-after-return")
+            "--checks=*,-fuchsia-*,-llvmlibc-*,-readability-convert-member-functions-to-static,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-llvm-else-after-return,-readability-else-after-return,-modernize-use-trailing-return-type")
+        set(_CMAKE_C_CLANG_TIDY_CHECKS "${_CMAKE_CXX_CLANG_TIDY_CHECKS}")
         set(_CMAKE_CXX_CLANG_TIDY "${clang_tidy}"
             "--header-filter=caosdb/.*[^\(\.pb\.h\)]$"
             "--warnings-as-errors=*")
+        set(_CMAKE_C_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY}")
         option(AUTO_FIX_LINTING "Append --fix option to clang-tidy" OFF)
         if(AUTO_FIX_LINTING)
             set(_CMAKE_CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};--fix")
@@ -246,8 +317,11 @@ if(_LINTING)
         set_target_properties(cxxcaosdbcli PROPERTIES
             CXX_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
             )
+        set_target_properties(ccaosdb PROPERTIES
+            C_CLANG_TIDY "${_CMAKE_C_CLANG_TIDY};${_CMAKE_C_CLANG_TIDY_CHECKS}"
+            )
         set_target_properties(ccaosdbcli PROPERTIES
-            C_CLANG_TIDY "${_CMAKE_CXX_CLANG_TIDY};${_CMAKE_CXX_CLANG_TIDY_CHECKS}"
+            C_CLANG_TIDY "${_CMAKE_C_CLANG_TIDY};${_CMAKE_C_CLANG_TIDY_CHECKS}"
             )
     endif()
 endif()
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 360292a3b5a51cad63e8a5526bff2db81af65a26..63a0678aa601762279c7c46bd71d7582051c4287 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -1,8 +1,13 @@
 # GENERAL
 
-* >=cmake-3.14
+* >=conan-1.37.2
+* >=cmake-3.13
 * >=gcc-10.2.0 | >=clang-11
 
+# OPTIONAL
+
+* For checking the schema of a json configuration file: >=jsonschema-3.2.0
+
 # BUILD DOCS
 
 * doxygen
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..6b881b98fc2d90ba4bff31e9d896590222164806
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,40 @@
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+
+# This Makefile is a wrapper for several other scripts.
+
+
+CLANG-FORMAT = clang-format-11
+
+.PHONY: help
+help:
+	@echo "Targets:"
+	@echo "    conan-install - Install locally with Conan."
+	@echo "    style - auto-format the source files."
+
+style:
+	$(CLANG-FORMAT) -i --verbose \
+	 $$(find test/ src/ include/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in")
+.PHONY: style
+
+conan-install:
+	conan create . -s "compiler.libcxx=libstdc++11"
+.PHONY: install
diff --git a/README_SETUP.md b/README_SETUP.md
index bcda25acc80f00ef9801357f786d973face177fa..aaf0e16a8966d8937ac44d224b4c3c181daf55f5 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -1,10 +1,15 @@
 # How to Develop and Use Libcaosdb
 
+## Dependencies
+
+* See [DEPENDENCIES.md](Dependencies.md)
+
 ## Build
 
 We use [cmake](https://cmake.org) as build tool.
 
-1. `mkdir build && cd build/`
+0. clone/update the subrepo `git submodule update --init proto`
+1. `mkdir build && cd build`
 2. `conan install .. -s "compiler.libcxx=libstdc++11"`
 3. `cmake -B . ..`
 4. `cmake --build .`
@@ -29,6 +34,37 @@ have to add `build/lib/` (or, alternatively after installation,
 `CMAKE_INSTALL_PREFIX/lib`) to your `DYLD_LIBRARY_PATH` environmental
 variable.
 
+### How to build on Windows
+
+We use [Visual Studio 2019](https://visualstudio.microsoft.com/de/vs/features/cplusplus/)
+as compiler. We use [cmake](https://cmake.org/download/) as build tool.
+
+0. clone/update the subrepo `git submodule update --init proto`
+1. `mkdir build`
+2. `cd build`
+3. `conan install .. -g visual_studio -s arch=x86_64 -s build_type=Release -s compiler.toolset=v142 -s compiler.version=16 -s compiler.runtime=MD  --build=missing --update`
+4. `cmake -B . ..`
+5. open ` libcaosdb.sln` with Visual Studio, change the buildtype to `Release`
+   and build the project. (You can open Tools/Command Line/Developer Command
+   Prompt and execute `msbuild libcaosdb.sln /property:Configuration=Release`)
+
+### Creating a Local Conan Build ##
+
+Building and installing libcaosdb with Conan is just a single command:  
+`conan create . -s "compiler.libcxx=libstdc++11"`
+
+For MacOS, you probably should adjust the option as mentioned above.
+
+### Troubleshooting
+
+#### `conan install` Fails Due to Missing Prebuilts
+
+When `conan install` fails during the installation of the dependencies because
+a precompiled package is not available for your specific settings, try adding
+the `--build=missing` option: `conan install .. [ other options
+] --build=missing`. This should download and compile the sources of the
+dependencies.
+
 ## Unit Tests
 
 ### Build
diff --git a/caosdb-client-configuration-schema.json b/caosdb-client-configuration-schema.json
index 882f757636ee82cce994c779b5e174276a89540d..ff1967f639400d26bd33714691f416f10c841cae 100644
--- a/caosdb-client-configuration-schema.json
+++ b/caosdb-client-configuration-schema.json
@@ -24,6 +24,30 @@
         "$ref": "#/definitions/connection_configuration"
       }
     },
+    "logging": {
+      "type": "object",
+      "description": "Configuration of logging",
+      "properties": {
+        "level": {
+          "allOf": [
+            {
+              "$ref": "#/definitions/log_level_configuration"
+            },
+            {
+            "default": "warn",
+            "description": "Global severity filter. Only log messages with at least the given severity are processed by the logging framework. 'off' means that the logging frame work is turned off entirely. Defaults to "
+            }
+          ]
+        },
+        "sinks": {
+          "type": "object",
+          "description": "Configuration of sinks, i.e. the log files, console or other logging frame works where the log messages are supposed to end up eventually.",
+          "additionalProperties": {
+            "$ref": "#/definitions/sink_configuration"
+          }
+        }
+      }
+    },
     "extension": {
       "type": "object",
       "description": "A reserved configuration object which may be used to store additional options for particular clients and special extensions.",
@@ -31,6 +55,58 @@
     }
   },
   "definitions": {
+    "log_level_configuration": {
+      "type": "string",
+      "enum": ["all", "trace", "debug", "info", "warn", "error", "fatal", "off"]
+    },
+    "sink_configuration": {
+      "type": "object",
+      "description": "A single sink configuration.",
+      "additionalProperties": false,
+      "properties": {
+        "level": {
+          "allOf": [
+            {
+              "$ref": "#/definitions/log_level_configuration"
+            },
+            {
+            "description": "Sink severity filter. Only log messages with at least the given severity are passed to this sink. 'off' means that this sink is turned off entirely. Defaults to the global log level."
+            }
+          ]
+        },
+        "destination": {
+          "type": "string",
+          "enum": [ "console", "file", "syslog"],
+          "description": "The type of sink-backend. 'console' writes to the STDERR, 'file' to a text file and 'syslog' uses the syslog API."
+        },
+        "directory": {
+          "type": "string",
+          "description": "Path to local directory. All log files are being stored to this directory. If not specified, the current working directory will be used."
+        },
+        "local_address": {
+          "type": "string",
+          "description": "Local IP address to initiate connection to the syslog server. If not specified, the default local address will be used."
+        },
+        "target_address": {
+          "type": "string",
+          "description": "Remote IP address of the syslog server. If not specified, the local address will be used. "
+        }
+      },
+      "required": [ "destination" ],
+      "allOf": [ {
+          "if": {"properties": {"destination": { "const": "console" } } },
+          "then": {"propertyNames" : { "enum": ["level", "destination"] } }
+        },
+        {
+          "if": {"properties": {"destination": { "const": "file" } } },
+          "then": {"propertyNames" : { "enum": ["level", "destination", "directory"] } }
+        },
+        {
+          "if": {"properties": {"destination": { "const": "syslog" } } },
+          "then": {"propertyNames" : { "enum": ["level", "destination", "target_address", "local_address"] } }
+        }
+      ]
+    },
     "connection_configuration": {
       "type": "object",
       "description": "A single connection configuration.",
diff --git a/conanfile.py b/conanfile.py
index 1ed1abd041788b27a8a63c7286528fbed7cf4740..0fee0db6e8f6bec28fb514d98bda07050171a630 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools
 
 class CaosdbConan(ConanFile):
     name = "caosdb"
-    version = "0.0.5"
+    version = "0.0.6"
     license = "AGPL-3.0-or-later"
     author = "Timm C. Fitschen <t.fitschen@indiscale.com>"
     url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git"
@@ -28,7 +28,7 @@ class CaosdbConan(ConanFile):
 
     def imports(self):
         self.copy("protoc*", "build_tools", "bin")
-        self.copy("grpc_cpp_plugin", "build_tools", "bin")
+        self.copy("grpc_cpp_plugin*", "build_tools", "bin")
 
     def build(self):
         cmake = CMake(self)
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 48f470b38e022dd9deb7c00bf53a09ac62091076..126c54ef2f7d4c26a43a163774c97b7c362643dd 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -21,13 +21,19 @@
 # add all header files to this list
 set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/authentication.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/certificate_provider.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.h
     ${CMAKE_CURRENT_BINARY_DIR}/caosdb/constants.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/entity.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/exceptions.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/info.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/log_level.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/logging.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/message_code.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/status_code.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction_status.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.h
     )
 
diff --git a/include/caosdb/authentication.h b/include/caosdb/authentication.h
index 8071414ef436619def4caf6881ebca027b757a60..fc2420c5dc3100682a16eefb56494a63e64e8162 100644
--- a/include/caosdb/authentication.h
+++ b/include/caosdb/authentication.h
@@ -27,16 +27,15 @@
  * @date 2021-06-28
  * @brief Configuration and setup of the client authentication.
  */
-
-#include <grpcpp/security/credentials.h>               // for CallCredentials
-#include <map>                                         // for multimap
-#include <memory>                                      // for shared_ptr
-#include <string>                                      // for string
 #include "caosdb/utility.h"                            // for base64_encode
 #include "grpcpp/impl/codegen/interceptor.h"           // for Status
 #include "grpcpp/impl/codegen/security/auth_context.h" // for AuthContext
 #include "grpcpp/impl/codegen/status.h"                // for Status
 #include "grpcpp/impl/codegen/string_ref.h"            // for string_ref
+#include <grpcpp/security/credentials.h>               // for CallCredentials
+#include <map>                                         // for multimap
+#include <memory>                                      // for shared_ptr
+#include <string>                                      // for string
 
 namespace caosdb {
 namespace authentication {
diff --git a/include/caosdb/certificate_provider.h b/include/caosdb/certificate_provider.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7d7a156efeaf05435fd3ccb8029549a82e38442
--- /dev/null
+++ b/include/caosdb/certificate_provider.h
@@ -0,0 +1,53 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_CERTIFICATE_PROVIDER_H
+#define CAOSDB_CERTIFICATE_PROVIDER_H
+
+#include "boost/filesystem/path.hpp" // for path
+namespace caosdb::configuration {
+using boost::filesystem::path;
+
+class CertificateProvider {
+public:
+  [[nodiscard]] auto virtual GetCertificatePem() const -> std::string = 0;
+  virtual ~CertificateProvider() = default;
+};
+
+class PemFileCertificateProvider : public CertificateProvider {
+private:
+  std::string certificate_provider;
+
+public:
+  explicit PemFileCertificateProvider(const path &path);
+  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
+};
+
+class PemCertificateProvider : public CertificateProvider {
+private:
+  std::string certificate_provider;
+
+public:
+  explicit PemCertificateProvider(const std::string &certificate_provider);
+  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
+};
+} // namespace caosdb::configuration
+#endif
diff --git a/include/caosdb/configuration.h b/include/caosdb/configuration.h
index 6a59880964f4cec402998e11529b60d459bdc338..6426ab4b9e5b6dbfebb3cce4d67983df58a57a6a 100644
--- a/include/caosdb/configuration.h
+++ b/include/caosdb/configuration.h
@@ -21,17 +21,20 @@
 
 #ifndef CAOSDB_CONFIGURATION_H
 #define CAOSDB_CONFIGURATION_H
-#include <memory>                          // for unique_ptr
-#include <string>                          // for string
 #include "boost/filesystem/operations.hpp" // for exists
 #include "boost/filesystem/path.hpp"       // for path
 #include "boost/json/object.hpp"           // for object
 #include "boost/json/value.hpp"            // for value
-#include "boost/json/value_ref.hpp"        // for array, object
+#include "boost/json/value_ref.hpp"        // IWYU pragma: keep
 #include "caosdb/authentication.h"         // for Authenticator, PlainPassw...
-#include "caosdb/connection.h" // for ConnectionConfiguration, Certifi...
-#include "caosdb/exceptions.h" // for ConfigurationError
-#include "caosdb/utility.h"    // for load_json_file
+#include "caosdb/certificate_provider.h"   // for CertificateProvider, path
+#include "caosdb/exceptions.h"             // for ConfigurationError
+#include "caosdb/logging.h"
+#include "caosdb/utility.h"              // for load_json_file
+#include "grpcpp/security/credentials.h" // for ChannelCredentials
+#include <iosfwd>                        // for ostream
+#include <memory>                        // for unique_ptr, shared_ptr
+#include <string>                        // for string
 
 namespace caosdb::configuration {
 using boost::filesystem::exists;
@@ -41,14 +44,89 @@ using boost::json::object;
 using boost::json::value;
 using caosdb::authentication::Authenticator;
 using caosdb::authentication::PlainPasswordAuthenticator;
-using caosdb::connection::CertificateProvider;
-using caosdb::connection::ConnectionConfiguration;
-using caosdb::connection::ConnectionManager;
-using caosdb::connection::InsecureConnectionConfiguration;
-using caosdb::connection::PemFileCertificateProvider;
-using caosdb::connection::TlsConnectionConfiguration;
 using caosdb::exceptions::ConfigurationError;
 using caosdb::utility::load_json_file;
+using grpc::ChannelCredentials;
+
+const std::string logger_name = "caosdb::configuration";
+
+/**
+ * @brief Configuration of the CaosDB connection.
+ */
+class ConnectionConfiguration {
+private:
+  std::string host;
+  int port;
+
+public:
+  ConnectionConfiguration(const std::string &host, int port);
+  virtual ~ConnectionConfiguration() = default;
+  friend auto operator<<(std::ostream &out,
+                         const ConnectionConfiguration &configuration)
+    -> std::ostream &;
+
+  [[nodiscard]] auto virtual ToString() const -> std::string = 0;
+  [[nodiscard]] auto GetHost() const -> std::string;
+  [[nodiscard]] auto GetPort() const -> int;
+  [[nodiscard]] auto virtual GetChannelCredentials() const
+    -> std::shared_ptr<ChannelCredentials> = 0;
+};
+
+class InsecureConnectionConfiguration : public ConnectionConfiguration {
+private:
+  std::shared_ptr<ChannelCredentials> credentials;
+
+public:
+  InsecureConnectionConfiguration(const std::string &host, int port);
+  [[nodiscard]] auto GetChannelCredentials() const
+    -> std::shared_ptr<ChannelCredentials> override;
+  [[nodiscard]] auto ToString() const -> std::string override;
+};
+
+class TlsConnectionConfiguration : public ConnectionConfiguration {
+private:
+  std::shared_ptr<ChannelCredentials> credentials;
+  std::string certificate_provider;
+
+public:
+  TlsConnectionConfiguration(const std::string &host, int port);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const Authenticator &authenticator);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const CertificateProvider &certificate_provider);
+  TlsConnectionConfiguration(const std::string &host, int port,
+                             const CertificateProvider &certificate_provider,
+                             const Authenticator &authenticator);
+  [[nodiscard]] auto GetChannelCredentials() const
+    -> std::shared_ptr<ChannelCredentials> override;
+  [[nodiscard]] auto ToString() const -> std::string override;
+};
+
+/**
+ * Helper class (no state, just member functions) which should only be used by
+ * the ConfigurationManager to initialize the logging framework from the stored
+ * configuration.
+ */
+class LoggingConfigurationHelper {
+public:
+  friend class ConfigurationManager;
+
+private:
+  auto CreateConsoleSinkConfiguration(const object &from,
+                                      const std::string &name, int level) const
+    -> std::shared_ptr<caosdb::logging::SinkConfiguration>;
+  auto CreateSyslogSinkConfiguration(const object &from,
+                                     const std::string &name, int level) const
+    -> std::shared_ptr<caosdb::logging::SinkConfiguration>;
+  auto CreateFileSinkConfiguration(const object &from, const std::string &name,
+                                   int level) const
+    -> std::shared_ptr<caosdb::logging::SinkConfiguration>;
+  auto CreateSinkConfiguration(const object &from, const std::string &name,
+                               int default_level) const
+    -> std::shared_ptr<caosdb::logging::SinkConfiguration>;
+  auto CreateLoggingConfiguration(const object &from) const
+    -> caosdb::logging::LoggingConfiguration;
+};
 
 /**
  * Helper class (no state, just member functions) which should only be used by
@@ -63,7 +141,7 @@ private:
   /**
    * @param from - a single connection configuration.
    */
-  inline auto CreateCertificateProvider(const object &from) const
+  auto CreateCertificateProvider(const object &from) const
     -> std::unique_ptr<CertificateProvider>;
 
   /**
@@ -110,12 +188,12 @@ public:
   /**
    * See mReset.
    */
-  inline static auto Reset() -> void { GetInstance().mReset(); }
+  inline static auto Reset() noexcept -> int { return GetInstance().mReset(); }
 
   /**
    * See mClear.
    */
-  inline static auto Clear() -> void { GetInstance().mClear(); }
+  inline static auto Clear() noexcept -> int { return GetInstance().mClear(); }
 
   /**
    * See mLoadSingleJSONConfiguration.
@@ -155,6 +233,8 @@ public:
 private:
   value json_configuration;
   ConnectionConfigurationHelper connection_configuration_helper;
+  LoggingConfigurationHelper logging_configuration_helper;
+
   inline ConfigurationManager() { InitializeDefaults(); };
 
   /**
@@ -164,7 +244,7 @@ private:
    * first existing file from the LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE list
    * of file locations.
    */
-  auto InitializeDefaults() -> void;
+  auto InitializeDefaults() -> int;
 
   /**
    * Return a json object representing the current configuration.
@@ -188,7 +268,7 @@ private:
    * The current configuration is deleted and a new configuration is being
    * loaded via InitializeDefaults.
    */
-  auto mReset() -> void;
+  auto mReset() noexcept -> int;
 
   /**
    * Clear this ConfigurationManager.
@@ -198,7 +278,7 @@ private:
    * In contrast to mReset, this method only deletes the current configuration
    * but does not load a new one via InitializeDefaults.
    */
-  auto mClear() -> void;
+  auto mClear() noexcept -> int;
 
   /**
    * Load a configuration from a json file.
diff --git a/include/caosdb/connection.h b/include/caosdb/connection.h
index 2c58d57ce66375d1650834872d461464dd912be0..5d7930a0d63e484f01be52dab7749d02b3916c33 100644
--- a/include/caosdb/connection.h
+++ b/include/caosdb/connection.h
@@ -27,116 +27,83 @@
  * @date 2021-05-18
  * @brief Configuration and setup of the connection.
  */
-#include <iosfwd>                                // for ostream
 #include <map>                                   // for map
 #include <memory>                                // for shared_ptr, unique_ptr
 #include <string>                                // for string, basic_string
+#include "boost/filesystem/path.hpp"             // for path
 #include "caosdb/authentication.h"               // for Authenticator
+#include "caosdb/configuration.h"                // for ConnectionConfigura...
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
 #include "caosdb/info.h"                         // for VersionInfo
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"   // for GeneralInfoService:...
 #include "caosdb/transaction.h"                  // for Transaction
+#include "caosdb/transaction_status.h"           // for TransactionStatus
 #include "grpcpp/channel.h"                      // for Channel
-#include "grpcpp/security/credentials.h"         // for ChannelCredentials
 
 namespace caosdb::connection {
+using boost::filesystem::path;
 using caosdb::authentication::Authenticator;
+using caosdb::configuration::ConnectionConfiguration;
 using caosdb::entity::v1alpha1::EntityTransactionService;
 using caosdb::info::VersionInfo;
 using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::transaction::Transaction;
-using grpc::ChannelCredentials;
-
-class CertificateProvider {
-public:
-  [[nodiscard]] auto virtual GetCertificatePem() const -> std::string = 0;
-  virtual ~CertificateProvider() = default;
-};
-
-class PemFileCertificateProvider : public CertificateProvider {
-private:
-  std::string certificate_provider;
-
-public:
-  explicit PemFileCertificateProvider(const std::string &path);
-  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
-};
-
-class PemCertificateProvider : public CertificateProvider {
-private:
-  std::string certificate_provider;
-
-public:
-  explicit PemCertificateProvider(const std::string &certificate_provider);
-  [[nodiscard]] auto GetCertificatePem() const -> std::string override;
-};
+using caosdb::transaction::TransactionStatus;
 
 /**
- * @brief Configuration of the CaosDB connection.
+ * @brief A reusable connection to a CaosDBServer.
  */
-class ConnectionConfiguration {
-private:
-  std::string host;
-  int port;
-
+class Connection {
 public:
-  ConnectionConfiguration(const std::string &host, int port);
-  virtual ~ConnectionConfiguration() = default;
-  friend auto operator<<(std::ostream &out,
-                         const ConnectionConfiguration &configuration)
-    -> std::ostream &;
-
-  [[nodiscard]] auto virtual ToString() const -> std::string = 0;
-  [[nodiscard]] auto GetHost() const -> std::string;
-  [[nodiscard]] auto GetPort() const -> int;
-  [[nodiscard]] auto virtual GetChannelCredentials() const
-    -> std::shared_ptr<ChannelCredentials> = 0;
-};
+  explicit Connection(const ConnectionConfiguration &configuration);
 
-class InsecureConnectionConfiguration : public ConnectionConfiguration {
-private:
-  std::shared_ptr<ChannelCredentials> credentials;
+  /**
+   * Request the server's version and return the status of this request after
+   * termination..
+   *
+   * The version is stored in the connection object and may be retrieved via
+   * GetVersionInfo() if the request was successful.
+   *
+   * This method does not throw any exceptions. Errors are indicated in the
+   * return value instead.
+   */
+  auto RetrieveVersionInfoNoExceptions() const noexcept -> TransactionStatus;
+
+  /**
+   * Request and return the server's version.
+   *
+   * If the request terminated unsuccessfully, a corresponding exception is
+   * being thrown.
+   */
+  auto RetrieveVersionInfo() const -> const VersionInfo &;
+
+  /**
+   * Return the server's version.
+   *
+   * Clients need to call RetrieveVersionInfo() or
+   * RetrieveVersionInfoNoExceptions() before the version info is locally
+   * available. Otherwise a nullptr is being returned.
+   */
+  [[nodiscard]] inline auto GetVersionInfo() const noexcept
+    -> const VersionInfo * {
+    return this->version_info.get();
+  };
 
-public:
-  InsecureConnectionConfiguration(const std::string &host, int port);
-  [[nodiscard]] auto GetChannelCredentials() const
-    -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto ToString() const -> std::string override;
-};
+  [[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>;
 
-class TlsConnectionConfiguration : public ConnectionConfiguration {
 private:
-  std::shared_ptr<ChannelCredentials> credentials;
-  std::string certificate_provider;
-
-public:
-  TlsConnectionConfiguration(const std::string &host, int port);
-  TlsConnectionConfiguration(const std::string &host, int port,
-                             const Authenticator &authenticator);
-  TlsConnectionConfiguration(const std::string &host, int port,
-                             const CertificateProvider &certificate_provider);
-  TlsConnectionConfiguration(const std::string &host, int port,
-                             const CertificateProvider &certificate_provider,
-                             const Authenticator &authenticator);
-  [[nodiscard]] auto GetChannelCredentials() const
-    -> std::shared_ptr<ChannelCredentials> override;
-  [[nodiscard]] auto ToString() const -> std::string override;
-};
-
-/**
- * @brief A reusable connection to a CaosDBServer.
- */
-class Connection {
+  /// GRPC-Channel (HTTP/2 Connection plus Authentication). We use a shared
+  /// pointer because Transaction instances also own the channel.
   std::shared_ptr<grpc::Channel> channel;
+  /// Service for retrieving the server's version. We use a unique pointer
+  /// because only this connection owns and uses this service.
   std::unique_ptr<GeneralInfoService::Stub> general_info_service;
+  /// The server's version. It's mutable because it is rather a cache than a
+  /// data member which is subject to change.
+  mutable std::unique_ptr<VersionInfo> version_info;
+  /// Service for entity transactions. We use a shared pointer because
+  /// Transaction instances also own this service stub.
   std::shared_ptr<EntityTransactionService::Stub> entity_transaction_service;
-
-public:
-  explicit Connection(const ConnectionConfiguration &configuration);
-  friend auto operator<<(std::ostream &out, const Connection &connection)
-    -> std::ostream &;
-  [[nodiscard]] auto GetVersionInfo() const -> std::unique_ptr<VersionInfo>;
-  [[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>;
 };
 
 /**
@@ -182,6 +149,9 @@ public:
     return ConnectionManager::GetInstance().mGetConnection(name);
   };
 
+  /**
+   * Get the connection marked by the "default" key in the configuration.
+   */
   inline static auto GetDefaultConnection()
     -> const std::shared_ptr<Connection> & {
     return ConnectionManager::GetInstance().mGetDefaultConnection();
diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index e98b852ea0ca63758ed0e9bd68cf039d0f728d1a..09d96f014f09a63e23a89a5156bccd38388842d4 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -28,9 +28,11 @@
 #ifndef CAOSDB_ENTITY_H
 #define CAOSDB_ENTITY_H
 
-#include <memory>                           // for unique_ptr
-#include <string>                           // for string
-#include "caosdb/entity/v1alpha1/main.pb.h" // for Entity, RepeatedField
+#include "caosdb/message_code.h"
+#include "caosdb/entity/v1alpha1/main.pb.h" // for Entity, RepeatedPtrField
+#include "google/protobuf/util/json_util.h"
+#include <memory> // for unique_ptr
+#include <string> // for string
 
 namespace caosdb::entity {
 
@@ -56,13 +58,14 @@ class Parents {
 public:
   inline Parents(){};
   explicit inline Parents(
-    ::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Parent>
+    ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent>
       *wrapped)
     : wrapped(wrapped){};
   [[nodiscard]] auto At(int index) const -> const Parent &;
 
 private:
-  ::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Parent> *wrapped;
+  ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent>
+    *wrapped;
   friend class Entity;
 };
 
@@ -97,13 +100,55 @@ class Properties {
 public:
   inline Properties(){};
   explicit inline Properties(
-    ::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Property>
+    ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property>
       *wrapped)
     : wrapped(wrapped){};
   [[nodiscard]] auto At(int index) const -> const Property &;
 
 private:
-  ::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Property>
+  ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property>
+    *wrapped;
+  friend class Entity;
+};
+
+/**
+ * Messages convey information about the state and result of transactions.
+ *
+ * A Message object can be thought of as kinf of a generalized error object in
+ * other frameworks. Please have a look at MessageCodes for more details.
+ */
+class Message {
+public:
+  explicit inline Message(caosdb::entity::v1alpha1::Message *wrapped)
+    : wrapped(wrapped){};
+  [[nodiscard]] inline auto GetCode() const -> MessageCode {
+    return get_message_code(wrapped->code());
+  }
+  [[nodiscard]] inline auto GetDescription() const -> std::string {
+    return wrapped->description();
+  }
+
+private:
+  caosdb::entity::v1alpha1::Message *wrapped;
+};
+
+/**
+ * Container for Messages.
+ */
+class Messages {
+public:
+  inline Messages() : wrapped(nullptr){};
+  explicit inline Messages(
+    ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Message>
+      *wrapped)
+    : wrapped(wrapped){};
+  [[nodiscard]] inline auto Size() const -> int { return wrapped->size(); }
+  [[nodiscard]] inline auto At(int index) const -> const Message {
+    return Message(&(wrapped->at(index)));
+  }
+
+private:
+  ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Message>
     *wrapped;
   friend class Entity;
 };
@@ -114,13 +159,10 @@ private:
 class Entity {
 public:
   explicit inline Entity(caosdb::entity::v1alpha1::Entity *wrapped)
-    : wrapped(wrapped),
-      properties(
-        (::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Property>
-           *)wrapped->mutable_properties()),
-      parents(
-        (::google::protobuf::RepeatedField<caosdb::entity::v1alpha1::Parent> *)
-          wrapped->mutable_parents()){};
+    : wrapped(wrapped) {
+    errors.wrapped = this->wrapped->mutable_errors();
+  };
+
   [[nodiscard]] inline auto GetId() const -> const std::string & {
     return wrapped->id();
   };
@@ -147,11 +189,26 @@ public:
 
   [[nodiscard]] auto GetParents() const -> const Parents &;
   [[nodiscard]] auto GetProperties() const -> const Properties &;
+  [[nodiscard]] inline auto GetErrors() const -> const Messages & {
+    return errors;
+  }
+  [[nodiscard]] inline auto HasErrors() const -> bool {
+    return this->errors.wrapped->size() > 0;
+  }
+
+  inline auto ToString() const -> const std::string {
+    google::protobuf::util::JsonOptions options;
+    std::string out;
+    google::protobuf::util::MessageToJsonString(*(this->wrapped.get()), &out,
+                                                options);
+    return out;
+  }
 
 private:
   std::unique_ptr<caosdb::entity::v1alpha1::Entity> wrapped;
   Properties properties;
   Parents parents;
+  Messages errors;
 };
 
 } // namespace caosdb::entity
diff --git a/include/caosdb/exceptions.h b/include/caosdb/exceptions.h
index a0971a13be02adc6c16837011f7df2c45fb7c089..4653ed29dfaaa6a7543b9ff5863d3f2dbce4316d 100644
--- a/include/caosdb/exceptions.h
+++ b/include/caosdb/exceptions.h
@@ -21,46 +21,71 @@
 
 #ifndef CAOSDB_EXCEPTIONS_H
 #define CAOSDB_EXCEPTIONS_H
+#include "caosdb/status_code.h"
 #include <stdexcept>
 #include <string>
 
 namespace caosdb::exceptions {
+using caosdb::StatusCode;
 using std::runtime_error;
 
+/**
+ * @brief Generic exception class of the caosdb client library.
+ */
+class Exception : public runtime_error {
+public:
+  explicit Exception(StatusCode code, const std::string &what_arg)
+    : runtime_error(what_arg), code(code) {}
+  [[nodiscard]] inline auto GetCode() const -> StatusCode { return this->code; }
+
+private:
+  StatusCode code;
+};
+
 /**
  * @brief Exception for authentication errors.
  */
-class AuthenticationError : public runtime_error {
+class AuthenticationError : public Exception {
 public:
   explicit AuthenticationError(const std::string &what_arg)
-    : runtime_error(what_arg) {}
+    : Exception(StatusCode::AUTHENTICATION_ERROR, what_arg) {}
 };
 
 /**
  * @brief The connection to the CaosDB server is down.
  */
-class ConnectionError : public runtime_error {
+class ConnectionError : public Exception {
 public:
   explicit ConnectionError(const std::string &what_arg)
-    : runtime_error(what_arg) {}
+    : Exception(StatusCode::CONNECTION_ERROR, what_arg) {}
 };
 
 /**
- * @brief The connection is known to the ConnectionManager under this name.
+ * @brief The transaction terminated unsuccessfully.
  */
-class UnknownConnectionError : public runtime_error {
+class TransactionError : public Exception {
 public:
-  explicit UnknownConnectionError(const std::string &what_arg)
-    : runtime_error(what_arg) {}
+  explicit TransactionError(const std::string &what_arg)
+    : Exception(StatusCode::GENERIC_TRANSACTION_ERROR, what_arg) {}
 };
 
 /**
- * @brief Exception for errors of the ConnectionManager.
+ * @brief Exception for errors of the ConfigurationManager or other components
+ * of the configuration.
  */
-class ConfigurationError : public runtime_error {
+class ConfigurationError : public Exception {
 public:
   explicit ConfigurationError(const std::string &what_arg)
-    : runtime_error(what_arg) {}
+    : Exception(StatusCode::CONFIGURATION_ERROR, what_arg) {}
+};
+
+/**
+ * @brief The connection isn't known to the ConnectionManager under this name.
+ */
+class UnknownConnectionError : public Exception {
+public:
+  explicit UnknownConnectionError(const std::string &what_arg)
+    : Exception(StatusCode::UNKNOWN_CONNECTION_ERROR, what_arg) {}
 };
 
 } // namespace caosdb::exceptions
diff --git a/include/caosdb/info.h b/include/caosdb/info.h
index f4f290b1ebde4a5ef89aaf8ecef9017b822296ba..a9e94c1077f809bd5e2e28c225773e91b13a9e98 100644
--- a/include/caosdb/info.h
+++ b/include/caosdb/info.h
@@ -27,9 +27,9 @@
  * @date 2021-07-02
  * @brief General information about the CaosDBServer.
  */
+#include "caosdb/info/v1alpha1/main.pb.h" // for VersionInfo
 #include <cstdint>                        // for uint32_t
 #include <string>                         // for string
-#include "caosdb/info/v1alpha1/main.pb.h" // for VersionInfo
 
 namespace caosdb::info {
 
@@ -54,13 +54,13 @@ public:
    * server behind the given connection.
    */
   explicit inline VersionInfo(ProtoVersionInfo *info) : info(info){};
-  [[nodiscard]] inline auto GetMajor() const -> uint32_t {
+  [[nodiscard]] inline auto GetMajor() const -> int32_t {
     return this->info->major();
   }
-  [[nodiscard]] inline auto GetMinor() const -> uint32_t {
+  [[nodiscard]] inline auto GetMinor() const -> int32_t {
     return this->info->minor();
   }
-  [[nodiscard]] inline auto GetPatch() const -> uint32_t {
+  [[nodiscard]] inline auto GetPatch() const -> int32_t {
     return this->info->patch();
   }
   [[nodiscard]] inline auto GetPreRelease() const -> const std::string & {
diff --git a/include/caosdb/log_level.h b/include/caosdb/log_level.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2fcb9b1181dcb86f91dfaf9a4ddecba3ea417cb
--- /dev/null
+++ b/include/caosdb/log_level.h
@@ -0,0 +1,38 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_LOG_LEVEL_H
+#define CAOSDB_LOG_LEVEL_H
+
+#define CAOSDB_LOG_LEVEL_OFF 1000000
+#define CAOSDB_LOG_LEVEL_FATAL 700
+#define CAOSDB_LOG_LEVEL_ERROR 600
+#define CAOSDB_LOG_LEVEL_WARN 500
+#define CAOSDB_LOG_LEVEL_INFO 400
+#define CAOSDB_LOG_LEVEL_DEBUG 300
+#define CAOSDB_LOG_LEVEL_TRACE 200
+#define CAOSDB_LOG_LEVEL_ALL 0
+
+#ifndef CAOSDB_DEFAULT_LOG_LEVEL
+#define CAOSDB_DEFAULT_LOG_LEVEL CAOSDB_LOG_LEVEL_ERROR
+#endif
+
+#endif
diff --git a/include/caosdb/logging.h b/include/caosdb/logging.h
new file mode 100644
index 0000000000000000000000000000000000000000..fcebc6df8b5db2eb9f800893f8d296d6174285ce
--- /dev/null
+++ b/include/caosdb/logging.h
@@ -0,0 +1,215 @@
+
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_LOGGING_H
+#define CAOSDB_LOGGING_H
+
+#include "caosdb/log_level.h"                            // for CAOSDB_LOG_...
+#include "boost/log/sources/global_logger_storage.hpp"   // for BOOST_LOG_I...
+#include "boost/log/sources/record_ostream.hpp"          // IWYU pragma: keep
+#include "boost/log/sources/severity_channel_logger.hpp" // for BOOST_LOG_C...
+#include "boost/log/utility/setup/settings.hpp"          // for settings
+#include "boost/smart_ptr/intrusive_ptr.hpp"             // for intrusive_ptr
+#include "boost/smart_ptr/intrusive_ref_counter.hpp"     // for intrusive_p...
+#include <memory>                                        // for shared_ptr
+#include <string>                                        // for string
+#include <vector>                                        // for vector
+
+namespace caosdb::logging {
+
+const std::string logger_name = "caosdb::logging";
+
+typedef boost::log::sources::severity_channel_logger<int, std::string>
+  boost_logger_class;
+
+BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(logger, boost_logger_class)
+
+/**
+ * This class stores the integer log level.
+ */
+class LevelConfiguration {
+public:
+  LevelConfiguration(int level) : level(level){};
+  [[nodiscard]] inline auto GetLevel() const -> int { return this->level; }
+
+private:
+  int level;
+};
+
+class SinkConfiguration;
+
+/**
+ * This class stores the logging level and log sinks.
+ *
+ * Sinks are represented by SinkConfiguration objects.
+ */
+class LoggingConfiguration : public LevelConfiguration {
+public:
+  virtual ~LoggingConfiguration() = default;
+  LoggingConfiguration(int level);
+  auto AddSink(const std::shared_ptr<SinkConfiguration> &sink) -> void;
+  auto GetSinks() const
+    -> const std::vector<std::shared_ptr<SinkConfiguration>> &;
+
+private:
+  std::vector<std::shared_ptr<SinkConfiguration>> sinks;
+};
+
+auto initialize_logging_defaults() -> int;
+auto initialize_logging(const LoggingConfiguration &configuration) -> void;
+
+/**
+ * A logging sink is characterized by a name and destination.
+ *
+ * Typical inheriting configurations exist for console, files and syslog.
+ *
+ * When a SinkConfiguration is created from a configuration, the sink
+ * configuration must contain a \p destination key which matches one of the
+ * keywords for implemented sinks.  At the moment of writing this documentation,
+ * valid destinations are:
+ *
+ * \li \p file
+ * \li \p console
+ * \li \p syslog
+ *
+ * A \p level keyword sets the logging level, if it exists at the sink or
+ * logging level of the configuration.
+ */
+class SinkConfiguration : public LevelConfiguration {
+public:
+  virtual ~SinkConfiguration() = default;
+  SinkConfiguration(std::string name, int level);
+  [[nodiscard]] auto GetName() const -> const std::string &;
+  [[nodiscard]] virtual auto GetDestination() const -> const std::string & = 0;
+
+  friend auto initialize_logging_defaults() -> int;
+  friend auto
+  initialize_logging(const LoggingConfiguration &logging_configuration) -> void;
+
+protected:
+  virtual auto Configure(boost::log::settings &settings) const -> void;
+
+private:
+  std::string name;
+};
+
+class ConsoleSinkConfiguration : public SinkConfiguration {
+public:
+  virtual ~ConsoleSinkConfiguration() = default;
+  ConsoleSinkConfiguration(const std::string &name, int level);
+  [[nodiscard]] auto GetDestination() const -> const std::string & override;
+  friend auto initialize_logging_defaults() -> int;
+  friend auto
+  initialize_logging(const LoggingConfiguration &logging_configuration) -> void;
+
+protected:
+  typedef SinkConfiguration sink_configuration;
+  virtual auto Configure(boost::log::settings &settings) const -> void override;
+
+private:
+  const std::string destination = "Console";
+};
+
+/**
+ * The file name is the destination, the directory can be set separately.
+ *
+ * If there is a `directory` key in the configuration, that will be used as a
+ * default, otherwise it is the current directory.
+ */
+class FileSinkConfiguration : public SinkConfiguration {
+public:
+  virtual ~FileSinkConfiguration() = default;
+  FileSinkConfiguration(const std::string &name, int level);
+  [[nodiscard]] virtual auto GetDestination() const
+    -> const std::string & override;
+  auto SetDirectory(const std::string &directory) -> void;
+  friend auto initialize_logging_defaults() -> int;
+  friend auto
+  initialize_logging(const LoggingConfiguration &logging_configuration) -> void;
+
+protected:
+  typedef SinkConfiguration sink_configuration;
+  virtual auto Configure(boost::log::settings &settings) const -> void override;
+
+private:
+  const std::string destination = "TextFile";
+  std::string directory = "./";
+};
+
+class SyslogSinkConfiguration : public SinkConfiguration {
+public:
+  virtual ~SyslogSinkConfiguration() = default;
+  SyslogSinkConfiguration(const std::string &name, int level);
+  [[nodiscard]] virtual auto GetDestination() const
+    -> const std::string & override;
+
+private:
+  const std::string destination = "Syslog";
+};
+
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_fatal(const char *channel, const char *msg);
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_error(const char *channel, const char *msg);
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_warn(const char *channel, const char *msg);
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_info(const char *channel, const char *msg);
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_debug(const char *channel, const char *msg);
+/**
+ * Convenience function for the C interface.
+ */
+void caosdb_log_trace(const char *channel, const char *msg);
+
+} // namespace caosdb::logging
+
+#define CAOSDB_LOG_FATAL(Channel)                                              \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_FATAL)
+#define CAOSDB_LOG_ERROR(Channel)                                              \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_ERROR)
+#define CAOSDB_LOG_WARN(Channel)                                               \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_WARN)
+#define CAOSDB_LOG_INFO(Channel)                                               \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_INFO)
+#define CAOSDB_LOG_DEBUG(Channel)                                              \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_DEBUG)
+#define CAOSDB_LOG_TRACE(Channel)                                              \
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), Channel,               \
+                        CAOSDB_LOG_LEVEL_TRACE)
+
+#endif
diff --git a/include/caosdb/message_code.h b/include/caosdb/message_code.h
new file mode 100644
index 0000000000000000000000000000000000000000..34a73198e41e35e5306f2ffe5a8c4efcaffbb9a4
--- /dev/null
+++ b/include/caosdb/message_code.h
@@ -0,0 +1,65 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_MESSAGE_CODE_H
+#define CAOSDB_MESSAGE_CODE_H
+
+#include "caosdb/entity/v1alpha1/main.pb.h" // for Entity, RepeatedField
+
+/**
+ * MessageCodes for entity messages.
+ *
+ * In contrast to the status codes, the message codes are part of the CaosDB
+ * API. Messages (and their codes) represent the state of the entities in a
+ * transaction or the server.
+ *
+ * For a specification of the message codes, look at the protobuf documentation.
+ * The sources and documentation can be found at
+ * https://gitlab.indiscale.com/caosdb/src/caosdb-proto.
+ */
+
+namespace caosdb::entity {
+
+enum MessageCode {
+  UNSPECIFIED = caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_UNSPECIFIED,
+  UNKNOWN = caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_UNKNOWN,
+  ENTITY_DOES_NOT_EXIST =
+    caosdb::entity::v1alpha1::MessageCode::MESSAGE_CODE_ENTITY_DOES_NOT_EXIST,
+};
+
+[[nodiscard]] inline auto get_message_code(int code) -> MessageCode {
+  // TODO(tf) smarter, less forgot-it-prone implementation
+  static MessageCode all_codes[] = {
+    MessageCode::UNSPECIFIED,
+    MessageCode::UNKNOWN,
+    MessageCode::ENTITY_DOES_NOT_EXIST,
+  };
+
+  for (MessageCode known_code : all_codes) {
+    if (known_code == code) {
+      return known_code;
+    }
+  }
+  return MessageCode::UNKNOWN;
+}
+
+} // namespace caosdb::entity
+#endif
diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h
new file mode 100644
index 0000000000000000000000000000000000000000..f24ffaeddb828dc9db9c5901d42f9f413993e4bd
--- /dev/null
+++ b/include/caosdb/status_code.h
@@ -0,0 +1,55 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_STATUS_CODE_H
+#define CAOSDB_STATUS_CODE_H
+
+#include <string>
+
+/**
+ * StatusCodes represent the status of this client, it's connections,
+ * configuration and so on.
+ *
+ * In contrast to MessageCodes, these status codes do not represent the status
+ * of the entities of a transaction or of the server (or only inasmuch the
+ * GENERIC_TRANSACTION_ERROR indicates that *there are* errors in a
+ * transaction).
+ */
+
+namespace caosdb {
+
+enum StatusCode {
+  INITIAL = -2,
+  EXECUTING = -1,
+  SUCCESS = 0,
+  AUTHENTICATION_ERROR = 16,
+  CONNECTION_ERROR = 14,
+  GENERIC_RPC_ERROR = 20,
+  GENERIC_ERROR = 21,
+  GENERIC_TRANSACTION_ERROR = 22,
+  CONFIGURATION_ERROR = 23,
+  UNKNOWN_CONNECTION_ERROR = 24,
+};
+
+auto get_status_description(int code) -> const std::string &;
+
+} // namespace caosdb
+#endif
diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h
index 8989951997a9da6bb821dec9e55345bbbb0c72b9..4e4fb2a76a0b4fb65824d1c2e54843062d382aef 100644
--- a/include/caosdb/transaction.h
+++ b/include/caosdb/transaction.h
@@ -19,24 +19,24 @@
  *
  */
 
+#ifndef CAOSDB_TRANSACTION_H
+#define CAOSDB_TRANSACTION_H
 /**
  * @brief Creation and execution of transactions.
  */
-
-#ifndef CAOSDB_TRANSACTION_H
-#define CAOSDB_TRANSACTION_H
-
 #include <memory>                                // for shared_ptr, unique_ptr
 #include <string>                                // for string
 #include "caosdb/entity.h"                       // for Entity
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
 #include "caosdb/entity/v1alpha1/main.pb.h"      // for Entity, RetrieveReq...
+#include "caosdb/transaction_status.h"           // for TransactionStatus
 
 namespace caosdb::transaction {
 using caosdb::entity::Entity;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 using caosdb::entity::v1alpha1::EntityTransactionService;
 using caosdb::entity::v1alpha1::RetrieveRequest;
+using caosdb::transaction::TransactionStatus;
 
 class ResultSet {
 public:
@@ -54,22 +54,50 @@ private:
   std::unique_ptr<Entity> entity;
 };
 
-enum TransactionState { INIT = 10, EXECUTING = 20, SUCCESS = 30, ERROR = 40 };
-
 /**
  * @brief Create a transaction via `CaosDBConnection.createTransaction()`
  */
 class Transaction {
 private:
   std::unique_ptr<ResultSet> result_set;
-  TransactionState state = TransactionState::INIT;
+  TransactionStatus status = TransactionStatus::INITIAL();
   std::shared_ptr<EntityTransactionService::Stub> service_stub;
   RetrieveRequest request; // TODO(tf)
+  std::string error_message;
 
 public:
   Transaction(std::shared_ptr<EntityTransactionService::Stub> service_stub);
   auto RetrieveById(const std::string &id) -> void;
-  auto Execute() -> void;
+
+  /**
+   * Execute this transaction in blocking mode and return the status.
+   */
+  auto Execute() -> TransactionStatus;
+
+  /**
+   * Execute this transaction in non-blocking mode and return immediately.
+   *
+   * A client may request the current status at any time via GetStatus().
+   *
+   * Use WaitForIt() to join the back-ground execution of this transaction.
+   */
+  auto ExecuteAsynchronously() noexcept -> void;
+
+  /**
+   * Join the background execution and return the status when the execution
+   * terminates.
+   *
+   * Use this after ExecuteAsynchronously().
+   */
+  [[nodiscard]] auto WaitForIt() const noexcept -> TransactionStatus;
+
+  /**
+   * Return the current status of the transaction.
+   */
+  [[nodiscard]] inline auto GetStatus() const -> TransactionStatus {
+    return this->status;
+  }
+
   [[nodiscard]] inline auto GetResultSet() const -> const ResultSet & {
     const ResultSet *result_set = this->result_set.get();
     return *result_set;
diff --git a/include/caosdb/transaction_status.h b/include/caosdb/transaction_status.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e54c8ba79c944f6b157a577cc9db64be01e1473
--- /dev/null
+++ b/include/caosdb/transaction_status.h
@@ -0,0 +1,170 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_TRANSACTION_STATUS_H
+#define CAOSDB_TRANSACTION_STATUS_H
+
+/**
+ * TransactionStatus indicates the current status of a transaction and, when it
+ * has already terminated, whether the transaction has been successful or not.
+ *
+ * A status code of 0 denotes a generic success state, positive values indicate
+ * errors, and negative values indicate other states, such as different stages
+ * of a transaction in process.
+ */
+
+#include "caosdb/status_code.h"
+#include "caosdb/exceptions.h"
+#include <memory> // for shared_ptr, unique_ptr
+#include <string> // for string
+
+namespace caosdb::transaction {
+using caosdb::StatusCode;
+using caosdb::exceptions::AuthenticationError;
+using caosdb::exceptions::ConnectionError;
+using caosdb::exceptions::Exception;
+using caosdb::exceptions::TransactionError;
+
+/**
+ * Status of a Request or Transaction.
+ */
+class TransactionStatus {
+public:
+  // REFACTORING NEEDED: When you touch this code again consider writing a
+  // macro, because this is a lot of redundant code here...
+  inline static auto INITIAL() -> const TransactionStatus & {
+    static const TransactionStatus initial(
+      StatusCode::INITIAL, caosdb::get_status_description(StatusCode::INITIAL));
+    return initial;
+  }
+  inline static auto EXECUTING() -> const TransactionStatus & {
+    static const TransactionStatus executing(
+      StatusCode::EXECUTING,
+      caosdb::get_status_description(StatusCode::EXECUTING));
+    return executing;
+  }
+  inline static auto SUCCESS() -> const TransactionStatus & {
+    static const TransactionStatus success(
+      StatusCode::SUCCESS, caosdb::get_status_description(StatusCode::SUCCESS));
+    return success;
+  }
+  inline static auto RPC_ERROR(const std::string &details)
+    -> const TransactionStatus {
+    // We use the GENERIC_RPC_ERROR here because we might want to add further
+    // RPC_ERROR states with different error codes (which stem from GRPC) here
+    // in the future.
+    return TransactionStatus(
+      StatusCode::GENERIC_RPC_ERROR,
+      caosdb::get_status_description(StatusCode::GENERIC_RPC_ERROR) +
+        " Original error: " + details);
+  }
+  inline static auto CONNECTION_ERROR() -> const TransactionStatus & {
+    static const TransactionStatus connection_error(
+      StatusCode::CONNECTION_ERROR,
+      caosdb::get_status_description(StatusCode::CONNECTION_ERROR));
+
+    return connection_error;
+  }
+  inline static auto AUTHENTICATION_ERROR(const std::string &details)
+    -> const TransactionStatus {
+    return TransactionStatus(
+      StatusCode::AUTHENTICATION_ERROR,
+      caosdb::get_status_description(StatusCode::AUTHENTICATION_ERROR) +
+        " Original error: " + details);
+  }
+  inline static auto AUTHENTICATION_ERROR() -> const TransactionStatus & {
+    static const TransactionStatus authentication_error(
+      StatusCode::AUTHENTICATION_ERROR,
+      caosdb::get_status_description(StatusCode::AUTHENTICATION_ERROR));
+
+    return authentication_error;
+  }
+  inline static auto TRANSACTION_ERROR() -> const TransactionStatus & {
+    static const TransactionStatus transaction_error(
+      StatusCode::GENERIC_TRANSACTION_ERROR,
+      caosdb::get_status_description(StatusCode::GENERIC_TRANSACTION_ERROR));
+    return transaction_error;
+  }
+  inline static auto TRANSACTION_ERROR(const std::string &details)
+    -> const TransactionStatus {
+    return TransactionStatus(
+      StatusCode::GENERIC_TRANSACTION_ERROR,
+      caosdb::get_status_description(StatusCode::GENERIC_TRANSACTION_ERROR) +
+        " Original error: " + details);
+  }
+
+  inline auto ThrowExceptionIfError() const -> void {
+    if (!IsError()) {
+      return;
+    }
+    switch (this->code) {
+    case StatusCode::CONNECTION_ERROR:
+      throw ConnectionError(this->description);
+    case StatusCode::AUTHENTICATION_ERROR:
+      throw AuthenticationError(this->description);
+    case StatusCode::GENERIC_TRANSACTION_ERROR:
+      throw TransactionError(this->description);
+    default:
+      throw Exception(StatusCode::GENERIC_ERROR, this->description);
+    }
+  }
+
+  /**
+   * Return true if this TransactionStatus represents a terminated state.
+   */
+  inline auto IsTerminated() const -> bool { return this->code >= 0; };
+
+  /**
+   * Return true if this TransactionStatus represents an erroneous state.
+   */
+  inline auto IsError() const -> bool { return this->code > 0; };
+
+  /**
+   * Return a description of the erroneous state.
+   *
+   * No description yields an empty string.
+   */
+  inline auto GetDescription() const -> const std::string & {
+    return this->description;
+  }
+
+  /**
+   * Return the status code of the state.
+   */
+  inline auto GetCode() const -> StatusCode { return this->code; }
+
+private:
+  /**
+   * The code is an identifier of errors.
+   */
+  StatusCode code;
+
+  /**
+   * Description of the error
+   */
+  std::string description;
+
+  TransactionStatus(StatusCode code, const std::string &description)
+    : code(code), description(description){};
+};
+
+} // namespace caosdb::transaction
+#endif
diff --git a/include/caosdb/utility.h b/include/caosdb/utility.h
index 5d3ac6147222f01b0edc4f659a6bc242b8bd21b0..30e21747ec4ce39493e24e10e09d7c53c7ab1308 100644
--- a/include/caosdb/utility.h
+++ b/include/caosdb/utility.h
@@ -21,16 +21,18 @@
 
 #ifndef CAOSDB_UTILS_H
 #define CAOSDB_UTILS_H
-#include <cassert>
-#include <iostream>
-#include <string_view>
-#include <fstream>
-#include <string>
-#include <cstdlib>
-#include <boost/json.hpp>
 #include <boost/beast/core/detail/base64.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/string_file.hpp>
+#include <boost/json.hpp>
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string_view>
 
 namespace caosdb::utility {
 using boost::filesystem::exists;
@@ -41,29 +43,20 @@ using boost::json::value;
 
 /**
  * @brief Read a text file into a string and return the file's content.
- *
- * TODO use boost-filesystem's "load_string_file"!
  */
-inline auto load_string_file(const std::string &path) -> std::string {
-  const auto path_view = std::string_view{path};
-  constexpr auto size = std::size_t{4096};
-  auto stream = std::ifstream{path_view.data()};
-  stream.exceptions(std::ios_base::badbit);
-
-  auto result = std::string();
-  auto buffer = std::string(size, '\0');
-  while (stream.read(&buffer[0], size)) {
-    result.append(buffer, 0, stream.gcount());
-  }
-  result.append(buffer, 0, stream.gcount());
+inline auto load_string_file(const path &path) -> std::string {
+  std::string result;
+  boost::filesystem::load_string_file(path, result);
   return result;
 }
 
-inline auto get_env_var(const char *key, const char *fall_back) -> const
-  char * {
+/**
+ * @brief Return the environment variable KEY, or FALLBACK if it does not exist.
+ */
+inline auto get_env_var(const char *key, const char *fallback) -> const char * {
   const char *val = getenv(key);
   if (val == nullptr) {
-    return fall_back;
+    return fallback;
   } else {
     return val;
   }
@@ -71,11 +64,11 @@ inline auto get_env_var(const char *key, const char *fall_back) -> const
 
 /**
  * @brief Return the value of an environment variable or - if undefined - the
- * fall_back value.
+ * fallback value.
  */
-inline auto get_env_var(const std::string &key, const std::string &fall_back)
+inline auto get_env_var(const std::string &key, const std::string &fallback)
   -> const std::string {
-  const char *val = get_env_var(key.c_str(), fall_back.c_str());
+  const char *val = get_env_var(key.c_str(), fallback.c_str());
 
   auto const result = std::string(val);
   return result;
@@ -88,11 +81,12 @@ inline auto base64_encode(const std::string &plain) -> std::string {
   auto size_plain = plain.size();
   auto size_encoded = boost::beast::detail::base64::encoded_size(size_plain);
 
-  char encoded[size_encoded];
-  boost::beast::detail::base64::encode(&encoded, plain.c_str(), size_plain);
+  std::unique_ptr<char[]> encoded(new char[size_encoded]);
+  boost::beast::detail::base64::encode(encoded.get(), plain.c_str(),
+                                       size_plain);
 
   // the encoded char[] is not null terminated, so explicitely set the length
-  return std::string(encoded, encoded + size_encoded);
+  return std::string(encoded.get(), encoded.get() + size_encoded);
 }
 
 inline auto load_json_file(const path &json_file) -> value {
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
index f9ed3bcc5725bbfd5cf8f6eec281f1a686e930f6..957e2fb7fc5d865d23cfcabdf5e078d270b5ba35 100644
--- a/include/ccaosdb.h
+++ b/include/ccaosdb.h
@@ -7,27 +7,27 @@ extern "C" {
 /**
  * Return the constant caosdb::LIBCAOSDB_VERSION_MAJOR.
  */
-const int caosdb_constants_LIBCAOSDB_VERSION_MAJOR();
+int caosdb_constants_LIBCAOSDB_VERSION_MAJOR();
 /**
  * Return the constant caosdb::LIBCAOSDB_VERSION_MINOR
  */
-const int caosdb_constants_LIBCAOSDB_VERSION_MINOR();
+int caosdb_constants_LIBCAOSDB_VERSION_MINOR();
 /**
  * Return the constant caosdb::LIBCAOSDB_VERSION_PATCH.
  */
-const int caosdb_constants_LIBCAOSDB_VERSION_PATCH();
+int caosdb_constants_LIBCAOSDB_VERSION_PATCH();
 /**
  * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MAJOR.
  */
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR();
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR();
 /**
  * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_MINOR.
  */
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR();
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR();
 /**
  * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PATCH.
  */
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH();
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH();
 /**
  * Return the constant caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE.
  */
@@ -81,9 +81,14 @@ typedef struct {
 /**
  * Return the environment variable of the given name.
  *
- * If the environment variable is not set, return the fall_back instead.
+ * If the environment variable is not set, return the fallback instead.
  */
-const char *caosdb_utility_get_env_var(const char *name, const char *fall_back);
+const char *caosdb_utility_get_env_var(const char *name, const char *fallback);
+
+/**
+ * Return a description of the status code.
+ */
+const char *caosdb_get_status_description(int code);
 
 /**
  * Create a pem-file certificate provider.
diff --git a/proto b/proto
index 12f072263c05208464b80c0124bde0396b100d86..bff5455a899a69d5988366b2f4d175029a7e2362 160000
--- a/proto
+++ b/proto
@@ -1 +1 @@
-Subproject commit 12f072263c05208464b80c0124bde0396b100d86
+Subproject commit bff5455a899a69d5988366b2f4d175029a7e2362
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 147bddc8312d260d1a00518ea781b5ca2aec330e..ce1997523888e90403dc4f3997a58981b5ba161e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -22,6 +22,7 @@
 # add all source files to this list
 set(libcaosdb_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/authentication.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/logging.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.cpp
diff --git a/src/caosdb/authentication.cpp b/src/caosdb/authentication.cpp
index 7bc70de4efbd5a09bf96fbfb240110d5a8bb45b3..a1c84e5b5b04ebc5e0e6909ad4d274f5389117bb 100644
--- a/src/caosdb/authentication.cpp
+++ b/src/caosdb/authentication.cpp
@@ -19,13 +19,13 @@
  *
  */
 #include "caosdb/authentication.h"
+#include "grpcpp/security/credentials.h"    // for MetadataCredentialsPlugin
 #include <grpcpp/impl/codegen/status.h>     // for Status, Status::OK
 #include <grpcpp/impl/codegen/string_ref.h> // for string_ref
 #include <map>                              // for multimap
 #include <memory>                           // for allocator, shared_ptr
 #include <string>                           // for basic_string, operator+
 #include <utility>                          // for pair, move, make_pair
-#include "grpcpp/security/credentials.h"    // for MetadataCredentialsFromP...
 
 namespace caosdb::authentication {
 using caosdb::utility::base64_encode;
@@ -36,7 +36,7 @@ using grpc::string_ref;
 
 MetadataCredentialsPluginImpl::MetadataCredentialsPluginImpl(std::string key,
                                                              std::string value)
-  : key(std::move(key)), value(std::move(value)){};
+  : key(std::move(key)), value(std::move(value)) {}
 
 auto MetadataCredentialsPluginImpl::GetMetadata(
   string_ref /*service_url*/, string_ref /*method_name*/,
@@ -45,12 +45,12 @@ auto MetadataCredentialsPluginImpl::GetMetadata(
 
   metadata->insert(std::make_pair(this->key, this->value));
   return Status::OK;
-};
+}
 
 PlainPasswordAuthenticator::PlainPasswordAuthenticator(
   const std::string &username, const std::string &password) {
   this->basic = "Basic " + base64_encode(username + ":" + password);
-};
+}
 
 auto PlainPasswordAuthenticator::GetCallCredentials() const
   -> std::shared_ptr<grpc::CallCredentials> {
@@ -58,6 +58,6 @@ auto PlainPasswordAuthenticator::GetCallCredentials() const
     std::unique_ptr<grpc::MetadataCredentialsPlugin>(
       new MetadataCredentialsPluginImpl("authentication", this->basic)));
   return call_creds;
-};
+}
 
 } // namespace caosdb::authentication
diff --git a/src/caosdb/configuration.cpp b/src/caosdb/configuration.cpp
index fd62e24865feab9b09f79248c4aaa70e2ec7ee13..618c212912ffb65b073d4a0685a89ceb7ad386c6 100644
--- a/src/caosdb/configuration.cpp
+++ b/src/caosdb/configuration.cpp
@@ -19,17 +19,29 @@
  *
  */
 #include "caosdb/configuration.h"
-#include <cstdlib>                            // for getenv
-#include <cassert>                            // for assert
-#include <string>                             // for char_traits, string
-#include "boost/iterator/iterator_facade.hpp" // for iterator_facade_base
-#include "boost/json/impl/object.hpp"         // for object::at, object::begin
-#include "boost/json/string.hpp"              // for string
-#include "boost/json/string_view.hpp"         // for string_view
-#include "caosdb/authentication.h"            // for PlainPasswordAuthentic...
-#include "caosdb/connection.h"                // for TlsConnectionConfiguration
-#include "caosdb/constants.h"                 // for LIBCAOSDB_CONFIGURATIO...
-#include "caosdb/exceptions.h"                // for ConfigurationError
+#include "boost/iterator/iterator_facade.hpp"         // for iterator_facad...
+#include "boost/json/impl/object.hpp"                 // for object::at
+#include "boost/json/string.hpp"                      // for string
+#include "boost/json/string_view.hpp"                 // for string_view
+#include "boost/log/core/record.hpp"                  // for record
+#include "boost/log/sources/record_ostream.hpp"       // for basic_record_o...
+#include "boost/preprocessor/seq/limits/enum_256.hpp" // for BOOST_PP_SEQ_E...
+#include "boost/preprocessor/seq/limits/size_256.hpp" // for BOOST_PP_SEQ_S...
+#include "caosdb/authentication.h"                    // for Authenticator
+#include "caosdb/connection.h"                        // for ConnectionManager
+#include "caosdb/constants.h"                         // for LIBCAOSDB_CONF...
+#include "caosdb/exceptions.h"                        // for ConfigurationE...
+#include "caosdb/log_level.h"                         // for CAOSDB_DEFAULT...
+#include "caosdb/status_code.h"                       // for StatusCode
+#include "caosdb/utility.h"                           // for get_home_direc...
+#include <bits/exception.h>                           // for exception
+#include <cassert>                                    // for assert
+#include <cstdlib>                                    // for getenv
+#include <grpcpp/security/credentials.h>              // for SslCredentials
+#include <iterator>                                   // for next
+#include <map>                                        // for map
+#include <stdexcept>                                  // for out_of_range
+#include <string>                                     // for string, operator+
 
 namespace caosdb::configuration {
 using boost::filesystem::exists;
@@ -38,14 +50,118 @@ using boost::json::object;
 using boost::json::value;
 using caosdb::authentication::Authenticator;
 using caosdb::authentication::PlainPasswordAuthenticator;
-using caosdb::connection::CertificateProvider;
-using caosdb::connection::ConnectionConfiguration;
-using caosdb::connection::InsecureConnectionConfiguration;
-using caosdb::connection::PemFileCertificateProvider;
-using caosdb::connection::TlsConnectionConfiguration;
+using caosdb::connection::ConnectionManager;
 using caosdb::exceptions::ConfigurationError;
+using caosdb::logging::ConsoleSinkConfiguration;
+using caosdb::logging::FileSinkConfiguration;
+using caosdb::logging::LoggingConfiguration;
+using caosdb::logging::SinkConfiguration;
+using caosdb::logging::SyslogSinkConfiguration;
 using caosdb::utility::get_home_directory;
 using caosdb::utility::load_json_file;
+using caosdb::utility::load_string_file;
+using grpc::InsecureChannelCredentials;
+using grpc::SslCredentials;
+using grpc::SslCredentialsOptions;
+
+PemFileCertificateProvider::PemFileCertificateProvider(const path &path) {
+  this->certificate_provider = load_string_file(path);
+}
+
+auto PemFileCertificateProvider::GetCertificatePem() const -> std::string {
+  return this->certificate_provider;
+}
+
+PemCertificateProvider::PemCertificateProvider(
+  const std::string &certificate_provider) {
+  this->certificate_provider = certificate_provider;
+}
+
+auto PemCertificateProvider::GetCertificatePem() const -> std::string {
+  return this->certificate_provider;
+}
+
+ConnectionConfiguration::ConnectionConfiguration(const std::string &host,
+                                                 int port) {
+  this->host = host;
+  this->port = port;
+}
+
+auto ConnectionConfiguration::GetHost() const -> std::string {
+  return this->host;
+}
+
+auto ConnectionConfiguration::GetPort() const -> int { return this->port; }
+
+auto operator<<(std::ostream &out, const ConnectionConfiguration &configuration)
+  -> std::ostream & {
+  out << configuration.ToString();
+  return out;
+}
+
+InsecureConnectionConfiguration::InsecureConnectionConfiguration(
+  const std::string &host, int port)
+  : ConnectionConfiguration(host, port) {
+  this->credentials = InsecureChannelCredentials();
+}
+
+auto InsecureConnectionConfiguration::GetChannelCredentials() const
+  -> std::shared_ptr<ChannelCredentials> {
+  return this->credentials;
+}
+
+auto InsecureConnectionConfiguration::ToString() const -> std::string {
+  return "InsecureConnectionConfiguration(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + ")";
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(const std::string &host,
+                                                       int port)
+  : ConnectionConfiguration(host, port) {
+  SslCredentialsOptions options;
+  this->credentials = SslCredentials(options);
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(
+  const std::string &host, int port,
+  const CertificateProvider &certificate_provider)
+  : ConnectionConfiguration(host, port) {
+  SslCredentialsOptions options;
+  options.pem_root_certs = certificate_provider.GetCertificatePem();
+  this->credentials = SslCredentials(options);
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(
+  const std::string &host, int port, const Authenticator &authenticator)
+  : ConnectionConfiguration(host, port) {
+
+  SslCredentialsOptions options;
+  this->credentials = grpc::CompositeChannelCredentials(
+    SslCredentials(options), authenticator.GetCallCredentials());
+}
+
+TlsConnectionConfiguration::TlsConnectionConfiguration(
+  const std::string &host, int port,
+  const CertificateProvider &certificate_provider,
+  const Authenticator &authenticator)
+  : ConnectionConfiguration(host, port) {
+
+  SslCredentialsOptions options;
+  options.pem_root_certs = certificate_provider.GetCertificatePem();
+  this->credentials = grpc::CompositeChannelCredentials(
+    SslCredentials(options), authenticator.GetCallCredentials());
+}
+
+auto TlsConnectionConfiguration::GetChannelCredentials() const
+  -> std::shared_ptr<ChannelCredentials> {
+  return this->credentials;
+}
+
+auto TlsConnectionConfiguration::ToString() const -> std::string {
+  return "TlsConnectionConfiguration(" + this->GetHost() + "," +
+         std::to_string(this->GetPort()) + "," + this->certificate_provider +
+         ")";
+}
 
 auto ConnectionConfigurationHelper::CreateCertificateProvider(
   const object &from) const -> std::unique_ptr<CertificateProvider> {
@@ -53,11 +169,17 @@ auto ConnectionConfigurationHelper::CreateCertificateProvider(
   if (from.contains("server_certificate_path")) {
     const value &path_str = from.at("server_certificate_path");
     assert(path_str.is_string() == true);
+    const path certificate_file = path(path_str.as_string().c_str());
+    if (!exists(certificate_file)) {
+      throw ConfigurationError(
+        "File does not exist (server_certificate_path): " +
+        certificate_file.string());
+    }
     certificate_provider = std::make_unique<PemFileCertificateProvider>(
-      std::string(path_str.as_string().c_str()));
+      path(path_str.as_string().c_str()));
   }
   return certificate_provider;
-};
+}
 
 auto ConnectionConfigurationHelper::CreateAuthenticator(
   const object &from) const -> std::unique_ptr<Authenticator> {
@@ -87,7 +209,7 @@ auto ConnectionConfigurationHelper::CreateAuthenticator(
     }
   }
   return authenticator;
-};
+}
 
 auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
   const bool tls, const std::string &host, const int port,
@@ -113,7 +235,7 @@ auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
   } else {
     return std::make_unique<InsecureConnectionConfiguration>(host, port);
   }
-};
+}
 
 auto ConnectionConfigurationHelper::IsTls(const object &from) const -> bool {
   bool tls = true;
@@ -123,7 +245,7 @@ auto ConnectionConfigurationHelper::IsTls(const object &from) const -> bool {
     tls = tls_switch.as_bool();
   }
   return tls;
-};
+}
 
 auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
   const object &from) const -> std::unique_ptr<ConnectionConfiguration> {
@@ -145,16 +267,119 @@ auto ConnectionConfigurationHelper::CreateConnectionConfiguration(
     tls, std::string(host.as_string().c_str()),
     static_cast<int>(port.as_int64()), certificate_provider.get(),
     authenticator.get());
-};
+}
+
+auto LoggingConfigurationHelper::CreateConsoleSinkConfiguration(
+  const object & /*from*/, const std::string &name, int level) const
+  -> std::shared_ptr<caosdb::logging::SinkConfiguration> {
+  auto result = std::make_shared<ConsoleSinkConfiguration>(name, level);
+  return result;
+}
+
+auto LoggingConfigurationHelper::CreateSyslogSinkConfiguration(
+  const object & /*from*/, const std::string &name, int level) const
+  -> std::shared_ptr<caosdb::logging::SinkConfiguration> {
+  auto result = std::make_shared<SyslogSinkConfiguration>(name, level);
+  return result;
+}
+
+auto LoggingConfigurationHelper::CreateFileSinkConfiguration(
+  const object &from, const std::string &name, int level) const
+  -> std::shared_ptr<caosdb::logging::SinkConfiguration> {
+  auto result = std::make_shared<FileSinkConfiguration>(name, level);
+  if (from.contains("directory")) {
+    result->SetDirectory(from.at("directory").as_string().c_str());
+  }
+  return result;
+}
+
+auto LoggingConfigurationHelper::CreateSinkConfiguration(
+  const object &from, const std::string &name, int default_level) const
+  -> std::shared_ptr<caosdb::logging::SinkConfiguration> {
+  assert(from.contains("destination"));
+  const auto &destination =
+    std::string(from.at("destination").as_string().c_str());
+  int level = from.contains("level")
+                ? static_cast<int>(from.at("level").as_int64())
+                : default_level;
+
+  if (destination == "file") {
+    return CreateFileSinkConfiguration(from, name, level);
+  } else if (destination == "console") {
+    return CreateConsoleSinkConfiguration(from, name, level);
+  } else if (destination == "syslog") {
+    return CreateSyslogSinkConfiguration(from, name, level);
+  } else {
+    throw ConfigurationError("Unknown sink destination: " + destination);
+  }
+}
+
+auto LoggingConfigurationHelper::CreateLoggingConfiguration(
+  const object &from) const -> LoggingConfiguration {
+  auto default_level_str = from.contains("level")
+                             ? std::string(from.at("level").as_string().c_str())
+                             : "";
+  int default_level = 0;
+  static std::map<std::string, int> log_level_names = {
+    {"", CAOSDB_DEFAULT_LOG_LEVEL},    {"off", CAOSDB_LOG_LEVEL_OFF},
+    {"fatal", CAOSDB_LOG_LEVEL_FATAL}, {"error", CAOSDB_LOG_LEVEL_ERROR},
+    {"warn", CAOSDB_LOG_LEVEL_WARN},   {"info", CAOSDB_LOG_LEVEL_INFO},
+    {"debug", CAOSDB_LOG_LEVEL_DEBUG}, {"trace", CAOSDB_LOG_LEVEL_TRACE},
+    {"all", CAOSDB_LOG_LEVEL_ALL}};
+
+  try {
+    default_level = CAOSDB_DEFAULT_LOG_LEVEL;
+  } catch (const std::out_of_range &exc) {
+    throw ConfigurationError("Unknown log level: " + default_level_str);
+  }
+
+  auto result = LoggingConfiguration(default_level);
+  if (default_level == CAOSDB_LOG_LEVEL_OFF) {
+    return result;
+  }
 
-auto ConfigurationManager::mReset() -> void {
-  mClear();
-  InitializeDefaults();
-};
+  const auto &sinks = from.at("sinks").as_object();
+  if (!sinks.empty()) {
+    const auto *elem = sinks.begin();
 
-auto ConfigurationManager::mClear() -> void {
-  json_configuration = value(nullptr);
-  ConnectionManager::Reset();
+    while (elem != sinks.end()) {
+      result.AddSink(CreateSinkConfiguration(
+        elem->value().as_object(), elem->key().to_string(), default_level));
+      elem = std::next(elem);
+    }
+  }
+
+  return result;
+}
+
+auto ConfigurationManager::mReset() noexcept -> int {
+  try {
+    mClear();
+    InitializeDefaults();
+    return StatusCode::SUCCESS;
+  } catch (const caosdb::exceptions::Exception &exc) {
+    return exc.GetCode();
+  } catch (const std::exception &exc) {
+    CAOSDB_LOG_ERROR(logger_name)
+      << "Unknown error during the reset of the ConfigurationManager: "
+      << exc.what();
+    return StatusCode::CONFIGURATION_ERROR;
+  }
+}
+
+auto ConfigurationManager::mClear() noexcept -> int {
+  try {
+    json_configuration = value(nullptr);
+    ConnectionManager::Reset();
+    return StatusCode::SUCCESS;
+  } catch (const caosdb::exceptions::Exception &exc) {
+    return exc.GetCode();
+  } catch (const std::exception &exc) {
+    CAOSDB_LOG_ERROR(logger_name)
+      << "Unknown error during the reset of the ConfigurationManager: "
+      << exc.what();
+    return StatusCode::CONFIGURATION_ERROR;
+  }
 }
 
 auto ConfigurationManager::mLoadSingleJSONConfiguration(const path &json_file)
@@ -167,15 +392,14 @@ auto ConfigurationManager::mLoadSingleJSONConfiguration(const path &json_file)
   }
 
   json_configuration = load_json_file(json_file);
-  // TODO(far future) validate against json-schema
-};
+}
 
 auto ConfigurationManager::mGetConnectionConfiguration(
   const std::string &name) const -> std::unique_ptr<ConnectionConfiguration> {
   auto connection_json = GetConnection(name);
   return connection_configuration_helper.CreateConnectionConfiguration(
     connection_json);
-};
+}
 
 auto ConfigurationManager::mGetDefaultConnectionName() const -> std::string {
   auto connections = GetConnections();
@@ -196,7 +420,7 @@ auto ConfigurationManager::mGetDefaultConnectionName() const -> std::string {
     return connections.begin()->key().to_string();
   }
   throw ConfigurationError("Could not determine the default connection.");
-};
+}
 
 auto ConfigurationManager::GetConfiguration() const -> const object & {
   if (json_configuration.is_null()) {
@@ -204,7 +428,7 @@ auto ConfigurationManager::GetConfiguration() const -> const object & {
   }
   assert(json_configuration.is_object());
   return json_configuration.as_object();
-};
+}
 
 auto ConfigurationManager::GetConnections() const -> const object & {
   const auto &configuration = GetConfiguration();
@@ -224,7 +448,7 @@ auto ConfigurationManager::GetConnections() const -> const object & {
       "This CaosDB client hasn't any configured connections.");
   }
   return connections_object;
-};
+}
 
 auto ConfigurationManager::GetConnection(const std::string &name) const
   -> const object & {
@@ -236,9 +460,9 @@ auto ConfigurationManager::GetConnection(const std::string &name) const
   }
   throw ConfigurationError("The connection '" + name +
                            "' has not been defined.");
-};
+}
 
-auto ConfigurationManager::InitializeDefaults() -> void {
+auto ConfigurationManager::InitializeDefaults() -> int {
 
   // find the configuration file...
   std::unique_ptr<path> configuration_file_path;
@@ -282,9 +506,23 @@ auto ConfigurationManager::InitializeDefaults() -> void {
   if (configuration_file_path != nullptr) {
     // TODO(tf): log which file has been used.
     mLoadSingleJSONConfiguration(*configuration_file_path);
+  }
+
+  // Logging in the configuration leads to additional content.
+  if (this->json_configuration.is_object() &&
+      this->json_configuration.as_object().contains("logging")) {
+    LoggingConfiguration logging_configuration =
+      logging_configuration_helper.CreateLoggingConfiguration(
+        json_configuration.at("logging").as_object());
+    logging::initialize_logging(logging_configuration);
   } else {
-    // TODO(tf): log warning: no configuration files has been found
+    logging::initialize_logging_defaults();
+    CAOSDB_LOG_WARN(logger_name) << "No configuration has been found.";
   }
+
+  return 0;
 }
 
+const int IS_INITIALIZED = ConfigurationManager::Reset();
+
 } // namespace caosdb::configuration
diff --git a/src/caosdb/connection.cpp b/src/caosdb/connection.cpp
index d2a2ae1fb2408362c18c5a7d5815ac75e3c91406..4ff50d24d329fa6ece3a8e87d304378f3d157782 100644
--- a/src/caosdb/connection.cpp
+++ b/src/caosdb/connection.cpp
@@ -20,139 +20,29 @@
  *
  */
 #include "caosdb/connection.h"
-#include <grpcpp/create_channel.h>              // for CreateChannel
-#include <grpcpp/impl/codegen/client_context.h> // for ClientContext
-#include <grpcpp/impl/codegen/status.h>         // for Status
-#include <grpcpp/security/credentials.h>        // for SslCredentials
-#include <iostream>                             // for operator<<, basic_...
-#include <stdexcept>                            // for runtime_error
-#include <string>                               // for operator+, char_tr...
-#include <memory>
-#include "caosdb/authentication.h" // for Authenticator
-#include "caosdb/configuration.h"
-#include "caosdb/exceptions.h"                    // for AuthenticationError
+#include "caosdb/configuration.h"                 // for ConnectionConfigur...
+#include "caosdb/exceptions.h"                    // for ConfigurationError
+#include "caosdb/info.h"                          // for VersionInfo
 #include "caosdb/info/v1alpha1/main.grpc.pb.h"    // for GeneralInfoService
-#include "caosdb/info/v1alpha1/main.pb.h"         // for GetVersionInfoResp...
+#include "caosdb/info/v1alpha1/main.pb.h"         // for GetVersionInfoRequest
 #include "caosdb/transaction.h"                   // for Transaction
-#include "caosdb/utility.h"                       // for load_string_file
-#include "caosdb/info.h"                          // for VersionInfo
+#include "caosdb/transaction_status.h"            // for TransactionStatus
 #include "grpcpp/impl/codegen/status_code_enum.h" // for StatusCode, UNAUTH...
+#include <grpcpp/create_channel.h>                // for CreateChannel
+#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
+#include <grpcpp/impl/codegen/status.h>           // for Status
+#include <string>                                 // for string, operator+
 
 namespace caosdb::connection {
-using caosdb::authentication::Authenticator;
 using caosdb::configuration::ConfigurationManager;
+using caosdb::configuration::ConnectionConfiguration;
 using caosdb::entity::v1alpha1::EntityTransactionService;
-using caosdb::exceptions::AuthenticationError;
-using caosdb::exceptions::ConnectionError;
 using caosdb::info::VersionInfo;
 using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::info::v1alpha1::GetVersionInfoRequest;
 using caosdb::info::v1alpha1::GetVersionInfoResponse;
 using caosdb::transaction::Transaction;
-using caosdb::utility::load_string_file;
-using grpc::InsecureChannelCredentials;
-using grpc::SslCredentials;
-using grpc::SslCredentialsOptions;
-
-PemFileCertificateProvider::PemFileCertificateProvider(
-  const std::string &path) {
-  this->certificate_provider = load_string_file(path);
-}
-
-auto PemFileCertificateProvider::GetCertificatePem() const -> std::string {
-  return this->certificate_provider;
-}
-
-PemCertificateProvider::PemCertificateProvider(
-  const std::string &certificate_provider) {
-  this->certificate_provider = certificate_provider;
-}
-
-auto PemCertificateProvider::GetCertificatePem() const -> std::string {
-  return this->certificate_provider;
-}
-
-ConnectionConfiguration::ConnectionConfiguration(const std::string &host,
-                                                 int port) {
-  this->host = host;
-  this->port = port;
-}
-
-auto ConnectionConfiguration::GetHost() const -> std::string {
-  return this->host;
-}
-
-auto ConnectionConfiguration::GetPort() const -> int { return this->port; }
-
-auto operator<<(std::ostream &out, const ConnectionConfiguration &configuration)
-  -> std::ostream & {
-  out << configuration.ToString();
-  return out;
-}
-
-InsecureConnectionConfiguration::InsecureConnectionConfiguration(
-  const std::string &host, int port)
-  : ConnectionConfiguration(host, port) {
-  this->credentials = InsecureChannelCredentials();
-}
-
-auto InsecureConnectionConfiguration::GetChannelCredentials() const
-  -> std::shared_ptr<ChannelCredentials> {
-  return this->credentials;
-}
-
-auto InsecureConnectionConfiguration::ToString() const -> std::string {
-  return "InsecureConnectionConfiguration(" + this->GetHost() + "," +
-         std::to_string(this->GetPort()) + ")";
-}
-
-TlsConnectionConfiguration::TlsConnectionConfiguration(const std::string &host,
-                                                       int port)
-  : ConnectionConfiguration(host, port) {
-  SslCredentialsOptions options;
-  this->credentials = SslCredentials(options);
-}
-
-TlsConnectionConfiguration::TlsConnectionConfiguration(
-  const std::string &host, int port,
-  const CertificateProvider &certificate_provider)
-  : ConnectionConfiguration(host, port) {
-  SslCredentialsOptions options;
-  options.pem_root_certs = certificate_provider.GetCertificatePem();
-  this->credentials = SslCredentials(options);
-}
-
-TlsConnectionConfiguration::TlsConnectionConfiguration(
-  const std::string &host, int port, const Authenticator &authenticator)
-  : ConnectionConfiguration(host, port) {
-
-  SslCredentialsOptions options;
-  this->credentials = grpc::CompositeChannelCredentials(
-    SslCredentials(options), authenticator.GetCallCredentials());
-}
-
-TlsConnectionConfiguration::TlsConnectionConfiguration(
-  const std::string &host, int port,
-  const CertificateProvider &certificate_provider,
-  const Authenticator &authenticator)
-  : ConnectionConfiguration(host, port) {
-
-  SslCredentialsOptions options;
-  options.pem_root_certs = certificate_provider.GetCertificatePem();
-  this->credentials = grpc::CompositeChannelCredentials(
-    SslCredentials(options), authenticator.GetCallCredentials());
-}
-
-auto TlsConnectionConfiguration::GetChannelCredentials() const
-  -> std::shared_ptr<ChannelCredentials> {
-  return this->credentials;
-}
-
-auto TlsConnectionConfiguration::ToString() const -> std::string {
-  return "TlsConnectionConfiguration(" + this->GetHost() + "," +
-         std::to_string(this->GetPort()) + "," + this->certificate_provider +
-         ")";
-}
+using caosdb::transaction::TransactionStatus;
 
 Connection::Connection(const ConnectionConfiguration &configuration) {
   const std::string target =
@@ -164,40 +54,50 @@ Connection::Connection(const ConnectionConfiguration &configuration) {
     std::make_shared<EntityTransactionService::Stub>(this->channel);
 }
 
-auto operator<<(std::ostream &out, const Connection & /*connection*/)
-  -> std::ostream & {
-  out << "Connection()";
-  return out;
-}
+auto Connection::RetrieveVersionInfoNoExceptions() const noexcept
+  -> TransactionStatus {
 
-[[nodiscard]] auto Connection::GetVersionInfo() const
-  -> std::unique_ptr<VersionInfo> {
   const GetVersionInfoRequest request;
   GetVersionInfoResponse response;
   grpc::ClientContext context;
-  const grpc::Status status =
+  const grpc::Status grpc_status =
     this->general_info_service->GetVersionInfo(&context, request, &response);
 
-  if (!status.ok()) {
-    switch (status.error_code()) {
+  auto status = TransactionStatus::SUCCESS();
+  if (!grpc_status.ok()) {
+    switch (grpc_status.error_code()) {
     case grpc::StatusCode::UNAUTHENTICATED:
-      throw AuthenticationError(status.error_message());
+      status =
+        TransactionStatus::AUTHENTICATION_ERROR(grpc_status.error_message());
+      break;
     case grpc::StatusCode::UNAVAILABLE:
-      throw ConnectionError(status.error_message());
+      status = TransactionStatus::CONNECTION_ERROR();
+      break;
     default:
-      throw std::runtime_error("Status Code " +
-                               std::to_string(status.error_code()) + " - " +
-                               status.error_message());
+      auto error_message = grpc_status.error_message();
+      status = TransactionStatus::RPC_ERROR(
+        std::to_string(grpc_status.error_code()) + " - " + error_message);
     }
+  } else {
+    this->version_info =
+      std::make_unique<VersionInfo>(response.release_version_info());
   }
-  return std::make_unique<VersionInfo>(response.release_version_info());
+
+  return status;
+}
+
+auto Connection::RetrieveVersionInfo() const -> const VersionInfo & {
+
+  TransactionStatus status = RetrieveVersionInfoNoExceptions();
+  status.ThrowExceptionIfError();
+  return *GetVersionInfo();
 }
 
 [[nodiscard]] auto Connection::CreateTransaction() const
   -> std::unique_ptr<Transaction> {
   auto service_stub = this->entity_transaction_service;
   return std::make_unique<Transaction>(service_stub);
-};
+}
 
 auto ConnectionManager::mHasConnection(const std::string &name) const -> bool {
   auto it = connections.find(name);
diff --git a/src/caosdb/logging.cpp b/src/caosdb/logging.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3618826a5ed6061f210c393b3429b641293fccae
--- /dev/null
+++ b/src/caosdb/logging.cpp
@@ -0,0 +1,213 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#include "caosdb/logging.h"
+#include "boost/core/swap.hpp" // for swap
+#include "boost/iterator/iterator_facade.hpp"
+#include "boost/log/attributes/clock.hpp"
+#include "boost/log/core/core.hpp" // for core
+#include "boost/log/core/record.hpp"
+#include "boost/log/sources/record_ostream.hpp"
+#include "boost/log/utility/setup/from_settings.hpp"
+#include "boost/log/utility/setup/settings.hpp"
+#include "boost/move/utility_core.hpp" // for move
+#include "boost/multi_index/detail/bidir_node_iterator.hpp"
+#include "boost/operators.hpp"
+#include "boost/preprocessor/seq/limits/enum_256.hpp"
+#include "boost/preprocessor/seq/limits/size_256.hpp"
+#include "boost/property_tree/detail/exception_implementation.hpp"
+#include "boost/smart_ptr/shared_ptr.hpp"
+#include "boost/tuple/detail/tuple_basic.hpp" // for get
+#include "caosdb/log_level.h"
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility> // for move
+#include <vector>
+
+namespace caosdb::logging {
+
+LoggingConfiguration::LoggingConfiguration(int level)
+  : LevelConfiguration(level) {}
+
+auto LoggingConfiguration::AddSink(
+  const std::shared_ptr<SinkConfiguration> &sink) -> void {
+  this->sinks.push_back(sink);
+}
+
+auto LoggingConfiguration::GetSinks() const
+  -> const std::vector<std::shared_ptr<SinkConfiguration>> & {
+  return this->sinks;
+}
+
+SinkConfiguration::SinkConfiguration(std::string name, int level)
+  : LevelConfiguration(level), name(std::move(name)) {}
+
+[[nodiscard]] auto SinkConfiguration::GetName() const -> const std::string & {
+  return this->name;
+}
+
+auto SinkConfiguration::Configure(boost::log::settings &settings) const
+  -> void {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter SinkConfiguration::Configure(&settings)";
+  auto sink = "Sinks." + GetName();
+  settings[sink]["Destination"] = GetDestination();
+  settings[sink]["Filter"] = "%Severity% >= " + std::to_string(GetLevel());
+  settings[sink]["AutoFlush"] = true;
+  settings[sink]["Format"] = "[%TimeStamp%][%Severity%] %Channel% - %Message%";
+}
+
+ConsoleSinkConfiguration::ConsoleSinkConfiguration(const std::string &name,
+                                                   int level)
+  : SinkConfiguration(name, level) {}
+
+[[nodiscard]] auto ConsoleSinkConfiguration::GetDestination() const
+  -> const std::string & {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter ConsoleSinkConfiguration::GetDestination()";
+  return this->destination;
+}
+
+auto ConsoleSinkConfiguration::Configure(boost::log::settings &settings) const
+  -> void {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter ConsoleSinkConfiguration::Configure(&settings)";
+  sink_configuration::Configure(settings);
+}
+
+FileSinkConfiguration::FileSinkConfiguration(const std::string &name, int level)
+  : SinkConfiguration(name, level) {}
+
+[[nodiscard]] auto FileSinkConfiguration::GetDestination() const
+  -> const std::string & {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter FileSinkConfiguration::GetDestination()";
+  return this->destination;
+}
+
+auto FileSinkConfiguration::SetDirectory(const std::string &directory) -> void {
+  this->directory = std::string(directory);
+}
+
+auto FileSinkConfiguration::Configure(boost::log::settings &settings) const
+  -> void {
+  CAOSDB_LOG_TRACE(logger_name)
+    << "Enter FileSinkConfiguration::Configure(&settings)";
+  sink_configuration::Configure(settings);
+  settings["Sink." + GetName() + ".Target"] = this->directory;
+}
+
+SyslogSinkConfiguration::SyslogSinkConfiguration(const std::string &name,
+                                                 int level)
+  : SinkConfiguration(name, level) {}
+
+[[nodiscard]] auto SyslogSinkConfiguration::GetDestination() const
+  -> const std::string & {
+  return this->destination;
+}
+
+// Called if no custom logging settings are specified.
+auto initialize_logging_defaults() -> int {
+  // first: turn everything off
+  boost::log::settings off_settings;
+  off_settings["Core.DisableLogging"] = true;
+  boost::log::init_from_settings(off_settings);
+
+  // now set everything up
+  const static std::vector<std::shared_ptr<SinkConfiguration>> default_sinks = {
+    std::make_shared<ConsoleSinkConfiguration>("DEFAULT_SINK_1",
+                                               CAOSDB_DEFAULT_LOG_LEVEL)};
+
+  boost::log::settings default_settings;
+
+  default_settings["Core.DisableLogging"] = false;
+
+  for (const auto &sink : default_sinks) {
+    sink->Configure(default_settings);
+  }
+
+  boost::log::init_from_settings(default_settings);
+  auto core = boost::log::core::get();
+  core->add_global_attribute("TimeStamp",
+                             boost::log::attributes::local_clock());
+
+  CAOSDB_LOG_DEBUG(logger_name) << "Initialized default settings.";
+
+  return 0;
+}
+
+// Called if custom logging settings are specified.
+auto initialize_logging(const LoggingConfiguration &configuration) -> void {
+  boost::log::settings new_settings;
+
+  if (configuration.GetLevel() == CAOSDB_LOG_LEVEL_OFF) {
+    new_settings["Core.DisableLogging"] = true;
+    return;
+  } else {
+    new_settings["Core.DisableLogging"] = false;
+  }
+
+  for (const auto &sink : configuration.GetSinks()) {
+    sink->Configure(new_settings);
+  }
+
+  boost::log::init_from_settings(new_settings);
+
+  CAOSDB_LOG_DEBUG(logger_name) << "Initialized logging with custom settings.";
+}
+
+void caosdb_log_fatal(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_FATAL)
+    << msg;
+}
+
+void caosdb_log_error(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_ERROR)
+    << msg;
+}
+
+void caosdb_log_warn(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_WARN)
+    << msg;
+}
+
+void caosdb_log_info(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_INFO)
+    << msg;
+}
+
+void caosdb_log_debug(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_DEBUG)
+    << msg;
+}
+
+void caosdb_log_trace(const char *channel, const char *msg) {
+  BOOST_LOG_CHANNEL_SEV(caosdb::logging::logger::get(), channel,
+                        CAOSDB_LOG_LEVEL_TRACE)
+    << msg;
+}
+
+} // namespace caosdb::logging
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index bf2e88411fa15fd94f7b1ca4a1546c1bc23e2b90..b0c00eb4a53881c54cdb4950b07a799960a98f4d 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -17,27 +17,65 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
-
-#include "caosdb/entity.h"
-#include "caosdb/exceptions.h" // for AuthenticationError
 #include "caosdb/transaction.h"
-#include <memory>
-#include <utility>
-#include <iostream>                               // for basic_ostream::ope...
-#include <stdexcept>                              // for runtime_error
-#include <grpcpp/impl/codegen/client_context.h>   // for ClientContext
-#include <grpcpp/impl/codegen/status.h>           // for Status
+#include "caosdb/entity/v1alpha1/main.grpc.pb.h"  // for EntityTransactionS...
+#include "caosdb/entity/v1alpha1/main.pb.h"       // for RetrieveRequest
+#include "caosdb/status_code.h"                   // for StatusCode, AUTHEN...
+#include "grpcpp/grpcpp.h"                        // for CompletionQueue
+#include "grpcpp/impl/codegen/async_unary_call.h" // for ClientAsyncRespons...
+#include "grpcpp/impl/codegen/client_context.h"   // for ClientContext
+#include "grpcpp/impl/codegen/completion_queue.h" // for CompletionQueue
+#include "grpcpp/impl/codegen/status.h"           // for Status
 #include "grpcpp/impl/codegen/status_code_enum.h" // for StatusCode, UNAUTH...
-#include "caosdb/entity/v1alpha1/main.pb.h"
-#include "caosdb/entity/v1alpha1/main.grpc.pb.h"
+#include <cassert>                                // for assert
+#include <map>                                    // for map
+#include <memory>                                 // for allocator, unique_ptr
+#include <stdexcept>                              // for out_of_range
+#include <utility>                                // for move
+
+namespace caosdb {
+
+// TODO(tf) move to another source file.
+auto get_status_description(int code) -> const std::string & {
+  static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION";
+  static const std::map<int, std::string> descriptions = {
+    {StatusCode::INITIAL, "The transaction has not been executed yet and "
+                          "clients can stil change it."},
+    {StatusCode::EXECUTING, "The transaction is currently being executed."},
+    {StatusCode::SUCCESS, "The action was successful"},
+    {StatusCode::CONNECTION_ERROR,
+     "The attempt to execute this transaction was not successful because the "
+     "connection to the server could not be established."},
+    {StatusCode::AUTHENTICATION_ERROR,
+     "The attempt to execute this transaction has not been executed at all "
+     "because the authentication did not succeed."},
+    {StatusCode::GENERIC_RPC_ERROR,
+     "The attempt to execute this transaction was not successful because an "
+     "error occured in the transport or RPC protocol layer."},
+    {StatusCode::GENERIC_ERROR,
+     "An error occured. Please review the logs for more information."},
+    {StatusCode::GENERIC_TRANSACTION_ERROR,
+     "The transaction terminated unsuccessfully with transaction errors."},
+    {StatusCode::CONFIGURATION_ERROR,
+     "An error occurred during the configuration of the ConfigurationManager."},
+    {StatusCode::UNKNOWN_CONNECTION_ERROR,
+     "The ConnectionManager does not know any connection of this name."}};
+  try {
+    return descriptions.at(code);
+  } catch (const std::out_of_range &exc) {
+    return MISSING_DESCRIPTION;
+  }
+}
+
+} // namespace caosdb
 
 namespace caosdb::transaction {
 using caosdb::entity::v1alpha1::EntityTransactionService;
 using caosdb::entity::v1alpha1::RetrieveRequest;
 using caosdb::entity::v1alpha1::RetrieveResponse;
+using grpc::ClientAsyncResponseReader;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
-using caosdb::exceptions::AuthenticationError;
-using caosdb::exceptions::ConnectionError;
+using grpc::CompletionQueue;
 
 [[nodiscard]] auto UniqueResult::GetEntity() const -> const Entity & {
   const Entity *result = this->entity.get();
@@ -57,30 +95,66 @@ auto Transaction::RetrieveById(const std::string &id) -> void {
   this->request = request;
 }
 
-auto Transaction::Execute() -> void {
-  // TODO(tf) throw error if not int INIT state
-  this->state = TransactionState::EXECUTING;
+auto Transaction::Execute() -> TransactionStatus {
+  ExecuteAsynchronously();
+  this->status.ThrowExceptionIfError();
+  return this->status;
+}
+
+auto Transaction::ExecuteAsynchronously() noexcept -> void {
+  this->status = TransactionStatus::EXECUTING();
 
+  grpc::Status grpc_status;
+  CompletionQueue cq;
   RetrieveResponse response;
+
   grpc::ClientContext context;
-  const grpc::Status status =
-    this->service_stub->Retrieve(&context, this->request, &response);
+  std::unique_ptr<ClientAsyncResponseReader<RetrieveResponse>> rpc(
+    this->service_stub->PrepareAsyncRetrieve(&context, this->request, &cq));
+  rpc->StartCall();
+
+  int tag = 1;
+  void *send_tag = static_cast<void *>(&tag);
+  rpc->Finish(&response, &grpc_status, send_tag);
+  void *recv_tag = nullptr;
+  bool ok = false;
+
+  // TODO(tf) make this actually asynchronous by moving this to WaitForIt()
+  cq.Next(&recv_tag, &ok);
+  assert(recv_tag == send_tag);
+  assert(ok);
+
+  // const grpc::Status grpc_status =
+  // this->service_stub->Retrieve(&context, this->request, &response);
 
-  if (!status.ok()) {
-    switch (status.error_code()) {
+  if (!grpc_status.ok()) {
+    switch (grpc_status.error_code()) {
     case grpc::StatusCode::UNAUTHENTICATED:
-      throw AuthenticationError(status.error_message());
+      this->status = TransactionStatus::AUTHENTICATION_ERROR();
+      break;
     case grpc::StatusCode::UNAVAILABLE:
-      throw ConnectionError(status.error_message());
+      this->status = TransactionStatus::CONNECTION_ERROR();
+      break;
     default:
-      std::cout << status.error_code() << "\n";
-      throw std::runtime_error(status.error_message());
+      auto error_details = std::to_string(grpc_status.error_code()) + " - " +
+                           grpc_status.error_message();
+      this->status = TransactionStatus::RPC_ERROR(error_details);
     }
+    return;
+  } else {
+    this->status = TransactionStatus::SUCCESS();
   }
 
   auto *entity = response.release_entity();
-  auto result_set = std::make_unique<UniqueResult>(entity);
-  this->result_set = std::move(result_set);
+  if (!entity->errors().empty()) {
+    this->status =
+      TransactionStatus::TRANSACTION_ERROR("The request returned with errors.");
+  }
+  this->result_set = std::make_unique<UniqueResult>(entity);
+}
+
+auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
+  return this->status;
 }
 
 } // namespace caosdb::transaction
diff --git a/src/ccaosdb.cpp b/src/ccaosdb.cpp
index 2c9539af8036de10d36bb41b6ce188070cb21bcc..5f89af16ed3da4de89df1d83e1de0bdecb79592f 100644
--- a/src/ccaosdb.cpp
+++ b/src/ccaosdb.cpp
@@ -1,35 +1,54 @@
-#include <iostream>
-#include <stdio.h>
-#include <cassert>
+#include "ccaosdb.h"
+#include "caosdb/connection.h"
 #include "caosdb/constants.h"
 #include "caosdb/utility.h"
-#include "caosdb/constants.h"
-#include "caosdb/connection.h"
-#include "ccaosdb.h"
+#include "caosdb/status_code.h"
+#include "caosdb/logging.h"
+#include <cassert>
+#include <exception>
+#include <iostream>
+#include <stdio.h>
+#include <string.h>
 
 extern "C" {
 
-const int caosdb_constants_LIBCAOSDB_VERSION_MAJOR() {
+#define CCAOSDB_LOGGER_NAME "ccaosdb"
+
+/*
+ * Macro for wrapping every function into a try-catch clause. If an exception
+ * occurs, the given StatusCode is being returned.
+ */
+#define ERROR_RETURN_CODE(code, fun, body)                                     \
+  fun {                                                                        \
+    try {                                                                      \
+      body                                                                     \
+    } catch (const std::exception &exc) {                                      \
+      caosdb::logging::caosdb_log_fatal(CCAOSDB_LOGGER_NAME, exc.what());      \
+      return caosdb::StatusCode::code;                                         \
+    }                                                                          \
+  }
+
+int caosdb_constants_LIBCAOSDB_VERSION_MAJOR() {
   return caosdb::LIBCAOSDB_VERSION_MAJOR;
 }
 
-const int caosdb_constants_LIBCAOSDB_VERSION_MINOR() {
+int caosdb_constants_LIBCAOSDB_VERSION_MINOR() {
   return caosdb::LIBCAOSDB_VERSION_MINOR;
 }
 
-const int caosdb_constants_LIBCAOSDB_VERSION_PATCH() {
+int caosdb_constants_LIBCAOSDB_VERSION_PATCH() {
   return caosdb::LIBCAOSDB_VERSION_PATCH;
 }
 
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR() {
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_MAJOR() {
   return caosdb::COMPATIBLE_SERVER_VERSION_MAJOR;
 }
 
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR() {
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_MINOR() {
   return caosdb::COMPATIBLE_SERVER_VERSION_MINOR;
 }
 
-const int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH() {
+int caosdb_constants_COMPATIBLE_SERVER_VERSION_PATCH() {
   return caosdb::COMPATIBLE_SERVER_VERSION_PATCH;
 }
 
@@ -37,148 +56,189 @@ const char *caosdb_constants_COMPATIBLE_SERVER_VERSION_PRE_RELEASE() {
   return caosdb::COMPATIBLE_SERVER_VERSION_PRE_RELEASE;
 }
 
-const char *caosdb_utility_get_env_var(const char *name,
-                                       const char *fall_back) {
-  return caosdb::utility::get_env_var(name, fall_back);
-}
-
-int caosdb_connection_create_pem_file_certificate_provider(
-  caosdb_connection_certificate_provider *out, const char *path) {
-  out->wrapped_certificate_provider =
-    new caosdb::connection::PemFileCertificateProvider(std::string(path));
-  return 0;
-}
-
-int caosdb_connection_delete_certificate_provider(
-  caosdb_connection_certificate_provider *provider) {
-  delete static_cast<caosdb::connection::CertificateProvider *>(
-    provider->wrapped_certificate_provider);
-  return 0;
-}
-
-int caosdb_authentication_create_plain_password_authenticator(
-  caosdb_authentication_authenticator *out, const char *username,
-  const char *password) {
-  out->wrapped_authenticator =
-    new caosdb::authentication::PlainPasswordAuthenticator(
-      std::string(username), std::string(password));
-  return 0;
-}
-
-int caosdb_authentication_delete_authenticator(
-  caosdb_authentication_authenticator *authenticator) {
-  delete static_cast<caosdb::authentication::Authenticator *>(
-    authenticator->wrapped_authenticator);
-  return 0;
-}
-
-int caosdb_connection_create_tls_connection_configuration(
-  caosdb_connection_connection_configuration *out, const char *host,
-  const int port, caosdb_authentication_authenticator *authenticator,
-  caosdb_connection_certificate_provider *provider) {
-
-  auto host_str = std::string(host);
-  if (authenticator != nullptr && provider != nullptr) {
-    auto wrapped_provider =
-      static_cast<caosdb::connection::CertificateProvider *>(
-        provider->wrapped_certificate_provider);
-    auto wrapped_authenticator =
-      static_cast<caosdb::authentication::Authenticator *>(
-        authenticator->wrapped_authenticator);
-    out->wrapped_connection_configuration =
-      new caosdb::connection::TlsConnectionConfiguration(
-        host_str, port, *wrapped_provider, *wrapped_authenticator);
-  } else if (authenticator != nullptr) {
-    auto wrapped_authenticator =
-      static_cast<caosdb::authentication::Authenticator *>(
-        authenticator->wrapped_authenticator);
+const char *caosdb_utility_get_env_var(const char *name, const char *fallback) {
+  return caosdb::utility::get_env_var(name, fallback);
+}
+
+const char *caosdb_get_status_description(int code) {
+  return caosdb::get_status_description(code).c_str();
+}
+
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_connection_create_pem_file_certificate_provider(
+                    caosdb_connection_certificate_provider *out,
+                    const char *path),
+                  {
+                    out->wrapped_certificate_provider =
+                      new caosdb::configuration::PemFileCertificateProvider(
+                        std::string(path));
+                    return 0;
+                  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_delete_certificate_provider(
+    caosdb_connection_certificate_provider *provider),
+  {
+    delete static_cast<caosdb::configuration::CertificateProvider *>(
+      provider->wrapped_certificate_provider);
+    return 0;
+  })
+
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_authentication_create_plain_password_authenticator(
+                    caosdb_authentication_authenticator *out,
+                    const char *username, const char *password),
+                  {
+                    out->wrapped_authenticator =
+                      new caosdb::authentication::PlainPasswordAuthenticator(
+                        std::string(username), std::string(password));
+                    return 0;
+                  })
+
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_authentication_delete_authenticator(
+                    caosdb_authentication_authenticator *authenticator),
+                  {
+                    delete static_cast<caosdb::authentication::Authenticator *>(
+                      authenticator->wrapped_authenticator);
+                    return 0;
+                  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_create_tls_connection_configuration(
+    caosdb_connection_connection_configuration *out, const char *host,
+    const int port, caosdb_authentication_authenticator *authenticator,
+    caosdb_connection_certificate_provider *provider),
+  {
+    auto host_str = std::string(host);
+    if (authenticator != nullptr && provider != nullptr) {
+      auto wrapped_provider =
+        static_cast<caosdb::configuration::CertificateProvider *>(
+          provider->wrapped_certificate_provider);
+      auto wrapped_authenticator =
+        static_cast<caosdb::authentication::Authenticator *>(
+          authenticator->wrapped_authenticator);
+      out->wrapped_connection_configuration =
+        new caosdb::configuration::TlsConnectionConfiguration(
+          host_str, port, *wrapped_provider, *wrapped_authenticator);
+    } else if (authenticator != nullptr) {
+      auto wrapped_authenticator =
+        static_cast<caosdb::authentication::Authenticator *>(
+          authenticator->wrapped_authenticator);
+      out->wrapped_connection_configuration =
+        new caosdb::configuration::TlsConnectionConfiguration(
+          host_str, port, *wrapped_authenticator);
+    } else if (provider != nullptr) {
+      auto wrapped_provider =
+        static_cast<caosdb::configuration::CertificateProvider *>(
+          provider->wrapped_certificate_provider);
+      out->wrapped_connection_configuration =
+        new caosdb::configuration::TlsConnectionConfiguration(
+          host_str, port, *wrapped_provider);
+    } else {
+      out->wrapped_connection_configuration =
+        new caosdb::configuration::TlsConnectionConfiguration(host_str, port);
+    }
+    return 0;
+  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_create_insecure_connection_configuration(
+    caosdb_connection_connection_configuration *out, const char *host,
+    const int port),
+  {
     out->wrapped_connection_configuration =
-      new caosdb::connection::TlsConnectionConfiguration(
-        host_str, port, *wrapped_authenticator);
-  } else if (provider != nullptr) {
-    auto wrapped_provider =
-      static_cast<caosdb::connection::CertificateProvider *>(
-        provider->wrapped_certificate_provider);
-    out->wrapped_connection_configuration =
-      new caosdb::connection::TlsConnectionConfiguration(host_str, port,
-                                                         *wrapped_provider);
-  } else {
-    out->wrapped_connection_configuration =
-      new caosdb::connection::TlsConnectionConfiguration(host_str, port);
-  }
-  return 0;
-}
-
-int caosdb_connection_create_insecure_connection_configuration(
-  caosdb_connection_connection_configuration *out, const char *host,
-  const int port) {
-  out->wrapped_connection_configuration =
-    new caosdb::connection::InsecureConnectionConfiguration(host, port);
-  return 0;
-}
-
-int caosdb_connection_delete_connection_configuration(
-  caosdb_connection_connection_configuration *configuration) {
-  delete static_cast<caosdb::connection::ConnectionConfiguration *>(
-    configuration->wrapped_connection_configuration);
-  return 0;
-}
-
-int caosdb_connection_create_connection(
-  caosdb_connection_connection *out,
-  const caosdb_connection_connection_configuration *configuration) {
-  caosdb::connection::ConnectionConfiguration *config =
-    static_cast<caosdb::connection::ConnectionConfiguration *>(
+      new caosdb::configuration::InsecureConnectionConfiguration(host, port);
+    return 0;
+  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_delete_connection_configuration(
+    caosdb_connection_connection_configuration *configuration),
+  {
+    delete static_cast<caosdb::configuration::ConnectionConfiguration *>(
       configuration->wrapped_connection_configuration);
-  out->wrapped_connection = new caosdb::connection::Connection(*config);
-  return 0;
-}
-
-int caosdb_connection_delete_connection(
-  caosdb_connection_connection *connection) {
-  delete static_cast<caosdb::connection::Connection *>(
-    connection->wrapped_connection);
-  return 0;
-}
-
-int caosdb_connection_get_version_info(
-  caosdb_info_version_info *out,
-  const caosdb_connection_connection *connection) {
-  auto *wrapped_connection = static_cast<caosdb::connection::Connection *>(
-    connection->wrapped_connection);
-  auto version_info = wrapped_connection->GetVersionInfo();
-
-  out->major = (int)version_info->GetMajor();
-  out->minor = (int)version_info->GetMinor();
-  out->patch = (int)version_info->GetPatch();
-
-  // copy pre_release, needs local variable because out->pre_release is const
-  char *pre_release =
-    (char *)malloc(sizeof(char) * (version_info->GetPreRelease().length() + 1));
-  strcpy(pre_release, version_info->GetPreRelease().c_str());
-  out->pre_release = pre_release;
-
-  // copy build, needs local variable because out->build is const
-  char *build =
-    (char *)malloc(sizeof(char) * (version_info->GetBuild().length() + 1));
-  strcpy(build, version_info->GetBuild().c_str());
-  out->build = build;
-
-  return 0;
-}
-
-int caosdb_connection_connection_manager_get_default_connection(
-  caosdb_connection_connection *out) {
-  out->wrapped_connection =
-    caosdb::connection::ConnectionManager::GetDefaultConnection().get();
-  return 0;
-}
-
-int caosdb_connection_connection_manager_get_connection(
-  caosdb_connection_connection *out, const char *name) {
-  out->wrapped_connection =
-    caosdb::connection::ConnectionManager::GetConnection(std::string(name))
-      .get();
-  return 0;
-}
+    return 0;
+  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_create_connection(
+    caosdb_connection_connection *out,
+    const caosdb_connection_connection_configuration *configuration),
+  {
+    caosdb::configuration::ConnectionConfiguration *config =
+      static_cast<caosdb::configuration::ConnectionConfiguration *>(
+        configuration->wrapped_connection_configuration);
+    out->wrapped_connection = new caosdb::connection::Connection(*config);
+    return 0;
+  })
+
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_connection_delete_connection(
+                    caosdb_connection_connection *connection),
+                  {
+                    delete static_cast<caosdb::connection::Connection *>(
+                      connection->wrapped_connection);
+                    return 0;
+                  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_get_version_info(
+    caosdb_info_version_info *out,
+    const caosdb_connection_connection *connection),
+  {
+    auto *wrapped_connection = static_cast<caosdb::connection::Connection *>(
+      connection->wrapped_connection);
+
+    auto status = wrapped_connection->RetrieveVersionInfoNoExceptions();
+    if (status.IsError()) {
+      return status.GetCode();
+    }
+    auto version_info = wrapped_connection->GetVersionInfo();
+
+    out->major = (int)version_info->GetMajor();
+    out->minor = (int)version_info->GetMinor();
+    out->patch = (int)version_info->GetPatch();
+
+    // copy pre_release, needs local variable because out->pre_release is const
+    char *pre_release = (char *)malloc(
+      sizeof(char) * (version_info->GetPreRelease().length() + 1));
+    strcpy(pre_release, version_info->GetPreRelease().c_str());
+    out->pre_release = pre_release;
+
+    // copy build, needs local variable because out->build is const
+    char *build =
+      (char *)malloc(sizeof(char) * (version_info->GetBuild().length() + 1));
+    strcpy(build, version_info->GetBuild().c_str());
+    out->build = build;
+
+    return 0;
+  })
+
+ERROR_RETURN_CODE(
+  GENERIC_ERROR,
+  int caosdb_connection_connection_manager_get_default_connection(
+    caosdb_connection_connection *out),
+  {
+    out->wrapped_connection =
+      caosdb::connection::ConnectionManager::GetDefaultConnection().get();
+    return 0;
+  })
+
+ERROR_RETURN_CODE(GENERIC_ERROR,
+                  int caosdb_connection_connection_manager_get_connection(
+                    caosdb_connection_connection *out, const char *name),
+                  {
+                    out->wrapped_connection =
+                      caosdb::connection::ConnectionManager::GetConnection(
+                        std::string(name))
+                        .get();
+                    return 0;
+                  })
 }
diff --git a/src/ccaosdbcli.c b/src/ccaosdbcli.c
index eaadc3c5822b1306140f530dddc7c4a49981e388..e06dff51bcc62fcc8ae2bc4e2db53647d6c3de06 100644
--- a/src/ccaosdbcli.c
+++ b/src/ccaosdbcli.c
@@ -1,17 +1,29 @@
-#include <stdio.h>            // for printf
 #include "caosdb/constants.h" // for LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VER...
 #include "ccaosdb.h"          // for caosdb_info_version_info, caosdb_conne...
+#include <stdio.h>            // for printf
 
 int main(void) {
+  int status = 0; // last function return value
   printf(
     "CaosDB C client (libcaosdb %d.%d.%d)\nWe don't miss the H of caos.\n\n",
     LIBCAOSDB_VERSION_MAJOR, LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_VERSION_PATCH);
 
   caosdb_connection_connection connection;
-  caosdb_connection_connection_manager_get_default_connection(&connection);
+  status =
+    caosdb_connection_connection_manager_get_default_connection(&connection);
+  if (status != 0) {
+    printf("An error occured: ERROR %d - %s\n", status,
+           caosdb_get_status_description(status));
+    return status;
+  }
 
   caosdb_info_version_info version_info;
-  caosdb_connection_get_version_info(&version_info, &connection);
+  status = caosdb_connection_get_version_info(&version_info, &connection);
+  if (status != 0) {
+    printf("An error occured: ERROR %d - %s\n", status,
+           caosdb_get_status_description(status));
+    return status;
+  }
 
   printf("Server version: %d.%d.%d-%s-%s\n", version_info.major,
          version_info.minor, version_info.patch, version_info.pre_release,
diff --git a/src/cxxcaosdbcli.cpp b/src/cxxcaosdbcli.cpp
index 763ddb5177ae372a06767b721b909742624bc2f1..f94144923b79fc4f2a511b6248f158067085b222 100644
--- a/src/cxxcaosdbcli.cpp
+++ b/src/cxxcaosdbcli.cpp
@@ -21,16 +21,18 @@
  */
 
 // A simple caosdb client
-#include <iostream>             // for operator<<, basic_ostream, basic_ost...
-#include <memory>               // for unique_ptr, allocator, __shared_ptr_...
-#include <string>               // for operator<<, char_traits
 #include "caosdb/connection.h"  // for Connection, ConnectionManager
 #include "caosdb/constants.h"   // for LIBCAOSDB_VERSION_MINOR, LIBCAOSDB_V...
 #include "caosdb/entity.h"      // for Entity
+#include "caosdb/exceptions.h"  // for ConfigurationError
 #include "caosdb/info.h"        // for VersionInfo
 #include "caosdb/transaction.h" // for Transaction, UniqueResult, ResultSet
+#include <iostream>             // for operator<<, basic_ostream, basic_ost...
+#include <memory>               // for unique_ptr, allocator, __shared_ptr_...
+#include <string>               // for operator<<, char_traits
 
 auto main() -> int {
+
   std::cout << "CaosDB C++ client (libcaosdb "
             << caosdb::LIBCAOSDB_VERSION_MINOR << "."
             << caosdb::LIBCAOSDB_VERSION_MINOR << "."
@@ -38,27 +40,33 @@ auto main() -> int {
             << "We don't miss the H of caos.\n"
             << std::endl;
 
-  const auto &connection =
-    caosdb::connection::ConnectionManager::GetDefaultConnection();
+  try {
+    const auto &connection =
+      caosdb::connection::ConnectionManager::GetDefaultConnection();
 
-  // get version info of the server
-  const auto &v_info = connection->GetVersionInfo();
-  std::cout << "Server Version: " << v_info->GetMajor() << "."
-            << v_info->GetMinor() << "." << v_info->GetPatch() << "-"
-            << v_info->GetPreRelease() << "-" << v_info->GetBuild()
-            << std::endl;
+    connection->RetrieveVersionInfoNoExceptions();
+    // get version info of the server
+    const auto &v_info = connection->GetVersionInfo();
+    std::cout << "Server Version: " << v_info->GetMajor() << "."
+              << v_info->GetMinor() << "." << v_info->GetPatch() << "-"
+              << v_info->GetPreRelease() << "-" << v_info->GetBuild()
+              << std::endl;
 
-  // retrieve an entity
-  auto transaction(connection->CreateTransaction());
-  transaction->RetrieveById("20");
-  transaction->Execute();
-  const auto &result_set =
-    dynamic_cast<const caosdb::transaction::UniqueResult &>(
-      transaction->GetResultSet());
+    // retrieve an entity
+    auto transaction(connection->CreateTransaction());
+    transaction->RetrieveById("20");
+    transaction->Execute();
+    const auto &result_set =
+      dynamic_cast<const caosdb::transaction::UniqueResult &>(
+        transaction->GetResultSet());
 
-  // print description
-  std::cout << "Entity Description: " << result_set.GetEntity().GetDescription()
-            << std::endl;
+    // print description
+    std::cout << "Entity Description: "
+              << result_set.GetEntity().GetDescription() << std::endl;
 
-  return 0;
+    return 0;
+  } catch (const caosdb::exceptions::ConfigurationError &exc) {
+    std::cout << "ConfigurationError: " << exc.what() << std::endl;
+    return exc.GetCode();
+  }
 }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 17613e450d2cf7c66fd41d215d0640c2df3d0292..4fbdc46040edcca4d92de9122a6520488a573017 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -82,6 +82,7 @@ include(CodeCoverage)
 if (LCOV_PATH)
     message(STATUS "Found LCOV: ${LCOV_PATH}")
     target_link_libraries(caosdb gcov)
+    target_link_libraries(ccaosdb gcov)
     setup_target_for_coverage_lcov(
         NAME unit_test_coverage
         EXECUTABLE ctest -L caosdb-cpplib-unit-tests
@@ -91,8 +92,14 @@ if (LCOV_PATH)
         GENHTML_ARGS --rc lcov_branch_coverage=1
         )
     message(STATUS "Adding COMPILE_FLAGS for coverage: ${COVERAGE_COMPILER_FLAGS}")
-    set_target_properties(caosdb ccaosdb PROPERTIES
-        COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
+    set(TARGET_CAOSDB_COMPILE_FLAGS "${TARGET_CAOSDB_COMPILE_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
+    set(TARGET_CCAOSDB_COMPILE_FLAGS "${TARGET_CCAOSDB_COMPILE_FLAGS} ${COVERAGE_COMPILER_FLAGS}")
+    set(TARGET_CAOSDB_COMPILE_FLAGS ${TARGET_CAOSDB_COMPILE_FLAGS} PARENT_SCOPE)
+    set(TARGET_CCAOSDB_COMPILE_FLAGS ${TARGET_CCAOSDB_COMPILE_FLAGS} PARENT_SCOPE)
+    set_target_properties(caosdb PROPERTIES
+        COMPILE_FLAGS "${TARGET_CAOSDB_COMPILE_FLAGS}")
+    set_target_properties(ccaosdb PROPERTIES
+        COMPILE_FLAGS "${TARGET_CCAOSDB_COMPILE_FLAGS}")
 else ()
     message(WARNING "Could not generate code coverage report. Please install lcov.")
 endif ()
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
index b32ace97cbd888d7f60134c995cefaa1483fa79c..abb06dd7426a642ef1e54593d67e28015168cc71 100644
--- a/test/test_ccaosdb.cpp
+++ b/test/test_ccaosdb.cpp
@@ -20,13 +20,13 @@
  *
  */
 
+#include "caosdb/configuration.h"
+#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
+#include "ccaosdb.h"               // for caosdb_utility_get_env_var
 #include <gtest/gtest-message.h>   // for Message
 #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
 #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
 #include <string>                  // for allocator
-#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
-#include "ccaosdb.h"               // for caosdb_utility_get_env_var
-#include "caosdb/configuration.h"
 
 class test_ccaosdb : public ::testing::Test {
 protected:
diff --git a/test/test_configuration.cpp b/test/test_configuration.cpp
index c7db0390fa220a60daa746f88900c0c6c3f33b87..abc797fbe97fb408e0ff05ea5deab2a8581f194a 100644
--- a/test/test_configuration.cpp
+++ b/test/test_configuration.cpp
@@ -19,13 +19,16 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
+#include "caosdb/configuration.h"  // for ConfigurationError, Configuration...
+#include "caosdb/exceptions.h"     // for ConfigurationError
+#include "caosdb/log_level.h"      // for CAOSDB_DEFAULT_LOG_LEVEL, CAOSDB_...
+#include "caosdb/logging.h"        // for ConsoleSinkConfiguration, Logging...
+#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
 #include <gtest/gtest-message.h>   // for Message
 #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
-#include <gtest/gtest_pred_impl.h> // for Test, TestInfo, TEST
+#include <gtest/gtest_pred_impl.h> // for TestInfo, TEST_F, Test
+#include <memory>                  // for make_shared
 #include <string>                  // for operator+, allocator, string
-#include "caosdb/configuration.h"  // for ConfigurationManager, Configurati...
-#include "caosdb/exceptions.h"     // for ConfigurationError
-#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
 
 namespace caosdb::configuration {
 
@@ -76,4 +79,15 @@ TEST_F(test_configuration, get_default_connection_configuration_error) {
   ConfigurationManager::Clear();
 }
 
+TEST_F(test_configuration, initialize_logging) {
+  auto logging_configuration =
+    caosdb::logging::LoggingConfiguration(CAOSDB_LOG_LEVEL_ALL);
+  auto console_sink =
+    std::make_shared<caosdb::logging::ConsoleSinkConfiguration>(
+      "console", CAOSDB_DEFAULT_LOG_LEVEL);
+  logging_configuration.AddSink(console_sink);
+
+  initialize_logging(logging_configuration);
+}
+
 } // namespace caosdb::configuration
diff --git a/test/test_connection.cpp b/test/test_connection.cpp
index 556b49e3cb01cce82a91f10a4b20b6ae7e84ee82..7c2efab9566d58c709c060e4206fc0d193ac75df 100644
--- a/test/test_connection.cpp
+++ b/test/test_connection.cpp
@@ -19,19 +19,22 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
-
-#include <gtest/gtest-message.h>   // for Message
-#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
-#include <memory>                  // for allocator, operator!=, shared_ptr
-#include <string>                  // for operator+, string
-#include "caosdb/configuration.h"  // for ConfigurationManager
-#include "caosdb/connection.h"     // for ConnectionManager, InsecureConnec...
-#include "caosdb/exceptions.h"     // for UnknownConnectionError
-#include "caosdb_test_utility.h"   // for EXPECT_THROW_MESSAGE, TEST_DATA_DIR
-#include "gtest/gtest_pred_impl.h" // for Test, AssertionResult, TestInfo
+#include "caosdb/certificate_provider.h" // for PemCertificateProvider
+#include "caosdb/configuration.h"        // for InsecureConnectionConfigura...
+#include "caosdb/connection.h"           // for ConnectionManager
+#include "caosdb/exceptions.h"           // for UnknownConnectionError
+#include "caosdb_test_utility.h"         // for EXPECT_THROW_MESSAGE, TEST_...
+#include <gtest/gtest-message.h>         // for Message
+#include <gtest/gtest-test-part.h>       // for SuiteApiResolver, TestPartR...
+#include "gtest/gtest_pred_impl.h"       // for AssertionResult, TestInfo
+#include <memory>                        // for allocator, operator!=, shar...
+#include <string>                        // for operator+, string
 
 namespace caosdb::connection {
 using caosdb::configuration::ConfigurationManager;
+using caosdb::configuration::InsecureConnectionConfiguration;
+using caosdb::configuration::PemCertificateProvider;
+using caosdb::configuration::TlsConnectionConfiguration;
 
 class test_connection : public ::testing::Test {
 protected:
diff --git a/test/test_data/test_broken_caosdb_client_certificate_file_non_existent.json b/test/test_data/test_broken_caosdb_client_certificate_file_non_existent.json
new file mode 100644
index 0000000000000000000000000000000000000000..832b0a62b674af5500815ca1726ccddf9802d757
--- /dev/null
+++ b/test/test_data/test_broken_caosdb_client_certificate_file_non_existent.json
@@ -0,0 +1,28 @@
+{
+  "connections": {
+    "default": "local-caosdb",
+    "local-caosdb-admin": {
+      "host": "localhost",
+      "port": 8443,
+      "server_certificate_path": "some/path/cacert.pem",
+      "authentication": {
+        "type": "plain",
+        "username": "admin",
+        "password": "caosdb"
+      }
+    },
+    "local-caosdb": {
+      "host": "localhost",
+      "port": 8443,
+      "server_certificate_path": "some/path/cacert.pem",
+      "authentication": {
+        "type": "plain",
+        "username": "me",
+        "password": "secret!"
+      }
+    }
+  },
+  "extension": {
+    "this is my": "special option"
+  }
+}
diff --git a/test/test_data/test_caosdb_client.json b/test/test_data/test_caosdb_client.json
index 832b0a62b674af5500815ca1726ccddf9802d757..2007413021b332c3bd32686363e241804a8d62ab 100644
--- a/test/test_data/test_caosdb_client.json
+++ b/test/test_data/test_caosdb_client.json
@@ -4,7 +4,6 @@
     "local-caosdb-admin": {
       "host": "localhost",
       "port": 8443,
-      "server_certificate_path": "some/path/cacert.pem",
       "authentication": {
         "type": "plain",
         "username": "admin",
@@ -14,7 +13,6 @@
     "local-caosdb": {
       "host": "localhost",
       "port": 8443,
-      "server_certificate_path": "some/path/cacert.pem",
       "authentication": {
         "type": "plain",
         "username": "me",
@@ -22,6 +20,20 @@
       }
     }
   },
+  "logging": {
+    "level": "info",
+    "sinks": {
+      "stderr": {
+        "destination": "console"
+      },
+      "file" : {
+        "destination": "file"
+      },
+      "syslog": {
+        "destination": "syslog"
+      }
+    }
+  },
   "extension": {
     "this is my": "special option"
   }
diff --git a/test/test_info.cpp b/test/test_info.cpp
index 2a806a63ac9fcc8a3c3a9bb8b8cdf582e63cf3d1..a95870200ac51343f86b7bb5ff4c0c433d692f0c 100644
--- a/test/test_info.cpp
+++ b/test/test_info.cpp
@@ -20,12 +20,12 @@
  *
  */
 
-#include <gtest/gtest-message.h>          // for Message
-#include <gtest/gtest-test-part.h>        // for TestPartResult, SuiteApiRe...
-#include <memory>                         // for allocator
 #include "caosdb/info.h"                  // for VersionInfo
 #include "caosdb/info/v1alpha1/main.pb.h" // for VersionInfo
+#include <gtest/gtest-message.h>          // for Message
+#include <gtest/gtest-test-part.h>        // for TestPartResult, SuiteApiRe...
 #include "gtest/gtest_pred_impl.h"        // for Test, EXPECT_EQ, TEST
+#include <memory>                         // for allocator
 
 namespace caosdb::info {
 using ProtoVersionInfo = caosdb::info::v1alpha1::VersionInfo;
diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp
index b528ae2ca01c53f2053002f7958a9a0cc466c50d..c2c76969580f7d915fd6bb4555fa32a1f91e11bb 100644
--- a/test/test_transaction.cpp
+++ b/test/test_transaction.cpp
@@ -19,21 +19,22 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
-
-#include <memory>                           // for allocator, make_shared
-#include "caosdb/connection.h"              // for InsecureConnection...
+#include "caosdb/configuration.h"           // for InsecureConnectionConfig...
+#include "caosdb/connection.h"              // for Connection
 #include "caosdb/entity.h"                  // for Entity
 #include "caosdb/entity/v1alpha1/main.pb.h" // for Entity
 #include "caosdb/exceptions.h"              // for ConnectionError
 #include "caosdb/transaction.h"             // for Transaction, UniqueResult
+#include "caosdb/transaction_status.h"      // for ConnectionError
 #include "caosdb_test_utility.h"            // for EXPECT_THROW_MESSAGE
 #include "gtest/gtest-message.h"            // for Message
 #include "gtest/gtest-test-part.h"          // for SuiteApiResolver, TestPa...
 #include "gtest/gtest_pred_impl.h"          // for Test, TestInfo, TEST
+#include <memory>                           // for allocator, unique_ptr
 
 namespace caosdb::transaction {
+using caosdb::configuration::InsecureConnectionConfiguration;
 using caosdb::connection::Connection;
-using caosdb::connection::InsecureConnectionConfiguration;
 using caosdb::exceptions::ConnectionError;
 using caosdb::transaction::UniqueResult;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
@@ -45,8 +46,10 @@ TEST(test_transaction, create_transaction) {
   auto transaction = connection.CreateTransaction();
 
   transaction->RetrieveById("100");
-  EXPECT_THROW_MESSAGE(transaction->Execute(), ConnectionError,
-                       "failed to connect to all addresses");
+  EXPECT_THROW_MESSAGE(
+    transaction->Execute(), ConnectionError,
+    "The attempt to execute this transaction was not successful because the "
+    "connection to the server could not be established.");
 }
 
 TEST(test_transaction, unique_result) {
diff --git a/test/test_utility.cpp b/test/test_utility.cpp
index 400e0a6e05a079b0d1eb63ef894789ea249a2fea..eec62641af4b2c704ad121cf83d3450cafc5aa2a 100644
--- a/test/test_utility.cpp
+++ b/test/test_utility.cpp
@@ -20,16 +20,16 @@
  *
  */
 
-#include <gtest/gtest-message.h>              // for Message
-#include <gtest/gtest-test-part.h>            // for TestPartResult, SuiteA...
-#include <gtest/gtest_pred_impl.h>            // for Test, EXPECT_EQ, TestInfo
-#include <boost/beast/core/detail/base64.hpp> // for encoded_size
-#include <string>                             // for allocator, string, ope...
 #include "boost/json/object.hpp"              // for object
 #include "boost/json/value.hpp"               // for value
 #include "caosdb/utility.h"                   // for base64_encode, load_js...
 #include "caosdb_test_utility.h"              // for TEST_DATA_DIR
 #include "gmock/gmock-matchers.h"             // for ElementsAre, EXPECT_THAT
+#include <boost/beast/core/detail/base64.hpp> // for encoded_size
+#include <gtest/gtest-message.h>              // for Message
+#include <gtest/gtest-test-part.h>            // for TestPartResult, SuiteA...
+#include <gtest/gtest_pred_impl.h>            // for Test, EXPECT_EQ, TestInfo
+#include <string>                             // for allocator, string, ope...
 
 namespace caosdb::utility {
 using ::testing::ElementsAre;