Newer
Older
dub_jkp / source / dub / logging.d
@Giacomo De Lazzari Giacomo De Lazzari on 28 Jul 2022 8 KB New logging module - initial functionality
/**
	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:

  "       <tag> <text>"
   '----------'
   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;
}