diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h
index 07062145..a6e21a2d 100644
--- a/include/libcamera/internal/global_configuration.h
+++ b/include/libcamera/internal/global_configuration.h
@@ -10,6 +10,8 @@
 #include <optional>
 #include <string>
 
+#include <libcamera/base/utils.h>
+
 #include "libcamera/internal/yaml_parser.h"
 
 namespace libcamera {
@@ -22,7 +24,30 @@ void initialize();
 
 unsigned int version();
 Configuration configuration();
-std::optional<std::string> option(const std::string &confPath);
+
+template<typename T,
+	 std::enable_if_t<
+		 std::is_same_v<bool, T> ||
+		 std::is_same_v<double, T> ||
+		 std::is_same_v<int8_t, T> ||
+		 std::is_same_v<uint8_t, T> ||
+		 std::is_same_v<int16_t, T> ||
+		 std::is_same_v<uint16_t, T> ||
+		 std::is_same_v<int32_t, T> ||
+		 std::is_same_v<uint32_t, T> ||
+		 std::is_same_v<std::string, T> ||
+		 std::is_same_v<Size, T>> * = nullptr>
+std::optional<T> option(const std::string &confPath)
+{
+	const YamlObject *c = &configuration();
+	for (auto part : utils::split(confPath, ".")) {
+		c = &(*c)[part];
+		if (!*c)
+			return {};
+	}
+	return c->get<T>();
+}
+
 std::optional<std::string> envOption(const char *const envVariable,
 				     const std::string &confPath);
 
diff --git a/src/libcamera/base/global_configuration.cpp b/src/libcamera/base/global_configuration.cpp
index 073096b5..3dc31c48 100644
--- a/src/libcamera/base/global_configuration.cpp
+++ b/src/libcamera/base/global_configuration.cpp
@@ -14,7 +14,6 @@
 
 #include <libcamera/base/file.h>
 #include <libcamera/base/log.h>
-#include <libcamera/base/utils.h>
 
 #include "libcamera/internal/yaml_parser.h"
 
@@ -144,22 +143,14 @@ void initialize()
  */
 
 /**
+ * \fn std::optional<T> GlobalConfiguration::option(const char *const confPath)
  * \brief Return value of the configuration option identified by \a confPath
+ * \tparam T The type of the retrieved configuration value
  * \param[in] confPath Sequence of the YAML section names (excluding
  * `configuration') leading to the requested option separated by dots
- * \return A value if an item corresponding to \a confPath exists in the
- * configuration file, no value otherwise
+ * \return A value of type \a T if an item corresponding to \a confPath exists
+ * in the configuration file and matches type \a T, no value otherwise
  */
-std::optional<std::string> option(const std::string &confPath)
-{
-	const YamlObject *c = &configuration();
-	for (auto part : utils::split(confPath, ".")) {
-		c = &(*c)[part];
-		if (!*c)
-			return {};
-	}
-	return c->get<std::string>();
-}
 
 /**
  * \brief Return value of the configuration option from a file or environment
@@ -185,7 +176,7 @@ std::optional<std::string> envOption(
 	const char *envValue = utils::secure_getenv(envVariable);
 	if (envValue)
 		return std::optional{ std::string{ envValue } };
-	return option(confPath);
+	return option<std::string>(confPath);
 }
 
 /**
diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp
index 07352f6c..d522e895 100644
--- a/src/libcamera/base/log.cpp
+++ b/src/libcamera/base/log.cpp
@@ -590,6 +590,8 @@ void Logger::logSetLevel(const char *category, const char *level)
 Logger::Logger()
 {
 	bool color = !utils::secure_getenv("LIBCAMERA_LOG_NO_COLOR");
+	if (color)
+		color = GlobalConfiguration::option<bool>("log.color").value_or(true);
 	logSetStream(&std::cerr, color);
 
 	parseLogFile();
