From 516ee5c611f61f398563dd85670e1e64d71d977d Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 26 Feb 2016 16:13:54 -0300 Subject: [PATCH 1/3] New LOG() function with different logging levels and std::ostream support --- src/app/app.cpp | 22 ++++++--- src/app/app_options.cpp | 11 +++-- src/app/app_options.h | 13 +++-- src/app/log.cpp | 22 +++------ src/app/log.h | 9 +--- src/base/log.cpp | 102 ++++++++++++++++++++++++++++++---------- src/base/log.h | 37 ++++++++++++--- 7 files changed, 149 insertions(+), 67 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index b7298892d..90ef4857c 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -109,10 +109,7 @@ public: // This is a raw pointer because we want to delete this explicitly. app::crash::DataRecovery* m_recovery; - Modules(bool verbose) - : m_loggerModule(verbose) - , m_recovery(nullptr) { - } + Modules() : m_recovery(nullptr) { } app::crash::DataRecovery* recovery() { return m_recovery; @@ -164,10 +161,21 @@ void App::initialize(const AppOptions& options) if (m_isGui) m_uiSystem.reset(new ui::UISystem); - // Initializes the application loading the modules, setting the - // graphics mode, loading the configuration and resources, etc. m_coreModules = new CoreModules; - m_modules = new Modules(options.verbose()); + + switch (options.verboseLevel()) { + case AppOptions::kNoVerbose: + base::set_log_level(ERROR); + break; + case AppOptions::kVerbose: + base::set_log_level(INFO); + break; + case AppOptions::kHighlyVerbose: + base::set_log_level(VERBOSE); + break; + } + + m_modules = new Modules; m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0); m_brushes.reset(new AppBrushes); diff --git a/src/app/app_options.cpp b/src/app/app_options.cpp index b9abab6e8..72fd91937 100644 --- a/src/app/app_options.cpp +++ b/src/app/app_options.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -24,7 +24,7 @@ AppOptions::AppOptions(int argc, const char* argv[]) : m_exeName(base::get_file_name(argv[0])) , m_startUI(true) , m_startShell(false) - , m_verboseEnabled(false) + , m_verboseLevel(kNoVerbose) , m_palette(m_po.add("palette").requiresValue("").description("Use a specific palette by default")) , m_shell(m_po.add("shell").description("Start an interactive console to execute scripts")) , m_batch(m_po.add("batch").mnemonic('b').description("Do not start the UI")) @@ -52,13 +52,18 @@ AppOptions::AppOptions(int argc, const char* argv[]) , m_listLayers(m_po.add("list-layers").description("List layers of the next given sprite\nor include layers in JSON data")) , m_listTags(m_po.add("list-tags").description("List tags of the next given sprite sprite\nor include frame tags in JSON data")) , m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done")) + , m_debug(m_po.add("debug").description("Extreme verbose mode and\ncopy log to desktop")) , m_help(m_po.add("help").mnemonic('?').description("Display this help and exits")) , m_version(m_po.add("version").description("Output version information and exit")) { try { m_po.parse(argc, argv); - m_verboseEnabled = m_po.enabled(m_verbose); + if (m_po.enabled(m_debug)) + m_verboseLevel = kHighlyVerbose; + else if (m_po.enabled(m_verbose)) + m_verboseLevel = kVerbose; + m_paletteFileName = m_po.value_of(m_palette); m_startShell = m_po.enabled(m_shell); diff --git a/src/app/app_options.h b/src/app/app_options.h index dc3df68a7..921ccbfee 100644 --- a/src/app/app_options.h +++ b/src/app/app_options.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -19,6 +19,12 @@ namespace app { class AppOptions { public: + enum VerboseLevel { + kNoVerbose, + kVerbose, + kHighlyVerbose, + }; + typedef base::ProgramOptions PO; typedef PO::Option Option; typedef PO::ValueList ValueList; @@ -27,7 +33,7 @@ public: bool startUI() const { return m_startUI; } bool startShell() const { return m_startShell; } - bool verbose() const { return m_verboseEnabled; } + VerboseLevel verboseLevel() const { return m_verboseLevel; } const std::string& paletteFileName() const { return m_paletteFileName; } @@ -70,7 +76,7 @@ private: base::ProgramOptions m_po; bool m_startUI; bool m_startShell; - bool m_verboseEnabled; + VerboseLevel m_verboseLevel; std::string m_paletteFileName; Option& m_palette; @@ -101,6 +107,7 @@ private: Option& m_listTags; Option& m_verbose; + Option& m_debug; Option& m_help; Option& m_version; diff --git a/src/app/log.cpp b/src/app/log.cpp index e7e41fbc4..56e59d493 100644 --- a/src/app/log.cpp +++ b/src/app/log.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -17,19 +17,12 @@ namespace app { -static LoggerModule* logger_instance = NULL; - -LoggerModule::LoggerModule(bool verbose) - : m_verbose(verbose) +LoggerModule::LoggerModule() { - logger_instance = this; - - if (verbose) { - app::ResourceFinder rf(false); - rf.includeUserDir("aseprite.log"); - auto filename = rf.defaultFilename(); - base_set_log_filename(filename.c_str()); - } + app::ResourceFinder rf(false); + rf.includeUserDir("aseprite.log"); + auto filename = rf.defaultFilename(); + base::set_log_filename(filename.c_str()); } LoggerModule::~LoggerModule() @@ -37,8 +30,7 @@ LoggerModule::~LoggerModule() LOG("Logger module: shutting down (this is the last line)\n"); // Close log file - base_set_log_filename(""); - logger_instance = nullptr; + base::set_log_filename(""); } } // namespace app diff --git a/src/app/log.h b/src/app/log.h index 7860e1501..cb0b668c7 100644 --- a/src/app/log.h +++ b/src/app/log.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -13,13 +13,8 @@ namespace app { class LoggerModule { public: - LoggerModule(bool verbose); + LoggerModule(); ~LoggerModule(); - - bool isVerbose() const { return m_verbose; } - - private: - bool m_verbose; }; } // namespace app diff --git a/src/base/log.cpp b/src/base/log.cpp index 74ebab015..c89ad94c3 100644 --- a/src/base/log.cpp +++ b/src/base/log.cpp @@ -1,5 +1,5 @@ // Aseprite Base Library -// Copyright (c) 2001-2015 David Capello +// Copyright (c) 2001-2016 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -10,47 +10,99 @@ #include "base/log.h" -#include "base/file_handle.h" +#include "base/fstream_path.h" #include #include #include +#include +#include #include -static FILE* log_fileptr = nullptr; -static std::string log_filename; +namespace { -void base_set_log_filename(const char* filename) -{ - if (log_fileptr) { - fclose(log_fileptr); - log_fileptr = nullptr; +class nullbuf : public std::streambuf { +protected: + int_type overflow(int_type ch) override { + return traits_type::not_eof(ch); } +}; + +class nullstream : public std::ostream { +public: + nullstream() + : std::basic_ios(&m_buf) + , std::ostream(&m_buf) { } +private: + nullbuf m_buf; +}; + +LogLevel log_level = LogLevel::NONE; +nullstream null_stream; +std::ofstream log_stream; +std::string log_filename; + +bool open_log_stream() +{ + if (!log_stream) { + if (log_filename.empty()) + return false; + + log_stream.open(FSTREAM_PATH(log_filename)); + } + return (bool)log_stream; +} + +void log_text(const char* text) +{ + if (!open_log_stream()) + return; + + log_stream.write(text, strlen(text)); + log_stream.flush(); +} + +} // anonymous namespace + +void base::set_log_filename(const char* filename) +{ + if (log_stream) + log_stream.close(); + log_filename = filename; } -void base_log(const char* format, ...) +void base::set_log_level(LogLevel level) { - if (!log_fileptr) { - if (log_filename.empty()) - return; + log_level = level; +} - log_fileptr = base::open_file_raw(log_filename, "w"); - } +std::ostream& base::get_log_stream(LogLevel level) +{ + ASSERT(level != NONE); - if (log_fileptr) { - va_list ap; - va_start(ap, format); + if ((log_level < level) || + (!log_stream && !open_log_stream())) + return null_stream; + else + return log_stream; +} - vfprintf(log_fileptr, format, ap); - fflush(log_fileptr); +void LOG(const char* format, ...) +{ + if (log_level < INFO) + return; + + char buf[2048]; + va_list ap; + va_start(ap, format); + std::vsnprintf(buf, sizeof(buf)-1, format, ap); + log_text(buf); #ifdef _DEBUG - va_start(ap, format); - vfprintf(stderr, format, ap); - fflush(stderr); + fputs(buf, stderr); + fflush(stderr); #endif - va_end(ap); - } + va_end(ap); } diff --git a/src/base/log.h b/src/base/log.h index 0eff40123..ba9039a22 100644 --- a/src/base/log.h +++ b/src/base/log.h @@ -1,5 +1,5 @@ // Aseprite Base Library -// Copyright (c) 2001-2015 David Capello +// Copyright (c) 2001-2016 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -8,12 +8,35 @@ #define BASE_LOG_H_INCLUDED #pragma once -// Define BASE_DONT_DEFINE_LOG_MACRO in case that you don't need LOG -#ifndef BASE_DONT_DEFINE_LOG_MACRO - #define LOG base_log -#endif +enum LogLevel { + NONE = 0, // Default log level: do not log + FATAL = 1, // Something failed and we CANNOT continue the execution + ERROR = 2, // Something failed, the UI should show this, and we can continue + WARNING = 3, // Something failed, the UI don't need to show this, and we can continue + INFO = 4, // Information about some important event + VERBOSE = 5, // Information step by step +}; -void base_set_log_filename(const char* filename); -void base_log(const char* format, ...); +// E.g. LOG("text in information log level\n"); +void LOG(const char* format, ...); + +#ifdef __cplusplus +#include + +namespace base { + + void set_log_filename(const char* filename); + void set_log_level(LogLevel level); + + std::ostream& get_log_stream(LogLevel level); + +} // namespace base + +// E.g. LOG(INFO) << "some information\n"; +inline std::ostream& LOG(LogLevel level) { + return base::get_log_stream(level); +} + +#endif #endif From b5d04525c0a271e01d7761686d35f95499a26246 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 26 Feb 2016 16:51:30 -0300 Subject: [PATCH 2/3] Use fstream::is_open() instead of operator bool() --- src/base/log.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/base/log.cpp b/src/base/log.cpp index c89ad94c3..7f4e837aa 100644 --- a/src/base/log.cpp +++ b/src/base/log.cpp @@ -44,13 +44,13 @@ std::string log_filename; bool open_log_stream() { - if (!log_stream) { + if (!log_stream.is_open()) { if (log_filename.empty()) return false; log_stream.open(FSTREAM_PATH(log_filename)); } - return (bool)log_stream; + return log_stream.is_open(); } void log_text(const char* text) @@ -66,7 +66,7 @@ void log_text(const char* text) void base::set_log_filename(const char* filename) { - if (log_stream) + if (log_stream.is_open()) log_stream.close(); log_filename = filename; @@ -82,7 +82,7 @@ std::ostream& base::get_log_stream(LogLevel level) ASSERT(level != NONE); if ((log_level < level) || - (!log_stream && !open_log_stream())) + (!log_stream.is_open() && !open_log_stream())) return null_stream; else return log_stream; From 5fbb4e90d73eaa8fef7d5bc5223062b825d228f6 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 26 Feb 2016 17:08:42 -0300 Subject: [PATCH 3/3] Create log in Desktop folder when --debug is used --- src/app/app.cpp | 9 ++++++-- src/app/log.cpp | 9 ++++++-- src/app/log.h | 2 +- src/app/resource_finder.cpp | 42 ++++++++++++++++++++++++++++++++++++- src/app/resource_finder.h | 4 +++- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index 90ef4857c..c781b7827 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -109,7 +109,10 @@ public: // This is a raw pointer because we want to delete this explicitly. app::crash::DataRecovery* m_recovery; - Modules() : m_recovery(nullptr) { } + Modules(bool createLogInDesktop) + : m_loggerModule(createLogInDesktop) + , m_recovery(nullptr) { + } app::crash::DataRecovery* recovery() { return m_recovery; @@ -163,6 +166,7 @@ void App::initialize(const AppOptions& options) m_coreModules = new CoreModules; + bool createLogInDesktop = false; switch (options.verboseLevel()) { case AppOptions::kNoVerbose: base::set_log_level(ERROR); @@ -172,10 +176,11 @@ void App::initialize(const AppOptions& options) break; case AppOptions::kHighlyVerbose: base::set_log_level(VERBOSE); + createLogInDesktop = true; break; } - m_modules = new Modules; + m_modules = new Modules(createLogInDesktop); m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0); m_brushes.reset(new AppBrushes); diff --git a/src/app/log.cpp b/src/app/log.cpp index 56e59d493..84949b072 100644 --- a/src/app/log.cpp +++ b/src/app/log.cpp @@ -17,10 +17,15 @@ namespace app { -LoggerModule::LoggerModule() +LoggerModule::LoggerModule(bool createLogInDesktop) { app::ResourceFinder rf(false); - rf.includeUserDir("aseprite.log"); + + if (createLogInDesktop) + rf.includeDesktopDir(PACKAGE "-v" VERSION "-DebugOutput.txt"); + else + rf.includeUserDir("aseprite.log"); + auto filename = rf.defaultFilename(); base::set_log_filename(filename.c_str()); } diff --git a/src/app/log.h b/src/app/log.h index cb0b668c7..108fa7361 100644 --- a/src/app/log.h +++ b/src/app/log.h @@ -13,7 +13,7 @@ namespace app { class LoggerModule { public: - LoggerModule(); + LoggerModule(bool createLogInDesktop); ~LoggerModule(); }; diff --git a/src/app/resource_finder.cpp b/src/app/resource_finder.cpp index 7d001c0ca..5ecbfc111 100644 --- a/src/app/resource_finder.cpp +++ b/src/app/resource_finder.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -19,6 +19,11 @@ #include #include +#ifdef _WIN32 + #include + #include +#endif + namespace app { ResourceFinder::ResourceFinder(bool log) @@ -163,6 +168,41 @@ void ResourceFinder::includeUserDir(const char* filename) #endif } +void ResourceFinder::includeDesktopDir(const char* filename) +{ +#ifdef _WIN32 + + std::vector buf(MAX_PATH); + HRESULT hr = SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, + SHGFP_TYPE_DEFAULT, &buf[0]); + if (hr == S_OK) { + addPath(base::join_path(base::to_utf8(&buf[0]), filename)); + } + else { + includeHomeDir(filename); + } + +#elif defined(__APPLE__) + + // TODO get the desktop folder + // $HOME/Desktop/filename + includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str()); + +#else + + char* desktopDir = std::getenv("XDG_DESKTOP_DIR"); + if (desktopDir) { + // $XDG_DESKTOP_DIR/filename + addPath(base::join_path(desktopDir, filename)); + } + else { + // $HOME/Desktop/filename + includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str()); + } + +#endif +} + std::string ResourceFinder::getFirstOrCreateDefault() { std::string fn; diff --git a/src/app/resource_finder.h b/src/app/resource_finder.h index d1cf38bd4..e147d4571 100644 --- a/src/app/resource_finder.h +++ b/src/app/resource_finder.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -51,6 +51,8 @@ namespace app { // - The filename will be in $HOME/.config/aseprite/ void includeUserDir(const char* filename); + void includeDesktopDir(const char* filename); + // Returns the first file found or creates the whole directory // structure to create the file in its default location. std::string getFirstOrCreateDefault();