COMPMID-619: Logging interface

Change-Id: I73d1433ee7a682aeabb7540aa2ea1f6564f90aae
Reviewed-on: http://mpd-gerrit.cambridge.arm.com/91775
Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
diff --git a/SConscript b/SConscript
index 79f6bf3..195eff1 100644
--- a/SConscript
+++ b/SConscript
@@ -137,6 +137,7 @@
 core_files = Glob('src/core/*.cpp')
 core_files += Glob('src/core/CPP/*.cpp')
 core_files += Glob('src/core/CPP/kernels/*.cpp')
+core_files += Glob('src/core/utils/*/*.cpp')
 
 runtime_files = Glob('src/runtime/*.cpp')
 # CLHarrisCorners uses the Scheduler to run CPP kernels
diff --git a/SConstruct b/SConstruct
index 4ef67ef..c2c76fb 100644
--- a/SConstruct
+++ b/SConstruct
@@ -39,6 +39,7 @@
 vars.AddVariables(
     BoolVariable("debug", "Debug", False),
     BoolVariable("asserts", "Enable asserts (this flag is forced to 1 for debug=1)", False),
+    BoolVariable("logging", "Logging", False),
     EnumVariable("arch", "Target Architecture", "armv7a", allowed_values=("armv7a", "arm64-v8a", "arm64-v8.2-a", "x86_32", "x86_64")),
     EnumVariable("os", "Target OS", "linux", allowed_values=("linux", "android", "bare_metal")),
     EnumVariable("build", "Build type", "cross_compile", allowed_values=("native", "cross_compile")),
@@ -207,6 +208,9 @@
     env.Append(CPPDEFINES = ['ARM_COMPUTE_ASSERTS_ENABLED'])
     env.Append(CXXFLAGS = ['-fstack-protector-strong'])
 
+if env['logging']:
+    env.Append(CPPDEFINES = ['ARM_COMPUTE_LOGGING_ENABLED'])
+
 env.Append(CPPPATH = ['#/include', "#"])
 env.Append(CXXFLAGS = env['extra_cxx_flags'])
 
diff --git a/arm_compute/core/Logger.h b/arm_compute/core/Logger.h
deleted file mode 100644
index 0848479..0000000
--- a/arm_compute/core/Logger.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2017 ARM Limited.
- *
- * SPDX-License-Identifier: MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef __ARM_COMPUTE_LOGGER_H__
-#define __ARM_COMPUTE_LOGGER_H__
-
-#include <iostream>
-#include <memory>
-
-#ifdef ARM_COMPUTE_DEBUG_ENABLED
-#define ARM_COMPUTE_LOG(x) (arm_compute::Logger::get().log_info() << x)
-#else /* ARM_COMPUTE_DEBUG_ENABLED */
-#define ARM_COMPUTE_LOG(...)
-#endif /* ARM_COMPUTE_DEBUG_ENABLED */
-
-namespace arm_compute
-{
-/**< Verbosity of the logger */
-enum class LoggerVerbosity
-{
-    NONE, /**< No info */
-    INFO  /**< Log info */
-};
-
-/** Logger singleton class */
-class Logger
-{
-public:
-    static Logger &get();
-    void set_logger(std::ostream &ostream, LoggerVerbosity verbosity);
-    std::ostream &log_info();
-
-private:
-    /** Default constructor */
-    Logger();
-    /** Allow instances of this class to be moved */
-    Logger(Logger &&) = default;
-    /** Prevent instances of this class from being copied (As this class contains pointers) */
-    Logger(const Logger &) = delete;
-    /** Prevent instances of this class from being copied (As this class contains pointers) */
-    Logger &operator=(const Logger &) = delete;
-    /** Allow instances of this class to be moved */
-    Logger &operator=(Logger &&) = default;
-
-    std::ostream   *_ostream;
-    std::ostream    _nullstream;
-    LoggerVerbosity _verbosity;
-};
-} // arm_compute
-#endif /* __ARM_COMPUTE_LOGGER_H__ */
\ No newline at end of file
diff --git a/arm_compute/core/utils/io/FileHandler.h b/arm_compute/core/utils/io/FileHandler.h
new file mode 100644
index 0000000..d915dbe
--- /dev/null
+++ b/arm_compute/core/utils/io/FileHandler.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_IO_FILE_HANDLER_H__
+#define __ARM_COMPUTE_IO_FILE_HANDLER_H__
+
+#include <fstream>
+#include <string>
+
+namespace arm_compute
+{
+namespace io
+{
+/** File Handling interface */
+class FileHandler
+{
+public:
+    /** Default Constructor */
+    FileHandler();
+    /** Default Destructor */
+    ~FileHandler();
+    /** Allow instances of this class to be moved */
+    FileHandler(FileHandler &&) = default;
+    /** Prevent instances of this class from being copied (As this class contains pointers) */
+    FileHandler(const FileHandler &) = delete;
+    /** Prevent instances of this class from being copied (As this class contains pointers) */
+    FileHandler &operator=(const FileHandler &) = delete;
+    /** Allow instances of this class to be moved */
+    FileHandler &operator=(FileHandler &&) = default;
+    /** Opens file
+     *
+     * @param[in] filename File name
+     * @param[in] mode     File open mode
+     */
+    void open(const std::string &filename, std::ios_base::openmode mode);
+    /** Closes file */
+    void close();
+    /** Returns the file stream
+     *
+     * @return File stream
+     */
+    std::fstream &stream();
+    /** Returns filename of the handled file
+     *
+     * @return File filename
+     */
+    std::string filename() const;
+
+private:
+    std::fstream            _filestream;
+    std::string             _filename;
+    std::ios_base::openmode _mode;
+};
+} // namespace io
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_IO_FILE_HANDLER_H__ */
diff --git a/src/core/Logger.cpp b/arm_compute/core/utils/logging/FilePrinter.h
similarity index 62%
copy from src/core/Logger.cpp
copy to arm_compute/core/utils/logging/FilePrinter.h
index 9c3bf26..e2ae952 100644
--- a/src/core/Logger.cpp
+++ b/arm_compute/core/utils/logging/FilePrinter.h
@@ -21,36 +21,34 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#ifndef __ARM_COMPUTE_LOGGING_FILE_PRINTER_H__
+#define __ARM_COMPUTE_LOGGING_FILE_PRINTER_H__
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/logging/IPrinter.h"
 
-using namespace arm_compute;
+#include "arm_compute/core/utils/io/FileHandler.h"
 
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+namespace arm_compute
 {
-}
-
-Logger &Logger::get()
+namespace logging
 {
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
+/** File Printer */
+class FilePrinter final : public Printer
 {
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
+public:
+    /** Default Constructor
+     *
+     * @param[in] filename File name
+     */
+    FilePrinter(const std::string &filename);
 
-std::ostream &Logger::log_info()
-{
-    if(_verbosity == LoggerVerbosity::INFO)
-    {
-        return *_ostream;
-    }
-    else
-    {
-        return _nullstream;
-    }
-}
\ No newline at end of file
+private:
+    // Inherited methods overridden:
+    void print_internal(const std::string &msg) override;
+
+private:
+    io::FileHandler _handler;
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_FILE_PRINTER_H__ */
diff --git a/arm_compute/core/utils/logging/Helpers.h b/arm_compute/core/utils/logging/Helpers.h
new file mode 100644
index 0000000..4bc54e8
--- /dev/null
+++ b/arm_compute/core/utils/logging/Helpers.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_HELPERS_H__
+#define __ARM_COMPUTE_LOGGING_HELPERS_H__
+
+#include "arm_compute/core/utils/logging/Types.h"
+#include "support/ToolchainSupport.h"
+
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace arm_compute
+{
+namespace logging
+{
+/** Create a string given a format
+ *
+ * @param[in] fmt  String format
+ * @param[in] args Arguments
+ *
+ * @return The formatted string
+ */
+template <typename... Ts>
+inline std::string string_with_format(const std::string &fmt, Ts &&... args)
+{
+    size_t size     = support::cpp11::snprintf(nullptr, 0, fmt.c_str(), args...) + 1;
+    auto   char_str = support::cpp14::make_unique<char[]>(size);
+    support::cpp11::snprintf(char_str.get(), size, fmt.c_str(), args...);
+    return std::string(char_str.get(), char_str.get() + size - 1);
+}
+/** Wraps a value with angles and returns the string
+ *
+ * @param[in] val Value to wrap
+ *
+ * @return Wrapped string
+ */
+template <typename T>
+inline std::string angle_wrap_value(const T &val)
+{
+    std::ostringstream ss;
+    ss << "[" << val << "]";
+    return ss.str();
+}
+/** Translates a given log level to a string.
+ *
+ * @param[in] log_level @ref LogLevel to be translated to string.
+ *
+ * @return The string describing the logging level.
+ */
+const std::string &string_from_log_level(LogLevel log_level);
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_HELPERS_H__ */
diff --git a/arm_compute/core/utils/logging/IPrinter.h b/arm_compute/core/utils/logging/IPrinter.h
new file mode 100644
index 0000000..6b410d4
--- /dev/null
+++ b/arm_compute/core/utils/logging/IPrinter.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_PRINTER_H__
+#define __ARM_COMPUTE_LOGGING_PRINTER_H__
+
+#include "support/Mutex.h"
+
+namespace arm_compute
+{
+namespace logging
+{
+/** Base printer class to be inherited by other printer classes */
+class Printer
+{
+public:
+    /** Default Constructor */
+    Printer()
+        : _mtx()
+    {
+    }
+    /** Prevent instances of this class from being copied */
+    Printer(const Printer &) = delete;
+    /** Prevent instances of this class from being copied */
+    Printer &operator=(const Printer &) = delete;
+    /** Prevent instances of this class from being moved */
+    Printer(Printer &&) = delete;
+    /** Prevent instances of this class from being moved */
+    Printer &operator=(Printer &&) = delete;
+    /** Defaults Destructor */
+    virtual ~Printer() = default;
+    /** Print message
+     *
+     * @param[in] msg Message to print
+     */
+    inline void print(const std::string &msg)
+    {
+        std::lock_guard<arm_compute::Mutex> lock(_mtx);
+        print_internal(msg);
+    }
+
+private:
+    /** Interface to be implemented by the child to print a message
+     *
+     * @param[in] msg Message to print
+     */
+    virtual void print_internal(const std::string &msg) = 0;
+
+private:
+    arm_compute::Mutex _mtx;
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_PRINTER_H__ */
diff --git a/arm_compute/core/utils/logging/LogMsgDecorators.h b/arm_compute/core/utils/logging/LogMsgDecorators.h
new file mode 100644
index 0000000..32cb977
--- /dev/null
+++ b/arm_compute/core/utils/logging/LogMsgDecorators.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016, 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H__
+#define __ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H__
+
+#include "arm_compute/core/utils/logging/Helpers.h"
+#include "arm_compute/core/utils/logging/Types.h"
+
+#include <chrono>
+#include <ctime>
+#include <string>
+#include <thread>
+
+namespace arm_compute
+{
+namespace logging
+{
+/** Log message decorator interface */
+class IDecorator
+{
+public:
+    /** Default Destructor */
+    virtual ~IDecorator() = default;
+    /** Decorates log message
+     *
+     * @param[in] log_msg Log message to decorate
+     */
+    virtual void decorate(LogMsg &log_msg) = 0;
+};
+
+/** String Decorator
+ *
+ * Appends a user defined string in the log message
+ */
+class StringDecorator : public IDecorator
+{
+public:
+    /** Defaults constructor
+     *
+     * @param str Sting to append
+     */
+    StringDecorator(const std::string &str)
+        : _str(str)
+    {
+        _str = angle_wrap_value(str);
+    }
+
+    // Inherited methods overridden:
+    void decorate(LogMsg &log_msg) override
+    {
+        log_msg.raw_ += _str;
+    }
+
+private:
+    std::string _str;
+};
+
+/** Date Decorator
+ *
+ * Appends the date and time in the log message
+ */
+class DateDecorator : public IDecorator
+{
+public:
+    // Inherited methods overridden:
+    void decorate(LogMsg &log_msg) override
+    {
+        log_msg.raw_ += angle_wrap_value(get_time());
+    }
+
+private:
+    /** Gets current system local time
+     *
+     * @return Local time
+     */
+    std::string get_time()
+    {
+        auto now  = std::chrono::system_clock::now();
+        auto time = std::chrono::system_clock::to_time_t(now);
+
+        // TODO: use put_time for gcc > 4.9
+        char buf[100] = { 0 };
+        std::strftime(buf, sizeof(buf), "%d-%m-%Y %I:%M:%S", std::localtime(&time));
+        return buf;
+    }
+};
+
+/** Thread ID Decorator
+ *
+ * Appends the thread ID in the log message
+ */
+class ThreadIdDecorator : public IDecorator
+{
+public:
+    // Inherited methods overridden:
+    void decorate(LogMsg &log_msg) override
+    {
+#ifndef NO_MULTI_THREADING
+        log_msg.raw_ += angle_wrap_value(std::this_thread::get_id());
+#endif /* NO_MULTI_THREADING */
+    }
+};
+
+/** Log Level Decorator
+ *
+ * Appends the logging level in the log message
+ */
+class LogLevelDecorator : public IDecorator
+{
+public:
+    // Inherited methods overridden:
+    void decorate(LogMsg &log_msg) override
+    {
+        log_msg.raw_ += angle_wrap_value(string_from_log_level(log_msg.log_level_));
+    }
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_LOG_MSG_DECORATORS_H__ */
diff --git a/arm_compute/core/utils/logging/Logger.h b/arm_compute/core/utils/logging/Logger.h
new file mode 100644
index 0000000..eb9bdd2
--- /dev/null
+++ b/arm_compute/core/utils/logging/Logger.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_LOGGER_H__
+#define __ARM_COMPUTE_LOGGING_LOGGER_H__
+
+#include "arm_compute/core/utils/logging/Helpers.h"
+#include "arm_compute/core/utils/logging/IPrinter.h"
+#include "arm_compute/core/utils/logging/LogMsgDecorators.h"
+#include "arm_compute/core/utils/logging/Types.h"
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace arm_compute
+{
+namespace logging
+{
+/** Logger class */
+class Logger
+{
+public:
+    /** Default Constructor
+     *
+     * @param[in] name      Name of the logger
+     * @param[in] log_level Logger log level
+     * @param[in] printer   Printer to push the messages
+     */
+    Logger(std::string name, LogLevel log_level, std::shared_ptr<Printer> printer);
+    /** Default Constructor
+     *
+     * @param[in] name      Name of the logger
+     * @param[in] log_level Logger log level
+     * @param[in] printers  Printers to push the messages
+     */
+    Logger(std::string name, LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers = {});
+    /** Default Constructor
+     *
+     * @param[in] name       Name of the logger
+     * @param[in] log_level  Logger log level
+     * @param[in] printers   Printers to push the messages
+     * @param[in] decorators Message decorators, which append information in the logged message
+     */
+    Logger(std::string                              name,
+           LogLevel                                 log_level,
+           std::vector<std::shared_ptr<Printer>>    printers,
+           std::vector<std::unique_ptr<IDecorator>> decorators);
+    /** Allow instances of this class to be moved */
+    Logger(Logger &&) = default;
+    /** Prevent instances of this class from being copied (As this class contains pointers) */
+    Logger(const Logger &) = delete;
+    /** Prevent instances of this class from being copied (As this class contains pointers) */
+    Logger &operator=(const Logger &) = delete;
+    /** Allow instances of this class to be moved */
+    Logger &operator=(Logger &&) = default;
+    /** Logs a message
+     *
+     * @param[in] log_level Log level of the message
+     * @param[in] msg       Message to log
+     */
+    void log(LogLevel log_level, const std::string &msg);
+    /** Logs a formatted message
+     *
+     * @param[in] log_level Log level of the message
+     * @param[in] fmt       Message format
+     * @param[in] args      Message arguments
+     */
+    template <typename... Ts>
+    void log(LogLevel log_level, const std::string &fmt, Ts &&... args);
+    /** Sets log level of the logger
+     *
+     * @warning Not thread-safe
+     *
+     * @param[in] log_level Log level to set
+     */
+    void set_log_level(LogLevel log_level);
+    /** Returns logger's log level
+     *
+     * @return Logger's log level
+     */
+    LogLevel log_level() const;
+    /** Returns logger's name
+     *
+     * @return Logger's name
+     */
+    std::string name() const;
+    /** Adds a printer to the logger
+     *
+     * @warning Not thread-safe
+     *
+     * @param[in] printer
+     */
+    void add_printer(std::shared_ptr<Printer> printer);
+    /** Adds a log message decorator to the logger
+     *
+     * @warning Not thread-safe
+     *
+     * @param[in] decorator
+     */
+    void add_decorator(std::unique_ptr<IDecorator> decorator);
+
+private:
+    /** Set default message decorators */
+    void set_default_decorators();
+    /** Checks if a message should be logged depending
+     *  on the message log level and the loggers one
+     *
+     * @param[in] log_level Log level
+     *
+     * @return True if message should be logged else false
+     */
+    bool is_loggable(LogLevel log_level);
+    /** Decorate log message
+     *
+     * @param[in] Log message to decorate
+     */
+    void decorate_log_msg(LogMsg &msg);
+    /** Creates final log message by creating the prefix
+     *
+     * @param[in] str       Log message
+     * @param[in] log_level Message's log level
+     *
+     * @return Final log message to print
+     */
+    std::string create_log_msg(const std::string &str, LogLevel log_level);
+    /** Prints the message to all the printers
+     *
+     * @param[in] msg Message to print
+     */
+    void print_all(const std::string &msg);
+
+private:
+    std::string                              _name;
+    LogLevel                                 _log_level;
+    std::vector<std::shared_ptr<Printer>>    _printers;
+    std::vector<std::unique_ptr<IDecorator>> _decorators;
+};
+
+template <typename... Ts>
+inline void Logger::log(LogLevel log_level, const std::string &fmt, Ts &&... args)
+{
+    // Return if message shouldn't be logged
+    // i.e. if log level does not match the logger's
+    if(!is_loggable(log_level))
+    {
+        return;
+    }
+
+    // Print message to all printers
+    print_all(create_log_msg(string_with_format(fmt, args...), log_level));
+}
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_LOGGER_H__ */
diff --git a/arm_compute/core/utils/logging/LoggerRegistry.h b/arm_compute/core/utils/logging/LoggerRegistry.h
new file mode 100644
index 0000000..d861476
--- /dev/null
+++ b/arm_compute/core/utils/logging/LoggerRegistry.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_LOGGER_REGISTRY_H__
+#define __ARM_COMPUTE_LOGGING_LOGGER_REGISTRY_H__
+
+#include "arm_compute/core/utils/logging/Logger.h"
+#include "arm_compute/core/utils/logging/Printers.h"
+#include "arm_compute/core/utils/logging/Types.h"
+#include "support/Mutex.h"
+
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+namespace arm_compute
+{
+namespace logging
+{
+/** Registry class holding all the instantiated loggers */
+class LoggerRegistry final
+{
+public:
+    /** Gets registry instance
+     *
+     * @return Logger registry instance
+     */
+    static LoggerRegistry &get();
+    /** Creates a logger
+     *
+     * @note Some names are reserved e.g. [CORE, RUNTIME, GRAPH]
+     *
+     * @param[in] name      Logger's name
+     * @param[in] log_level Logger's log level
+     * @param[in] printers  Printers to attach to the system loggers
+     */
+    void create_logger(const std::string &name, LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers = {});
+    /** Remove a logger
+     *
+     * @param name Logger's name
+     */
+    void remove_logger(const std::string &name);
+    /** Returns a logger instance
+     *
+     * @param[in] name Logger to return
+     *
+     * @return Logger
+     */
+    std::shared_ptr<Logger> logger(const std::string &name);
+    /** Creates reserved library loggers
+     *
+     * @param[in] log_level Logger's log level
+     * @param[in] printers  Printers to attach to the system loggers
+     */
+    void create_reserved_loggers(LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers = {});
+
+private:
+    /** Default constructor */
+    LoggerRegistry();
+
+private:
+    arm_compute::Mutex _mtx;
+    std::unordered_map<std::string, std::shared_ptr<Logger>> _loggers;
+    static std::set<std::string> _reserved_loggers;
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_LOGGER_REGISTRY_H__ */
diff --git a/arm_compute/core/utils/logging/Macros.h b/arm_compute/core/utils/logging/Macros.h
new file mode 100644
index 0000000..b17354b
--- /dev/null
+++ b/arm_compute/core/utils/logging/Macros.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_LOGGING_MACROS_H__
+#define __ARM_COMPUTE_LOGGING_MACROS_H__
+
+#include "arm_compute/core/utils/logging/LoggerRegistry.h"
+
+#include <sstream>
+
+#ifdef ARM_COMPUTE_LOGGING_ENABLED
+
+#define ARM_COMPUTE_LOG_MSG(logger_name, log_level, msg)                               \
+    {                                                                                  \
+        auto logger = arm_compute::logging::LoggerRegistry::get().logger(logger_name); \
+        if(logger != nullptr)                                                          \
+        {                                                                              \
+            logger->log(log_level, msg);                                               \
+        }                                                                              \
+    }
+
+#define ARM_COMPUTE_LOG_MSG_WITH_FORMAT(logger_name, log_level, fmt, ...)              \
+    {                                                                                  \
+        auto logger = arm_compute::logging::LoggerRegistry::get().logger(logger_name); \
+        if(logger != nullptr)                                                          \
+        {                                                                              \
+            logger->log(log_level, fmt, __VA_ARGS__);                                  \
+        }                                                                              \
+    }
+
+#define ARM_COMPUTE_LOG_STREAM(logger_name, log_level, stream)                                               \
+    {                                                                                                        \
+        auto logger = arm_compute::logging::LoggerRegistry::get().logger(logger_name);                       \
+        if(logger != nullptr)                                                                                \
+        {                                                                                                    \
+            logger->log(log_level, static_cast<std::ostringstream &>(std::ostringstream() << stream).str()); \
+        }                                                                                                    \
+    }
+
+#else /* ARM_COMPUTE_LOGGING_ENABLED */
+
+#define ARM_COMPUTE_LOG_MSG(logger_name, log_level, msg)
+#define ARM_COMPUTE_LOG_MSG_WITH_FORMAT(logger_name, log_level, fmt, ...)
+#define ARM_COMPUTE_LOG_STREAM(logger_name, log_level, stream)
+
+#endif /* ARM_COMPUTE_LOGGING_ENABLED */
+
+#endif /* __ARM_COMPUTE_LOGGING_MACROS_H__ */
diff --git a/src/core/Logger.cpp b/arm_compute/core/utils/logging/Printers.h
similarity index 66%
copy from src/core/Logger.cpp
copy to arm_compute/core/utils/logging/Printers.h
index 9c3bf26..7e5eef6 100644
--- a/src/core/Logger.cpp
+++ b/arm_compute/core/utils/logging/Printers.h
@@ -21,36 +21,11 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#ifndef __ARM_COMPUTE_LOGGING_PRINTERS_H__
+#define __ARM_COMPUTE_LOGGING_PRINTERS_H__
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/logging/FilePrinter.h"
+#include "arm_compute/core/utils/logging/IPrinter.h"
+#include "arm_compute/core/utils/logging/StdPrinter.h"
 
-using namespace arm_compute;
-
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
-{
-}
-
-Logger &Logger::get()
-{
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
-{
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
-
-std::ostream &Logger::log_info()
-{
-    if(_verbosity == LoggerVerbosity::INFO)
-    {
-        return *_ostream;
-    }
-    else
-    {
-        return _nullstream;
-    }
-}
\ No newline at end of file
+#endif /* __ARM_COMPUTE_LOGGING_PRINTERS_H__ */
diff --git a/src/core/Logger.cpp b/arm_compute/core/utils/logging/StdPrinter.h
similarity index 68%
rename from src/core/Logger.cpp
rename to arm_compute/core/utils/logging/StdPrinter.h
index 9c3bf26..0b41b26 100644
--- a/src/core/Logger.cpp
+++ b/arm_compute/core/utils/logging/StdPrinter.h
@@ -21,36 +21,27 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#ifndef __ARM_COMPUTE_LOGGING_STD_PRINTER_H__
+#define __ARM_COMPUTE_LOGGING_STD_PRINTER_H__
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/logging/IPrinter.h"
 
-using namespace arm_compute;
+#include <iostream>
 
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+namespace arm_compute
 {
-}
-
-Logger &Logger::get()
+namespace logging
 {
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
+/** Std Printer */
+class StdPrinter final : public Printer
 {
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
-
-std::ostream &Logger::log_info()
-{
-    if(_verbosity == LoggerVerbosity::INFO)
+private:
+    // Inherited methods overridden:
+    void print_internal(const std::string &msg) override
     {
-        return *_ostream;
+        std::cout << msg << std::endl;
     }
-    else
-    {
-        return _nullstream;
-    }
-}
\ No newline at end of file
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_LOGGING_STD_PRINTER_H__ */
diff --git a/src/core/Logger.cpp b/arm_compute/core/utils/logging/Types.h
similarity index 60%
copy from src/core/Logger.cpp
copy to arm_compute/core/utils/logging/Types.h
index 9c3bf26..171270d 100644
--- a/src/core/Logger.cpp
+++ b/arm_compute/core/utils/logging/Types.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 ARM Limited.
+ * Copyright (c) 2016, 2017 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -21,36 +21,38 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#ifndef __ARM_COMPUTE_LOGGING_TYPES_H__
+#define __ARM_COMPUTE_LOGGING_TYPES_H__
 
-#include "arm_compute/core/Logger.h"
+#include <string>
 
-using namespace arm_compute;
-
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+namespace arm_compute
 {
-}
-
-Logger &Logger::get()
+namespace logging
 {
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
+/** Logging level enumeration */
+enum class LogLevel : unsigned int
 {
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
+    VERBOSE, /**< All logging messages */
+    INFO,    /**< Information log level */
+    WARN,    /**< Warning log level */
+    OFF      /**< No logging */
+};
 
-std::ostream &Logger::log_info()
+struct LogMsg
 {
-    if(_verbosity == LoggerVerbosity::INFO)
+    LogMsg()
+        : raw_(), log_level_(LogLevel::OFF)
     {
-        return *_ostream;
     }
-    else
+    LogMsg(std::string msg, LogLevel log_level = LogLevel::OFF)
+        : raw_(msg), log_level_(log_level)
     {
-        return _nullstream;
     }
-}
\ No newline at end of file
+
+    std::string raw_;
+    LogLevel    log_level_;
+};
+} // namespace logging
+} // namespace arm_compute
+#endif /* __ARM_COMPUTE_TYPES_H__ */
diff --git a/arm_compute/graph/Types.h b/arm_compute/graph/Types.h
index c439641..f02fa7d 100644
--- a/arm_compute/graph/Types.h
+++ b/arm_compute/graph/Types.h
@@ -27,6 +27,13 @@
 #include "arm_compute/core/ITensor.h"
 #include "arm_compute/core/SubTensorInfo.h"
 #include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/core/utils/logging/Macros.h"
+
+#define ARM_COMPUTE_LOG_GRAPH(log_level, x) \
+    ARM_COMPUTE_LOG_STREAM("GRAPH", log_level, x)
+
+#define ARM_COMPUTE_LOG_GRAPH_INFO(x) \
+    ARM_COMPUTE_LOG_STREAM("GRAPH", arm_compute::logging::LogLevel::INFO, x)
 
 namespace arm_compute
 {
@@ -47,6 +54,8 @@
 using arm_compute::TensorShape;
 using arm_compute::WeightsInfo;
 
+using arm_compute::logging::LogLevel;
+
 /**< Execution hint to the graph executor */
 enum class TargetHint
 {
diff --git a/examples/graph_alexnet.cpp b/examples/graph_alexnet.cpp
index dce7132..9a747b6 100644
--- a/examples/graph_alexnet.cpp
+++ b/examples/graph_alexnet.cpp
@@ -25,7 +25,7 @@
 #error "This example needs to be built with -DARM_COMPUTE_CL"
 #endif /* ARM_COMPUTE_CL */
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/logging/LoggerRegistry.h"
 #include "arm_compute/graph/Graph.h"
 #include "arm_compute/graph/Nodes.h"
 #include "arm_compute/runtime/CL/CLScheduler.h"
@@ -41,6 +41,7 @@
 
 using namespace arm_compute::graph;
 using namespace arm_compute::graph_utils;
+using namespace arm_compute::logging;
 
 /** Generates appropriate accessor according to the specified path
  *
@@ -103,7 +104,7 @@
     }
 
     Graph graph;
-    arm_compute::Logger::get().set_logger(std::cout, arm_compute::LoggerVerbosity::INFO);
+    LoggerRegistry::get().create_reserved_loggers(LogLevel::INFO, { std::make_shared<StdPrinter>() });
 
     graph << hint
           << Tensor(TensorInfo(TensorShape(227U, 227U, 3U, batches), 1, DataType::F32), DummyAccessor())
diff --git a/examples/graph_lenet.cpp b/examples/graph_lenet.cpp
index 1427abe..82f4c5a 100644
--- a/examples/graph_lenet.cpp
+++ b/examples/graph_lenet.cpp
@@ -25,7 +25,7 @@
 #error "This example needs to be built with -DARM_COMPUTE_CL"
 #endif /* ARM_COMPUTE_CL */
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/logging/LoggerRegistry.h"
 #include "arm_compute/graph/Graph.h"
 #include "arm_compute/graph/Nodes.h"
 #include "arm_compute/runtime/CL/CLScheduler.h"
@@ -40,6 +40,7 @@
 
 using namespace arm_compute::graph;
 using namespace arm_compute::graph_utils;
+using namespace arm_compute::logging;
 
 /** Generates appropriate accessor according to the specified path
  *
@@ -102,7 +103,7 @@
     }
 
     Graph graph;
-    arm_compute::Logger::get().set_logger(std::cout, arm_compute::LoggerVerbosity::INFO);
+    LoggerRegistry::get().create_reserved_loggers(LogLevel::INFO, { std::make_shared<StdPrinter>() });
 
     //conv1 << pool1 << conv2 << pool2 << fc1 << act1 << fc2 << smx
     graph << hint
diff --git a/src/core/Logger.cpp b/src/core/utils/io/FileHandler.cpp
similarity index 61%
copy from src/core/Logger.cpp
copy to src/core/utils/io/FileHandler.cpp
index 9c3bf26..70bce42 100644
--- a/src/core/Logger.cpp
+++ b/src/core/utils/io/FileHandler.cpp
@@ -21,36 +21,46 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#include <string>
 
-#include "arm_compute/core/Logger.h"
+#include "arm_compute/core/utils/io/FileHandler.h"
 
-using namespace arm_compute;
+#include "arm_compute/core/Error.h"
+#include "support/ToolchainSupport.h"
 
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+using namespace arm_compute::io;
+
+FileHandler::FileHandler()
+    : _filestream(), _filename(" "), _mode()
 {
 }
 
-Logger &Logger::get()
+FileHandler::~FileHandler()
 {
-    static Logger _instance;
-    return _instance;
+    close();
 }
 
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
+void FileHandler::open(const std::string &filename, std::ios_base::openmode mode)
 {
-    _ostream   = &ostream;
-    _verbosity = verbosity;
+    close();
+    ;
+    _filestream.open(filename, mode);
+    ARM_COMPUTE_ERROR_ON(!_filestream.good());
+    _filename = filename;
+    _mode     = mode;
 }
 
-std::ostream &Logger::log_info()
+void FileHandler::close()
 {
-    if(_verbosity == LoggerVerbosity::INFO)
-    {
-        return *_ostream;
-    }
-    else
-    {
-        return _nullstream;
-    }
-}
\ No newline at end of file
+    _filestream.close();
+}
+
+std::fstream &FileHandler::stream()
+{
+    return _filestream;
+}
+
+std::string FileHandler::filename() const
+{
+    return _filename;
+}
diff --git a/src/core/Logger.cpp b/src/core/utils/logging/FilePrinter.cpp
similarity index 67%
copy from src/core/Logger.cpp
copy to src/core/utils/logging/FilePrinter.cpp
index 9c3bf26..b699afc 100644
--- a/src/core/Logger.cpp
+++ b/src/core/utils/logging/FilePrinter.cpp
@@ -21,36 +21,17 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#include "arm_compute/core/utils/logging/FilePrinter.h"
 
-#include "arm_compute/core/Logger.h"
+using namespace arm_compute::logging;
 
-using namespace arm_compute;
-
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+FilePrinter::FilePrinter(const std::string &filename)
+    : _handler()
 {
+    _handler.open(filename, std::fstream::out | std::fstream::trunc);
 }
 
-Logger &Logger::get()
+void FilePrinter::print_internal(const std::string &msg)
 {
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
-{
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
-
-std::ostream &Logger::log_info()
-{
-    if(_verbosity == LoggerVerbosity::INFO)
-    {
-        return *_ostream;
-    }
-    else
-    {
-        return _nullstream;
-    }
+    _handler.stream() << msg << std::endl;
 }
\ No newline at end of file
diff --git a/src/core/Logger.cpp b/src/core/utils/logging/Helpers.cpp
similarity index 67%
copy from src/core/Logger.cpp
copy to src/core/utils/logging/Helpers.cpp
index 9c3bf26..f5ab608 100644
--- a/src/core/Logger.cpp
+++ b/src/core/utils/logging/Helpers.cpp
@@ -21,36 +21,22 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#include "arm_compute/core/utils/logging/Helpers.h"
 
-#include "arm_compute/core/Logger.h"
+#include <map>
+#include <string>
 
-using namespace arm_compute;
+using namespace arm_compute::logging;
 
-Logger::Logger()
-    : _ostream(&std::cout), _nullstream(nullptr), _verbosity(LoggerVerbosity::NONE)
+const std::string &arm_compute::logging::string_from_log_level(LogLevel log_level)
 {
-}
-
-Logger &Logger::get()
-{
-    static Logger _instance;
-    return _instance;
-}
-
-void Logger::set_logger(std::ostream &ostream, LoggerVerbosity verbosity)
-{
-    _ostream   = &ostream;
-    _verbosity = verbosity;
-}
-
-std::ostream &Logger::log_info()
-{
-    if(_verbosity == LoggerVerbosity::INFO)
+    static std::map<LogLevel, const std::string> log_level_map =
     {
-        return *_ostream;
-    }
-    else
-    {
-        return _nullstream;
-    }
+        { LogLevel::VERBOSE, "VERBOSE" },
+        { LogLevel::INFO, "INFO" },
+        { LogLevel::WARN, "WARN" },
+        { LogLevel::OFF, "OFF" },
+    };
+
+    return log_level_map[log_level];
 }
\ No newline at end of file
diff --git a/src/core/utils/logging/Logger.cpp b/src/core/utils/logging/Logger.cpp
new file mode 100644
index 0000000..b025ca8
--- /dev/null
+++ b/src/core/utils/logging/Logger.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/utils/logging/Logger.h"
+
+#include "arm_compute/core/Error.h"
+#include "support/ToolchainSupport.h"
+
+using namespace arm_compute::logging;
+
+Logger::Logger(std::string name, LogLevel log_level, std::shared_ptr<Printer> printer)
+    : _name(std::move(name)), _log_level(log_level), _printers(
+{
+    std::move(printer)
+}), _decorators()
+{
+    // Check printer
+    ARM_COMPUTE_ERROR_ON(printer == nullptr);
+
+    // Set default message decorators
+    set_default_decorators();
+}
+
+Logger::Logger(std::string name, LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers)
+    : _name(std::move(name)), _log_level(log_level), _printers(std::move(printers)), _decorators()
+{
+    // Check printers
+    for(const auto &p : _printers)
+    {
+        ARM_COMPUTE_UNUSED(p);
+        ARM_COMPUTE_ERROR_ON(p == nullptr);
+    }
+    // Set default message decorators
+    set_default_decorators();
+}
+
+Logger::Logger(std::string                              name,
+               LogLevel                                 log_level,
+               std::vector<std::shared_ptr<Printer>>    printers,
+               std::vector<std::unique_ptr<IDecorator>> decorators)
+    : _name(std::move(name)), _log_level(log_level), _printers(std::move(printers)), _decorators(std::move(decorators))
+{
+    // Check printers
+    for(const auto &p : _printers)
+    {
+        ARM_COMPUTE_UNUSED(p);
+        ARM_COMPUTE_ERROR_ON(p == nullptr);
+    }
+    // Check decorators
+    for(const auto &d : _decorators)
+    {
+        ARM_COMPUTE_UNUSED(d);
+        ARM_COMPUTE_ERROR_ON(d == nullptr);
+    }
+}
+
+void Logger::log(LogLevel log_level, const std::string &msg)
+{
+    // Return if message shouldn't be logged
+    // i.e. if log level does not match the logger's
+    if(!is_loggable(log_level))
+    {
+        return;
+    }
+
+    // Print message to all printers
+    print_all(create_log_msg(msg, log_level));
+}
+
+void Logger::set_log_level(LogLevel log_level)
+{
+    _log_level = log_level;
+}
+
+LogLevel Logger::log_level() const
+{
+    return _log_level;
+}
+
+std::string Logger::name() const
+{
+    return _name;
+}
+
+void Logger::add_printer(std::shared_ptr<Printer> printer)
+{
+    ARM_COMPUTE_ERROR_ON(printer == nullptr);
+    _printers.push_back(std::move(printer));
+}
+
+void Logger::add_decorator(std::unique_ptr<IDecorator> decorator)
+{
+    ARM_COMPUTE_ERROR_ON(decorator == nullptr);
+    _decorators.push_back(std::move(decorator));
+}
+
+void Logger::set_default_decorators()
+{
+    _decorators.emplace_back(support::cpp14::make_unique<StringDecorator>(_name));
+    _decorators.emplace_back(support::cpp14::make_unique<DateDecorator>());
+    _decorators.emplace_back(support::cpp14::make_unique<LogLevelDecorator>());
+}
+
+bool Logger::is_loggable(LogLevel log_level)
+{
+    return (log_level >= _log_level);
+}
+
+void Logger::decorate_log_msg(LogMsg &msg)
+{
+    for(const auto &d : _decorators)
+    {
+        d->decorate(msg);
+    }
+    msg.raw_ += std::string(" ");
+}
+
+std::string Logger::create_log_msg(const std::string &str, LogLevel log_level)
+{
+    // Adding space string to avoid Android failures
+    LogMsg log_msg(" ", log_level);
+    decorate_log_msg(log_msg);
+    std::ostringstream ss;
+    ss << log_msg.raw_ << " " << str;
+    return ss.str();
+}
+
+void Logger::print_all(const std::string &msg)
+{
+    for(auto &p : _printers)
+    {
+        p->print(msg);
+    }
+}
\ No newline at end of file
diff --git a/src/core/utils/logging/LoggerRegistry.cpp b/src/core/utils/logging/LoggerRegistry.cpp
new file mode 100644
index 0000000..99236d2
--- /dev/null
+++ b/src/core/utils/logging/LoggerRegistry.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/utils/logging/LoggerRegistry.h"
+
+#include "arm_compute/core/Error.h"
+#include "support/ToolchainSupport.h"
+
+using namespace arm_compute::logging;
+
+/** Reserved logger used by the library */
+std::set<std::string> LoggerRegistry::_reserved_loggers = { "CORE", "RUNTIME", "GRAPH" };
+
+LoggerRegistry::LoggerRegistry()
+    : _mtx(), _loggers()
+{
+}
+
+LoggerRegistry &LoggerRegistry::get()
+{
+    static LoggerRegistry _instance;
+    return _instance;
+}
+
+void LoggerRegistry::create_logger(const std::string &name, LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers)
+{
+    std::lock_guard<arm_compute::Mutex> lock(_mtx);
+    if((_loggers.find(name) == _loggers.end()) && (_reserved_loggers.find(name) == _reserved_loggers.end()))
+    {
+        _loggers[name] = std::make_shared<Logger>(name, log_level, std::move(printers));
+    }
+}
+
+void LoggerRegistry::remove_logger(const std::string &name)
+{
+    std::lock_guard<arm_compute::Mutex> lock(_mtx);
+    if(_loggers.find(name) != _loggers.end())
+    {
+        _loggers.erase(name);
+    }
+}
+
+std::shared_ptr<Logger> LoggerRegistry::logger(const std::string &name)
+{
+    std::lock_guard<arm_compute::Mutex> lock(_mtx);
+    return (_loggers.find(name) != _loggers.end()) ? _loggers[name] : nullptr;
+}
+
+void LoggerRegistry::create_reserved_loggers(LogLevel log_level, std::vector<std::shared_ptr<Printer>> printers)
+{
+    std::lock_guard<arm_compute::Mutex> lock(_mtx);
+    for(const auto &r : _reserved_loggers)
+    {
+        if(_loggers.find(r) == _loggers.end())
+        {
+            _loggers[r] = std::make_shared<Logger>(r, log_level, printers);
+        }
+    }
+}
diff --git a/src/graph/nodes/ActivationLayer.cpp b/src/graph/nodes/ActivationLayer.cpp
index 5e75c28..df73ba7 100644
--- a/src/graph/nodes/ActivationLayer.cpp
+++ b/src/graph/nodes/ActivationLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/ActivationLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLActivationLayer.h"
 #include "arm_compute/runtime/NEON/functions/NEActivationLayer.h"
@@ -82,18 +81,20 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out, _activation_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLActivationLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out, _activation_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEActivationLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << " Activation function: " << _activation_info.activation()
-                    << " a: " << _activation_info.a()
-                    << " b: " << _activation_info.b()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << " Activation function: " << _activation_info.activation()
+                               << " a: " << _activation_info.a()
+                               << " b: " << _activation_info.b()
+                               << std::endl);
     return func;
 }
diff --git a/src/graph/nodes/BatchNormalizationLayer.cpp b/src/graph/nodes/BatchNormalizationLayer.cpp
index 25e9e9b..db809f4 100644
--- a/src/graph/nodes/BatchNormalizationLayer.cpp
+++ b/src/graph/nodes/BatchNormalizationLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/BatchNormalizationLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLBatchNormalizationLayer.h"
 #include "arm_compute/runtime/NEON/functions/NEBatchNormalizationLayer.h"
@@ -100,18 +99,18 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out, _mean, _var, _beta, _gamma, _epsilon);
-        ARM_COMPUTE_LOG("Instantiating CLBatchNormalizationLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLBatchNormalizationLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out, _mean, _var, _beta, _gamma, _epsilon);
-        ARM_COMPUTE_LOG("Instantiating NEBatchNormalizationLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEBatchNormalizationLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << std::endl);
 
     return func;
 }
\ No newline at end of file
diff --git a/src/graph/nodes/ConvolutionLayer.cpp b/src/graph/nodes/ConvolutionLayer.cpp
index 303780f..07d4261 100644
--- a/src/graph/nodes/ConvolutionLayer.cpp
+++ b/src/graph/nodes/ConvolutionLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/ConvolutionLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h"
 #include "arm_compute/runtime/CL/functions/CLDirectConvolutionLayer.h"
 #include "arm_compute/runtime/IFunction.h"
@@ -217,12 +216,12 @@
     if(_num_groups == 1)
     {
         func = instantiate_convolution(in, out, conv_method_hint);
-        ARM_COMPUTE_LOG("Instantiating CLConvolutionLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLConvolutionLayer");
     }
     else
     {
         func = instantiate_grouped_convolution(in, out, conv_method_hint);
-        ARM_COMPUTE_LOG("Instantiating NEConvolutionLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEConvolutionLayer");
     }
 
     // Fill weights
@@ -236,15 +235,15 @@
         _biases.allocate_and_fill_if_needed();
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input Shape: " << in->info()->tensor_shape()
-                    << " Weights shape: " << _weights.info().tensor_shape()
-                    << " Biases Shape: " << _biases.info().tensor_shape()
-                    << " Output Shape: " << out->info()->tensor_shape()
-                    << " PadStrideInfo: " << _conv_info
-                    << " Groups: " << _num_groups
-                    << " WeightsInfo: " << _weights_info
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input Shape: " << in->info()->tensor_shape()
+                               << " Weights shape: " << _weights.info().tensor_shape()
+                               << " Biases Shape: " << _biases.info().tensor_shape()
+                               << " Output Shape: " << out->info()->tensor_shape()
+                               << " PadStrideInfo: " << _conv_info
+                               << " Groups: " << _num_groups
+                               << " WeightsInfo: " << _weights_info
+                               << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/FloorLayer.cpp b/src/graph/nodes/FloorLayer.cpp
index 3224799..45e2c3e 100644
--- a/src/graph/nodes/FloorLayer.cpp
+++ b/src/graph/nodes/FloorLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/FloorLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLFloor.h"
 #include "arm_compute/runtime/NEON/functions/NEFloor.h"
@@ -76,18 +75,18 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out);
-        ARM_COMPUTE_LOG("Instantiating CLFloorLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFloorLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out);
-        ARM_COMPUTE_LOG("Instantiating NEFloorLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFloorLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/FullyConnectedLayer.cpp b/src/graph/nodes/FullyConnectedLayer.cpp
index fa5ead8..5f4807a 100644
--- a/src/graph/nodes/FullyConnectedLayer.cpp
+++ b/src/graph/nodes/FullyConnectedLayer.cpp
@@ -24,7 +24,6 @@
 #include "arm_compute/graph/nodes/FullyConnectedLayer.h"
 
 #include "arm_compute/core/Helpers.h"
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h"
 #include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h"
 #include "support/ToolchainSupport.h"
@@ -123,18 +122,20 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, _weights, _biases, out);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLFullyConnectedLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, _weights, _biases, out);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEFullyConnectedLayer");
     }
 
-    ARM_COMPUTE_LOG(" Type: " << in->info()->data_type()
-                    << " Input Shape: " << in->info()->tensor_shape()
-                    << " Weights shape: " << _weights.info().tensor_shape()
-                    << " Biases Shape: " << _biases.info().tensor_shape()
-                    << " Output Shape: " << out->info()->tensor_shape()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Type: " << in->info()->data_type()
+                               << " Input Shape: " << in->info()->tensor_shape()
+                               << " Weights shape: " << _weights.info().tensor_shape()
+                               << " Biases Shape: " << _biases.info().tensor_shape()
+                               << " Output Shape: " << out->info()->tensor_shape()
+                               << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/L2NormalizeLayer.cpp b/src/graph/nodes/L2NormalizeLayer.cpp
index 7abc69c..c5689e1 100644
--- a/src/graph/nodes/L2NormalizeLayer.cpp
+++ b/src/graph/nodes/L2NormalizeLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/L2NormalizeLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLL2Normalize.h"
 #include "arm_compute/runtime/NEON/functions/NEL2Normalize.h"
@@ -78,18 +77,18 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out, _axis, _epsilon);
-        ARM_COMPUTE_LOG("Instantiating CLL2NormalizeLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLL2NormalizeLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out, _axis, _epsilon);
-        ARM_COMPUTE_LOG("Instantiating NEL2NormalizeLayer");
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEL2NormalizeLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/NormalizationLayer.cpp b/src/graph/nodes/NormalizationLayer.cpp
index 319a425..680925a 100644
--- a/src/graph/nodes/NormalizationLayer.cpp
+++ b/src/graph/nodes/NormalizationLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/NormalizationLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLNormalizationLayer.h"
 #include "arm_compute/runtime/NEON/functions/NENormalizationLayer.h"
@@ -82,17 +81,19 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out, _norm_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLNormalizationLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out, _norm_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NENormalizationLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << " Normalization info: " << _norm_info
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << " Normalization info: " << _norm_info
+                               << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/PoolingLayer.cpp b/src/graph/nodes/PoolingLayer.cpp
index 904ba18..6357915 100644
--- a/src/graph/nodes/PoolingLayer.cpp
+++ b/src/graph/nodes/PoolingLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/PoolingLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLPoolingLayer.h"
 #include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h"
@@ -82,16 +81,18 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out, _pool_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLPoolingLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out, _pool_info);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NEPoolingLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << " Pooling info: " << _pool_info << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << " Pooling info: " << _pool_info << std::endl);
 
     return func;
 }
diff --git a/src/graph/nodes/SoftmaxLayer.cpp b/src/graph/nodes/SoftmaxLayer.cpp
index e3345f1..3cdbc9c 100644
--- a/src/graph/nodes/SoftmaxLayer.cpp
+++ b/src/graph/nodes/SoftmaxLayer.cpp
@@ -23,7 +23,6 @@
  */
 #include "arm_compute/graph/nodes/SoftmaxLayer.h"
 
-#include "arm_compute/core/Logger.h"
 #include "arm_compute/runtime/CL/CLTensor.h"
 #include "arm_compute/runtime/CL/functions/CLSoftmaxLayer.h"
 #include "arm_compute/runtime/NEON/functions/NESoftmaxLayer.h"
@@ -76,16 +75,18 @@
     if(_target_hint == TargetHint::OPENCL)
     {
         func = instantiate<TargetHint::OPENCL>(in, out);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating CLSoftmaxLayer");
     }
     else
     {
         func = instantiate<TargetHint::NEON>(in, out);
+        ARM_COMPUTE_LOG_GRAPH_INFO("Instantiating NESoftmaxLayer");
     }
 
-    ARM_COMPUTE_LOG(" Data Type: " << in->info()->data_type()
-                    << " Input shape: " << in->info()->tensor_shape()
-                    << " Output shape: " << out->info()->tensor_shape()
-                    << std::endl);
+    ARM_COMPUTE_LOG_GRAPH_INFO(" Data Type: " << in->info()->data_type()
+                               << " Input shape: " << in->info()->tensor_shape()
+                               << " Output shape: " << out->info()->tensor_shape()
+                               << std::endl);
 
     return func;
 }
diff --git a/support/ToolchainSupport.h b/support/ToolchainSupport.h
index b9d9103..ab2a9fe 100644
--- a/support/ToolchainSupport.h
+++ b/support/ToolchainSupport.h
@@ -144,8 +144,8 @@
  * @note This function implements the same behaviour as std::copysign except that it doesn't
  *       support Integral type. The latter is not in the namespace std in some Android toolchains.
  *
- * @param[in] x value that contains the magnitued to be used in constructing the result.
- * @param[in] y value that contains the sign to be used in constructin the result.
+ * @param[in] x value that contains the magnitude to be used in constructing the result.
+ * @param[in] y value that contains the sign to be used in construct in the result.
  *
  * @return Floating-point value with magnitude of @p x and sign of @p y.
  */
@@ -154,6 +154,23 @@
 {
     return ::copysign(x, y);
 }
+
+/** Loads the data from the given location, converts them to character string equivalents
+ *  and writes the result to a character string buffer.
+ *
+ * @param[in] s    Pointer to a character string to write to
+ * @param[in] n    Up to buf_size - 1 characters may be written, plus the null terminator
+ * @param[in] fmt  Pointer to a null-terminated multibyte string specifying how to interpret the data.
+ * @param[in] args Arguments forwarded to snprintf.
+ *
+ * @return  Number of characters that would have been written for a sufficiently large buffer
+ *          if successful (not including the terminating null character), or a negative value if an error occurred.
+ */
+template <typename... Ts>
+inline int snprintf(char *s, size_t n, const char *fmt, Ts &&... args)
+{
+    return ::snprintf(s, n, fmt, std::forward<Ts>(args)...);
+}
 #else  /* (__ANDROID__ || BARE_METAL) */
 /** Convert integer and float values to string.
  *
@@ -250,8 +267,8 @@
  * @note This function implements the same behaviour as std::copysign except that it doesn't
  *       support Integral type. The latter is not in the namespace std in some Android toolchains.
  *
- * @param[in] x value that contains the magnitued to be used in constructing the result.
- * @param[in] y value that contains the sign to be used in constructin the result.
+ * @param[in] x value that contains the magnitude to be used in constructing the result.
+ * @param[in] y value that contains the sign to be used in construct in the result.
  *
  * @return Floating-point value with magnitude of @p x and sign of @p y.
  */
@@ -260,6 +277,23 @@
 {
     return std::copysign(x, y);
 }
+
+/** Loads the data from the given location, converts them to character string equivalents
+ *  and writes the result to a character string buffer.
+ *
+ * @param[in] s    Pointer to a character string to write to
+ * @param[in] n    Up to buf_size - 1 characters may be written, plus the null terminator
+ * @param[in] fmt  Pointer to a null-terminated multibyte string specifying how to interpret the data.
+ * @param[in] args Arguments forwarded to std::snprintf.
+ *
+ * @return  Number of characters that would have been written for a sufficiently large buffer
+ *          if successful (not including the terminating null character), or a negative value if an error occurred.
+ */
+template <typename... Ts>
+inline int snprintf(char *s, std::size_t n, const char *fmt, Ts &&... args)
+{
+    return std::snprintf(s, n, fmt, std::forward<Ts>(args)...);
+}
 #endif /* (__ANDROID__ || BARE_METAL) */
 
 inline std::string to_string(bool value)