diff --git a/source/dub/commandline.d b/source/dub/commandline.d index e428d61..13a02c8 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -12,9 +12,9 @@ import dub.dub; import dub.generators.generator; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.packagesuppliers; diff --git a/source/dub/compilers/compiler.d b/source/dub/compilers/compiler.d index ff59b26..b8e2107 100644 --- a/source/dub/compilers/compiler.d +++ b/source/dub/compilers/compiler.d @@ -11,9 +11,10 @@ deprecated("Please `import dub.dependency : Dependency` instead") public import dub.dependency : Dependency; public import dub.platform : BuildPlatform, matchesSpecification; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; + import std.algorithm; import std.array; import std.exception; diff --git a/source/dub/compilers/dmd.d b/source/dub/compilers/dmd.d index 7c0bb32..ba49e90 100644 --- a/source/dub/compilers/dmd.d +++ b/source/dub/compilers/dmd.d @@ -10,8 +10,8 @@ import dub.compilers.compiler; import dub.compilers.utils; import dub.internal.utils; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; import std.algorithm; import std.array; diff --git a/source/dub/compilers/gdc.d b/source/dub/compilers/gdc.d index fd47d01..cab4759 100644 --- a/source/dub/compilers/gdc.d +++ b/source/dub/compilers/gdc.d @@ -10,8 +10,8 @@ import dub.compilers.compiler; import dub.compilers.utils; import dub.internal.utils; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; import std.algorithm; import std.array; diff --git a/source/dub/compilers/ldc.d b/source/dub/compilers/ldc.d index f37409d..a850907 100644 --- a/source/dub/compilers/ldc.d +++ b/source/dub/compilers/ldc.d @@ -10,8 +10,8 @@ import dub.compilers.compiler; import dub.compilers.utils; import dub.internal.utils; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; import std.algorithm; import std.array; diff --git a/source/dub/compilers/utils.d b/source/dub/compilers/utils.d index e047113..86bde90 100644 --- a/source/dub/compilers/utils.d +++ b/source/dub/compilers/utils.d @@ -9,10 +9,10 @@ import dub.compilers.buildsettings; import dub.platform : BuildPlatform, archCheck, compilerCheck, platformCheck; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; -import std.algorithm : canFind, endsWith, filter; +import dub.logging; +import std.algorithm : canFind, endsWith, filter; /** Alters the build options to comply with the specified build requirements. diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 678d7ba..16210db 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -8,12 +8,12 @@ module dub.dependency; import dub.internal.utils; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; import dub.package_; import dub.semver; +import dub.logging; import std.algorithm; import std.array; diff --git a/source/dub/dependencyresolver.d b/source/dub/dependencyresolver.d index 8ec82cc..527b632 100644 --- a/source/dub/dependencyresolver.d +++ b/source/dub/dependencyresolver.d @@ -8,7 +8,7 @@ module dub.dependencyresolver; import dub.dependency; -import dub.internal.vibecompat.core.log; +import dub.logging; import std.algorithm : all, canFind, filter, map, sort; import std.array : appender, array, join; diff --git a/source/dub/dub.d b/source/dub/dub.d index f0a8466..c80e7cb 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -12,9 +12,9 @@ import dub.dependencyresolver; import dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.url; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.packagesuppliers; diff --git a/source/dub/generators/build.d b/source/dub/generators/build.d index 490b47d..d9f9113 100644 --- a/source/dub/generators/build.d +++ b/source/dub/generators/build.d @@ -12,8 +12,8 @@ import dub.generators.generator; import dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.project; diff --git a/source/dub/generators/cmake.d b/source/dub/generators/cmake.d index 595442d..3bf84bb 100644 --- a/source/dub/generators/cmake.d +++ b/source/dub/generators/cmake.d @@ -9,9 +9,9 @@ import dub.compilers.buildsettings; import dub.generators.generator; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.project; import std.algorithm: map, uniq; diff --git a/source/dub/generators/generator.d b/source/dub/generators/generator.d index 534b38c..a88affc 100644 --- a/source/dub/generators/generator.d +++ b/source/dub/generators/generator.d @@ -13,8 +13,8 @@ import dub.generators.sublimetext; import dub.generators.visuald; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.project; diff --git a/source/dub/generators/sublimetext.d b/source/dub/generators/sublimetext.d index f53ae53..880d0be 100644 --- a/source/dub/generators/sublimetext.d +++ b/source/dub/generators/sublimetext.d @@ -9,9 +9,9 @@ import dub.compilers.compiler; import dub.generators.generator; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.packagemanager; import dub.project; diff --git a/source/dub/generators/visuald.d b/source/dub/generators/visuald.d index 7f45122..1eef903 100644 --- a/source/dub/generators/visuald.d +++ b/source/dub/generators/visuald.d @@ -11,7 +11,7 @@ import dub.generators.generator; import dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.project; diff --git a/source/dub/init.d b/source/dub/init.d index 876b791..160449b 100644 --- a/source/dub/init.d +++ b/source/dub/init.d @@ -8,7 +8,7 @@ module dub.init; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; +import dub.logging; import dub.package_ : PackageFormat, packageInfoFiles, defaultPackageFilename; import dub.recipe.packagerecipe; import dub.dependency; diff --git a/source/dub/internal/colorize/colors.d b/source/dub/internal/colorize/colors.d new file mode 100644 index 0000000..2555887 --- /dev/null +++ b/source/dub/internal/colorize/colors.d @@ -0,0 +1,193 @@ +/** + * Authors: Pedro Tacla Yamada + * Date: June 9, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 1.0.2 + */ +module dub.internal.colorize.colors; + +import std.string : format; + +private template color_type(int offset) +{ + static enum type : int + { + init = 39 + offset, + + black = 30 + offset, + red = 31 + offset, + green = 32 + offset, + yellow = 33 + offset, + blue = 34 + offset, + magenta = 35 + offset, + cyan = 36 + offset, + white = 37 + offset, + + light_black = 90 + offset, + light_red = 91 + offset, + light_green = 92 + offset, + light_yellow = 93 + offset, + light_blue = 94 + offset, + light_magenta = 95 + offset, + light_cyan = 96 + offset, + light_white = 97 + offset + } +} + +alias color_type!0 .type fg; +alias color_type!10 .type bg; + +// Text modes +static enum mode : int +{ + init = 0, + bold = 1, + underline = 4, + blink = 5, + swap = 7, + hide = 8 +} + +/** + * Wraps a string around color escape sequences. + * + * Params: + * str = The string to wrap with colors and modes + * c = The foreground color (see the fg enum type) + * b = The background color (see the bg enum type) + * m = The text mode (see the mode enum type) + * Example: + * --- + * writeln("This is blue".color(fg.blue)); + * writeln( + * color("This is red over green blinking", fg.blue, bg.green, mode.blink) + * ); + * --- + */ + +string color( + const string str, + const fg c=fg.init, + const bg b=bg.init, + const mode m=mode.init +) pure +{ + return format("\033[%d;%d;%dm%s\033[0m", m, c, b, str); +} + +unittest +{ + import colorize.cwrite; + string ret; + + ret = "This is yellow".color(fg.yellow); + cwriteln(ret); + assert(ret == "\033[33mThis is yellow\033[0m"); + + ret = "This is light green".color(fg.light_green); + cwriteln(ret); + assert(ret == "\033[92mThis is light green\033[0m"); + + ret = "This is light blue with red background".color(fg.light_blue, bg.red); + cwriteln(ret); + assert(ret == "\033[0;94;41mThis is light blue with red background\033[0m"); + + ret = "This is red on blue blinking".color(fg.red, bg.blue, mode.blink); + cwriteln(ret); + assert(ret == "\033[5;31;44mThis is red on blue blinking\033[0m"); + + ret = color("This is magenta", "magenta"); + cwriteln(ret); + assert(ret == "\033[35mThis is magenta\033[0m"); +} + +string colorHelper(const string str, const string name) pure +{ + int code; + + switch(name) + { + case "init": code = 39; break; + + case "black" : code = 30; break; + case "red" : code = 31; break; + case "green" : code = 32; break; + case "yellow" : code = 33; break; + case "blue" : code = 34; break; + case "magenta": code = 35; break; + case "cyan" : code = 36; break; + case "white" : code = 37; break; + + case "light_black" : code = 90; break; + case "light_red" : code = 91; break; + case "light_green" : code = 92; break; + case "light_yellow" : code = 93; break; + case "light_blue" : code = 94; break; + case "light_magenta": code = 95; break; + case "light_cyan" : code = 96; break; + case "light_white" : code = 97; break; + + case "bg_init": code = 49; break; + + case "bg_black" : code = 40; break; + case "bg_red" : code = 41; break; + case "bg_green" : code = 42; break; + case "bg_yellow" : code = 43; break; + case "bg_blue" : code = 44; break; + case "bg_magenta": code = 45; break; + case "bg_cyan" : code = 46; break; + case "bg_white" : code = 47; break; + + case "bg_light_black" : code = 100; break; + case "bg_light_red" : code = 101; break; + case "bg_light_green" : code = 102; break; + case "bg_light_yellow" : code = 103; break; + case "bg_light_blue" : code = 104; break; + case "bg_light_magenta": code = 105; break; + case "bg_light_cyan" : code = 106; break; + case "bg_light_white" : code = 107; break; + + case "mode_init": code = 0; break; + case "mode_bold" : code = 1; break; + case "mode_underline": code = 4; break; + case "mode_blink" : code = 5; break; + case "mode_swap" : code = 7; break; + case "mode_hide" : code = 8; break; + + default: + throw new Exception( + "Unknown fg color, bg color or mode \"" ~ name ~ "\"" + ); + } + + return format("\033[%dm%s\033[0m", code, str); +} + +string colorHelper(T)(const string str, const T t=T.init) pure + if(is(T : fg) || is(T : bg) || is(T : mode)) +{ + return format("\033[%dm%s\033[0m", t, str); +} + +alias colorHelper!bg background; +alias colorHelper!fg foreground; +alias colorHelper!mode style; + +alias background color; +alias foreground color; +alias style color; +alias colorHelper color; + +unittest +{ + import colorize.cwrite; + string ret; + + ret = "This is red on blue blinking" + .foreground(fg.red) + .background(bg.blue) + .style(mode.blink); + + cwriteln(ret); + assert(ret == "\033[5m\033[44m\033[31mThis is red on blue blinking\033[0m\033[0m\033[0m"); +} diff --git a/source/dub/internal/colorize/cwrite.d b/source/dub/internal/colorize/cwrite.d new file mode 100644 index 0000000..8c8c269 --- /dev/null +++ b/source/dub/internal/colorize/cwrite.d @@ -0,0 +1,74 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 1.0.2 + */ +module dub.internal.colorize.cwrite; + +import std.stdio : File, stdout; + +import dub.internal.colorize.winterm; + +/// Coloured write. +void cwrite(T...)(T args) if (!is(T[0] : File)) +{ + stdout.cwrite(args); +} + +/// Coloured writef. +void cwritef(Char, T...)(in Char[] fmt, T args) if (!is(T[0] : File)) +{ + stdout.cwritef(fmt, args); +} + +/// Coloured writefln. +void cwritefln(Char, T...)(in Char[] fmt, T args) +{ + stdout.cwritef(fmt ~ "\n", args); +} + +/// Coloured writeln. +void cwriteln(T...)(T args) +{ + // Most general instance + stdout.cwrite(args, '\n'); +} + +/// Coloured writef to a File. +void cwritef(Char, A...)(File f, in Char[] fmt, A args) +{ + import std.string : format; + auto s = format(fmt, args); + f.cwrite(s); +} + +/// Coloured writef to a File. +void cwrite(S...)(File f, S args) +{ + import std.conv : to; + + string s = ""; + foreach(arg; args) + s ~= to!string(arg); + + version(Windows) + { + WinTermEmulation winterm; + winterm.initialize(); + foreach(dchar c ; s) + { + auto charAction = winterm.feed(c); + final switch(charAction) with (WinTermEmulation.CharAction) + { + case drop: break; + case write: f.write(c); break; + case flush: f.flush(); break; + } + } + } + else + { + f.write(s); + } +} diff --git a/source/dub/internal/colorize/package.d b/source/dub/internal/colorize/package.d new file mode 100644 index 0000000..b4ff384 --- /dev/null +++ b/source/dub/internal/colorize/package.d @@ -0,0 +1,10 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 1.0.2 + */ +module dub.internal.colorize; + +public import dub.internal.colorize.colors; +public import dub.internal.colorize.cwrite; diff --git a/source/dub/internal/colorize/winterm.d b/source/dub/internal/colorize/winterm.d new file mode 100644 index 0000000..ef21981 --- /dev/null +++ b/source/dub/internal/colorize/winterm.d @@ -0,0 +1,161 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 1.0.2 + */ +module dub.internal.colorize.winterm; + +version(Windows) +{ + import core.sys.windows.windows; + + // Patch for DMD 2.065 compatibility + static if( __VERSION__ < 2066 ) private enum nogc = 1; + + // This is a state machine to enable terminal colors on Windows. + // Parses and interpret ANSI/VT100 Terminal Control Escape Sequences. + // Only supports colour sequences, will output char incorrectly on invalid input. + struct WinTermEmulation + { + public: + @nogc void initialize() nothrow + { + // saves console attributes + _console = GetStdHandle(STD_OUTPUT_HANDLE); + _savedInitialColor = (0 != GetConsoleScreenBufferInfo(_console, &consoleInfo)); + _state = State.initial; + } + + @nogc ~this() nothrow + { + // Restore initial text attributes on release + if (_savedInitialColor) + { + SetConsoleTextAttribute(_console, consoleInfo.wAttributes); + _savedInitialColor = false; + } + } + + enum CharAction + { + write, + drop, + flush + } + + // Eat one character and update color state accordingly. + // Returns what to do with the fed character. + @nogc CharAction feed(dchar d) nothrow + { + final switch(_state) with (State) + { + case initial: + if (d == '\x1B') + { + _state = escaped; + return CharAction.flush; + } + break; + + case escaped: + if (d == '[') + { + _state = readingAttribute; + _parsedAttr = 0; + return CharAction.drop; + } + break; + + + case readingAttribute: + if (d >= '0' && d <= '9') + { + _parsedAttr = _parsedAttr * 10 + (d - '0'); + return CharAction.drop; + } + else if (d == ';') + { + executeAttribute(_parsedAttr); + _parsedAttr = 0; + return CharAction.drop; + } + else if (d == 'm') + { + executeAttribute(_parsedAttr); + _state = State.initial; + return CharAction.drop; + } + break; + } + return CharAction.write; + } + + private: + HANDLE _console; + bool _savedInitialColor; + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + State _state; + WORD _currentAttr; + int _parsedAttr; + + enum State + { + initial, + escaped, + readingAttribute + } + + @nogc void setForegroundColor(WORD fgFlags) nothrow + { + _currentAttr = _currentAttr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); + _currentAttr = _currentAttr | fgFlags; + SetConsoleTextAttribute(_console, _currentAttr); + } + + @nogc void setBackgroundColor(WORD bgFlags) nothrow + { + _currentAttr = _currentAttr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY); + _currentAttr = _currentAttr | bgFlags; + SetConsoleTextAttribute(_console, _currentAttr); + } + + @nogc void executeAttribute(int attr) nothrow + { + switch (attr) + { + case 0: + // reset all attributes + SetConsoleTextAttribute(_console, consoleInfo.wAttributes); + break; + + default: + if ( (30 <= attr && attr <= 37) || (90 <= attr && attr <= 97) ) + { + WORD color = 0; + if (90 <= attr && attr <= 97) + { + color = FOREGROUND_INTENSITY; + attr -= 60; + } + attr -= 30; + color |= (attr & 1 ? FOREGROUND_RED : 0) | (attr & 2 ? FOREGROUND_GREEN : 0) | (attr & 4 ? FOREGROUND_BLUE : 0); + setForegroundColor(color); + } + + if ( (40 <= attr && attr <= 47) || (100 <= attr && attr <= 107) ) + { + WORD color = 0; + if (100 <= attr && attr <= 107) + { + color = BACKGROUND_INTENSITY; + attr -= 60; + } + attr -= 40; + color |= (attr & 1 ? BACKGROUND_RED : 0) | (attr & 2 ? BACKGROUND_GREEN : 0) | (attr & 4 ? BACKGROUND_BLUE : 0); + setBackgroundColor(color); + } + } + } + } +} diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index c53d708..16fdfdf 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -8,11 +8,11 @@ module dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.url; import dub.compilers.buildsettings : BuildSettings; import dub.version_; +import dub.logging; import core.time : Duration; import std.algorithm : canFind, startsWith; diff --git a/source/dub/internal/vibecompat/core/file.d b/source/dub/internal/vibecompat/core/file.d index 6ee345d..d1b55e6 100644 --- a/source/dub/internal/vibecompat/core/file.d +++ b/source/dub/internal/vibecompat/core/file.d @@ -9,7 +9,7 @@ public import dub.internal.vibecompat.inet.url; -import dub.internal.vibecompat.core.log; +import dub.logging; import std.conv; import core.stdc.stdio; diff --git a/source/dub/internal/vibecompat/core/log.d b/source/dub/internal/vibecompat/core/log.d deleted file mode 100644 index e48569e..0000000 --- a/source/dub/internal/vibecompat/core/log.d +++ /dev/null @@ -1,99 +0,0 @@ -/** - Central logging facility for vibe. - - Copyright: © 2012 rejectedsoftware e.K. - License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. - Authors: Sönke Ludwig -*/ -module dub.internal.vibecompat.core.log; - -import std.array; -import std.datetime; -import std.format; -import std.stdio; -import core.thread; - -private { - shared LogLevel s_minLevel = LogLevel.info; - shared LogLevel s_logFileLevel; -} - -/// Sets the minimum log level to be printed. -void setLogLevel(LogLevel level) nothrow -{ - s_minLevel = level; -} - -LogLevel getLogLevel() -{ - return s_minLevel; -} - -/** - Logs a message. - - Params: - level = The log level for the logged message - fmt = See http://dlang.org/phobos/std_format.html#format-string -*/ -void logDebug(T...)(string fmt, lazy T args) nothrow { log(LogLevel.debug_, fmt, args); } -/// ditto -void logDiagnostic(T...)(string fmt, lazy T args) nothrow { log(LogLevel.diagnostic, fmt, args); } -/// ditto -void logInfo(T...)(string fmt, lazy T args) nothrow { log(LogLevel.info, fmt, args); } -/// ditto -void logWarn(T...)(string fmt, lazy T args) nothrow { log(LogLevel.warn, fmt, args); } -/// ditto -void logError(T...)(string fmt, lazy T args) nothrow { log(LogLevel.error, fmt, args); } - -/// ditto -void log(T...)(LogLevel level, string fmt, lazy T args) -nothrow { - if( level < s_minLevel ) return; - string pref; - final switch( level ){ - case LogLevel.debug_: pref = "trc"; break; - case LogLevel.diagnostic: pref = "dbg"; break; - case LogLevel.info: pref = "INF"; break; - case LogLevel.warn: pref = "WRN"; break; - case LogLevel.error: pref = "ERR"; break; - case LogLevel.fatal: pref = "FATAL"; break; - case LogLevel.none: assert(false); - } - - try { - auto txt = appender!string(); - txt.reserve(256); - formattedWrite(txt, fmt, args); - - auto threadid = () @trusted { return cast(ulong)cast(void*)Thread.getThis(); } (); - auto fiberid = () @trusted { return cast(ulong)cast(void*)Fiber.getThis(); } (); - threadid ^= threadid >> 32; - fiberid ^= fiberid >> 32; - - if (level >= s_minLevel) { - File output; - if (level == LogLevel.info) () @trusted { output = stdout; } (); - else () @trusted { output = stderr; } (); - if (output.isOpen) { - output.writeln(txt.data); - output.flush(); - } - } - } catch( Exception e ){ - // this is bad but what can we do.. - debug assert(false, e.msg); - } -} - -/// Specifies the log level for a particular log message. -enum LogLevel { - debug_, - diagnostic, - info, - warn, - error, - fatal, - none -} - diff --git a/source/dub/internal/vibecompat/data/json.d b/source/dub/internal/vibecompat/data/json.d index b55166a..aced940 100644 --- a/source/dub/internal/vibecompat/data/json.d +++ b/source/dub/internal/vibecompat/data/json.d @@ -869,7 +869,6 @@ skipWhitespace(range, line); version(JsonLineNumbers) { - import dub.internal.vibecompat.core.log; int curline = line ? *line : 0; } diff --git a/source/dub/logging.d b/source/dub/logging.d new file mode 100644 index 0000000..0ad1db4 --- /dev/null +++ b/source/dub/logging.d @@ -0,0 +1,279 @@ +/** + Handles all the console output of the Dub package manager, by providing useful + methods for handling colored text. The module also disables colors when stdout + and stderr are not a TTY in order to avoid ASCII escape sequences in piped + output. The module can autodetect and configure itself in this regard by + calling initLogging() at the beginning of the program. But, whether to color + text or not can also be set manually with printColorsInLog(bool). + + The output for the log levels error, warn and info is formatted like this: + + " " + '----------' + fixed width + + the "tag" part can be colored (most oftenly will be) and always has a fixed + width, which is defined as a const at the beginning of this module. + + The output for the log levels debug and diagnostic will be just the plain + string. + + There are some default tag string and color values for some logging levels: + - warn: "Warning", yellow bold + - error: "Error", red bold + + Actually, for error and warn levels, the tag color is fixed to the ones listed + above. + + Also, the default tag string for the info level is "" (the empty string) and + the default color is white (usually it's manually set when calling logInfo + with the wanted tag string, but this allows to just logInfo("text") without + having to worry about the tag if it's not needed). + + Usage: + After initializing the logging module with initLogging(), the functions + logDebug(..), logDiagnostic(..), logInfo(..), logWarning(..) and logError(..) + can be used to print log messages. Whether the messages are printed on stdout + or stderr depends on the log level (warning and error go to stderr). + The log(..) function can also be used. Check the signature and documentation + of the functions for more information. + + The minimum log level to print can be configured using setLogLevel(..), and + whether to color outputted text or not can be set with printColorsInLog(..). + + The color(str, color) function can be used to color text within a log + message, for instance like this: + + logInfo("Tag", Color.green, "My %s message", "colored".color(Color.red)) + + Copyright: © 2018 Giacomo De Lazzari + License: Subject to the terms of the MIT license, as written in the included LICENSE file. + Authors: Giacomo De Lazzari +*/ + +module dub.logging; + +import std.stdio; +import std.array; +import std.format; +import std.string; + +import dub.internal.colorize : fg; + +/** + An enum listing possible colors for terminal output, useful to set the color + of a tag +*/ +public alias Color = fg; + +/// The tag width in chars +private const int TAG_WIDTH = 12; + +/// Possible log levels supported +enum LogLevel { + debug_, + diagnostic, + info, + warn, + error, + none +} + +// The current minimum log level to be printed +private LogLevel _minLevel = LogLevel.info; + +/* + Whether to print text with colors or not, defaults to true but will be set + to false in initLogging() if stdout or stderr are not a TTY (which means the + output is probably being piped and we don't want ASCII escape chars in it) +*/ +private bool _printColors = true; + +/// Sets the minimum log level to be printed +void setLogLevel(LogLevel level) nothrow +{ + _minLevel = level; +} + +/// Gets the minimum log level to be printed +LogLevel getLogLevel() +{ + return _minLevel; +} + +/// Set whether to print colors or not +void printColorsInLog(bool enabled) +{ + _printColors = enabled; +} + +/** + Shorthand function to log a message with debug/diagnostic level, no tag string + or tag color required (since there will be no tag). + + Params: + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logDebug(T...)(string fmt, lazy T args) nothrow +{ + log(LogLevel.debug_, "", Color.init, fmt, args); +} + +/// ditto +void logDiagnostic(T...)(string fmt, lazy T args) nothrow +{ + log(LogLevel.diagnostic, "", Color.init, fmt, args); +} + +/** + Shorthand function to log a message with info level, with custom tag string + and tag color. + + Params: + tag = The string the tag at the beginning of the line should contain + tagColor = The color the tag string should have + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logInfo(T...)(string tag, Color tagColor, string fmt, lazy T args) nothrow +{ + log(LogLevel.info, tag, tagColor, fmt, args); +} + +/** + Shorthand function to log a message with info level, this version prints an + empty tag automatically (which is different from not having a tag - in this + case there will be an identation of TAG_WIDTH chars on the left anyway). + + Params: + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logInfo(T...)(string fmt, lazy T args) nothrow +{ + log(LogLevel.info, "", Color.init, fmt, args); +} + +/** + Shorthand function to log a message with warning level, with custom tag string. + The tag color is fixed to yellow. + + Params: + tag = The string the tag at the beginning of the line should contain + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logWarnTag(T...)(string tag, string fmt, lazy T args) nothrow +{ + log(LogLevel.warn, tag, Color.yellow, fmt, args); +} + +/** + Shorthand function to log a message with warning level, using the default + tag "Warning". The tag color is also fixed to yellow. + + Params: + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logWarn(T...)(string fmt, lazy T args) nothrow +{ + log(LogLevel.warn, "Warning", Color.yellow, fmt, args); +} + +/** + Shorthand function to log a message with error level, with custom tag string. + The tag color is fixed to red. + + Params: + tag = The string the tag at the beginning of the line should contain + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logErrorTag(T...)(string tag, string fmt, lazy T args) nothrow +{ + log(LogLevel.error, tag, Color.red, fmt, args); +} + +/** + Shorthand function to log a message with error level, using the default + tag "Error". The tag color is also fixed to red. + + Params: + level = The log level for the logged message + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void logError(T...)(string fmt, lazy T args) nothrow +{ + log(LogLevel.error, "Error", Color.red, fmt, args); +} + +/** + Log a message with the specified log level and with the specified tag string + and color. If the log level is debug or diagnostic, the tag is not printed + thus the tag string and tag color will be ignored. + + Params: + level = The log level for the logged message + tag = The string the tag at the beginning of the line should contain + tagColor = The color the tag string should have + fmt = See http://dlang.org/phobos/std_format.html#format-string +*/ +void log(T...)( + LogLevel level, + string tag, + Color tagColor, + string fmt, + lazy T args +) nothrow +{ + if (level < _minLevel) + return; + + auto hasTag = true; + if (level <= LogLevel.diagnostic) + hasTag = false; + + try + { + string result = format(fmt, args); + + if (hasTag) + result = tag.rightJustify(TAG_WIDTH, ' ').color(tagColor) ~ " " ~ result; + + import dub.internal.colorize : cwrite; + + File output = (level <= LogLevel.info) ? stdout : stderr; + + if (output.isOpen) + { + output.cwrite(result, "\n"); + output.flush(); + } + } + catch (Exception e) + { + debug assert(false, e.msg); + } +} + +/** + Colors the specified string with the specified color. The function is used to + print colored text within a log message. The function also checks whether + color output is enabled or disabled (when not outputting to a TTY) and, in the + last case, just returns the plain string. This allows to use it like so: + + logInfo("Tag", Color.green, "My %s log message", "colored".color(Color.red)); + + without worring whether or not colored output is enabled or not. +*/ +string color(const string str, const Color c = Color.init) +{ + import dub.internal.colorize; + + if (_printColors == true) + return dub.internal.colorize.color(str, c); + else + return str; +} diff --git a/source/dub/package_.d b/source/dub/package_.d index 40a8eda..71d019a 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -12,11 +12,11 @@ import dub.compilers.compiler; import dub.dependency; import dub.description; +import dub.logging; import dub.recipe.json; import dub.recipe.sdl; import dub.internal.utils; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.core.file; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 845f4ca..4244526 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -11,9 +11,9 @@ static import dub.dependency; import dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.package_; import std.algorithm : countUntil, filter, sort, canFind, remove; diff --git a/source/dub/packagesuppliers/fallback.d b/source/dub/packagesuppliers/fallback.d index 39a61e6..d7793e4 100644 --- a/source/dub/packagesuppliers/fallback.d +++ b/source/dub/packagesuppliers/fallback.d @@ -46,14 +46,14 @@ { import std.format : format; enum fallback = q{ - import dub.internal.vibecompat.core.log : logDiagnostic; + import dub.logging : logDebug; Exception firstEx; try return m_suppliers[0].ps.%1$s(args); catch (Exception e) { - logDiagnostic("Package supplier %%s failed with '%%s', trying fallbacks.", + logDebug("Package supplier %%s failed with '%%s', trying fallbacks.", m_suppliers[0].ps.description, e.msg); firstEx = e; } @@ -65,13 +65,13 @@ continue; try { - scope (success) logDiagnostic("Fallback %%s succeeded", pair.ps.description); + scope (success) logDebug("Fallback %%s succeeded", pair.ps.description); return pair.ps.%1$s(args); } catch (Exception e) { pair.failTime = now; - logDiagnostic("Fallback package supplier %%s failed with '%%s'.", + logDebug("Fallback package supplier %%s failed with '%%s'.", pair.ps.description, e.msg); } } diff --git a/source/dub/packagesuppliers/filesystem.d b/source/dub/packagesuppliers/filesystem.d index fa752bd..1e53f00 100644 --- a/source/dub/packagesuppliers/filesystem.d +++ b/source/dub/packagesuppliers/filesystem.d @@ -9,7 +9,8 @@ the form "[package name]-[version].zip". */ class FileSystemPackageSupplier : PackageSupplier { - import dub.internal.vibecompat.core.log; + import dub.logging; + version (Have_vibe_core) import dub.internal.vibecompat.inet.path : toNativeString; import std.exception : enforce; private { diff --git a/source/dub/packagesuppliers/maven.d b/source/dub/packagesuppliers/maven.d index 387269d..8d85b9d 100644 --- a/source/dub/packagesuppliers/maven.d +++ b/source/dub/packagesuppliers/maven.d @@ -11,8 +11,8 @@ class MavenRegistryPackageSupplier : PackageSupplier { import dub.internal.utils : retryDownload, HTTPStatusException; import dub.internal.vibecompat.data.json : serializeToJson; - import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.url : URL; + import dub.logging; import std.datetime : Clock, Duration, hours, SysTime, UTC; diff --git a/source/dub/packagesuppliers/registry.d b/source/dub/packagesuppliers/registry.d index 83b01e5..2e132ad 100644 --- a/source/dub/packagesuppliers/registry.d +++ b/source/dub/packagesuppliers/registry.d @@ -12,9 +12,9 @@ */ class RegistryPackageSupplier : PackageSupplier { import dub.internal.utils : download, retryDownload, HTTPStatusException; - import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json : parseJson, parseJsonString, serializeToJson; import dub.internal.vibecompat.inet.url : URL; + import dub.logging; import std.uri : encodeComponent; import std.datetime : Clock, Duration, hours, SysTime, UTC; @@ -131,4 +131,3 @@ .array; } } - diff --git a/source/dub/project.d b/source/dub/project.d index 2e18433..4b514a2 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -13,9 +13,9 @@ import dub.generators.generator; import dub.internal.utils; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.data.json; import dub.internal.vibecompat.inet.path; +import dub.logging; import dub.package_; import dub.packagemanager; import dub.recipe.selection; diff --git a/source/dub/recipe/packagerecipe.d b/source/dub/recipe/packagerecipe.d index 14bb983..8509d12 100644 --- a/source/dub/recipe/packagerecipe.d +++ b/source/dub/recipe/packagerecipe.d @@ -10,9 +10,9 @@ import dub.compilers.compiler; import dub.compilers.utils : warnOnSpecialCompilerFlags; import dub.dependency; +import dub.logging; import dub.internal.vibecompat.core.file; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; import std.algorithm : findSplit, sort; diff --git a/source/dub/recipe/sdl.d b/source/dub/recipe/sdl.d index 31ac460..779beb8 100644 --- a/source/dub/recipe/sdl.d +++ b/source/dub/recipe/sdl.d @@ -9,8 +9,8 @@ import dub.compilers.compiler; import dub.dependency; +import dub.logging; import dub.internal.sdlang; -import dub.internal.vibecompat.core.log; import dub.internal.vibecompat.inet.path; import dub.recipe.packagerecipe;