make splitting of log lines configurable
- it might make sense to have newlines in a log message (e.g. when printing a backtrace) and they shouldn't be split into multiple log messages, e.g. when using syslog - the splitting was fixed to work with more than one LF (cherry picked from commit d14ce3dea1b98227dcd116acc6b175c56d9a1fb0)
This commit is contained in:
parent
7e2d1dd038
commit
dc9002de0e
|
@ -196,6 +196,12 @@ struct LogLine {
|
||||||
class Logger {
|
class Logger {
|
||||||
LogLevel minLevel = LogLevel.min;
|
LogLevel minLevel = LogLevel.min;
|
||||||
|
|
||||||
|
/** Whether the logger can handle multiple lines in a single beginLine/endLine.
|
||||||
|
|
||||||
|
By default log text with newlines gets split into multiple log lines.
|
||||||
|
*/
|
||||||
|
protected bool multilineLogger = false;
|
||||||
|
|
||||||
private {
|
private {
|
||||||
LogLine m_curLine;
|
LogLine m_curLine;
|
||||||
Appender!string m_curLineText;
|
Appender!string m_curLineText;
|
||||||
|
@ -204,7 +210,7 @@ class Logger {
|
||||||
final bool acceptsLevel(LogLevel value) nothrow pure @safe { return value >= this.minLevel; }
|
final bool acceptsLevel(LogLevel value) nothrow pure @safe { return value >= this.minLevel; }
|
||||||
|
|
||||||
/** Legacy logging interface relying on dynamic memory allocation.
|
/** Legacy logging interface relying on dynamic memory allocation.
|
||||||
|
|
||||||
Override `beginLine`, `put`, `endLine` instead for a more efficient and
|
Override `beginLine`, `put`, `endLine` instead for a more efficient and
|
||||||
possibly allocation-free implementation.
|
possibly allocation-free implementation.
|
||||||
*/
|
*/
|
||||||
|
@ -830,14 +836,23 @@ private struct LogOutputRange {
|
||||||
|
|
||||||
void put(scope const(char)[] text)
|
void put(scope const(char)[] text)
|
||||||
{
|
{
|
||||||
import std.string : indexOf;
|
if (text.empty)
|
||||||
auto idx = text.indexOf('\n');
|
return;
|
||||||
if (idx >= 0) {
|
|
||||||
logger.put(text[0 .. idx]);
|
if (logger.multilineLogger)
|
||||||
logger.endLine();
|
logger.put(text);
|
||||||
logger.beginLine(info);
|
else
|
||||||
logger.put(text[idx+1 .. $]);
|
{
|
||||||
} else logger.put(text);
|
auto rng = text.splitter('\n');
|
||||||
|
logger.put(rng.front);
|
||||||
|
rng.popFront;
|
||||||
|
foreach (line; rng)
|
||||||
|
{
|
||||||
|
logger.endLine();
|
||||||
|
logger.beginLine(info);
|
||||||
|
logger.put(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void put(char ch) @trusted { put((&ch)[0 .. 1]); }
|
void put(char ch) @trusted { put((&ch)[0 .. 1]); }
|
||||||
|
@ -862,6 +877,31 @@ private version (Windows) {
|
||||||
extern(System) HANDLE GetStdHandle(DWORD nStdHandle);
|
extern(System) HANDLE GetStdHandle(DWORD nStdHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
static class TestLogger : Logger
|
||||||
|
{
|
||||||
|
string[] lines;
|
||||||
|
override void beginLine(ref LogLine msg) { lines.length += 1; }
|
||||||
|
override void put(scope const(char)[] text) { lines[$-1] ~= text; }
|
||||||
|
override void endLine() { }
|
||||||
|
}
|
||||||
|
auto logger = new TestLogger;
|
||||||
|
auto ll = (cast(shared(Logger))logger).lock();
|
||||||
|
auto rng = LogOutputRange(ll, __FILE__, __LINE__, LogLevel.info);
|
||||||
|
rng.formattedWrite("text\nwith\nnewlines");
|
||||||
|
rng.finalize();
|
||||||
|
|
||||||
|
assert(logger.lines == ["text", "with", "newlines"]);
|
||||||
|
logger.lines = null;
|
||||||
|
logger.multilineLogger = true;
|
||||||
|
|
||||||
|
rng = LogOutputRange(ll, __FILE__, __LINE__, LogLevel.info);
|
||||||
|
rng.formattedWrite("text\nwith\nnewlines");
|
||||||
|
rng.finalize();
|
||||||
|
assert(logger.lines == ["text\nwith\nnewlines"]);
|
||||||
|
}
|
||||||
|
|
||||||
unittest { // make sure the default logger doesn't allocate/is usable within finalizers
|
unittest { // make sure the default logger doesn't allocate/is usable within finalizers
|
||||||
bool destroyed = false;
|
bool destroyed = false;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue