summaryrefslogtreecommitdiff
path: root/libphobos/src
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-08-25 19:04:50 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2022-08-28 00:16:34 +0200
commitb7a586beae1027ea0c82411637920a5032d1dedf (patch)
tree4c41a84c4113e90cd0caaa7aa9925f4232dc22d5 /libphobos/src
parentcace77f4fb8df18c01dfdf9040cc944eedef1147 (diff)
downloadgcc-b7a586beae1027ea0c82411637920a5032d1dedf.tar.gz
d: Merge upstream dmd 817610b16d, phobos b578dfad9
D front-end changes: - Import latest bug fixes to mainline. Phobos changes: - Import latest bug fixes to mainline. - std.logger module has been moved out of experimental. - Removed std.experimental.typecons module. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 817610b16d. * d-ctfloat.cc (CTFloat::parse): Update for new front-end interface. * d-lang.cc (d_parse_file): Likewise. * expr.cc (ExprVisitor::visit (AssignExp *)): Remove handling of array assignments to non-trivial static and dynamic arrays. * runtime.def (ARRAYASSIGN): Remove. (ARRAYASSIGN_L): Remove. (ARRAYASSIGN_R): Remove. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 817610b16d. * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add core/internal/array/arrayassign.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos b578dfad9. * src/Makefile.am (PHOBOS_DSOURCES): Remove std/experimental/typecons.d. Add std/logger package. * src/Makefile.in: Regenerate.
Diffstat (limited to 'libphobos/src')
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/Makefile.am13
-rw-r--r--libphobos/src/Makefile.in33
-rw-r--r--libphobos/src/index.dd25
-rw-r--r--libphobos/src/std/algorithm/iteration.d15
-rw-r--r--libphobos/src/std/algorithm/searching.d2
-rw-r--r--libphobos/src/std/array.d6
-rw-r--r--libphobos/src/std/bigint.d2
-rw-r--r--libphobos/src/std/complex.d25
-rw-r--r--libphobos/src/std/container/rbtree.d2
-rw-r--r--libphobos/src/std/experimental/logger/core.d3058
-rw-r--r--libphobos/src/std/experimental/logger/filelogger.d281
-rw-r--r--libphobos/src/std/experimental/logger/multilogger.d209
-rw-r--r--libphobos/src/std/experimental/logger/nulllogger.d50
-rw-r--r--libphobos/src/std/experimental/logger/package.d179
-rw-r--r--libphobos/src/std/experimental/typecons.d1083
-rw-r--r--libphobos/src/std/getopt.d2
-rw-r--r--libphobos/src/std/json.d28
-rw-r--r--libphobos/src/std/logger/core.d3049
-rw-r--r--libphobos/src/std/logger/filelogger.d272
-rw-r--r--libphobos/src/std/logger/multilogger.d200
-rw-r--r--libphobos/src/std/logger/nulllogger.d41
-rw-r--r--libphobos/src/std/logger/package.d168
-rw-r--r--libphobos/src/std/meta.d32
-rw-r--r--libphobos/src/std/package.d1
-rw-r--r--libphobos/src/std/random.d2
-rw-r--r--libphobos/src/std/regex/package.d54
-rw-r--r--libphobos/src/std/string.d9
-rw-r--r--libphobos/src/std/typecons.d4
29 files changed, 3934 insertions, 4913 deletions
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index 1f0cfbf3e29..c8a37714303 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
-5748ca43fd5c3e31ce7a8511f542b67e5d5a3dc6
+b578dfad94770574d7e522557a77276c35943daa
The first line of this file holds the git revision number of the last
merge done from the dlang/phobos repository.
diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am
index da7a2004ff8..2413024e162 100644
--- a/libphobos/src/Makefile.am
+++ b/libphobos/src/Makefile.am
@@ -127,11 +127,10 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
std/experimental/logger/core.d std/experimental/logger/filelogger.d \
std/experimental/logger/multilogger.d \
std/experimental/logger/nulllogger.d std/experimental/logger/package.d \
- std/experimental/typecons.d std/file.d std/format/internal/floats.d \
- std/format/internal/read.d std/format/internal/write.d \
- std/format/package.d std/format/read.d std/format/spec.d \
- std/format/write.d std/functional.d std/getopt.d std/int128.d \
- std/internal/attributes.d std/internal/cstring.d \
+ std/file.d std/format/internal/floats.d std/format/internal/read.d \
+ std/format/internal/write.d std/format/package.d std/format/read.d \
+ std/format/spec.d std/format/write.d std/functional.d std/getopt.d \
+ std/int128.d std/internal/attributes.d std/internal/cstring.d \
std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
std/internal/memory.d std/internal/scopebuffer.d \
@@ -139,7 +138,9 @@ PHOBOS_DSOURCES = etc/c/curl.d etc/c/zlib.d std/algorithm/comparison.d \
std/internal/test/uda.d std/internal/unicode_comp.d \
std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \
std/internal/unicode_norm.d std/internal/unicode_tables.d \
- std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \
+ std/internal/windows/advapi32.d std/json.d std/logger/core.d \
+ std/logger/filelogger.d std/logger/multilogger.d \
+ std/logger/nulllogger.d std/logger/package.d std/math/algebraic.d \
std/math/constants.d std/math/exponential.d std/math/hardware.d \
std/math/operations.d std/math/package.d std/math/remainder.d \
std/math/rounding.d std/math/traits.d std/math/trigonometry.d \
diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in
index 6f58fee01ac..562a428412f 100644
--- a/libphobos/src/Makefile.in
+++ b/libphobos/src/Makefile.in
@@ -218,7 +218,6 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/multilogger.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/package.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/floats.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.lo \
@@ -246,7 +245,11 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_tables.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.lo \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/json.lo std/logger/core.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/filelogger.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/multilogger.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/nulllogger.lo \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/package.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/algebraic.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.lo \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/exponential.lo \
@@ -589,11 +592,10 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/core.d std/experimental/logger/filelogger.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/multilogger.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/logger/nulllogger.d std/experimental/logger/package.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/experimental/typecons.d std/file.d std/format/internal/floats.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/read.d std/format/internal/write.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/package.d std/format/read.d std/format/spec.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/write.d std/functional.d std/getopt.d std/int128.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/attributes.d std/internal/cstring.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/file.d std/format/internal/floats.d std/format/internal/read.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/internal/write.d std/format/package.d std/format/read.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/format/spec.d std/format/write.d std/functional.d std/getopt.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/int128.d std/internal/attributes.d std/internal/cstring.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/biguintcore.d std/internal/math/biguintnoasm.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/math/errorfunction.d std/internal/math/gammafunction.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/memory.d std/internal/scopebuffer.d \
@@ -601,7 +603,9 @@ libgphobos_la_LINK = $(LIBTOOL) --tag=D $(libgphobos_la_LIBTOOLFLAGS) \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/test/uda.d std/internal/unicode_comp.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_decomp.d std/internal/unicode_grapheme.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/unicode_norm.d std/internal/unicode_tables.d \
-@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/math/algebraic.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/internal/windows/advapi32.d std/json.d std/logger/core.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/filelogger.d std/logger/multilogger.d \
+@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/logger/nulllogger.d std/logger/package.d std/math/algebraic.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/constants.d std/math/exponential.d std/math/hardware.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/operations.d std/math/package.d std/math/remainder.d \
@ENABLE_LIBDRUNTIME_ONLY_FALSE@ std/math/rounding.d std/math/traits.d std/math/trigonometry.d \
@@ -830,7 +834,6 @@ std/experimental/logger/nulllogger.lo: \
std/experimental/logger/$(am__dirstamp)
std/experimental/logger/package.lo: \
std/experimental/logger/$(am__dirstamp)
-std/experimental/typecons.lo: std/experimental/$(am__dirstamp)
std/file.lo: std/$(am__dirstamp)
std/format/internal/$(am__dirstamp):
@$(MKDIR_P) std/format/internal
@@ -879,6 +882,14 @@ std/internal/windows/$(am__dirstamp):
std/internal/windows/advapi32.lo: \
std/internal/windows/$(am__dirstamp)
std/json.lo: std/$(am__dirstamp)
+std/logger/$(am__dirstamp):
+ @$(MKDIR_P) std/logger
+ @: > std/logger/$(am__dirstamp)
+std/logger/core.lo: std/logger/$(am__dirstamp)
+std/logger/filelogger.lo: std/logger/$(am__dirstamp)
+std/logger/multilogger.lo: std/logger/$(am__dirstamp)
+std/logger/nulllogger.lo: std/logger/$(am__dirstamp)
+std/logger/package.lo: std/logger/$(am__dirstamp)
std/math/$(am__dirstamp):
@$(MKDIR_P) std/math
@: > std/math/$(am__dirstamp)
@@ -994,6 +1005,8 @@ mostlyclean-compile:
-rm -f std/internal/test/*.lo
-rm -f std/internal/windows/*.$(OBJEXT)
-rm -f std/internal/windows/*.lo
+ -rm -f std/logger/*.$(OBJEXT)
+ -rm -f std/logger/*.lo
-rm -f std/math/*.$(OBJEXT)
-rm -f std/math/*.lo
-rm -f std/net/*.$(OBJEXT)
@@ -1033,6 +1046,7 @@ clean-libtool:
-rm -rf std/internal/math/.libs std/internal/math/_libs
-rm -rf std/internal/test/.libs std/internal/test/_libs
-rm -rf std/internal/windows/.libs std/internal/windows/_libs
+ -rm -rf std/logger/.libs std/logger/_libs
-rm -rf std/math/.libs std/math/_libs
-rm -rf std/net/.libs std/net/_libs
-rm -rf std/range/.libs std/range/_libs
@@ -1162,6 +1176,7 @@ distclean-generic:
-rm -f std/internal/math/$(am__dirstamp)
-rm -f std/internal/test/$(am__dirstamp)
-rm -f std/internal/windows/$(am__dirstamp)
+ -rm -f std/logger/$(am__dirstamp)
-rm -f std/math/$(am__dirstamp)
-rm -f std/net/$(am__dirstamp)
-rm -f std/range/$(am__dirstamp)
diff --git a/libphobos/src/index.dd b/libphobos/src/index.dd
index cbc173d891d..45c248e1178 100644
--- a/libphobos/src/index.dd
+++ b/libphobos/src/index.dd
@@ -459,6 +459,19 @@ $(BOOKTABLE ,
$(TDNW $(MREF core,simd))
$(TD SIMD intrinsics)
)
+ $(LEADINGROW Logging)
+ $(TR
+ $(TDNW
+ $(MREF std,logger)$(BR)
+ $(MREF std,logger,core)$(BR)
+ $(MREF std,logger,filelogger)$(BR)
+ $(MREF std,logger,multilogger)$(BR)
+ $(MREF std,logger,nulllogger)$(BR)
+ )
+ $(TD
+ Logging.
+ )
+ )
$(COMMENT
$(LEADINGROW Undocumented modules (intentionally omitted).)
@@ -509,18 +522,6 @@ $(COMMENT
Deprecated modules.
)
)
- $(TR
- $(TDNW
- $(MREF std,experimental,logger)$(BR)
- $(MREF std,experimental,logger,core)$(BR)
- $(MREF std,experimental,logger,filelogger)$(BR)
- $(MREF std,experimental,logger,multilogger)$(BR)
- $(MREF std,experimental,logger,nulllogger)$(BR)
- )
- $(TD
- Experimental modules.
- )
- )
)
)
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
index 300a8978fe5..3e828cee9bb 100644
--- a/libphobos/src/std/algorithm/iteration.d
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -1798,7 +1798,7 @@ if (isInputRange!R)
assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
interface I {}
- class C : I { override size_t toHash() const nothrow @safe { return 0; } }
+ static class C : I { override size_t toHash() const nothrow @safe { return 0; } }
const C[] a4 = [new const C()];
auto g4 = a4.group!"a is b";
assert(g4.front[1] == 1);
@@ -2255,25 +2255,26 @@ if (isForwardRange!Range)
import std.algorithm.comparison : equal;
size_t popCount = 0;
- class RefFwdRange
+ static class RefFwdRange
{
int[] impl;
+ size_t* pcount;
@safe nothrow:
- this(int[] data) { impl = data; }
+ this(int[] data, size_t* pcount) { impl = data; this.pcount = pcount; }
@property bool empty() { return impl.empty; }
@property auto ref front() { return impl.front; }
void popFront()
{
impl.popFront();
- popCount++;
+ (*pcount)++;
}
- @property auto save() { return new RefFwdRange(impl); }
+ @property auto save() { return new RefFwdRange(impl, pcount); }
}
static assert(isForwardRange!RefFwdRange);
- auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]);
+ auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9], &popCount);
auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2));
auto outerSave1 = groups.save;
@@ -6058,7 +6059,7 @@ if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
import std.algorithm.comparison : equal;
// Test by-reference separator
- class RefSep {
+ static class RefSep {
@safe:
string _impl;
this(string s) { _impl = s; }
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
index daa4b99045e..870b1b40219 100644
--- a/libphobos/src/std/algorithm/searching.d
+++ b/libphobos/src/std/algorithm/searching.d
@@ -13,7 +13,7 @@ $(T2 any,
`any!"a > 0"([1, 2, -3, -4])` returns `true` because at least one
element is positive)
$(T2 balancedParens,
- `balancedParens("((1 + 1) / 2)")` returns `true` because the
+ `balancedParens("((1 + 1) / 2)", '(', ')')` returns `true` because the
string has balanced parentheses.)
$(T2 boyerMooreFinder,
`find("hello world", boyerMooreFinder("or"))` returns `"orld"`
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
index b86e0f99225..f48602e4cd6 100644
--- a/libphobos/src/std/array.d
+++ b/libphobos/src/std/array.d
@@ -2297,7 +2297,7 @@ if (isInputRange!RoR &&
// https://issues.dlang.org/show_bug.cgi?id=10895
@safe unittest
{
- class A
+ static class A
{
string name;
alias name this;
@@ -4376,8 +4376,8 @@ unittest
return app[];
}
- class C {}
- struct S { const(C) c; }
+ static class C {}
+ static struct S { const(C) c; }
S[] s = [ S(new C) ];
auto t = fastCopy(s); // Does not compile
diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d
index b2fcc07142b..33d0eae7ced 100644
--- a/libphobos/src/std/bigint.d
+++ b/libphobos/src/std/bigint.d
@@ -1541,7 +1541,7 @@ Returns:
number in upper case.
*/
-string toHex(const(BigInt) x) @safe
+string toHex(const(BigInt) x) pure @safe
{
import std.array : appender;
auto outbuff = appender!string();
diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d
index 5a155387adc..3b860fe476b 100644
--- a/libphobos/src/std/complex.d
+++ b/libphobos/src/std/complex.d
@@ -478,6 +478,20 @@ if (isFloatingPoint!T)
}
return this;
}
+
+ /** Returns a complex number instance that correponds in size and in ABI
+ to the associated C compiler's `_Complex` type.
+ */
+ auto toNative()
+ {
+ import core.stdc.config : c_complex_float, c_complex_double, c_complex_real;
+ static if (is(T == float))
+ return c_complex_float(re, im);
+ else static if (is(T == double))
+ return c_complex_double(re, im);
+ else
+ return c_complex_real(re, im);
+ }
}
@safe pure nothrow unittest
@@ -1910,3 +1924,14 @@ Complex!T pow(T)(const T x, Complex!T n) @trusted pure nothrow @nogc
}
}}
}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ static foreach (T; AliasSeq!(float, double, real))
+ {{
+ auto c = Complex!T(123, 456);
+ auto n = c.toNative();
+ assert(c.re == n.re && c.im == n.im);
+ }}
+}
diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d
index 622dee4df2f..9bd8d27c065 100644
--- a/libphobos/src/std/container/rbtree.d
+++ b/libphobos/src/std/container/rbtree.d
@@ -2055,7 +2055,7 @@ if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init
}
//Combinations not in examples.
-@safe pure unittest
+@system pure unittest
{
auto rbt1 = redBlackTree!(true, string)("hello", "hello");
auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3);
diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d
index f3c69324ba1..a30ae580881 100644
--- a/libphobos/src/std/experimental/logger/core.d
+++ b/libphobos/src/std/experimental/logger/core.d
@@ -1,3049 +1,13 @@
-// Written in the D programming language.
/**
-Source: $(PHOBOSSRC std/experimental/logger/core.d)
-*/
-module std.experimental.logger.core;
-
-import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder;
-import core.sync.mutex : Mutex;
-import std.datetime.date : DateTime;
-import std.datetime.systime : Clock, SysTime;
-import std.range.primitives;
-import std.traits;
-
-import std.experimental.logger.filelogger;
-
-/** This functions is used at runtime to determine if a `LogLevel` is
-active. The same previously defined version statements are used to disable
-certain levels. Again the version statements are associated with a compile
-unit and can therefore not disable logging in other compile units.
-pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc
-*/
-bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
- LogLevel globalLL, lazy bool condition = true) @safe
-{
- return ll >= globalLL
- && ll >= loggerLL
- && ll != LogLevel.off
- && globalLL != LogLevel.off
- && loggerLL != LogLevel.off
- && condition;
-}
-
-/* This function formates a `SysTime` into an `OutputRange`.
-
-The `SysTime` is formatted similar to
-$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part.
-The fractional second part is in milliseconds and is always 3 digits.
-*/
-void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time)
-if (isOutputRange!(OutputRange,string))
-{
- import std.format.write : formattedWrite;
-
- const auto dt = cast(DateTime) time;
- const auto fsec = time.fracSecs.total!"msecs";
-
- formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
- dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
- fsec);
-}
-
-/** This function logs data.
-
-In order for the data to be processed, the `LogLevel` of the log call must
-be greater or equal to the `LogLevel` of the `sharedLog` and the
-`defaultLogLevel`; additionally the condition passed must be `true`.
-
-Params:
- ll = The `LogLevel` used by this log call.
- condition = The condition must be `true` for the data to be logged.
- args = The data that should be logged.
-
-Example:
---------------------
-log(LogLevel.warning, true, "Hello World", 3.1415);
---------------------
-*/
-void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll,
- lazy bool condition, lazy A args)
-if (args.length != 1)
-{
- stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
- (ll, condition, args);
-}
-
-/// Ditto
-void log(T, string moduleName = __MODULE__)(const LogLevel ll,
- lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__)
-{
- stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName,
- prettyFuncName);
-}
-
-/** This function logs data.
-
-In order for the data to be processed the `LogLevel` of the log call must
-be greater or equal to the `LogLevel` of the `sharedLog`.
-
-Params:
- ll = The `LogLevel` used by this log call.
- args = The data that should be logged.
-
-Example:
---------------------
-log(LogLevel.warning, "Hello World", 3.1415);
---------------------
-*/
-void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
-if (args.length > 1 && !is(Unqual!(A[0]) : bool))
-{
- stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
- (ll, args);
-}
-
-/// Ditto
-void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg,
- int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__)
-{
- stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName,
- moduleName);
-}
-
-/** This function logs data.
-
-In order for the data to be processed the `LogLevel` of the
-`sharedLog` must be greater or equal to the `defaultLogLevel`
-add the condition passed must be `true`.
-
-Params:
- condition = The condition must be `true` for the data to be logged.
- args = The data that should be logged.
-
-Example:
---------------------
-log(true, "Hello World", 3.1415);
---------------------
-*/
-void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
-if (args.length != 1)
-{
- stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
- (stdThreadLocalLog.logLevel, condition, args);
-}
-
-/// Ditto
-void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg,
- int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__)
-{
- stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel,
- condition, arg, line, file, funcName, prettyFuncName);
-}
-
-/** This function logs data.
-
-In order for the data to be processed the `LogLevel` of the
-`sharedLog` must be greater or equal to the `defaultLogLevel`.
-
-Params:
- args = The data that should be logged.
-
-Example:
---------------------
-log("Hello World", 3.1415);
---------------------
-*/
-void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy A args)
-if ((args.length > 1 && !is(Unqual!(A[0]) : bool)
- && !is(Unqual!(A[0]) == LogLevel))
- || args.length == 0)
-{
- stdThreadLocalLog.log!(line, file, funcName,
- prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args);
-}
-
-void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__)
-{
- stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file,
- funcName, prettyFuncName, moduleName);
-}
-
-/** This function logs data in a `printf`-style manner.
-
-In order for the data to be processed the `LogLevel` of the log call must
-be greater or equal to the `LogLevel` of the `sharedLog` and the
-`defaultLogLevel` additionally the condition passed must be `true`.
-
-Params:
- ll = The `LogLevel` used by this log call.
- condition = The condition must be `true` for the data to be logged.
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-logf(LogLevel.warning, true, "Hello World %f", 3.1415);
---------------------
-*/
-void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll,
- lazy bool condition, lazy string msg, lazy A args)
-{
- stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
- (ll, condition, msg, args);
-}
-
-/** This function logs data in a `printf`-style manner.
-
-In order for the data to be processed the `LogLevel` of the log call must
-be greater or equal to the `LogLevel` of the `sharedLog` and the
-`defaultLogLevel`.
-
-Params:
- ll = The `LogLevel` used by this log call.
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-logf(LogLevel.warning, "Hello World %f", 3.1415);
---------------------
-*/
-void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg,
- lazy A args)
-{
- stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
- (ll, msg, args);
-}
-
-/** This function logs data in a `printf`-style manner.
-
-In order for the data to be processed the `LogLevel` of the log call must
-be greater or equal to the `defaultLogLevel` additionally the condition
-passed must be `true`.
-
-Params:
- condition = The condition must be `true` for the data to be logged.
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-logf(true, "Hello World %f", 3.1415);
---------------------
-*/
-void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition,
- lazy string msg, lazy A args)
-{
- stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
- (stdThreadLocalLog.logLevel, condition, msg, args);
-}
-
-/** This function logs data in a `printf`-style manner.
-
-In order for the data to be processed the `LogLevel` of the log call must
-be greater or equal to the `defaultLogLevel`.
-
-Params:
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-logf("Hello World %f", 3.1415);
---------------------
-*/
-void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
-{
- stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName)
- (stdThreadLocalLog.logLevel, msg, args);
-}
-
-/** This template provides the global log functions with the `LogLevel`
-is encoded in the function name.
-
-The aliases following this template create the public names of these log
-functions.
-*/
-template defaultLogFunction(LogLevel ll)
-{
- void defaultLogFunction(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy A args)
- if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
- {
- stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
- prettyFuncName, moduleName)(args);
- }
-
- void defaultLogFunction(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
- {
- stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
- prettyFuncName, moduleName)(condition, args);
- }
-}
-
-/** This function logs data to the `stdThreadLocalLog`, optionally depending
-on a condition.
-
-In order for the resulting log message to be logged the `LogLevel` must
-be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and
-must be greater or equal than the global `LogLevel`.
-Additionally the `LogLevel` must be greater or equal than the `LogLevel`
-of the `stdSharedLogger`.
-If a condition is given, it must evaluate to `true`.
-
-Params:
- condition = The condition must be `true` for the data to be logged.
- args = The data that should be logged.
-
-Example:
---------------------
-trace(1337, "is number");
-info(1337, "is number");
-error(1337, "is number");
-critical(1337, "is number");
-fatal(1337, "is number");
-trace(true, 1337, "is number");
-info(false, 1337, "is number");
-error(true, 1337, "is number");
-critical(false, 1337, "is number");
-fatal(true, 1337, "is number");
---------------------
-*/
-alias trace = defaultLogFunction!(LogLevel.trace);
-/// Ditto
-alias info = defaultLogFunction!(LogLevel.info);
-/// Ditto
-alias warning = defaultLogFunction!(LogLevel.warning);
-/// Ditto
-alias error = defaultLogFunction!(LogLevel.error);
-/// Ditto
-alias critical = defaultLogFunction!(LogLevel.critical);
-/// Ditto
-alias fatal = defaultLogFunction!(LogLevel.fatal);
-
-/** This template provides the global `printf`-style log functions with
-the `LogLevel` is encoded in the function name.
-
-The aliases following this template create the public names of the log
-functions.
-*/
-template defaultLogFunctionf(LogLevel ll)
-{
- void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
- {
- stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
- prettyFuncName, moduleName)(msg, args);
- }
-
- void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition,
- lazy string msg, lazy A args)
- {
- stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
- prettyFuncName, moduleName)(condition, msg, args);
- }
-}
-
-/** This function logs data to the `sharedLog` in a `printf`-style
-manner.
-
-In order for the resulting log message to be logged the `LogLevel` must
-be greater or equal than the `LogLevel` of the `sharedLog` and
-must be greater or equal than the global `LogLevel`.
-Additionally the `LogLevel` must be greater or equal than the `LogLevel`
-of the `stdSharedLogger`.
-
-Params:
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-tracef("is number %d", 1);
-infof("is number %d", 2);
-errorf("is number %d", 3);
-criticalf("is number %d", 4);
-fatalf("is number %d", 5);
---------------------
-
-The second version of the function logs data to the `sharedLog` in a $(D
-printf)-style manner.
-
-In order for the resulting log message to be logged the `LogLevel` must
-be greater or equal than the `LogLevel` of the `sharedLog` and
-must be greater or equal than the global `LogLevel`.
-Additionally the `LogLevel` must be greater or equal than the `LogLevel`
-of the `stdSharedLogger`.
-
-Params:
- condition = The condition must be `true` for the data to be logged.
- msg = The `printf`-style string.
- args = The data that should be logged.
-
-Example:
---------------------
-tracef(false, "is number %d", 1);
-infof(false, "is number %d", 2);
-errorf(true, "is number %d", 3);
-criticalf(true, "is number %d", 4);
-fatalf(someFunct(), "is number %d", 5);
---------------------
-*/
-alias tracef = defaultLogFunctionf!(LogLevel.trace);
-/// Ditto
-alias infof = defaultLogFunctionf!(LogLevel.info);
-/// Ditto
-alias warningf = defaultLogFunctionf!(LogLevel.warning);
-/// Ditto
-alias errorf = defaultLogFunctionf!(LogLevel.error);
-/// Ditto
-alias criticalf = defaultLogFunctionf!(LogLevel.critical);
-/// Ditto
-alias fatalf = defaultLogFunctionf!(LogLevel.fatal);
-
-private struct MsgRange
-{
- import std.traits : isSomeString, isSomeChar;
-
- private Logger log;
-
- this(Logger log) @safe
- {
- this.log = log;
- }
-
- void put(T)(T msg) @safe
- if (isSomeString!T)
- {
- log.logMsgPart(msg);
- }
-
- void put(dchar elem) @safe
- {
- import std.utf : encode;
- char[4] buffer;
- size_t len = encode(buffer, elem);
- log.logMsgPart(buffer[0 .. len]);
- }
-}
-
-private void formatString(A...)(MsgRange oRange, A args)
-{
- import std.format.write : formattedWrite;
-
- foreach (arg; args)
- {
- formattedWrite(oRange, "%s", arg);
- }
-}
-
-@system unittest
-{
- void dummy() @safe
- {
- auto tl = new TestLogger();
- auto dst = MsgRange(tl);
- formatString(dst, "aaa", "bbb");
- }
-
- dummy();
-}
-
-/**
-There are eight usable logging level. These level are $(I all), $(I trace),
-$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
-If a log function with `LogLevel.fatal` is called the shutdown handler of
-that logger is called.
-*/
-enum LogLevel : ubyte
-{
- all = 1, /** Lowest possible assignable `LogLevel`. */
- trace = 32, /** `LogLevel` for tracing the execution of the program. */
- info = 64, /** This level is used to display information about the
- program. */
- warning = 96, /** warnings about the program should be displayed with this
- level. */
- error = 128, /** Information about errors should be logged with this
- level.*/
- critical = 160, /** Messages that inform about critical errors should be
- logged with this level. */
- fatal = 192, /** Log messages that describe fatal errors should use this
- level. */
- off = ubyte.max /** Highest possible `LogLevel`. */
-}
-
-/** This class is the base of every logger. In order to create a new kind of
-logger a deriving class needs to implement the `writeLogMsg` method. By
-default this is not thread-safe.
-
-It is also possible to `override` the three methods `beginLogMsg`,
-`logMsgPart` and `finishLogMsg` together, this option gives more
-flexibility.
-*/
-abstract class Logger
-{
- import std.array : appender, Appender;
- import std.concurrency : thisTid, Tid;
-
- /** LogEntry is a aggregation combining all information associated
- with a log message. This aggregation will be passed to the method
- writeLogMsg.
- */
- protected struct LogEntry
- {
- /// the filename the log function was called from
- string file;
- /// the line number the log function was called from
- int line;
- /// the name of the function the log function was called from
- string funcName;
- /// the pretty formatted name of the function the log function was
- /// called from
- string prettyFuncName;
- /// the name of the module the log message is coming from
- string moduleName;
- /// the `LogLevel` associated with the log message
- LogLevel logLevel;
- /// thread id of the log message
- Tid threadId;
- /// the time the message was logged
- SysTime timestamp;
- /// the message of the log message
- string msg;
- /// A refernce to the `Logger` used to create this `LogEntry`
- Logger logger;
- }
-
- /**
- Every subclass of `Logger` has to call this constructor from their
- constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal
- handler will throw an `Error` if a log call is made with level
- `LogLevel.fatal`.
-
- Params:
- lv = `LogLevel` to use for this `Logger` instance.
- */
- this(this This)(LogLevel lv)
- {
- this.logLevel_ = lv;
- this.fatalHandler_ = delegate() {
- throw new Error("A fatal log message was logged");
- };
-
- this.mutex = new typeof(mutex)();
- }
-
- /** A custom logger must implement this method in order to work in a
- `MultiLogger` and `ArrayLogger`.
-
- Params:
- payload = All information associated with call to log function.
-
- See_Also: beginLogMsg, logMsgPart, finishLogMsg
- */
- abstract protected void writeLogMsg(ref LogEntry payload) @safe;
-
- /* The default implementation will use an `std.array.appender`
- internally to construct the message string. This means dynamic,
- GC memory allocation. A logger can avoid this allocation by
- reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`.
- `beginLogMsg` is always called first, followed by any number of calls
- to `logMsgPart` and one call to `finishLogMsg`.
-
- As an example for such a custom `Logger` compare this:
- ----------------
- class CLogger : Logger
- {
- override void beginLogMsg(string file, int line, string funcName,
- string prettyFuncName, string moduleName, LogLevel logLevel,
- Tid threadId, SysTime timestamp)
- {
- ... logic here
- }
-
- override void logMsgPart(const(char)[] msg)
- {
- ... logic here
- }
-
- override void finishLogMsg()
- {
- ... logic here
- }
-
- void writeLogMsg(ref LogEntry payload)
- {
- this.beginLogMsg(payload.file, payload.line, payload.funcName,
- payload.prettyFuncName, payload.moduleName, payload.logLevel,
- payload.threadId, payload.timestamp, payload.logger);
-
- this.logMsgPart(payload.msg);
- this.finishLogMsg();
- }
- }
- ----------------
- */
- protected void beginLogMsg(string file, int line, string funcName,
- string prettyFuncName, string moduleName, LogLevel logLevel,
- Tid threadId, SysTime timestamp, Logger logger)
- @safe
- {
- msgAppender = appender!string();
- header = LogEntry(file, line, funcName, prettyFuncName,
- moduleName, logLevel, threadId, timestamp, null, logger);
- }
-
- /** Logs a part of the log message. */
- protected void logMsgPart(scope const(char)[] msg) @safe
- {
- msgAppender.put(msg);
- }
-
- /** Signals that the message has been written and no more calls to
- `logMsgPart` follow. */
- protected void finishLogMsg() @safe
- {
- header.msg = msgAppender.data;
- this.writeLogMsg(header);
- }
-
- /** The `LogLevel` determines if the log call are processed or dropped
- by the `Logger`. In order for the log call to be processed the
- `LogLevel` of the log call must be greater or equal to the `LogLevel`
- of the `logger`.
-
- These two methods set and get the `LogLevel` of the used `Logger`.
-
- Example:
- -----------
- auto f = new FileLogger(stdout);
- f.logLevel = LogLevel.info;
- assert(f.logLevel == LogLevel.info);
- -----------
- */
- @property final LogLevel logLevel() const pure @safe @nogc
- {
- return trustedLoad(this.logLevel_);
- }
-
- /// Ditto
- @property final void logLevel(const LogLevel lv) @safe @nogc
- {
- atomicStore(this.logLevel_, lv);
- }
-
- /** This `delegate` is called in case a log message with
- `LogLevel.fatal` gets logged.
-
- By default an `Error` will be thrown.
- */
- @property final void delegate() fatalHandler() @safe @nogc
- {
- synchronized (mutex) return this.fatalHandler_;
- }
-
- /// Ditto
- @property final void fatalHandler(void delegate() @safe fh) @safe @nogc
- {
- synchronized (mutex) this.fatalHandler_ = fh;
- }
-
- /** This method allows forwarding log entries from one logger to another.
-
- `forwardMsg` will ensure proper synchronization and then call
- `writeLogMsg`. This is an API for implementing your own loggers and
- should not be called by normal user code. A notable difference from other
- logging functions is that the `globalLogLevel` wont be evaluated again
- since it is assumed that the caller already checked that.
- */
- void forwardMsg(ref LogEntry payload) @trusted
- {
- if (isLoggingEnabled(payload.logLevel, this.logLevel_,
- globalLogLevel))
- {
- this.writeLogMsg(payload);
-
- if (payload.logLevel == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
-
- /** This template provides the log functions for the `Logger` `class`
- with the `LogLevel` encoded in the function name.
-
- For further information see the the two functions defined inside of this
- template.
-
- The aliases following this template create the public names of these log
- functions.
- */
- template memLogFunctions(LogLevel ll)
- {
- /** This function logs data to the used `Logger`.
-
- In order for the resulting log message to be logged the `LogLevel`
- must be greater or equal than the `LogLevel` of the used `Logger`
- and must be greater or equal than the global `LogLevel`.
-
- Params:
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.trace(1337, "is number");
- s.info(1337, "is number");
- s.error(1337, "is number");
- s.critical(1337, "is number");
- s.fatal(1337, "is number");
- --------------------
- */
- void logImpl(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy A args)
- if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- static if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` depending on a
- condition.
-
- In order for the resulting log message to be logged the `LogLevel` must
- be greater or equal than the `LogLevel` of the used `Logger` and
- must be greater or equal than the global `LogLevel` additionally the
- condition passed must be `true`.
-
- Params:
- condition = The condition must be `true` for the data to be logged.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.trace(true, 1337, "is number");
- s.info(false, 1337, "is number");
- s.error(true, 1337, "is number");
- s.critical(false, 1337, "is number");
- s.fatal(true, 1337, "is number");
- --------------------
- */
- void logImpl(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition,
- lazy A args)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
- condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- static if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` in a
- `printf`-style manner.
-
- In order for the resulting log message to be logged the `LogLevel`
- must be greater or equal than the `LogLevel` of the used `Logger`
- and must be greater or equal than the global `LogLevel` additionally
- the passed condition must be `true`.
-
- Params:
- condition = The condition must be `true` for the data to be logged.
- msg = The `printf`-style string.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stderr);
- s.tracef(true, "is number %d", 1);
- s.infof(true, "is number %d", 2);
- s.errorf(false, "is number %d", 3);
- s.criticalf(someFunc(), "is number %d", 4);
- s.fatalf(true, "is number %d", 5);
- --------------------
- */
- void logImplf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition,
- lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
- condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- static if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` in a
- `printf`-style manner.
-
- In order for the resulting log message to be logged the `LogLevel` must
- be greater or equal than the `LogLevel` of the used `Logger` and
- must be greater or equal than the global `LogLevel`.
-
- Params:
- msg = The `printf`-style string.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stderr);
- s.tracef("is number %d", 1);
- s.infof("is number %d", 2);
- s.errorf("is number %d", 3);
- s.criticalf("is number %d", 4);
- s.fatalf("is number %d", 5);
- --------------------
- */
- void logImplf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- static if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
- }
-
- /// Ditto
- alias trace = memLogFunctions!(LogLevel.trace).logImpl;
- /// Ditto
- alias tracef = memLogFunctions!(LogLevel.trace).logImplf;
- /// Ditto
- alias info = memLogFunctions!(LogLevel.info).logImpl;
- /// Ditto
- alias infof = memLogFunctions!(LogLevel.info).logImplf;
- /// Ditto
- alias warning = memLogFunctions!(LogLevel.warning).logImpl;
- /// Ditto
- alias warningf = memLogFunctions!(LogLevel.warning).logImplf;
- /// Ditto
- alias error = memLogFunctions!(LogLevel.error).logImpl;
- /// Ditto
- alias errorf = memLogFunctions!(LogLevel.error).logImplf;
- /// Ditto
- alias critical = memLogFunctions!(LogLevel.critical).logImpl;
- /// Ditto
- alias criticalf = memLogFunctions!(LogLevel.critical).logImplf;
- /// Ditto
- alias fatal = memLogFunctions!(LogLevel.fatal).logImpl;
- /// Ditto
- alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
-
- /** This method logs data with the `LogLevel` of the used `Logger`.
-
- This method takes a `bool` as first argument. In order for the
- data to be processed the `bool` must be `true` and the `LogLevel`
- of the Logger must be greater or equal to the global `LogLevel`.
-
- Params:
- args = The data that should be logged.
- condition = The condition must be `true` for the data to be logged.
- args = The data that is to be logged.
-
- Returns: The logger used by the logging function as reference.
-
- Example:
- --------------------
- auto l = new StdioLogger();
- l.log(1337);
- --------------------
- */
- void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll,
- lazy bool condition, lazy A args)
- if (args.length != 1)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /// Ditto
- void log(T, string moduleName = __MODULE__)(const LogLevel ll,
- lazy bool condition, lazy T args, int line = __LINE__,
- string file = __FILE__, string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` with a specific
- `LogLevel`.
-
- In order for the resulting log message to be logged the `LogLevel`
- must be greater or equal than the `LogLevel` of the used `Logger`
- and must be greater or equal than the global `LogLevel`.
-
- Params:
- ll = The specific `LogLevel` used for logging the log message.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.log(LogLevel.trace, 1337, "is number");
- s.log(LogLevel.info, 1337, "is number");
- s.log(LogLevel.warning, 1337, "is number");
- s.log(LogLevel.error, 1337, "is number");
- s.log(LogLevel.fatal, 1337, "is number");
- --------------------
- */
- void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
- if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /// Ditto
- void log(T)(const LogLevel ll, lazy T args, int line = __LINE__,
- string file = __FILE__, string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` depending on a
- explicitly passed condition with the `LogLevel` of the used
- `Logger`.
-
- In order for the resulting log message to be logged the `LogLevel`
- of the used `Logger` must be greater or equal than the global
- `LogLevel` and the condition must be `true`.
-
- Params:
- condition = The condition must be `true` for the data to be logged.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.log(true, 1337, "is number");
- s.log(true, 1337, "is number");
- s.log(true, 1337, "is number");
- s.log(false, 1337, "is number");
- s.log(false, 1337, "is number");
- --------------------
- */
- void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
- if (args.length != 1)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(this.logLevel_, this.logLevel_,
- globalLogLevel, condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /// Ditto
- void log(T)(lazy bool condition, lazy T args, int line = __LINE__,
- string file = __FILE__, string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
- condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` with the `LogLevel`
- of the used `Logger`.
-
- In order for the resulting log message to be logged the `LogLevel`
- of the used `Logger` must be greater or equal than the global
- `LogLevel`.
-
- Params:
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.log(1337, "is number");
- s.log(info, 1337, "is number");
- s.log(1337, "is number");
- s.log(1337, "is number");
- s.log(1337, "is number");
- --------------------
- */
- void log(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy A args)
- if ((args.length > 1
- && !is(Unqual!(A[0]) : bool)
- && !is(immutable A[0] == immutable LogLevel))
- || args.length == 0)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(this.logLevel_, this.logLevel_,
- globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
- auto writer = MsgRange(this);
- formatString(writer, args);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /// Ditto
- void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__)
- {
- synchronized (mutex)
- {
- if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
- auto writer = MsgRange(this);
- formatString(writer, arg);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` with a specific
- `LogLevel` and depending on a condition in a `printf`-style manner.
-
- In order for the resulting log message to be logged the `LogLevel`
- must be greater or equal than the `LogLevel` of the used `Logger`
- and must be greater or equal than the global `LogLevel` and the
- condition must be `true`.
-
- Params:
- ll = The specific `LogLevel` used for logging the log message.
- condition = The condition must be `true` for the data to be logged.
- msg = The format string used for this log call.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number");
- s.logf(LogLevel.info, true ,"%d %s", 1337, "is number");
- s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number");
- s.logf(LogLevel.error, false ,"%d %s", 1337, "is number");
- s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number");
- --------------------
- */
- void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll,
- lazy bool condition, lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` with a specific
- `LogLevel` in a `printf`-style manner.
-
- In order for the resulting log message to be logged the `LogLevel`
- must be greater or equal than the `LogLevel` of the used `Logger`
- and must be greater or equal than the global `LogLevel`.
-
- Params:
- ll = The specific `LogLevel` used for logging the log message.
- msg = The format string used for this log call.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.logf(LogLevel.trace, "%d %s", 1337, "is number");
- s.logf(LogLevel.info, "%d %s", 1337, "is number");
- s.logf(LogLevel.warning, "%d %s", 1337, "is number");
- s.logf(LogLevel.error, "%d %s", 1337, "is number");
- s.logf(LogLevel.fatal, "%d %s", 1337, "is number");
- --------------------
- */
- void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(const LogLevel ll,
- lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, ll, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- if (ll == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This function logs data to the used `Logger` depending on a
- condition with the `LogLevel` of the used `Logger` in a
- `printf`-style manner.
-
- In order for the resulting log message to be logged the `LogLevel`
- of the used `Logger` must be greater or equal than the global
- `LogLevel` and the condition must be `true`.
-
- Params:
- condition = The condition must be `true` for the data to be logged.
- msg = The format string used for this log call.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.logf(true ,"%d %s", 1337, "is number");
- s.logf(true ,"%d %s", 1337, "is number");
- s.logf(true ,"%d %s", 1337, "is number");
- s.logf(false ,"%d %s", 1337, "is number");
- s.logf(true ,"%d %s", 1337, "is number");
- --------------------
- */
- void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy bool condition,
- lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
- condition))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- /** This method logs data to the used `Logger` with the `LogLevel`
- of the this `Logger` in a `printf`-style manner.
-
- In order for the data to be processed the `LogLevel` of the `Logger`
- must be greater or equal to the global `LogLevel`.
-
- Params:
- msg = The format string used for this log call.
- args = The data that should be logged.
-
- Example:
- --------------------
- auto s = new FileLogger(stdout);
- s.logf("%d %s", 1337, "is number");
- s.logf("%d %s", 1337, "is number");
- s.logf("%d %s", 1337, "is number");
- s.logf("%d %s", 1337, "is number");
- s.logf("%d %s", 1337, "is number");
- --------------------
- */
- void logf(int line = __LINE__, string file = __FILE__,
- string funcName = __FUNCTION__,
- string prettyFuncName = __PRETTY_FUNCTION__,
- string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
- {
- synchronized (mutex)
- {
- import std.format.write : formattedWrite;
-
- if (isLoggingEnabled(this.logLevel_, this.logLevel_,
- globalLogLevel))
- {
- this.beginLogMsg(file, line, funcName, prettyFuncName,
- moduleName, this.logLevel_, thisTid, Clock.currTime, this);
-
- auto writer = MsgRange(this);
- formattedWrite(writer, msg, args);
-
- this.finishLogMsg();
-
- if (this.logLevel_ == LogLevel.fatal)
- this.fatalHandler_();
- }
- }
- }
-
- private void delegate() @safe fatalHandler_;
- private shared LogLevel logLevel_ = LogLevel.info;
- private Mutex mutex;
-
- protected Appender!string msgAppender;
- protected LogEntry header;
-}
-
-// Thread Global
-
-private shared Logger stdSharedDefaultLogger;
-private shared Logger stdSharedLogger;
-private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all;
-
-/* This method returns the global default Logger.
- * Marked @trusted because of excessive reliance on __gshared data
+ * This module is now deprecated, use $(MREF std, logger, core)
+ * instead.
+ *
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/experimental/logger/core.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
*/
-private @property shared(Logger) defaultSharedLoggerImpl() @trusted
-{
- import core.lifetime : emplace;
- import std.stdio : stderr;
-
- __gshared align(__traits(classInstanceAlignment, FileLogger))
- void[__traits(classInstanceSize, FileLogger)] _buffer = void;
-
- import std.concurrency : initOnce;
- initOnce!stdSharedDefaultLogger({
- auto buffer = cast(ubyte[]) _buffer;
- return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info);
- }());
-
- return atomicLoad(stdSharedDefaultLogger);
-}
-
-/** This property sets and gets the default `Logger`. Unless set to another
-logger by the user, the default logger's log level is LogLevel.info.
-
-Example:
--------------
-sharedLog = new FileLogger(yourFile);
--------------
-The example sets a new `FileLogger` as new `sharedLog`.
-
-If at some point you want to use the original default logger again, you can
-use $(D sharedLog = null;). This will put back the original.
-
-Note:
-While getting and setting `sharedLog` is thread-safe, it has to be considered
-that the returned reference is only a current snapshot and in the following
-code, you must make sure no other thread reassigns to it between reading and
-writing `sharedLog`.
-
-`sharedLog` is only thread-safe if the the used `Logger` is thread-safe.
-The default `Logger` is thread-safe.
--------------
-if (sharedLog !is myLogger)
- sharedLog = new myLogger;
--------------
-*/
-@property shared(Logger) sharedLog() @safe
-{
- // If we have set up our own logger use that
- if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger))
- {
- return atomicLoad(logger);
- }
- else
- {
- // Otherwise resort to the default logger
- return defaultSharedLoggerImpl;
- }
-}
-
-/// Ditto
-@property void sharedLog(shared(Logger) logger) @safe
-{
- atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger));
-}
-
-/** This methods get and set the global `LogLevel`.
-
-Every log message with a `LogLevel` lower as the global `LogLevel`
-will be discarded before it reaches `writeLogMessage` method of any
-`Logger`.
-*/
-/* Implementation note:
-For any public logging call, the global log level shall only be queried once on
-entry. Otherwise when another threads changes the level, we would work with
-different levels at different spots in the code.
-*/
-@property LogLevel globalLogLevel() @safe @nogc
-{
- return trustedLoad(stdLoggerGlobalLogLevel);
-}
-
-/// Ditto
-@property void globalLogLevel(LogLevel ll) @safe
-{
- trustedStore(stdLoggerGlobalLogLevel, ll);
-}
-
-// Thread Local
-
-/** The `StdForwardLogger` will always forward anything to the sharedLog.
-
-The `StdForwardLogger` will not throw if data is logged with $(D
-LogLevel.fatal).
-*/
-class StdForwardLogger : Logger
-{
- /** The default constructor for the `StdForwardLogger`.
-
- Params:
- lv = The `LogLevel` for the `MultiLogger`. By default the $(D
- LogLevel) is `all`.
- */
- this(const LogLevel lv = LogLevel.all) @safe
- {
- super(lv);
- this.fatalHandler = delegate() {};
- }
-
- override protected void writeLogMsg(ref LogEntry payload) @trusted
- {
- synchronized (sharedLog.mutex)
- {
- (cast() sharedLog).forwardMsg(payload);
- }
- }
-}
-
-///
-@safe unittest
-{
- auto nl1 = new StdForwardLogger(LogLevel.all);
-}
-
-@safe unittest
-{
- import core.thread : Thread, msecs;
-
- static class RaceLogger : Logger
- {
- int value;
- this() @safe shared
- {
- super(LogLevel.init);
- }
- override void writeLogMsg(ref LogEntry payload) @safe
- {
- import core.thread : Thread, msecs;
- if (payload.msg == "foo")
- {
- value = 42;
- () @trusted { Thread.sleep(100.msecs); }();
- assert(value == 42, "Another thread changed the value");
- }
- else
- {
- () @trusted { Thread.sleep(50.msecs); } ();
- value = 13;
- }
- }
- }
-
- sharedLog = new shared RaceLogger;
- scope(exit) { sharedLog = null; }
- () @trusted { new Thread(() { log("foo"); }).start(); }();
- log("bar");
-}
-
-/** This `LogLevel` is unqiue to every thread.
-
-The thread local `Logger` will use this `LogLevel` to filter log calls
-every same way as presented earlier.
-*/
-//public LogLevel threadLogLevel = LogLevel.all;
-private Logger stdLoggerThreadLogger;
-private Logger stdLoggerDefaultThreadLogger;
-
-/* This method returns the thread local default Logger.
-*/
-private @property Logger stdThreadLocalLogImpl() @trusted
-{
- import core.lifetime : emplace;
-
- static align(__traits(classInstanceAlignment, StdForwardLogger))
- void[__traits(classInstanceSize, StdForwardLogger)] buffer;
-
- if (stdLoggerDefaultThreadLogger is null)
- {
- stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all);
- }
- return stdLoggerDefaultThreadLogger;
-}
-
-/** This function returns a thread unique `Logger`, that by default
-propagates all data logged to it to the `sharedLog`.
-
-These properties can be used to set and get this `Logger`. Every
-modification to this `Logger` will only be visible in the thread the
-modification has been done from.
-
-This `Logger` is called by the free standing log functions. This allows to
-create thread local redirections and still use the free standing log
-functions.
-*/
-@property Logger stdThreadLocalLog() @safe
-{
- // If we have set up our own logger use that
- if (auto logger = stdLoggerThreadLogger)
- return logger;
- else
- // Otherwise resort to the default logger
- return stdThreadLocalLogImpl;
-}
-
-/// Ditto
-@property void stdThreadLocalLog(Logger logger) @safe
-{
- stdLoggerThreadLogger = logger;
-}
-
-/// Ditto
-@system unittest
-{
- import std.experimental.logger.filelogger : FileLogger;
- import std.file : deleteme, remove;
- Logger l = stdThreadLocalLog;
- stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
- scope(exit) remove(deleteme ~ "-someFile.log");
-
- auto tempLog = stdThreadLocalLog;
- stdThreadLocalLog = l;
- destroy(tempLog);
-}
-
-@safe unittest
-{
- LogLevel ll = globalLogLevel;
- globalLogLevel = LogLevel.fatal;
- assert(globalLogLevel == LogLevel.fatal);
- globalLogLevel = ll;
-}
-
-package class TestLogger : Logger
-{
- int line = -1;
- string file = null;
- string func = null;
- string prettyFunc = null;
- string msg = null;
- LogLevel lvl;
-
- this(const LogLevel lv = LogLevel.all) @safe
- {
- super(lv);
- }
-
- override protected void writeLogMsg(ref LogEntry payload) @safe
- {
- this.line = payload.line;
- this.file = payload.file;
- this.func = payload.funcName;
- this.prettyFunc = payload.prettyFuncName;
- this.lvl = payload.logLevel;
- this.msg = payload.msg;
- }
-}
-
-version (StdUnittest) private void testFuncNames(Logger logger) @safe
-{
- string s = "I'm here";
- logger.log(s);
-}
-
-@safe unittest
-{
- auto tl1 = new TestLogger();
- testFuncNames(tl1);
- assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func);
- assert(tl1.prettyFunc ==
- "void std.experimental.logger.core.testFuncNames(Logger logger) @safe",
- tl1.prettyFunc);
- assert(tl1.msg == "I'm here", tl1.msg);
-}
-
-@safe unittest
-{
- auto tl1 = new TestLogger(LogLevel.all);
- tl1.log();
- assert(tl1.line == __LINE__ - 1);
- tl1.log(true);
- assert(tl1.line == __LINE__ - 1);
- tl1.log(false);
- assert(tl1.line == __LINE__ - 3);
- tl1.log(LogLevel.info);
- assert(tl1.line == __LINE__ - 1);
- tl1.log(LogLevel.off);
- assert(tl1.line == __LINE__ - 3);
- tl1.log(LogLevel.info, true);
- assert(tl1.line == __LINE__ - 1);
- tl1.log(LogLevel.info, false);
- assert(tl1.line == __LINE__ - 3);
-
- auto oldunspecificLogger = sharedLog;
- scope(exit) {
- sharedLog = atomicLoad(oldunspecificLogger);
- }
-
- () @trusted {
- sharedLog = cast(shared) tl1;
- }();
-
- log();
- assert(tl1.line == __LINE__ - 1);
-
- log(LogLevel.info);
- assert(tl1.line == __LINE__ - 1);
-
- log(true);
- assert(tl1.line == __LINE__ - 1);
-
- log(LogLevel.warning, true);
- assert(tl1.line == __LINE__ - 1);
-
- trace();
- assert(tl1.line == __LINE__ - 1);
-}
-
-@safe unittest
-{
- import std.experimental.logger.multilogger : MultiLogger;
-
- auto tl1 = new TestLogger;
- auto tl2 = new TestLogger;
-
- auto ml = new MultiLogger();
- ml.insertLogger("one", tl1);
- ml.insertLogger("two", tl2);
-
- string msg = "Hello Logger World";
- ml.log(msg);
- int lineNumber = __LINE__ - 1;
- assert(tl1.msg == msg);
- assert(tl1.line == lineNumber);
- assert(tl2.msg == msg);
- assert(tl2.line == lineNumber);
-
- ml.removeLogger("one");
- ml.removeLogger("two");
- auto n = ml.removeLogger("one");
- assert(n is null);
-}
-
-@safe unittest
-{
- bool errorThrown = false;
- auto tl = new TestLogger;
- auto dele = delegate() {
- errorThrown = true;
- };
- tl.fatalHandler = dele;
- tl.fatal();
- assert(errorThrown);
-}
-
-@safe unittest
-{
- import std.conv : to;
- import std.exception : assertThrown, assertNotThrown;
- import std.format : format;
-
- auto l = new TestLogger(LogLevel.all);
- string msg = "Hello Logger World";
- l.log(msg);
- int lineNumber = __LINE__ - 1;
- assert(l.msg == msg);
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- l.log(true, msg);
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg, l.msg);
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- l.log(false, msg);
- assert(l.msg == msg);
- assert(l.line == lineNumber, to!string(l.line));
- assert(l.logLevel == LogLevel.all);
-
- msg = "%s Another message";
- l.logf(msg, "Yet");
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- l.logf(true, msg, "Yet");
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- l.logf(false, msg, "Yet");
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- () @trusted {
- assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
- } ();
- lineNumber = __LINE__ - 2;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- () @trusted {
- assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
- } ();
- lineNumber = __LINE__ - 2;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- Logger oldunspecificLogger;
- () @trusted {
- oldunspecificLogger = cast() sharedLog;
- }();
-
- assert(oldunspecificLogger.logLevel == LogLevel.info,
- to!string(oldunspecificLogger.logLevel));
-
- assert(l.logLevel == LogLevel.all);
-
- () @trusted {
- sharedLog = cast(shared) l;
- }();
-
- assert(globalLogLevel == LogLevel.all,
- to!string(globalLogLevel));
-
- scope(exit)
- {
- () @trusted {
- sharedLog = atomicLoad(cast(shared) oldunspecificLogger);
- }();
- }
-
- () @trusted {
- assert((cast() sharedLog).logLevel == LogLevel.all);
- }();
-
- assert(stdThreadLocalLog.logLevel == LogLevel.all);
- assert(globalLogLevel == LogLevel.all);
-
- msg = "Another message";
- log(msg);
- lineNumber = __LINE__ - 1;
- assert(l.logLevel == LogLevel.all);
- assert(l.line == lineNumber, to!string(l.line));
- assert(l.msg == msg, l.msg);
-
- log(true, msg);
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg);
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- log(false, msg);
- assert(l.msg == msg);
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- msg = "%s Another message";
- logf(msg, "Yet");
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- logf(true, msg, "Yet");
- lineNumber = __LINE__ - 1;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- logf(false, msg, "Yet");
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- msg = "%s Another message";
- () @trusted {
- assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
- } ();
- lineNumber = __LINE__ - 2;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- () @trusted {
- assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
- } ();
- lineNumber = __LINE__ - 2;
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-
- assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
- assert(l.msg == msg.format("Yet"));
- assert(l.line == lineNumber);
- assert(l.logLevel == LogLevel.all);
-}
-
-@system unittest // default logger
-{
- import std.file : deleteme, exists, remove;
- import std.stdio : File;
- import std.string : indexOf;
-
- string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
- FileLogger l = new FileLogger(filename);
- auto oldunspecificLogger = sharedLog;
-
- sharedLog = cast(shared) l;
-
- scope(exit)
- {
- remove(filename);
- assert(!exists(filename));
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- string notWritten = "this should not be written to file";
- string written = "this should be written to file";
-
- globalLogLevel = LogLevel.critical;
- assert(globalLogLevel == LogLevel.critical);
-
- log(LogLevel.warning, notWritten);
- log(LogLevel.critical, written);
-
- l.file.flush();
- l.file.close();
-
- auto file = File(filename, "r");
- assert(!file.eof);
-
- string readLine = file.readln();
- assert(readLine.indexOf(written) != -1, readLine);
- assert(readLine.indexOf(notWritten) == -1, readLine);
- file.close();
-}
-
-@system unittest
-{
- import std.file : deleteme, remove;
- import std.stdio : File;
- import std.string : indexOf;
-
- string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
- auto oldunspecificLogger = sharedLog;
-
- scope(exit)
- {
- remove(filename);
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- string notWritten = "this should not be written to file";
- string written = "this should be written to file";
-
- auto l = new FileLogger(filename);
- sharedLog = cast(shared) l;
-
- () @trusted {
- (cast() sharedLog).logLevel = LogLevel.critical;
- }();
-
- log(LogLevel.error, false, notWritten);
- log(LogLevel.critical, true, written);
- destroy(l);
-
- auto file = File(filename, "r");
- auto readLine = file.readln();
- assert(!readLine.empty, readLine);
- assert(readLine.indexOf(written) != -1);
- assert(readLine.indexOf(notWritten) == -1);
- file.close();
-}
-
-@safe unittest
-{
- import std.conv : to;
-
- auto tl = new TestLogger(LogLevel.all);
- int l = __LINE__;
- tl.info("a");
- assert(tl.line == l+1);
- assert(tl.msg == "a");
- assert(tl.logLevel == LogLevel.all);
- assert(globalLogLevel == LogLevel.all);
- l = __LINE__;
- tl.trace("b");
- assert(tl.msg == "b", tl.msg);
- assert(tl.line == l+1, to!string(tl.line));
-}
-
-// testing possible log conditions
-@safe unittest
-{
- import std.conv : to;
- import std.format : format;
- import std.string : indexOf;
-
- auto oldunspecificLogger = sharedLog;
-
- auto mem = new TestLogger;
- mem.fatalHandler = delegate() {};
-
- () @trusted {
- sharedLog = cast(shared) mem;
- }();
-
- scope(exit)
- {
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- int value = 0;
- foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
-
- globalLogLevel = gll;
-
- foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
-
- mem.logLevel = ll;
-
- foreach (cond; [true, false])
- {
- foreach (condValue; [true, false])
- {
- foreach (memOrG; [true, false])
- {
- foreach (prntf; [true, false])
- {
- foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning,
- LogLevel.error, LogLevel.critical,
- LogLevel.fatal, LogLevel.off])
- {
- foreach (singleMulti; 0 .. 2)
- {
- int lineCall;
- mem.msg = "-1";
- if (memOrG)
- {
- if (prntf)
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- mem.logf(ll2, condValue, "%s",
- value);
- lineCall = __LINE__;
- }
- else
- {
- mem.logf(ll2, condValue,
- "%d %d", value, value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- mem.logf(ll2, "%s", value);
- lineCall = __LINE__;
- }
- else
- {
- mem.logf(ll2, "%d %d",
- value, value);
- lineCall = __LINE__;
- }
- }
- }
- else
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- mem.log(ll2, condValue,
- to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- mem.log(ll2, condValue,
- to!string(value), value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- mem.log(ll2, to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- mem.log(ll2,
- to!string(value),
- value);
- lineCall = __LINE__;
- }
- }
- }
- }
- else
- {
- if (prntf)
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- logf(ll2, condValue, "%s",
- value);
- lineCall = __LINE__;
- }
- else
- {
- logf(ll2, condValue,
- "%s %d", value, value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- logf(ll2, "%s", value);
- lineCall = __LINE__;
- }
- else
- {
- logf(ll2, "%s %s", value,
- value);
- lineCall = __LINE__;
- }
- }
- }
- else
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- log(ll2, condValue,
- to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- log(ll2, condValue, value,
- to!string(value));
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- log(ll2, to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- log(ll2, value,
- to!string(value));
- lineCall = __LINE__;
- }
- }
- }
- }
-
- string valueStr = to!string(value);
- ++value;
-
- bool ll2Off = (ll2 != LogLevel.off);
- bool gllOff = (gll != LogLevel.off);
- bool llOff = (ll != LogLevel.off);
- bool condFalse = (cond ? condValue : true);
- bool ll2VSgll = (ll2 >= gll);
- bool ll2VSll = (ll2 >= ll);
-
- bool shouldLog = ll2Off && gllOff && llOff
- && condFalse && ll2VSgll && ll2VSll;
-
- /*
- writefln(
- "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
- , gll != LogLevel.off, ll2 != LogLevel.off,
- cond ? condValue : true,
- ll2 >= gll, ll2 >= ll, shouldLog);
- */
-
-
- if (shouldLog)
- {
- assert(mem.msg.indexOf(valueStr) != -1,
- format(
- "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
- "cond(%b) condValue(%b)" ~
- " memOrG(%b) shouldLog(%b) %s == %s" ~
- " %b %b %b %b %b",
- lineCall, ll2Off, gll, ll, ll2, cond,
- condValue, memOrG, shouldLog, mem.msg,
- valueStr, gllOff, llOff, condFalse,
- ll2VSgll, ll2VSll
- ));
- }
- else
- {
- assert(mem.msg.indexOf(valueStr),
- format(
- "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
- "cond(%b) condValue(%b)" ~
- " memOrG(%b) shouldLog(%b) %s == %s" ~
- " %b %b %b %b %b",
- lineCall, ll2Off, gll, ll, ll2, cond,
- condValue, memOrG, shouldLog, mem.msg,
- valueStr, gllOff, llOff, condFalse,
- ll2VSgll, ll2VSll
- ));
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-// more testing
-@safe unittest
-{
- import std.conv : to;
- import std.format : format;
- import std.string : indexOf;
-
- auto oldunspecificLogger = sharedLog;
-
- auto mem = new TestLogger;
- mem.fatalHandler = delegate() {};
-
- () @trusted {
- sharedLog = cast(shared) mem;
- }();
-
- scope(exit)
- {
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- int value = 0;
- foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
-
- globalLogLevel = gll;
-
- foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
- mem.logLevel = ll;
-
- foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
- stdThreadLocalLog.logLevel = tll;
-
- foreach (cond; [true, false])
- {
- foreach (condValue; [true, false])
- {
- foreach (memOrG; [true, false])
- {
- foreach (prntf; [true, false])
- {
- foreach (singleMulti; 0 .. 2)
- {
- int lineCall;
- mem.msg = "-1";
- if (memOrG)
- {
- if (prntf)
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- mem.logf(condValue, "%s",
- value);
- lineCall = __LINE__;
- }
- else
- {
- mem.logf(condValue,
- "%d %d", value, value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- mem.logf("%s", value);
- lineCall = __LINE__;
- }
- else
- {
- mem.logf("%d %d",
- value, value);
- lineCall = __LINE__;
- }
- }
- }
- else
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- mem.log(condValue,
- to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- mem.log(condValue,
- to!string(value), value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- mem.log(to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- mem.log(to!string(value),
- value);
- lineCall = __LINE__;
- }
- }
- }
- }
- else
- {
- if (prntf)
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- logf(condValue, "%s", value);
- lineCall = __LINE__;
- }
- else
- {
- logf(condValue, "%s %d", value,
- value);
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- logf("%s", value);
- lineCall = __LINE__;
- }
- else
- {
- logf("%s %s", value, value);
- lineCall = __LINE__;
- }
- }
- }
- else
- {
- if (cond)
- {
- if (singleMulti == 0)
- {
- log(condValue,
- to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- log(condValue, value,
- to!string(value));
- lineCall = __LINE__;
- }
- }
- else
- {
- if (singleMulti == 0)
- {
- log(to!string(value));
- lineCall = __LINE__;
- }
- else
- {
- log(value, to!string(value));
- lineCall = __LINE__;
- }
- }
- }
- }
-
- string valueStr = to!string(value);
- ++value;
-
- bool gllOff = (gll != LogLevel.off);
- bool llOff = (ll != LogLevel.off);
- bool tllOff = (tll != LogLevel.off);
- bool llVSgll = (ll >= gll);
- bool tllVSll =
- (stdThreadLocalLog.logLevel >= ll);
- bool condFalse = (cond ? condValue : true);
-
- bool shouldLog = gllOff && llOff
- && (memOrG ? true : tllOff)
- && (memOrG ?
- (ll >= gll) :
- (tll >= gll && tll >= ll))
- && condFalse;
-
- if (shouldLog)
- {
- assert(mem.msg.indexOf(valueStr) != -1,
- format("\ngll(%s) ll(%s) tll(%s) " ~
- "cond(%s) condValue(%s) " ~
- "memOrG(%s) prntf(%s) " ~
- "singleMulti(%s)",
- gll, ll, tll, cond, condValue,
- memOrG, prntf, singleMulti)
- ~ format(" gllOff(%s) llOff(%s) " ~
- "llVSgll(%s) tllVSll(%s) " ~
- "tllOff(%s) condFalse(%s) "
- ~ "shoudlLog(%s)",
- gll != LogLevel.off,
- ll != LogLevel.off, llVSgll,
- tllVSll, tllOff, condFalse,
- shouldLog)
- ~ format("msg(%s) line(%s) " ~
- "lineCall(%s) valueStr(%s)",
- mem.msg, mem.line, lineCall,
- valueStr)
- );
- }
- else
- {
- assert(mem.msg.indexOf(valueStr) == -1,
- format("\ngll(%s) ll(%s) tll(%s) " ~
- "cond(%s) condValue(%s) " ~
- "memOrG(%s) prntf(%s) " ~
- "singleMulti(%s)",
- gll, ll, tll, cond, condValue,
- memOrG, prntf, singleMulti)
- ~ format(" gllOff(%s) llOff(%s) " ~
- "llVSgll(%s) tllVSll(%s) " ~
- "tllOff(%s) condFalse(%s) "
- ~ "shoudlLog(%s)",
- gll != LogLevel.off,
- ll != LogLevel.off, llVSgll,
- tllVSll, tllOff, condFalse,
- shouldLog)
- ~ format("msg(%s) line(%s) " ~
- "lineCall(%s) valueStr(%s)",
- mem.msg, mem.line, lineCall,
- valueStr)
- );
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-// testing more possible log conditions
-@safe unittest
-{
- bool fatalLog;
- auto mem = new TestLogger;
- mem.fatalHandler = delegate() { fatalLog = true; };
- auto oldunspecificLogger = sharedLog;
-
- stdThreadLocalLog.logLevel = LogLevel.all;
-
- () @trusted {
- sharedLog = cast(shared) mem;
- }();
-
- scope(exit)
- {
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
-
- globalLogLevel = gll;
-
- foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
- mem.logLevel = ll;
-
- foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
- LogLevel.info, LogLevel.warning, LogLevel.error,
- LogLevel.critical, LogLevel.fatal, LogLevel.off])
- {
- stdThreadLocalLog.logLevel = tll;
-
- foreach (cond; [true, false])
- {
- assert(globalLogLevel == gll);
- assert(mem.logLevel == ll);
-
- bool gllVSll = LogLevel.trace >= globalLogLevel;
- bool llVSgll = ll >= globalLogLevel;
- bool lVSll = LogLevel.trace >= ll;
- bool gllOff = globalLogLevel != LogLevel.off;
- bool llOff = mem.logLevel != LogLevel.off;
- bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off;
- bool tllVSll = tll >= ll;
- bool tllVSgll = tll >= gll;
- bool lVSgll = LogLevel.trace >= tll;
-
- bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
- bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond;
-
- mem.line = -1;
- /*
- writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
- gll, ll, cond, test);
- writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
- gllOff, llOff, cond, test2);
- */
-
- mem.trace(__LINE__); int line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- trace(__LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.trace(cond, __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- trace(cond, __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.tracef("%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- tracef("%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.tracef(cond, "%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- tracef(cond, "%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- llVSgll = ll >= globalLogLevel;
- lVSll = LogLevel.info >= ll;
- lVSgll = LogLevel.info >= tll;
- test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
- testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
- lVSgll && cond;
-
- mem.info(__LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- info(__LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.info(cond, __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- info(cond, __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.infof("%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- infof("%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.infof(cond, "%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- infof(cond, "%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- llVSgll = ll >= globalLogLevel;
- lVSll = LogLevel.warning >= ll;
- lVSgll = LogLevel.warning >= tll;
- test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
- testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
- lVSgll && cond;
-
- mem.warning(__LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- warning(__LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.warning(cond, __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- warning(cond, __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.warningf("%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- warningf("%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.warningf(cond, "%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- warningf(cond, "%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- llVSgll = ll >= globalLogLevel;
- lVSll = LogLevel.critical >= ll;
- lVSgll = LogLevel.critical >= tll;
- test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
- testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
- lVSgll && cond;
-
- mem.critical(__LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- critical(__LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.critical(cond, __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- critical(cond, __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.criticalf("%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- criticalf("%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
-
- criticalf(cond, "%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
-
- llVSgll = ll >= globalLogLevel;
- lVSll = LogLevel.fatal >= ll;
- lVSgll = LogLevel.fatal >= tll;
- test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
- testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
- lVSgll && cond;
-
- mem.fatal(__LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
- assert(test ? fatalLog : true);
- fatalLog = false;
-
- fatal(__LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
- assert(testG ? fatalLog : true);
- fatalLog = false;
-
- mem.fatal(cond, __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
- assert(test ? fatalLog : true);
- fatalLog = false;
-
- fatal(cond, __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
- assert(testG ? fatalLog : true);
- fatalLog = false;
-
- mem.fatalf("%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
- assert(test ? fatalLog : true);
- fatalLog = false;
-
- fatalf("%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
- assert(testG ? fatalLog : true);
- fatalLog = false;
-
- mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
- assert(test ? mem.line == line : true); line = -1;
- assert(test ? fatalLog : true);
- fatalLog = false;
-
- fatalf(cond, "%d", __LINE__); line = __LINE__;
- assert(testG ? mem.line == line : true); line = -1;
- assert(testG ? fatalLog : true);
- fatalLog = false;
- }
- }
- }
- }
-}
-
-// Issue #5
-@safe unittest
-{
- import std.string : indexOf;
-
- auto oldunspecificLogger = sharedLog;
-
- scope(exit)
- {
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- auto tl = new TestLogger(LogLevel.info);
-
- () @trusted {
- sharedLog = cast(shared) tl;
- }();
-
- trace("trace");
- assert(tl.msg.indexOf("trace") == -1);
-}
-
-// Issue #5
-@safe unittest
-{
- import std.experimental.logger.multilogger : MultiLogger;
- import std.string : indexOf;
-
- stdThreadLocalLog.logLevel = LogLevel.all;
-
- auto oldunspecificLogger = sharedLog;
-
- scope(exit)
- {
- sharedLog = atomicLoad(oldunspecificLogger);
- globalLogLevel = LogLevel.all;
- }
-
- auto logger = new MultiLogger(LogLevel.error);
-
- auto tl = new TestLogger(LogLevel.info);
- logger.insertLogger("required", tl);
-
- () @trusted {
- sharedLog = cast(shared) logger;
- }();
-
- trace("trace");
- assert(tl.msg.indexOf("trace") == -1);
- info("info");
- assert(tl.msg.indexOf("info") == -1);
- error("error");
- assert(tl.msg.indexOf("error") == 0);
-}
-
-@system unittest
-{
- import std.exception : assertThrown;
- auto tl = new TestLogger();
- assertThrown!Throwable(tl.fatal("fatal"));
-}
-
-// log objects with non-safe toString
-@system unittest
-{
- struct Test
- {
- string toString() const @system
- {
- return "test";
- }
- }
-
- auto tl = new TestLogger();
- tl.info(Test.init);
- assert(tl.msg == "test");
-}
-
-// Workaround for atomics not allowed in @safe code
-private auto trustedLoad(T)(ref shared T value) @trusted
-{
- return atomicLoad!(MemoryOrder.acq)(value);
-}
-
-// ditto
-private void trustedStore(T)(ref shared T dst, ref T src) @trusted
-{
- atomicStore!(MemoryOrder.rel)(dst, src);
-}
-
-// check that thread-local logging does not propagate
-// to shared logger
-@system unittest
-{
- import core.thread, std.concurrency;
-
- static shared logged_count = 0;
-
- class TestLog : Logger
- {
- Tid tid;
-
- this()
- {
- super (LogLevel.trace);
- this.tid = thisTid;
- }
-
- override void writeLogMsg(ref LogEntry payload) @trusted
- {
- assert(thisTid == this.tid);
- atomicOp!"+="(logged_count, 1);
- }
- }
-
- class IgnoredLog : Logger
- {
- this()
- {
- super (LogLevel.trace);
- }
-
- override void writeLogMsg(ref LogEntry payload) @trusted
- {
- assert(false);
- }
- }
-
- auto oldSharedLog = sharedLog;
- scope(exit)
- {
- sharedLog = atomicLoad(oldSharedLog);
- }
-
- () @trusted {
- sharedLog = cast(shared) new IgnoredLog;
- }();
-
- Thread[] spawned;
-
- foreach (i; 0 .. 4)
- {
- spawned ~= new Thread({
- stdThreadLocalLog = new TestLog;
- trace("zzzzzzzzzz");
- });
- spawned[$-1].start();
- }
-
- foreach (t; spawned)
- t.join();
-
- assert(atomicOp!"=="(logged_count, 4));
-}
-
-@safe unittest
-{
- auto dl = () @trusted {
- return cast(FileLogger) cast() sharedLog;
- }();
- assert(dl !is null);
- assert(dl.logLevel == LogLevel.info);
- assert(globalLogLevel == LogLevel.all);
-
- auto tl = cast(StdForwardLogger) stdThreadLocalLog;
- assert(tl !is null);
- stdThreadLocalLog.logLevel = LogLevel.all;
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=14940
-@safe unittest
-{
- import std.typecons : Nullable;
-
- Nullable!int a = 1;
- auto l = new TestLogger();
- l.infof("log: %s", a);
- assert(l.msg == "log: 1");
-}
-
-// Ensure @system toString methods work
-@system unittest
-{
- enum SystemToStringMsg = "SystemToString";
- static struct SystemToString
- {
- string toString() @system
- {
- return SystemToStringMsg;
- }
- }
-
- auto tl = new TestLogger();
-
- SystemToString sts;
- tl.logf("%s", sts);
- assert(tl.msg == SystemToStringMsg);
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=17328
-@safe unittest
-{
- import std.format : format;
-
- ubyte[] data = [0];
- string s = format("%(%02x%)", data); // format 00
- assert(s == "00");
-
- auto tl = new TestLogger();
-
- tl.infof("%(%02x%)", data); // infof 000
-
- size_t i;
- string fs = tl.msg;
- for (; i < s.length; ++i)
- {
- assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs);
- }
- assert(fs.length == 2);
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=15954
-@safe unittest
-{
- import std.conv : to;
- auto tl = new TestLogger();
- tl.log("123456789".to!wstring);
- assert(tl.msg == "123456789");
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=16256
-@safe unittest
-{
- import std.conv : to;
- auto tl = new TestLogger();
- tl.log("123456789"d);
- assert(tl.msg == "123456789");
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=15517
-@system unittest
-{
- import std.file : exists, remove, tempDir;
- import std.path : buildPath;
- import std.stdio : File;
- import std.string : indexOf;
-
- string fn = tempDir.buildPath("logfile.log");
- if (exists(fn))
- {
- remove(fn);
- }
-
- auto oldShared = sharedLog;
- scope(exit)
- {
- sharedLog = atomicLoad(oldShared);
- if (exists(fn))
- {
- remove(fn);
- }
- }
-
- auto ts = [ "Test log 1", "Test log 2", "Test log 3"];
-
- auto fl = new FileLogger(fn);
-
- () @trusted {
- sharedLog = cast(shared) fl;
- }();
-
- assert(exists(fn));
-
- foreach (t; ts)
- {
- log(t);
- }
-
- auto f = File(fn);
- auto l = f.byLine();
- assert(!l.empty);
- size_t idx;
- foreach (it; l)
- {
- assert(it.indexOf(ts[idx]) != -1, it);
- ++idx;
- }
-
- assert(exists(fn));
- fl.file.close();
-}
+module std.experimental.logger.core;
+public import std.logger.core;
diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d
index 457012e21b5..3205a253b93 100644
--- a/libphobos/src/std/experimental/logger/filelogger.d
+++ b/libphobos/src/std/experimental/logger/filelogger.d
@@ -1,272 +1,13 @@
-// Written in the D programming language.
/**
-Source: $(PHOBOSSRC std/experimental/logger/filelogger.d)
-*/
+ * This module is now deprecated, use $(MREF std, logger, filelogger)
+ * instead.
+ *
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/experimental/logger/filelogger.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
module std.experimental.logger.filelogger;
-
-import std.experimental.logger.core;
-import std.stdio;
-
-import std.typecons : Flag;
-
-/** An option to create $(LREF FileLogger) directory if it is non-existent.
-*/
-alias CreateFolder = Flag!"CreateFolder";
-
-/** This `Logger` implementation writes log messages to the associated
-file. The name of the file has to be passed on construction time. If the file
-is already present new log messages will be append at its end.
-*/
-class FileLogger : Logger
-{
- import std.concurrency : Tid;
- import std.datetime.systime : SysTime;
- import std.format.write : formattedWrite;
-
- /** A constructor for the `FileLogger` Logger.
-
- Params:
- fn = The filename of the output file of the `FileLogger`. If that
- file can not be opened for writting an exception will be thrown.
- lv = The `LogLevel` for the `FileLogger`. By default the
-
- Example:
- -------------
- auto l1 = new FileLogger("logFile");
- auto l2 = new FileLogger("logFile", LogLevel.fatal);
- auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
- -------------
- */
- this(const string fn, const LogLevel lv = LogLevel.all) @safe
- {
- this(fn, lv, CreateFolder.yes);
- }
-
- /** A constructor for the `FileLogger` Logger that takes a reference to
- a `File`.
-
- The `File` passed must be open for all the log call to the
- `FileLogger`. If the `File` gets closed, using the `FileLogger`
- for logging will result in undefined behaviour.
-
- Params:
- fn = The file used for logging.
- lv = The `LogLevel` for the `FileLogger`. By default the
- `LogLevel` for `FileLogger` is `LogLevel.all`.
- createFileNameFolder = if yes and fn contains a folder name, this
- folder will be created.
-
- Example:
- -------------
- auto file = File("logFile.log", "w");
- auto l1 = new FileLogger(file);
- auto l2 = new FileLogger(file, LogLevel.fatal);
- -------------
- */
- this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
- {
- import std.file : exists, mkdirRecurse;
- import std.path : dirName;
- import std.conv : text;
-
- super(lv);
- this.filename = fn;
-
- if (createFileNameFolder)
- {
- auto d = dirName(this.filename);
- mkdirRecurse(d);
- assert(exists(d), text("The folder the FileLogger should have",
- " created in '", d,"' could not be created."));
- }
-
- this.file_.open(this.filename, "a");
- }
-
- /** A constructor for the `FileLogger` Logger that takes a reference to
- a `File`.
-
- The `File` passed must be open for all the log call to the
- `FileLogger`. If the `File` gets closed, using the `FileLogger`
- for logging will result in undefined behaviour.
-
- Params:
- file = The file used for logging.
- lv = The `LogLevel` for the `FileLogger`. By default the
- `LogLevel` for `FileLogger` is `LogLevel.all`.
-
- Example:
- -------------
- auto file = File("logFile.log", "w");
- auto l1 = new FileLogger(file);
- auto l2 = new FileLogger(file, LogLevel.fatal);
- -------------
- */
- this(File file, const LogLevel lv = LogLevel.all) @safe
- {
- super(lv);
- this.file_ = file;
- }
-
- /** If the `FileLogger` is managing the `File` it logs to, this
- method will return a reference to this File.
- */
- @property File file() @safe
- {
- return this.file_;
- }
-
- /* This method overrides the base class method in order to log to a file
- without requiring heap allocated memory. Additionally, the `FileLogger`
- local mutex is logged to serialize the log calls.
- */
- override protected void beginLogMsg(string file, int line, string funcName,
- string prettyFuncName, string moduleName, LogLevel logLevel,
- Tid threadId, SysTime timestamp, Logger logger)
- @safe
- {
- import std.string : lastIndexOf;
- ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
- ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
-
- auto lt = this.file_.lockingTextWriter();
- systimeToISOString(lt, timestamp);
- import std.conv : to;
- formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string,
- file[fnIdx .. $], line, funcName[funIdx .. $]);
- }
-
- /* This methods overrides the base class method and writes the parts of
- the log call directly to the file.
- */
- override protected void logMsgPart(scope const(char)[] msg)
- {
- formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
- }
-
- /* This methods overrides the base class method and finalizes the active
- log call. This requires flushing the `File` and releasing the
- `FileLogger` local mutex.
- */
- override protected void finishLogMsg()
- {
- this.file_.lockingTextWriter().put("\n");
- this.file_.flush();
- }
-
- /* This methods overrides the base class method and delegates the
- `LogEntry` data to the actual implementation.
- */
- override protected void writeLogMsg(ref LogEntry payload)
- {
- this.beginLogMsg(payload.file, payload.line, payload.funcName,
- payload.prettyFuncName, payload.moduleName, payload.logLevel,
- payload.threadId, payload.timestamp, payload.logger);
- this.logMsgPart(payload.msg);
- this.finishLogMsg();
- }
-
- /** If the `FileLogger` was constructed with a filename, this method
- returns this filename. Otherwise an empty `string` is returned.
- */
- string getFilename()
- {
- return this.filename;
- }
-
- /** The `File` log messages are written to. */
- protected File file_;
-
- /** The filename of the `File` log messages are written to. */
- protected string filename;
-}
-
-@system unittest
-{
- import std.array : empty;
- import std.file : deleteme, remove;
- import std.string : indexOf;
-
- string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
- auto l = new FileLogger(filename);
-
- scope(exit)
- {
- remove(filename);
- }
-
- string notWritten = "this should not be written to file";
- string written = "this should be written to file";
-
- l.logLevel = LogLevel.critical;
- l.log(LogLevel.warning, notWritten);
- l.log(LogLevel.critical, written);
- destroy(l);
-
- auto file = File(filename, "r");
- string readLine = file.readln();
- assert(readLine.indexOf(written) != -1, readLine);
- readLine = file.readln();
- assert(readLine.indexOf(notWritten) == -1, readLine);
-}
-
-@safe unittest
-{
- import std.file : rmdirRecurse, exists, deleteme;
- import std.path : dirName;
-
- const string tmpFolder = dirName(deleteme);
- const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
- const string filename = filepath ~ "output.txt";
- assert(!exists(filepath));
-
- auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
- scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
-
- f.log("Hello World!");
- assert(exists(filepath));
- f.file.close();
-}
-
-@system unittest
-{
- import std.array : empty;
- import std.file : deleteme, remove;
- import std.string : indexOf;
-
- string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
- auto file = File(filename, "w");
- auto l = new FileLogger(file);
-
- scope(exit)
- {
- remove(filename);
- }
-
- string notWritten = "this should not be written to file";
- string written = "this should be written to file";
-
- l.logLevel = LogLevel.critical;
- l.log(LogLevel.warning, notWritten);
- l.log(LogLevel.critical, written);
- file.close();
-
- file = File(filename, "r");
- string readLine = file.readln();
- assert(readLine.indexOf(written) != -1, readLine);
- readLine = file.readln();
- assert(readLine.indexOf(notWritten) == -1, readLine);
- file.close();
-}
-
-@system unittest
-{
- auto dl = cast(FileLogger) sharedLog;
- assert(dl !is null);
- assert(dl.logLevel == LogLevel.info);
- assert(globalLogLevel == LogLevel.all);
-
- auto tl = cast(StdForwardLogger) stdThreadLocalLog;
- assert(tl !is null);
- stdThreadLocalLog.logLevel = LogLevel.all;
-}
+public import std.logger.filelogger;
diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d
index 35936901964..ae00b251d9b 100644
--- a/libphobos/src/std/experimental/logger/multilogger.d
+++ b/libphobos/src/std/experimental/logger/multilogger.d
@@ -1,200 +1,13 @@
-// Written in the D programming language.
/**
-Source: $(PHOBOSSRC std/experimental/logger/multilogger.d)
-*/
+ * This module is now deprecated, use $(MREF std, logger, multilogger)
+ * instead.
+ *
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/experimental/logger/multilogger.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
module std.experimental.logger.multilogger;
-
-import std.experimental.logger.core;
-import std.experimental.logger.filelogger;
-
-/** This Element is stored inside the `MultiLogger` and associates a
-`Logger` to a `string`.
-*/
-struct MultiLoggerEntry
-{
- string name; /// The name if the `Logger`
- Logger logger; /// The stored `Logger`
-}
-
-/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
-`Logger[]` in their order of insertion.
-
-Every data logged to this `MultiLogger` will be distributed to all the $(D
-Logger)s inserted into it. This `MultiLogger` implementation can
-hold multiple `Logger`s with the same name. If the method `removeLogger`
-is used to remove a `Logger` only the first occurrence with that name will
-be removed.
-*/
-class MultiLogger : Logger
-{
- /** A constructor for the `MultiLogger` Logger.
-
- Params:
- lv = The `LogLevel` for the `MultiLogger`. By default the
- `LogLevel` for `MultiLogger` is `LogLevel.all`.
-
- Example:
- -------------
- auto l1 = new MultiLogger(LogLevel.trace);
- -------------
- */
- this(const LogLevel lv = LogLevel.all) @safe
- {
- super(lv);
- }
-
- /** This member holds all `Logger`s stored in the `MultiLogger`.
-
- When inheriting from `MultiLogger` this member can be used to gain
- access to the stored `Logger`.
- */
- protected MultiLoggerEntry[] logger;
-
- /** This method inserts a new Logger into the `MultiLogger`.
-
- Params:
- name = The name of the `Logger` to insert.
- newLogger = The `Logger` to insert.
- */
- void insertLogger(string name, Logger newLogger) @safe
- {
- this.logger ~= MultiLoggerEntry(name, newLogger);
- }
-
- /** This method removes a Logger from the `MultiLogger`.
-
- Params:
- toRemove = The name of the `Logger` to remove. If the `Logger`
- is not found `null` will be returned. Only the first occurrence of
- a `Logger` with the given name will be removed.
-
- Returns: The removed `Logger`.
- */
- Logger removeLogger(in char[] toRemove) @safe
- {
- import std.algorithm.mutation : copy;
- import std.range.primitives : back, popBack;
- for (size_t i = 0; i < this.logger.length; ++i)
- {
- if (this.logger[i].name == toRemove)
- {
- Logger ret = this.logger[i].logger;
- this.logger[i] = this.logger.back;
- this.logger.popBack();
-
- return ret;
- }
- }
-
- return null;
- }
-
- /* The override to pass the payload to all children of the
- `MultiLoggerBase`.
- */
- override protected void writeLogMsg(ref LogEntry payload) @safe
- {
- foreach (it; this.logger)
- {
- /* We don't perform any checks here to avoid race conditions.
- Instead the child will check on its own if its log level matches
- and assume LogLevel.all for the globalLogLevel (since we already
- know the message passes this test).
- */
- it.logger.forwardMsg(payload);
- }
- }
-}
-
-@safe unittest
-{
- import std.exception : assertThrown;
- import std.experimental.logger.nulllogger;
- auto a = new MultiLogger;
- auto n0 = new NullLogger();
- auto n1 = new NullLogger();
- a.insertLogger("zero", n0);
- a.insertLogger("one", n1);
-
- auto n0_1 = a.removeLogger("zero");
- assert(n0_1 is n0);
- auto n = a.removeLogger("zero");
- assert(n is null);
-
- auto n1_1 = a.removeLogger("one");
- assert(n1_1 is n1);
- n = a.removeLogger("one");
- assert(n is null);
-}
-
-@safe unittest
-{
- auto a = new MultiLogger;
- auto n0 = new TestLogger;
- auto n1 = new TestLogger;
- a.insertLogger("zero", n0);
- a.insertLogger("one", n1);
-
- a.log("Hello TestLogger"); int line = __LINE__;
- assert(n0.msg == "Hello TestLogger");
- assert(n0.line == line);
- assert(n1.msg == "Hello TestLogger");
- assert(n1.line == line);
-}
-
-// Issue #16
-@system unittest
-{
- import std.file : deleteme;
- import std.stdio : File;
- import std.string : indexOf;
- string logName = deleteme ~ __FUNCTION__ ~ ".log";
- auto logFileOutput = File(logName, "w");
- scope(exit)
- {
- import std.file : remove;
- logFileOutput.close();
- remove(logName);
- }
- auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
- auto infoLog = new TestLogger(LogLevel.info);
-
- auto root = new MultiLogger(LogLevel.all);
- root.insertLogger("fileLogger", traceLog);
- root.insertLogger("stdoutLogger", infoLog);
-
- string tMsg = "A trace message";
- root.trace(tMsg); int line1 = __LINE__;
-
- assert(infoLog.line != line1);
- assert(infoLog.msg != tMsg);
-
- string iMsg = "A info message";
- root.info(iMsg); int line2 = __LINE__;
-
- assert(infoLog.line == line2);
- assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
-
- logFileOutput.close();
- logFileOutput = File(logName, "r");
- assert(logFileOutput.isOpen);
- assert(!logFileOutput.eof);
-
- auto line = logFileOutput.readln();
- assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg);
- assert(!logFileOutput.eof);
- line = logFileOutput.readln();
- assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg);
-}
-
-@system unittest
-{
- auto dl = cast(FileLogger) sharedLog;
- assert(dl !is null);
- assert(dl.logLevel == LogLevel.info);
- assert(globalLogLevel == LogLevel.all);
-
- auto tl = cast(StdForwardLogger) stdThreadLocalLog;
- assert(tl !is null);
- stdThreadLocalLog.logLevel = LogLevel.all;
-}
+public import std.logger.multilogger;
diff --git a/libphobos/src/std/experimental/logger/nulllogger.d b/libphobos/src/std/experimental/logger/nulllogger.d
index 0ff7663bd92..2c1f0ba9db5 100644
--- a/libphobos/src/std/experimental/logger/nulllogger.d
+++ b/libphobos/src/std/experimental/logger/nulllogger.d
@@ -1,41 +1,13 @@
-// Written in the D programming language.
/**
-Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d)
-*/
+ * This module is now deprecated, use $(MREF std, logger, nulllogger)
+ * instead.
+ *
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/experimental/logger/nulllogger.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
module std.experimental.logger.nulllogger;
-
-import std.experimental.logger.core;
-
-/** The `NullLogger` will not process any log messages.
-
-In case of a log message with `LogLevel.fatal` nothing will happen.
-*/
-class NullLogger : Logger
-{
- /** The default constructor for the `NullLogger`.
-
- Independent of the parameter this Logger will never log a message.
-
- Params:
- lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel`
- for `NullLogger` is `LogLevel.all`.
- */
- this(const LogLevel lv = LogLevel.all) @safe
- {
- super(lv);
- this.fatalHandler = delegate() {};
- }
-
- override protected void writeLogMsg(ref LogEntry payload) @safe @nogc
- {
- }
-}
-
-///
-@safe unittest
-{
- import std.experimental.logger.core : LogLevel;
- auto nl1 = new NullLogger(LogLevel.all);
- nl1.info("You will never read this.");
- nl1.fatal("You will never read this, either and it will not throw");
-}
+public import std.logger.nulllogger;
diff --git a/libphobos/src/std/experimental/logger/package.d b/libphobos/src/std/experimental/logger/package.d
index 89dc7131fb3..4d19ea9c03b 100644
--- a/libphobos/src/std/experimental/logger/package.d
+++ b/libphobos/src/std/experimental/logger/package.d
@@ -1,168 +1,17 @@
-// Written in the D programming language.
/**
-Implements logging facilities.
-
-Copyright: Copyright Robert "burner" Schadek 2013 --
-License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
-Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek)
-
-$(H3 Basic Logging)
-
-Message logging is a common approach to expose runtime information of a
-program. Logging should be easy, but also flexible and powerful, therefore
-`D` provides a standard interface for logging.
-
-The easiest way to create a log message is to write:
--------------
-import std.experimental.logger;
-
-void main() {
- log("Hello World");
-}
--------------
-This will print a message to the `stderr` device. The message will contain
-the filename, the line number, the name of the surrounding function, the time
-and the message.
-
-More complex log call can go along the lines like:
--------------
-log("Logging to the sharedLog with its default LogLevel");
-logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging");
-info("Logging to the sharedLog with its info LogLevel");
-warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6");
-error("Logging to the sharedLog with its error LogLevel");
-errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with");
-critical("Logging to the"," sharedLog with its error LogLevel");
-fatal("Logging to the sharedLog with its fatal LogLevel");
-
-auto fLogger = new FileLogger("NameOfTheLogFile");
-fLogger.log("Logging to the fileLogger with its default LogLevel");
-fLogger.info("Logging to the fileLogger with its default LogLevel");
-fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6");
-fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less");
-fLogger.critical("Logging to the fileLogger with its info LogLevel");
-fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6");
-fLogger.fatal("Logging to the fileLogger with its warning LogLevel");
--------------
-Additionally, this example shows how a new `FileLogger` is created.
-Individual `Logger` and the global log functions share commonly named
-functions to log data.
-
-The names of the functions are as follows:
-$(UL
- $(LI `log`)
- $(LI `trace`)
- $(LI `info`)
- $(LI `warning`)
- $(LI `critical`)
- $(LI `fatal`)
-)
-The default `Logger` will by default log to `stderr` and has a default
-`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
-using the property called `sharedLog`. This property is a reference to the
-current default `Logger`. This reference can be used to assign a new
-default `Logger`.
--------------
-sharedLog = new FileLogger("New_Default_Log_File.log");
--------------
-
-Additional `Logger` can be created by creating a new instance of the
-required `Logger`.
-
-$(H3 Logging Fundamentals)
-$(H4 LogLevel)
-The `LogLevel` of a log call can be defined in two ways. The first is by
-calling `log` and passing the `LogLevel` explicitly as the first argument.
-The second way of setting the `LogLevel` of a
-log call, is by calling either `trace`, `info`, `warning`,
-`critical`, or `fatal`. The log call will then have the respective
-`LogLevel`. If no `LogLevel` is defined the log call will use the
-current `LogLevel` of the used `Logger`. If data is logged with
-`LogLevel` `fatal` by default an `Error` will be thrown.
-This behaviour can be modified by using the member `fatalHandler` to
-assign a custom delegate to handle log call with `LogLevel` `fatal`.
-
-$(H4 Conditional Logging)
-Conditional logging can be achieved be passing a `bool` as first
-argument to a log function. If conditional logging is used the condition must
-be `true` in order to have the log message logged.
-
-In order to combine an explicit `LogLevel` passing with conditional
-logging, the `LogLevel` has to be passed as first argument followed by the
-`bool`.
-
-$(H4 Filtering Log Messages)
-Messages are logged if the `LogLevel` of the log message is greater than or
-equal to the `LogLevel` of the used `Logger` and additionally if the
-`LogLevel` of the log message is greater than or equal to the global `LogLevel`.
-If a condition is passed into the log call, this condition must be true.
-
-The global `LogLevel` is accessible by using `globalLogLevel`.
-To assign a `LogLevel` of a `Logger` use the `logLevel` property of
-the logger.
-
-$(H4 Printf Style Logging)
-If `printf`-style logging is needed add a $(B f) to the logging call, such as
-$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)).
-The additional $(B f) appended to the function name enables `printf`-style
-logging for all combinations of explicit `LogLevel` and conditional
-logging functions and methods.
-
-$(H4 Thread Local Redirection)
-Calls to the free standing log functions are not directly forwarded to the
-global `Logger` `sharedLog`. Actually, a thread local `Logger` of
-type `StdForwardLogger` processes the log call and then, by default, forwards
-the created `Logger.LogEntry` to the `sharedLog` `Logger`.
-The thread local `Logger` is accessible by the `stdThreadLocalLog`
-property. This property allows to assign user defined `Logger`. The default
-`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all`
-and it will therefore forward all messages to the `sharedLog` `Logger`.
-The `LogLevel` of the `stdThreadLocalLog` can be used to filter log
-calls before they reach the `sharedLog` `Logger`.
-
-$(H3 User Defined Logger)
-To customize the `Logger` behavior, create a new `class` that inherits from
-the abstract `Logger` `class`, and implements the `writeLogMsg`
-method.
--------------
-class MyCustomLogger : Logger
-{
- this(LogLevel lv) @safe
- {
- super(lv);
- }
-
- override void writeLogMsg(ref LogEntry payload)
- {
- // log message in my custom way
- }
-}
-
-auto logger = new MyCustomLogger(LogLevel.info);
-logger.log("Awesome log message with LogLevel.info");
--------------
-
-To gain more precise control over the logging process, additionally to
-overriding the `writeLogMsg` method the methods `beginLogMsg`,
-`logMsgPart` and `finishLogMsg` can be overridden.
-
-$(H3 Provided Logger)
-By default four `Logger` implementations are given. The `FileLogger`
-logs data to files. It can also be used to log to `stdout` and `stderr`
-as these devices are files as well. A `Logger` that logs to `stdout` can
-therefore be created by $(D new FileLogger(stdout)).
-The `MultiLogger` is basically an associative array of `string`s to
-`Logger`. It propagates log calls to its stored `Logger`. The
-`ArrayLogger` contains an array of `Logger` and also propagates log
-calls to its stored `Logger`. The `NullLogger` does not do anything. It
-will never log a message and will never throw on a log call with `LogLevel`
-`error`.
-
-Source: $(PHOBOSSRC std/experimental/logger/package.d)
-*/
+ * This module is now deprecated, use $(MREF std, logger)
+ * instead.
+ *
+ * Copyright: Copyright The D Language Foundation 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/experimental/logger/package.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
module std.experimental.logger;
-public import std.experimental.logger.core;
-public import std.experimental.logger.filelogger;
-public import std.experimental.logger.multilogger;
-public import std.experimental.logger.nulllogger;
+public import std.logger.core;
+public import std.logger.filelogger;
+public import std.logger.multilogger;
+public import std.logger.nulllogger;
diff --git a/libphobos/src/std/experimental/typecons.d b/libphobos/src/std/experimental/typecons.d
deleted file mode 100644
index 46e21e77e7a..00000000000
--- a/libphobos/src/std/experimental/typecons.d
+++ /dev/null
@@ -1,1083 +0,0 @@
-// Written in the D programming language.
-
-/**
-This module implements experimental additions/modifications to $(MREF std, typecons).
-
-Use this module to test out new functionality for $(REF wrap, std, typecons)
-which allows for a struct to be wrapped against an interface; the
-implementation in $(MREF std, typecons) only allows for classes to use the wrap
-functionality.
-
-Source: $(PHOBOSSRC std/experimental/typecons.d)
-
-Copyright: Copyright the respective authors, 2008-
-License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
-Authors: $(HTTP erdani.org, Andrei Alexandrescu),
- $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
- Don Clugston,
- Shin Fujishiro,
- Kenji Hara
- */
-module std.experimental.typecons;
-
-import std.meta; // : AliasSeq, allSatisfy;
-import std.traits;
-
-import std.typecons : Tuple, tuple, Bind, DerivedFunctionType, GetOverloadedMethods;
-
-private
-{
- pragma(mangle, "_d_toObject")
- extern(C) pure nothrow Object typecons_d_toObject(void* p);
-}
-
-/*
- * Avoids opCast operator overloading.
- */
-private template dynamicCast(T)
-if (is(T == class) || is(T == interface))
-{
- @trusted
- T dynamicCast(S)(inout S source)
- if (is(S == class) || is(S == interface))
- {
- static if (is(Unqual!S : Unqual!T))
- {
- import std.traits : QualifierOf;
- alias Qual = QualifierOf!S; // SharedOf or MutableOf
- alias TmpT = Qual!(Unqual!T);
- inout(TmpT) tmp = source; // bypass opCast by implicit conversion
- return *cast(T*)(&tmp); // + variable pointer cast + dereference
- }
- else
- {
- return cast(T) typecons_d_toObject(*cast(void**)(&source));
- }
- }
-}
-
-@system unittest
-{
- class C { @disable void opCast(T)(); }
- auto c = new C;
- static assert(!__traits(compiles, cast(Object) c));
- auto o = dynamicCast!Object(c);
- assert(c is o);
-
- interface I { @disable void opCast(T)(); Object instance(); }
- interface J { @disable void opCast(T)(); Object instance(); }
- class D : I, J { Object instance() { return this; } }
- I i = new D();
- static assert(!__traits(compiles, cast(J) i));
- J j = dynamicCast!J(i);
- assert(i.instance() is j.instance());
-}
-
-/*
- * Determines if the `Source` type satisfies all interface requirements of
- * `Targets`.
- */
-private template implementsInterface(Source, Targets...)
-if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
-{
- import std.meta : staticMap;
-
- // strict upcast
- bool implementsInterface()()
- if (Targets.length == 1 && is(Source : Targets[0]))
- {
- return true;
- }
- // structural upcast
- template implementsInterface()
- if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets))
- {
- auto implementsInterface()
- {
- return hasRequiredMethods!();
- }
-
- // list of FuncInfo
- alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!Targets);
- // list of function symbols
- alias SourceMembers = GetOverloadedMethods!Source;
-
- // Check whether all of SourceMembers satisfy covariance target in
- // TargetMembers
- template hasRequiredMethods(size_t i = 0)
- {
- static if (i >= TargetMembers.length)
- enum hasRequiredMethods = true;
- else
- {
- enum foundFunc = findCovariantFunction!(TargetMembers[i], Source, SourceMembers);
-
- version (StdUnittest) {}
- else debug
- {
- static if (foundFunc == -1)
- pragma(msg, "Could not locate matching function for: ",
- TargetMembers[i].stringof);
- }
-
- enum hasRequiredMethods =
- foundFunc != -1 &&
- hasRequiredMethods!(i + 1);
- }
- }
- }
-}
-// ditto
-private template implementsInterface(Source, Targets...)
-if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
-{
- import std.meta : staticMap;
-
- alias implementsInterface = .implementsInterface!(Source, staticMap!(Unqual, Targets));
-}
-
-@safe unittest
-{
- interface Foo {
- void foo();
- }
- interface Bar {
- void bar();
- }
- interface FooBar : Foo, Bar {
- void foobar();
- }
-
- struct A {
- void foo() {}
- }
- struct B {
- void bar() {}
- void foobar() {}
- }
- class C {
- void foo() {}
- void bar() {}
- }
- struct D {
- void foo() {}
- void bar() {}
- void foobar() {}
- }
- // Implements interface
- static assert(implementsInterface!(A, Foo));
- static assert(implementsInterface!(A, const(Foo)));
- static assert(implementsInterface!(A, immutable(Foo)));
- // Doesn't implement interface
- static assert(!implementsInterface!(B, Foo));
- static assert(implementsInterface!(B, Bar));
- // Implements both interfaces
- static assert(implementsInterface!(C, Foo));
- static assert(implementsInterface!(C, Bar));
- static assert(implementsInterface!(C, Foo, Bar));
- static assert(implementsInterface!(C, Foo, const(Bar)));
- static assert(!implementsInterface!(A, Foo, Bar));
- static assert(!implementsInterface!(A, Foo, immutable(Bar)));
- // Implements inherited
- static assert(implementsInterface!(D, FooBar));
- static assert(!implementsInterface!(B, FooBar));
-}
-
-private enum isInterface(ConceptType) = is(ConceptType == interface);
-
-///
-template wrap(Targets...)
-if (Targets.length >= 1 && allSatisfy!(isInterface, Targets))
-{
- import std.meta : ApplyLeft, staticMap;
-
- version (StdDdoc)
- {
- /**
- * Wrap src in an anonymous class implementing $(D_PARAM Targets).
- *
- * wrap creates an internal wrapper class which implements the
- * interfaces in `Targets` using the methods of `src`, then returns a
- * GC-allocated instance of it.
- *
- * $(D_PARAM Source) can be either a `class` or a `struct`, but it must
- * $(I structurally conform) with all the $(D_PARAM Targets)
- * interfaces; i.e. it must provide concrete methods with compatible
- * signatures of those in $(D_PARAM Targets).
- *
- * If $(D_PARAM Source) is a `struct` then wrapping/unwrapping will
- * create a copy; it is not possible to affect the original `struct`
- * through the wrapper.
- *
- * The returned object additionally supports $(LREF unwrap).
- *
- * Note:
- * If $(D_PARAM Targets) has only one entry and $(D_PARAM Source) is a
- * class which explicitly implements it, wrap simply returns src
- * upcasted to `Targets[0]`.
- *
- * Bugs:
- * wrap does not support interfaces which take their own type as either
- * a parameter type or return type in any of its methods.
- *
- * See_Also: $(LREF unwrap) for examples
- */
- auto wrap(Source)(inout Source src)
- if (implementsInterface!(Source, Targets));
- }
-
- static if (!allSatisfy!(isMutable, Targets))
- alias wrap = .wrap!(staticMap!(Unqual, Targets));
- else
- {
- // strict upcast
- auto wrap(Source)(inout Source src)
- if (Targets.length == 1 && is(Source : Targets[0]))
- {
- alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]);
- return dynamicCast!(inout T)(src);
- }
-
- // structural upcast
- template wrap(Source)
- if (!allSatisfy!(ApplyLeft!(isImplicitlyConvertible, Source), Targets))
- {
- auto wrap(inout Source src)
- {
- static assert(implementsInterface!(Source, Targets),
- "Source "~Source.stringof~
- " does not have structural conformance to "~
- Targets.stringof);
-
- alias T = Select!(is(Source == shared), shared Impl, Impl);
- return new inout T(src);
- }
-
- // list of FuncInfo
- alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!(Targets));
- // list of function symbols
- alias SourceMembers = GetOverloadedMethods!Source;
-
- static if (is(Source == class) || is(Source == interface))
- alias StructuralType = Object;
- else static if (is(Source == struct))
- alias StructuralType = Source;
-
- // Check whether all of SourceMembers satisfy covariance target in TargetMembers
- // Internal wrapper class
- final class Impl : Structural!StructuralType, Targets
- {
- private:
- Source _wrap_source;
-
- this( inout Source s) inout @safe pure nothrow { _wrap_source = s; }
- this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; }
-
- static if (is(Source == class) || is(Source == interface))
- {
- // BUG: making private should work with NVI.
- protected inout(Object) _wrap_getSource() inout @safe
- {
- return dynamicCast!(inout Object)(_wrap_source);
- }
- }
- else
- {
- // BUG: making private should work with NVI.
- protected inout(Source) _wrap_getSource() inout @safe
- {
- return _wrap_source;
- }
- }
-
- import std.conv : to;
- import core.lifetime : forward;
- template generateFun(size_t i)
- {
- enum name = TargetMembers[i].name;
- enum fa = functionAttributes!(TargetMembers[i].type);
- static args(int num)()
- {
- string r;
- bool first = true;
- foreach (i; 0 .. num)
- {
- import std.conv : to;
- r ~= (first ? "" : ", ") ~ " a" ~ (i+1).to!string;
- first = false;
- }
- return r;
- }
- static if (fa & FunctionAttribute.property)
- {
- static if (Parameters!(TargetMembers[i].type).length == 0)
- enum fbody = "_wrap_source."~name;
- else
- enum fbody = "_wrap_source."~name~" = a1";
- }
- else
- {
- enum fbody = "_wrap_source."~name~"("~args!(Parameters!(TargetMembers[i].type).length)~")";
- }
- enum generateFun =
- "override "~wrapperSignature!(TargetMembers[i]) ~
- "{ return "~fbody~"; }";
- }
-
- public:
- static foreach (i; 0 .. TargetMembers.length)
- mixin(generateFun!i);
- }
- }
- }
-}
-
-// Build a signature that matches the provided function
-// Each argument will be provided a name in the form a#
-private template wrapperSignature(alias fun)
-{
- enum name = fun.name;
- enum fa = functionAttributes!(fun.type);
- static @property stc()
- {
- string r;
- if (fa & FunctionAttribute.property) r ~= "@property ";
- if (fa & FunctionAttribute.ref_) r ~= "ref ";
- if (fa & FunctionAttribute.pure_) r ~= "pure ";
- if (fa & FunctionAttribute.nothrow_) r ~= "nothrow ";
- if (fa & FunctionAttribute.trusted) r ~= "@trusted ";
- if (fa & FunctionAttribute.safe) r ~= "@safe ";
- return r;
- }
- static @property mod()
- {
- alias type = AliasSeq!(fun.type)[0];
- string r;
- static if (is(type == immutable)) r ~= " immutable";
- else
- {
- static if (is(type == shared)) r ~= " shared";
- static if (is(type == const)) r ~= " const";
- else static if (is(type == inout)) r ~= " inout";
- //else --> mutable
- }
- return r;
- }
- alias param = Parameters!(fun.type);
- static @property wrapperParameters()
- {
- string r;
- bool first = true;
- foreach (i, p; param)
- {
- import std.conv : to;
- r ~= (first ? "" : ", ") ~ p.stringof ~ " a" ~ (i+1).to!string;
- first = false;
- }
- return r;
- }
-
- enum wrapperSignature =
- stc~ReturnType!(fun.type).stringof ~ " "
- ~ name~"("~wrapperParameters~")"~mod;
-}
-
-@safe unittest
-{
- interface M
- {
- void f1();
- void f2(string[] args, int count);
- void f3(string[] args, int count) pure const;
- }
-
- alias TargetMembers = UniqMembers!(ConcatInterfaceMembers!M);
- static assert(wrapperSignature!(TargetMembers[0]) == "void f1()"
- , wrapperSignature!(TargetMembers[0]));
-
- static assert(wrapperSignature!(TargetMembers[1]) == "void f2(string[] a1, int a2)"
- , wrapperSignature!(TargetMembers[1]));
-
- static assert(wrapperSignature!(TargetMembers[2]) == "pure void f3(string[] a1, int a2) const"
- , wrapperSignature!(TargetMembers[2]));
-}
-
-// Internal class to support dynamic cross-casting
-private interface Structural(T)
-{
- inout(T) _wrap_getSource() inout @safe pure nothrow;
-}
-
-private string unwrapExceptionText(Source, Target)()
-{
- return Target.stringof~ " not wrapped into "~ Source.stringof;
-}
-
-version (StdDdoc)
-{
- /**
- * Extract object previously wrapped by $(LREF wrap).
- *
- * Params:
- * Target = type of wrapped object
- * src = wrapper object returned by $(LREF wrap)
- *
- * Returns: the wrapped object, or null if src is not a wrapper created
- * by $(LREF wrap) and $(D_PARAM Target) is a class
- *
- * Throws: $(REF ConvException, std, conv) when attempting to extract a
- * struct which is not the wrapped type
- *
- * See_also: $(LREF wrap)
- */
- public inout(Target) unwrap(Target, Source)(inout Source src);
-}
-
-///
-@system unittest
-{
- interface Quack
- {
- int quack();
- @property int height();
- }
- interface Flyer
- {
- @property int height();
- }
- class Duck : Quack
- {
- int quack() { return 1; }
- @property int height() { return 10; }
- }
- class Human
- {
- int quack() { return 2; }
- @property int height() { return 20; }
- }
- struct HumanStructure
- {
- int quack() { return 3; }
- @property int height() { return 30; }
- }
-
- Duck d1 = new Duck();
- Human h1 = new Human();
- HumanStructure hs1;
-
- interface Refreshable
- {
- int refresh();
- }
- // does not have structural conformance
- static assert(!__traits(compiles, d1.wrap!Refreshable));
- static assert(!__traits(compiles, h1.wrap!Refreshable));
- static assert(!__traits(compiles, hs1.wrap!Refreshable));
-
- // strict upcast
- Quack qd = d1.wrap!Quack;
- assert(qd is d1);
- assert(qd.quack() == 1); // calls Duck.quack
- // strict downcast
- Duck d2 = qd.unwrap!Duck;
- assert(d2 is d1);
-
- // structural upcast
- Quack qh = h1.wrap!Quack;
- Quack qhs = hs1.wrap!Quack;
- assert(qh.quack() == 2); // calls Human.quack
- assert(qhs.quack() == 3); // calls HumanStructure.quack
- // structural downcast
- Human h2 = qh.unwrap!Human;
- HumanStructure hs2 = qhs.unwrap!HumanStructure;
- assert(h2 is h1);
- assert(hs2 is hs1);
-
- // structural upcast (two steps)
- Quack qx = h1.wrap!Quack; // Human -> Quack
- Quack qxs = hs1.wrap!Quack; // HumanStructure -> Quack
- Flyer fx = qx.wrap!Flyer; // Quack -> Flyer
- Flyer fxs = qxs.wrap!Flyer; // Quack -> Flyer
- assert(fx.height == 20); // calls Human.height
- assert(fxs.height == 30); // calls HumanStructure.height
- // strucural downcast (two steps)
- Quack qy = fx.unwrap!Quack; // Flyer -> Quack
- Quack qys = fxs.unwrap!Quack; // Flyer -> Quack
- Human hy = qy.unwrap!Human; // Quack -> Human
- HumanStructure hys = qys.unwrap!HumanStructure; // Quack -> HumanStructure
- assert(hy is h1);
- assert(hys is hs1);
- // strucural downcast (one step)
- Human hz = fx.unwrap!Human; // Flyer -> Human
- HumanStructure hzs = fxs.unwrap!HumanStructure; // Flyer -> HumanStructure
- assert(hz is h1);
- assert(hzs is hs1);
-}
-
-///
-@system unittest
-{
- import std.traits : functionAttributes, FunctionAttribute;
- interface A { int run(); }
- interface B { int stop(); @property int status(); }
- class X
- {
- int run() { return 1; }
- int stop() { return 2; }
- @property int status() { return 3; }
- }
-
- auto x = new X();
- auto ab = x.wrap!(A, B);
- A a = ab;
- B b = ab;
- assert(a.run() == 1);
- assert(b.stop() == 2);
- assert(b.status == 3);
- static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
-}
-
-template unwrap(Target)
-{
- static if (!isMutable!Target)
- alias unwrap = .unwrap!(Unqual!Target);
- else
- {
- // strict downcast
- auto unwrap(Source)(inout Source src)
- if (is(Target : Source))
- {
- alias T = Select!(is(Source == shared), shared Target, Target);
- return dynamicCast!(inout T)(src);
- }
-
- // structural downcast for struct target
- auto unwrap(Source)(inout Source src)
- if (is(Target == struct))
- {
- alias T = Select!(is(Source == shared), shared Target, Target);
- auto upCastSource = dynamicCast!Object(src); // remove qualifier
- do
- {
- if (auto a = dynamicCast!(Structural!Object)(upCastSource))
- {
- upCastSource = a._wrap_getSource();
- }
- else if (auto a = dynamicCast!(Structural!T)(upCastSource))
- {
- return a._wrap_getSource();
- }
- else
- {
- static if (hasMember!(Source, "_wrap_getSource"))
- return unwrap!Target(src._wrap_getSource());
- else
- break;
- }
- } while (upCastSource);
- import std.conv : ConvException;
- throw new ConvException(unwrapExceptionText!(Source,Target));
- }
- // structural downcast for class target
- auto unwrap(Source)(inout Source src)
- if (!is(Target : Source) && !is(Target == struct))
- {
- alias T = Select!(is(Source == shared), shared Target, Target);
- Object upCastSource = dynamicCast!(Object)(src); // remove qualifier
- do
- {
- // Unwrap classes
- if (auto a = dynamicCast!(Structural!Object)(upCastSource))
- {
- if (auto d = dynamicCast!(inout T)(upCastSource = a._wrap_getSource()))
- return d;
- }
- // Unwrap a structure of type T
- else if (auto a = dynamicCast!(Structural!T)(upCastSource))
- {
- return a._wrap_getSource();
- }
- // Unwrap class that already inherited from interface
- else if (auto d = dynamicCast!(inout T)(upCastSource))
- {
- return d;
- }
- // Recurse to find the struct Target within a wrapped tree
- else
- {
- static if (hasMember!(Source, "_wrap_getSource"))
- return unwrap!Target(src._wrap_getSource());
- else
- break;
- }
- } while (upCastSource);
- return null;
- }
- }
-}
-
-@system unittest
-{
- // Validate const/immutable
- class A
- {
- int draw() { return 1; }
- int draw(int v) { return v; }
-
- int draw() const { return 2; }
- int draw() shared { return 3; }
- int draw() shared const { return 4; }
- int draw() immutable { return 5; }
- }
- interface Drawable
- {
- int draw();
- int draw() const;
- int draw() shared;
- int draw() shared const;
- int draw() immutable;
- }
- interface Drawable2
- {
- int draw(int v);
- }
-
- auto ma = new A();
- auto sa = new shared A();
- auto ia = new immutable A();
- {
- Drawable md = ma.wrap!Drawable;
- const Drawable cd = ma.wrap!Drawable;
- shared Drawable sd = sa.wrap!Drawable;
- shared const Drawable scd = sa.wrap!Drawable;
- immutable Drawable id = ia.wrap!Drawable;
- assert( md.draw() == 1);
- assert( cd.draw() == 2);
- assert( sd.draw() == 3);
- assert(scd.draw() == 4);
- assert( id.draw() == 5);
- }
- {
- Drawable2 d = ma.wrap!Drawable2;
- static assert(!__traits(compiles, d.draw()));
- assert(d.draw(10) == 10);
- }
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=10377
-@system unittest
-{
- import std.algorithm, std.range;
-
- interface MyInputRange(T)
- {
- @property T front();
- void popFront();
- @property bool empty();
- }
-
- //auto o = iota(0,10,1).inputRangeObject();
- //pragma(msg, __traits(allMembers, typeof(o)));
- auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
- assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=10536
-@system unittest
-{
- interface Interface
- {
- int foo();
- }
- class Pluggable
- {
- int foo() { return 1; }
- @disable void opCast(T, this X)(); // !
- }
-
- Interface i = new Pluggable().wrap!Interface;
- assert(i.foo() == 1);
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=10538
-@system unittest
-{
- interface Interface
- {
- int foo();
- int bar(int);
- }
- class Pluggable
- {
- int opDispatch(string name, A...)(A args) { return 100; }
- }
-
- Interface i = wrap!Interface(new Pluggable());
- assert(i.foo() == 100);
- assert(i.bar(10) == 100);
-}
-
-// Concat all Targets function members into one tuple
-private template ConcatInterfaceMembers(Targets...)
-{
- static if (Targets.length == 0)
- alias ConcatInterfaceMembers = AliasSeq!();
- else static if (Targets.length == 1)
- alias ConcatInterfaceMembers
- = AliasSeq!(GetOverloadedMethods!(Targets[0]));
- else
- alias ConcatInterfaceMembers = AliasSeq!(
- GetOverloadedMethods!(Targets[0]),
- ConcatInterfaceMembers!(Targets[1..$]));
-}
-// Remove duplicated functions based on the identifier name and function type covariance
-private template UniqMembers(members...)
-{
- template FuncInfo(string s, F)
- {
- enum name = s;
- alias type = F;
- }
-
- static if (members.length == 0)
- alias UniqMembers = AliasSeq!();
- else
- {
- alias func = members[0];
- enum name = __traits(identifier, func);
- alias type = FunctionTypeOf!func;
- template check(size_t i, mem...)
- {
- static if (i >= mem.length)
- enum ptrdiff_t check = -1;
- else static if
- (__traits(identifier, func) == __traits(identifier, mem[i]) &&
- !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void))
- {
- enum ptrdiff_t check = i;
- }
- else
- enum ptrdiff_t check = check!(i + 1, mem);
- }
- enum ptrdiff_t x = 1 + check!(0, members[1 .. $]);
- static if (x >= 1)
- {
- alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x]));
- alias remain = UniqMembers!(members[1 .. x], members[x + 1 .. $]);
-
- static if (remain.length >= 1 && remain[0].name == name &&
- !is(DerivedFunctionType!(typex, remain[0].type) == void))
- {
- alias F = DerivedFunctionType!(typex, remain[0].type);
- alias UniqMembers = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]);
- }
- else
- alias UniqMembers = AliasSeq!(FuncInfo!(name, typex), remain);
- }
- else
- {
- alias UniqMembers = AliasSeq!(FuncInfo!(name, type), UniqMembers!(members[1 .. $]));
- }
- }
-}
-
-// find a function from Fs that has same identifier and covariant type with f
-private template findCovariantFunction(alias finfo, Source, Fs...)
-{
- template check(size_t i = 0)
- {
- static if (i >= Fs.length)
- enum ptrdiff_t check = -1;
- else
- {
- enum ptrdiff_t check =
- (finfo.name == __traits(identifier, Fs[i])) &&
- isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type)
- ? i : check!(i + 1);
- }
- }
- enum x = check!();
- static if (x == -1 && is(typeof(Source.opDispatch)))
- {
- alias Params = Parameters!(finfo.type);
- enum ptrdiff_t findCovariantFunction =
- is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) ||
- is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) ||
- is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) ||
- is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) ||
- is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init)))
- ? ptrdiff_t.max : -1;
- }
- else
- enum ptrdiff_t findCovariantFunction = x;
-}
-
-/**
-Type constructor for final (aka head-const) variables.
-
-Final variables cannot be directly mutated or rebound, but references
-reached through the variable are typed with their original mutability.
-It is equivalent to `final` variables in D1 and Java, as well as
-`readonly` variables in C#.
-
-When `T` is a `const` or `immutable` type, `Final` aliases
-to `T`.
-*/
-template Final(T)
-{
-static if (is(T == const) || is(T == immutable))
- alias Final = T;
-else
-{
- struct Final
- {
- import std.typecons : Proxy;
-
- private T final_value;
- mixin Proxy!final_value;
-
- /**
- * Construction is forwarded to the underlying type.
- */
- this(T other)
- {
- this.final_value = other;
- }
-
- /// Ditto
- this(Args...)(auto ref Args args)
- if (__traits(compiles, T(args)))
- {
- static assert((!is(T == struct) && !is(T == union)) || !isNested!T,
- "Non-static nested type " ~ fullyQualifiedName!T ~ " must be " ~
- "constructed explicitly at the call-site (e.g. auto s = " ~
- "makeFinal(" ~ T.stringof ~ "(...));)");
- this.final_value = T(args);
- }
-
- // Attaching function attributes gives less noisy error messages
- pure nothrow @safe @nogc
- {
- /++
- + All operators, including member access, are forwarded to the
- + underlying value of type `T` except for these mutating operators,
- + which are disabled.
- +/
- void opAssign(Other)(Other other)
- {
- static assert(0, typeof(this).stringof ~
- " cannot be reassigned.");
- }
-
- /// Ditto
- void opOpAssign(string op, Other)(Other other)
- {
- static assert(0, typeof(this).stringof ~
- " cannot be reassigned.");
- }
-
- /// Ditto
- void opUnary(string op : "--")()
- {
- static assert(0, typeof(this).stringof ~
- " cannot be mutated.");
- }
-
- /// Ditto
- void opUnary(string op : "++")()
- {
- static assert(0, typeof(this).stringof ~
- " cannot be mutated.");
- }
- }
-
- /**
- *
- * `Final!T` implicitly converts to an rvalue of type `T` through
- * `AliasThis`.
- */
- inout(T) final_get() inout
- {
- return final_value;
- }
-
- /// Ditto
- alias final_get this;
-
- /// Ditto
- auto ref opUnary(string op)()
- if (__traits(compiles, mixin(op ~ "T.init")))
- {
- return mixin(op ~ "this.final_value");
- }
- }
-}
-}
-
-/// Ditto
-Final!T makeFinal(T)(T t)
-{
- return Final!T(t);
-}
-
-/// `Final` can be used to create class references which cannot be rebound:
-pure nothrow @safe unittest
-{
- static class A
- {
- int i;
-
- this(int i) pure nothrow @nogc @safe
- {
- this.i = i;
- }
- }
-
- auto a = makeFinal(new A(42));
- assert(a.i == 42);
-
- //a = new A(24); // Reassignment is illegal,
- a.i = 24; // But fields are still mutable.
-
- assert(a.i == 24);
-}
-
-/// `Final` can also be used to create read-only data fields without using transitive immutability:
-pure nothrow @safe unittest
-{
- static class A
- {
- int i;
-
- this(int i) pure nothrow @nogc @safe
- {
- this.i = i;
- }
- }
-
- static class B
- {
- Final!A a;
-
- this(A a) pure nothrow @nogc @safe
- {
- this.a = a; // Construction, thus allowed.
- }
- }
-
- auto b = new B(new A(42));
- assert(b.a.i == 42);
-
- // b.a = new A(24); // Reassignment is illegal,
- b.a.i = 24; // but `a` is still mutable.
-
- assert(b.a.i == 24);
-}
-
-pure nothrow @safe unittest
-{
- static class A { int i; }
- static assert(!is(Final!A == A));
- static assert(is(Final!(const A) == const A));
- static assert(is(Final!(immutable A) == immutable A));
-
- Final!A a = new A;
- static assert(!__traits(compiles, a = new A));
-
- assert(a.i == 0);
- a.i = 42;
- assert(a.i == 42);
-
- Final!int i = 42;
- static assert(!__traits(compiles, i = 24));
- static assert(!__traits(compiles, --i));
- static assert(!__traits(compiles, ++i));
- assert(i == 42);
- int iCopy = i;
- assert(iCopy == 42);
- iCopy = -i; // non-mutating unary operators must work
- assert(iCopy == -42);
-
- static struct S
- {
- int i;
-
- pure nothrow @safe @nogc:
- this(int i){}
- this(string s){}
- this(int i, string s, float f){ this.i = i; }
- }
-
- Final!S sint = 42;
- Final!S sstr = "foo";
- static assert(!__traits(compiles, sint = sstr));
-
- auto sboth = Final!S(42, "foo", 3.14);
- assert(sboth.i == 42);
-
- sboth.i = 24;
- assert(sboth.i == 24);
-
- struct NestedS
- {
- int i;
- int get() pure nothrow @safe @nogc { return sboth.i + i; }
- }
-
- // Nested structs must be constructed at the call-site
- static assert(!__traits(compiles, Final!NestedS(6)));
- auto s = makeFinal(NestedS(6));
- assert(s.i == 6);
- assert(s.get == 30);
-
- class NestedC
- {
- int i;
-
- pure nothrow @safe @nogc:
- this(int i) { this.i = i; }
- int get() { return sboth.i + i; }
- }
-
- auto c = makeFinal(new NestedC(6));
- assert(c.i == 6);
- assert(c.get == 30);
-}
-
-pure nothrow @safe unittest
-{
- auto arr = makeFinal([1, 2, 3]);
- static assert(!__traits(compiles, arr = null));
- static assert(!__traits(compiles, arr ~= 4));
- assert((arr ~ 4) == [1, 2, 3, 4]);
-}
-
-// https://issues.dlang.org/show_bug.cgi?id=17270
-pure nothrow @nogc @system unittest
-{
- int i = 1;
- Final!(int*) fp = &i;
- assert(*fp == 1);
- static assert(!__traits(compiles,
- fp = &i // direct assignment
- ));
- static assert(is(typeof(*fp) == int));
- *fp = 2; // indirect assignment
- assert(*fp == 2);
- int* p = fp;
- assert(*p == 2);
-}
-
-pure nothrow @system unittest
-{
- Final!(int[]) arr;
- // static assert(!__traits(compiles,
- // arr.length = 10; // bug!
- // ));
- static assert(!__traits(compiles,
- arr.ptr = null
- ));
- static assert(!__traits(compiles,
- arr.ptr++
- ));
-}
diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d
index c1c5cd2b36e..b2399013b45 100644
--- a/libphobos/src/std/getopt.d
+++ b/libphobos/src/std/getopt.d
@@ -1835,7 +1835,7 @@ void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, st
assert(flag);
}
-@safe unittest // Delegates as callbacks
+@system unittest // Delegates as callbacks
{
alias TwoArgOptionHandler = void delegate(string option, string value) @safe;
diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d
index c6e746a0beb..ac397e5f973 100644
--- a/libphobos/src/std/json.d
+++ b/libphobos/src/std/json.d
@@ -1537,19 +1537,15 @@ if (isOutputRange!(Out,char))
toStringImpl!char(str);
}
- // recursive @safe inference is broken here
- // workaround: if json.put is @safe, we should be too,
- // so annotate the recursion as @safe manually
- static if (isSafe!({ json.put(""); }))
- {
- void delegate(ref const JSONValue, ulong) @safe toValue;
- }
- else
- {
- void delegate(ref const JSONValue, ulong) @system toValue;
- }
+ /* make the function infer @system when json.put() is @system
+ */
+ if (0)
+ json.put(' ');
- void toValueImpl(ref const JSONValue value, ulong indentLevel)
+ /* Mark as @trusted because json.put() may be @system. This has difficulty
+ * inferring @safe because it is recursive.
+ */
+ void toValueImpl(ref const JSONValue value, ulong indentLevel) @trusted
{
void putTabs(ulong additionalIndent = 0)
{
@@ -1594,7 +1590,7 @@ if (isOutputRange!(Out,char))
json.put(':');
if (pretty)
json.put(' ');
- toValue(member, indentLevel + 1);
+ toValueImpl(member, indentLevel + 1);
}
}
@@ -1631,7 +1627,7 @@ if (isOutputRange!(Out,char))
if (i)
putCharAndEOL(',');
putTabs(1);
- toValue(el, indentLevel + 1);
+ toValueImpl(el, indentLevel + 1);
}
putEOL();
putTabs();
@@ -1710,9 +1706,7 @@ if (isOutputRange!(Out,char))
}
}
- toValue = &toValueImpl;
-
- toValue(root, 0);
+ toValueImpl(root, 0);
}
// https://issues.dlang.org/show_bug.cgi?id=12897
diff --git a/libphobos/src/std/logger/core.d b/libphobos/src/std/logger/core.d
new file mode 100644
index 00000000000..be2bd8d175e
--- /dev/null
+++ b/libphobos/src/std/logger/core.d
@@ -0,0 +1,3049 @@
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/logger/core.d)
+*/
+module std.logger.core;
+
+import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder;
+import core.sync.mutex : Mutex;
+import std.datetime.date : DateTime;
+import std.datetime.systime : Clock, SysTime;
+import std.range.primitives;
+import std.traits;
+
+import std.logger.filelogger;
+
+/** This functions is used at runtime to determine if a `LogLevel` is
+active. The same previously defined version statements are used to disable
+certain levels. Again the version statements are associated with a compile
+unit and can therefore not disable logging in other compile units.
+pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc
+*/
+bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
+ LogLevel globalLL, lazy bool condition = true) @safe
+{
+ return ll >= globalLL
+ && ll >= loggerLL
+ && ll != LogLevel.off
+ && globalLL != LogLevel.off
+ && loggerLL != LogLevel.off
+ && condition;
+}
+
+/* This function formates a `SysTime` into an `OutputRange`.
+
+The `SysTime` is formatted similar to
+$(LREF std.datatime.DateTime.toISOExtString) except the fractional second part.
+The fractional second part is in milliseconds and is always 3 digits.
+*/
+void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time)
+if (isOutputRange!(OutputRange,string))
+{
+ import std.format.write : formattedWrite;
+
+ const auto dt = cast(DateTime) time;
+ const auto fsec = time.fracSecs.total!"msecs";
+
+ formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
+ dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
+ fsec);
+}
+
+/** This function logs data.
+
+In order for the data to be processed, the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel`; additionally the condition passed must be `true`.
+
+Params:
+ ll = The `LogLevel` used by this log call.
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that should be logged.
+
+Example:
+--------------------
+log(LogLevel.warning, true, "Hello World", 3.1415);
+--------------------
+*/
+void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll,
+ lazy bool condition, lazy A args)
+if (args.length != 1)
+{
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, condition, args);
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(const LogLevel ll,
+ lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName,
+ prettyFuncName);
+}
+
+/** This function logs data.
+
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog`.
+
+Params:
+ ll = The `LogLevel` used by this log call.
+ args = The data that should be logged.
+
+Example:
+--------------------
+log(LogLevel.warning, "Hello World", 3.1415);
+--------------------
+*/
+void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
+if (args.length > 1 && !is(Unqual!(A[0]) : bool))
+{
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, args);
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg,
+ int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName,
+ moduleName);
+}
+
+/** This function logs data.
+
+In order for the data to be processed the `LogLevel` of the
+`sharedLog` must be greater or equal to the `defaultLogLevel`
+add the condition passed must be `true`.
+
+Params:
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that should be logged.
+
+Example:
+--------------------
+log(true, "Hello World", 3.1415);
+--------------------
+*/
+void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
+if (args.length != 1)
+{
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, condition, args);
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg,
+ int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel,
+ condition, arg, line, file, funcName, prettyFuncName);
+}
+
+/** This function logs data.
+
+In order for the data to be processed the `LogLevel` of the
+`sharedLog` must be greater or equal to the `defaultLogLevel`.
+
+Params:
+ args = The data that should be logged.
+
+Example:
+--------------------
+log("Hello World", 3.1415);
+--------------------
+*/
+void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy A args)
+if ((args.length > 1 && !is(Unqual!(A[0]) : bool)
+ && !is(Unqual!(A[0]) == LogLevel))
+ || args.length == 0)
+{
+ stdThreadLocalLog.log!(line, file, funcName,
+ prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args);
+}
+
+void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+{
+ stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file,
+ funcName, prettyFuncName, moduleName);
+}
+
+/** This function logs data in a `printf`-style manner.
+
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel` additionally the condition passed must be `true`.
+
+Params:
+ ll = The `LogLevel` used by this log call.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(LogLevel.warning, true, "Hello World %f", 3.1415);
+--------------------
+*/
+void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll,
+ lazy bool condition, lazy string msg, lazy A args)
+{
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, condition, msg, args);
+}
+
+/** This function logs data in a `printf`-style manner.
+
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `LogLevel` of the `sharedLog` and the
+`defaultLogLevel`.
+
+Params:
+ ll = The `LogLevel` used by this log call.
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(LogLevel.warning, "Hello World %f", 3.1415);
+--------------------
+*/
+void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg,
+ lazy A args)
+{
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, msg, args);
+}
+
+/** This function logs data in a `printf`-style manner.
+
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `defaultLogLevel` additionally the condition
+passed must be `true`.
+
+Params:
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(true, "Hello World %f", 3.1415);
+--------------------
+*/
+void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy string msg, lazy A args)
+{
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, condition, msg, args);
+}
+
+/** This function logs data in a `printf`-style manner.
+
+In order for the data to be processed the `LogLevel` of the log call must
+be greater or equal to the `defaultLogLevel`.
+
+Params:
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf("Hello World %f", 3.1415);
+--------------------
+*/
+void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
+{
+ stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, msg, args);
+}
+
+/** This template provides the global log functions with the `LogLevel`
+is encoded in the function name.
+
+The aliases following this template create the public names of these log
+functions.
+*/
+template defaultLogFunction(LogLevel ll)
+{
+ void defaultLogFunction(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy A args)
+ if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
+ prettyFuncName, moduleName)(args);
+ }
+
+ void defaultLogFunction(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
+ prettyFuncName, moduleName)(condition, args);
+ }
+}
+
+/** This function logs data to the `stdThreadLocalLog`, optionally depending
+on a condition.
+
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `stdThreadLocalLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
+If a condition is given, it must evaluate to `true`.
+
+Params:
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that should be logged.
+
+Example:
+--------------------
+trace(1337, "is number");
+info(1337, "is number");
+error(1337, "is number");
+critical(1337, "is number");
+fatal(1337, "is number");
+trace(true, 1337, "is number");
+info(false, 1337, "is number");
+error(true, 1337, "is number");
+critical(false, 1337, "is number");
+fatal(true, 1337, "is number");
+--------------------
+*/
+alias trace = defaultLogFunction!(LogLevel.trace);
+/// Ditto
+alias info = defaultLogFunction!(LogLevel.info);
+/// Ditto
+alias warning = defaultLogFunction!(LogLevel.warning);
+/// Ditto
+alias error = defaultLogFunction!(LogLevel.error);
+/// Ditto
+alias critical = defaultLogFunction!(LogLevel.critical);
+/// Ditto
+alias fatal = defaultLogFunction!(LogLevel.fatal);
+
+/** This template provides the global `printf`-style log functions with
+the `LogLevel` is encoded in the function name.
+
+The aliases following this template create the public names of the log
+functions.
+*/
+template defaultLogFunctionf(LogLevel ll)
+{
+ void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
+ prettyFuncName, moduleName)(msg, args);
+ }
+
+ void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy string msg, lazy A args)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
+ prettyFuncName, moduleName)(condition, msg, args);
+ }
+}
+
+/** This function logs data to the `sharedLog` in a `printf`-style
+manner.
+
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `sharedLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
+
+Params:
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+tracef("is number %d", 1);
+infof("is number %d", 2);
+errorf("is number %d", 3);
+criticalf("is number %d", 4);
+fatalf("is number %d", 5);
+--------------------
+
+The second version of the function logs data to the `sharedLog` in a $(D
+printf)-style manner.
+
+In order for the resulting log message to be logged the `LogLevel` must
+be greater or equal than the `LogLevel` of the `sharedLog` and
+must be greater or equal than the global `LogLevel`.
+Additionally the `LogLevel` must be greater or equal than the `LogLevel`
+of the `stdSharedLogger`.
+
+Params:
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+tracef(false, "is number %d", 1);
+infof(false, "is number %d", 2);
+errorf(true, "is number %d", 3);
+criticalf(true, "is number %d", 4);
+fatalf(someFunct(), "is number %d", 5);
+--------------------
+*/
+alias tracef = defaultLogFunctionf!(LogLevel.trace);
+/// Ditto
+alias infof = defaultLogFunctionf!(LogLevel.info);
+/// Ditto
+alias warningf = defaultLogFunctionf!(LogLevel.warning);
+/// Ditto
+alias errorf = defaultLogFunctionf!(LogLevel.error);
+/// Ditto
+alias criticalf = defaultLogFunctionf!(LogLevel.critical);
+/// Ditto
+alias fatalf = defaultLogFunctionf!(LogLevel.fatal);
+
+private struct MsgRange
+{
+ import std.traits : isSomeString, isSomeChar;
+
+ private Logger log;
+
+ this(Logger log) @safe
+ {
+ this.log = log;
+ }
+
+ void put(T)(T msg) @safe
+ if (isSomeString!T)
+ {
+ log.logMsgPart(msg);
+ }
+
+ void put(dchar elem) @safe
+ {
+ import std.utf : encode;
+ char[4] buffer;
+ size_t len = encode(buffer, elem);
+ log.logMsgPart(buffer[0 .. len]);
+ }
+}
+
+private void formatString(A...)(MsgRange oRange, A args)
+{
+ import std.format.write : formattedWrite;
+
+ foreach (arg; args)
+ {
+ formattedWrite(oRange, "%s", arg);
+ }
+}
+
+@system unittest
+{
+ void dummy() @safe
+ {
+ auto tl = new TestLogger();
+ auto dst = MsgRange(tl);
+ formatString(dst, "aaa", "bbb");
+ }
+
+ dummy();
+}
+
+/**
+There are eight usable logging level. These level are $(I all), $(I trace),
+$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
+If a log function with `LogLevel.fatal` is called the shutdown handler of
+that logger is called.
+*/
+enum LogLevel : ubyte
+{
+ all = 1, /** Lowest possible assignable `LogLevel`. */
+ trace = 32, /** `LogLevel` for tracing the execution of the program. */
+ info = 64, /** This level is used to display information about the
+ program. */
+ warning = 96, /** warnings about the program should be displayed with this
+ level. */
+ error = 128, /** Information about errors should be logged with this
+ level.*/
+ critical = 160, /** Messages that inform about critical errors should be
+ logged with this level. */
+ fatal = 192, /** Log messages that describe fatal errors should use this
+ level. */
+ off = ubyte.max /** Highest possible `LogLevel`. */
+}
+
+/** This class is the base of every logger. In order to create a new kind of
+logger a deriving class needs to implement the `writeLogMsg` method. By
+default this is not thread-safe.
+
+It is also possible to `override` the three methods `beginLogMsg`,
+`logMsgPart` and `finishLogMsg` together, this option gives more
+flexibility.
+*/
+abstract class Logger
+{
+ import std.array : appender, Appender;
+ import std.concurrency : thisTid, Tid;
+
+ /** LogEntry is a aggregation combining all information associated
+ with a log message. This aggregation will be passed to the method
+ writeLogMsg.
+ */
+ protected struct LogEntry
+ {
+ /// the filename the log function was called from
+ string file;
+ /// the line number the log function was called from
+ int line;
+ /// the name of the function the log function was called from
+ string funcName;
+ /// the pretty formatted name of the function the log function was
+ /// called from
+ string prettyFuncName;
+ /// the name of the module the log message is coming from
+ string moduleName;
+ /// the `LogLevel` associated with the log message
+ LogLevel logLevel;
+ /// thread id of the log message
+ Tid threadId;
+ /// the time the message was logged
+ SysTime timestamp;
+ /// the message of the log message
+ string msg;
+ /// A refernce to the `Logger` used to create this `LogEntry`
+ Logger logger;
+ }
+
+ /**
+ Every subclass of `Logger` has to call this constructor from their
+ constructor. It sets the `LogLevel`, and creates a fatal handler. The fatal
+ handler will throw an `Error` if a log call is made with level
+ `LogLevel.fatal`.
+
+ Params:
+ lv = `LogLevel` to use for this `Logger` instance.
+ */
+ this(this This)(LogLevel lv)
+ {
+ this.logLevel_ = lv;
+ this.fatalHandler_ = delegate() {
+ throw new Error("A fatal log message was logged");
+ };
+
+ this.mutex = new typeof(mutex)();
+ }
+
+ /** A custom logger must implement this method in order to work in a
+ `MultiLogger` and `ArrayLogger`.
+
+ Params:
+ payload = All information associated with call to log function.
+
+ See_Also: beginLogMsg, logMsgPart, finishLogMsg
+ */
+ abstract protected void writeLogMsg(ref LogEntry payload) @safe;
+
+ /* The default implementation will use an `std.array.appender`
+ internally to construct the message string. This means dynamic,
+ GC memory allocation. A logger can avoid this allocation by
+ reimplementing `beginLogMsg`, `logMsgPart` and `finishLogMsg`.
+ `beginLogMsg` is always called first, followed by any number of calls
+ to `logMsgPart` and one call to `finishLogMsg`.
+
+ As an example for such a custom `Logger` compare this:
+ ----------------
+ class CLogger : Logger
+ {
+ override void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp)
+ {
+ ... logic here
+ }
+
+ override void logMsgPart(const(char)[] msg)
+ {
+ ... logic here
+ }
+
+ override void finishLogMsg()
+ {
+ ... logic here
+ }
+
+ void writeLogMsg(ref LogEntry payload)
+ {
+ this.beginLogMsg(payload.file, payload.line, payload.funcName,
+ payload.prettyFuncName, payload.moduleName, payload.logLevel,
+ payload.threadId, payload.timestamp, payload.logger);
+
+ this.logMsgPart(payload.msg);
+ this.finishLogMsg();
+ }
+ }
+ ----------------
+ */
+ protected void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp, Logger logger)
+ @safe
+ {
+ msgAppender = appender!string();
+ header = LogEntry(file, line, funcName, prettyFuncName,
+ moduleName, logLevel, threadId, timestamp, null, logger);
+ }
+
+ /** Logs a part of the log message. */
+ protected void logMsgPart(scope const(char)[] msg) @safe
+ {
+ msgAppender.put(msg);
+ }
+
+ /** Signals that the message has been written and no more calls to
+ `logMsgPart` follow. */
+ protected void finishLogMsg() @safe
+ {
+ header.msg = msgAppender.data;
+ this.writeLogMsg(header);
+ }
+
+ /** The `LogLevel` determines if the log call are processed or dropped
+ by the `Logger`. In order for the log call to be processed the
+ `LogLevel` of the log call must be greater or equal to the `LogLevel`
+ of the `logger`.
+
+ These two methods set and get the `LogLevel` of the used `Logger`.
+
+ Example:
+ -----------
+ auto f = new FileLogger(stdout);
+ f.logLevel = LogLevel.info;
+ assert(f.logLevel == LogLevel.info);
+ -----------
+ */
+ @property final LogLevel logLevel() const pure @safe @nogc
+ {
+ return trustedLoad(this.logLevel_);
+ }
+
+ /// Ditto
+ @property final void logLevel(const LogLevel lv) @safe @nogc
+ {
+ atomicStore(this.logLevel_, lv);
+ }
+
+ /** This `delegate` is called in case a log message with
+ `LogLevel.fatal` gets logged.
+
+ By default an `Error` will be thrown.
+ */
+ @property final void delegate() fatalHandler() @safe @nogc
+ {
+ synchronized (mutex) return this.fatalHandler_;
+ }
+
+ /// Ditto
+ @property final void fatalHandler(void delegate() @safe fh) @safe @nogc
+ {
+ synchronized (mutex) this.fatalHandler_ = fh;
+ }
+
+ /** This method allows forwarding log entries from one logger to another.
+
+ `forwardMsg` will ensure proper synchronization and then call
+ `writeLogMsg`. This is an API for implementing your own loggers and
+ should not be called by normal user code. A notable difference from other
+ logging functions is that the `globalLogLevel` wont be evaluated again
+ since it is assumed that the caller already checked that.
+ */
+ void forwardMsg(ref LogEntry payload) @trusted
+ {
+ if (isLoggingEnabled(payload.logLevel, this.logLevel_,
+ globalLogLevel))
+ {
+ this.writeLogMsg(payload);
+
+ if (payload.logLevel == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+
+ /** This template provides the log functions for the `Logger` `class`
+ with the `LogLevel` encoded in the function name.
+
+ For further information see the the two functions defined inside of this
+ template.
+
+ The aliases following this template create the public names of these log
+ functions.
+ */
+ template memLogFunctions(LogLevel ll)
+ {
+ /** This function logs data to the used `Logger`.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
+
+ Params:
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.trace(1337, "is number");
+ s.info(1337, "is number");
+ s.error(1337, "is number");
+ s.critical(1337, "is number");
+ s.fatal(1337, "is number");
+ --------------------
+ */
+ void logImpl(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy A args)
+ if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` depending on a
+ condition.
+
+ In order for the resulting log message to be logged the `LogLevel` must
+ be greater or equal than the `LogLevel` of the used `Logger` and
+ must be greater or equal than the global `LogLevel` additionally the
+ condition passed must be `true`.
+
+ Params:
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.trace(true, 1337, "is number");
+ s.info(false, 1337, "is number");
+ s.error(true, 1337, "is number");
+ s.critical(false, 1337, "is number");
+ s.fatal(true, 1337, "is number");
+ --------------------
+ */
+ void logImpl(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy A args)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` in a
+ `printf`-style manner.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel` additionally
+ the passed condition must be `true`.
+
+ Params:
+ condition = The condition must be `true` for the data to be logged.
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stderr);
+ s.tracef(true, "is number %d", 1);
+ s.infof(true, "is number %d", 2);
+ s.errorf(false, "is number %d", 3);
+ s.criticalf(someFunc(), "is number %d", 4);
+ s.fatalf(true, "is number %d", 5);
+ --------------------
+ */
+ void logImplf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` in a
+ `printf`-style manner.
+
+ In order for the resulting log message to be logged the `LogLevel` must
+ be greater or equal than the `LogLevel` of the used `Logger` and
+ must be greater or equal than the global `LogLevel`.
+
+ Params:
+ msg = The `printf`-style string.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stderr);
+ s.tracef("is number %d", 1);
+ s.infof("is number %d", 2);
+ s.errorf("is number %d", 3);
+ s.criticalf("is number %d", 4);
+ s.fatalf("is number %d", 5);
+ --------------------
+ */
+ void logImplf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+ }
+
+ /// Ditto
+ alias trace = memLogFunctions!(LogLevel.trace).logImpl;
+ /// Ditto
+ alias tracef = memLogFunctions!(LogLevel.trace).logImplf;
+ /// Ditto
+ alias info = memLogFunctions!(LogLevel.info).logImpl;
+ /// Ditto
+ alias infof = memLogFunctions!(LogLevel.info).logImplf;
+ /// Ditto
+ alias warning = memLogFunctions!(LogLevel.warning).logImpl;
+ /// Ditto
+ alias warningf = memLogFunctions!(LogLevel.warning).logImplf;
+ /// Ditto
+ alias error = memLogFunctions!(LogLevel.error).logImpl;
+ /// Ditto
+ alias errorf = memLogFunctions!(LogLevel.error).logImplf;
+ /// Ditto
+ alias critical = memLogFunctions!(LogLevel.critical).logImpl;
+ /// Ditto
+ alias criticalf = memLogFunctions!(LogLevel.critical).logImplf;
+ /// Ditto
+ alias fatal = memLogFunctions!(LogLevel.fatal).logImpl;
+ /// Ditto
+ alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
+
+ /** This method logs data with the `LogLevel` of the used `Logger`.
+
+ This method takes a `bool` as first argument. In order for the
+ data to be processed the `bool` must be `true` and the `LogLevel`
+ of the Logger must be greater or equal to the global `LogLevel`.
+
+ Params:
+ args = The data that should be logged.
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that is to be logged.
+
+ Returns: The logger used by the logging function as reference.
+
+ Example:
+ --------------------
+ auto l = new StdioLogger();
+ l.log(1337);
+ --------------------
+ */
+ void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll,
+ lazy bool condition, lazy A args)
+ if (args.length != 1)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ void log(T, string moduleName = __MODULE__)(const LogLevel ll,
+ lazy bool condition, lazy T args, int line = __LINE__,
+ string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel`.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
+
+ Params:
+ ll = The specific `LogLevel` used for logging the log message.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(LogLevel.trace, 1337, "is number");
+ s.log(LogLevel.info, 1337, "is number");
+ s.log(LogLevel.warning, 1337, "is number");
+ s.log(LogLevel.error, 1337, "is number");
+ s.log(LogLevel.fatal, 1337, "is number");
+ --------------------
+ */
+ void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
+ if ((args.length > 1 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ void log(T)(const LogLevel ll, lazy T args, int line = __LINE__,
+ string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` depending on a
+ explicitly passed condition with the `LogLevel` of the used
+ `Logger`.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel` and the condition must be `true`.
+
+ Params:
+ condition = The condition must be `true` for the data to be logged.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(true, 1337, "is number");
+ s.log(true, 1337, "is number");
+ s.log(true, 1337, "is number");
+ s.log(false, 1337, "is number");
+ s.log(false, 1337, "is number");
+ --------------------
+ */
+ void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
+ if (args.length != 1)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ void log(T)(lazy bool condition, lazy T args, int line = __LINE__,
+ string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` with the `LogLevel`
+ of the used `Logger`.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel`.
+
+ Params:
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(1337, "is number");
+ s.log(info, 1337, "is number");
+ s.log(1337, "is number");
+ s.log(1337, "is number");
+ s.log(1337, "is number");
+ --------------------
+ */
+ void log(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy A args)
+ if ((args.length > 1
+ && !is(Unqual!(A[0]) : bool)
+ && !is(immutable A[0] == immutable LogLevel))
+ || args.length == 0)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+ {
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, arg);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel` and depending on a condition in a `printf`-style manner.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel` and the
+ condition must be `true`.
+
+ Params:
+ ll = The specific `LogLevel` used for logging the log message.
+ condition = The condition must be `true` for the data to be logged.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.info, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.error, false ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number");
+ --------------------
+ */
+ void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll,
+ lazy bool condition, lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` with a specific
+ `LogLevel` in a `printf`-style manner.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ must be greater or equal than the `LogLevel` of the used `Logger`
+ and must be greater or equal than the global `LogLevel`.
+
+ Params:
+ ll = The specific `LogLevel` used for logging the log message.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(LogLevel.trace, "%d %s", 1337, "is number");
+ s.logf(LogLevel.info, "%d %s", 1337, "is number");
+ s.logf(LogLevel.warning, "%d %s", 1337, "is number");
+ s.logf(LogLevel.error, "%d %s", 1337, "is number");
+ s.logf(LogLevel.fatal, "%d %s", 1337, "is number");
+ --------------------
+ */
+ void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(const LogLevel ll,
+ lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used `Logger` depending on a
+ condition with the `LogLevel` of the used `Logger` in a
+ `printf`-style manner.
+
+ In order for the resulting log message to be logged the `LogLevel`
+ of the used `Logger` must be greater or equal than the global
+ `LogLevel` and the condition must be `true`.
+
+ Params:
+ condition = The condition must be `true` for the data to be logged.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(false ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ --------------------
+ */
+ void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This method logs data to the used `Logger` with the `LogLevel`
+ of the this `Logger` in a `printf`-style manner.
+
+ In order for the data to be processed the `LogLevel` of the `Logger`
+ must be greater or equal to the global `LogLevel`.
+
+ Params:
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ --------------------
+ */
+ void logf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
+ {
+ synchronized (mutex)
+ {
+ import std.format.write : formattedWrite;
+
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ private void delegate() @safe fatalHandler_;
+ private shared LogLevel logLevel_ = LogLevel.info;
+ private Mutex mutex;
+
+ protected Appender!string msgAppender;
+ protected LogEntry header;
+}
+
+// Thread Global
+
+private shared Logger stdSharedDefaultLogger;
+private shared Logger stdSharedLogger;
+private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all;
+
+/* This method returns the global default Logger.
+ * Marked @trusted because of excessive reliance on __gshared data
+ */
+private @property shared(Logger) defaultSharedLoggerImpl() @trusted
+{
+ import core.lifetime : emplace;
+ import std.stdio : stderr;
+
+ __gshared align(__traits(classInstanceAlignment, FileLogger))
+ void[__traits(classInstanceSize, FileLogger)] _buffer = void;
+
+ import std.concurrency : initOnce;
+ initOnce!stdSharedDefaultLogger({
+ auto buffer = cast(ubyte[]) _buffer;
+ return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info);
+ }());
+
+ return atomicLoad(stdSharedDefaultLogger);
+}
+
+/** This property sets and gets the default `Logger`. Unless set to another
+logger by the user, the default logger's log level is LogLevel.info.
+
+Example:
+-------------
+sharedLog = new FileLogger(yourFile);
+-------------
+The example sets a new `FileLogger` as new `sharedLog`.
+
+If at some point you want to use the original default logger again, you can
+use $(D sharedLog = null;). This will put back the original.
+
+Note:
+While getting and setting `sharedLog` is thread-safe, it has to be considered
+that the returned reference is only a current snapshot and in the following
+code, you must make sure no other thread reassigns to it between reading and
+writing `sharedLog`.
+
+`sharedLog` is only thread-safe if the the used `Logger` is thread-safe.
+The default `Logger` is thread-safe.
+-------------
+if (sharedLog !is myLogger)
+ sharedLog = new myLogger;
+-------------
+*/
+@property shared(Logger) sharedLog() @safe
+{
+ // If we have set up our own logger use that
+ if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger))
+ {
+ return atomicLoad(logger);
+ }
+ else
+ {
+ // Otherwise resort to the default logger
+ return defaultSharedLoggerImpl;
+ }
+}
+
+/// Ditto
+@property void sharedLog(shared(Logger) logger) @safe
+{
+ atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger));
+}
+
+/** This methods get and set the global `LogLevel`.
+
+Every log message with a `LogLevel` lower as the global `LogLevel`
+will be discarded before it reaches `writeLogMessage` method of any
+`Logger`.
+*/
+/* Implementation note:
+For any public logging call, the global log level shall only be queried once on
+entry. Otherwise when another threads changes the level, we would work with
+different levels at different spots in the code.
+*/
+@property LogLevel globalLogLevel() @safe @nogc
+{
+ return trustedLoad(stdLoggerGlobalLogLevel);
+}
+
+/// Ditto
+@property void globalLogLevel(LogLevel ll) @safe
+{
+ trustedStore(stdLoggerGlobalLogLevel, ll);
+}
+
+// Thread Local
+
+/** The `StdForwardLogger` will always forward anything to the sharedLog.
+
+The `StdForwardLogger` will not throw if data is logged with $(D
+LogLevel.fatal).
+*/
+class StdForwardLogger : Logger
+{
+ /** The default constructor for the `StdForwardLogger`.
+
+ Params:
+ lv = The `LogLevel` for the `MultiLogger`. By default the $(D
+ LogLevel) is `all`.
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.fatalHandler = delegate() {};
+ }
+
+ override protected void writeLogMsg(ref LogEntry payload) @trusted
+ {
+ synchronized (sharedLog.mutex)
+ {
+ (cast() sharedLog).forwardMsg(payload);
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ auto nl1 = new StdForwardLogger(LogLevel.all);
+}
+
+@safe unittest
+{
+ import core.thread : Thread, msecs;
+
+ static class RaceLogger : Logger
+ {
+ int value;
+ this() @safe shared
+ {
+ super(LogLevel.init);
+ }
+ override void writeLogMsg(ref LogEntry payload) @safe
+ {
+ import core.thread : Thread, msecs;
+ if (payload.msg == "foo")
+ {
+ value = 42;
+ () @trusted { Thread.sleep(100.msecs); }();
+ assert(value == 42, "Another thread changed the value");
+ }
+ else
+ {
+ () @trusted { Thread.sleep(50.msecs); } ();
+ value = 13;
+ }
+ }
+ }
+
+ sharedLog = new shared RaceLogger;
+ scope(exit) { sharedLog = null; }
+ () @trusted { new Thread(() { log("foo"); }).start(); }();
+ log("bar");
+}
+
+/** This `LogLevel` is unqiue to every thread.
+
+The thread local `Logger` will use this `LogLevel` to filter log calls
+every same way as presented earlier.
+*/
+//public LogLevel threadLogLevel = LogLevel.all;
+private Logger stdLoggerThreadLogger;
+private Logger stdLoggerDefaultThreadLogger;
+
+/* This method returns the thread local default Logger.
+*/
+private @property Logger stdThreadLocalLogImpl() @trusted
+{
+ import core.lifetime : emplace;
+
+ static align(__traits(classInstanceAlignment, StdForwardLogger))
+ void[__traits(classInstanceSize, StdForwardLogger)] buffer;
+
+ if (stdLoggerDefaultThreadLogger is null)
+ {
+ stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all);
+ }
+ return stdLoggerDefaultThreadLogger;
+}
+
+/** This function returns a thread unique `Logger`, that by default
+propagates all data logged to it to the `sharedLog`.
+
+These properties can be used to set and get this `Logger`. Every
+modification to this `Logger` will only be visible in the thread the
+modification has been done from.
+
+This `Logger` is called by the free standing log functions. This allows to
+create thread local redirections and still use the free standing log
+functions.
+*/
+@property Logger stdThreadLocalLog() @safe
+{
+ // If we have set up our own logger use that
+ if (auto logger = stdLoggerThreadLogger)
+ return logger;
+ else
+ // Otherwise resort to the default logger
+ return stdThreadLocalLogImpl;
+}
+
+/// Ditto
+@property void stdThreadLocalLog(Logger logger) @safe
+{
+ stdLoggerThreadLogger = logger;
+}
+
+/// Ditto
+@system unittest
+{
+ import std.logger.filelogger : FileLogger;
+ import std.file : deleteme, remove;
+ Logger l = stdThreadLocalLog;
+ stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
+ scope(exit) remove(deleteme ~ "-someFile.log");
+
+ auto tempLog = stdThreadLocalLog;
+ stdThreadLocalLog = l;
+ destroy(tempLog);
+}
+
+@safe unittest
+{
+ LogLevel ll = globalLogLevel;
+ globalLogLevel = LogLevel.fatal;
+ assert(globalLogLevel == LogLevel.fatal);
+ globalLogLevel = ll;
+}
+
+package class TestLogger : Logger
+{
+ int line = -1;
+ string file = null;
+ string func = null;
+ string prettyFunc = null;
+ string msg = null;
+ LogLevel lvl;
+
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ }
+
+ override protected void writeLogMsg(ref LogEntry payload) @safe
+ {
+ this.line = payload.line;
+ this.file = payload.file;
+ this.func = payload.funcName;
+ this.prettyFunc = payload.prettyFuncName;
+ this.lvl = payload.logLevel;
+ this.msg = payload.msg;
+ }
+}
+
+version (StdUnittest) private void testFuncNames(Logger logger) @safe
+{
+ string s = "I'm here";
+ logger.log(s);
+}
+
+@safe unittest
+{
+ auto tl1 = new TestLogger();
+ testFuncNames(tl1);
+ assert(tl1.func == "std.logger.core.testFuncNames", tl1.func);
+ assert(tl1.prettyFunc ==
+ "void std.logger.core.testFuncNames(Logger logger) @safe",
+ tl1.prettyFunc);
+ assert(tl1.msg == "I'm here", tl1.msg);
+}
+
+@safe unittest
+{
+ auto tl1 = new TestLogger(LogLevel.all);
+ tl1.log();
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(true);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(false);
+ assert(tl1.line == __LINE__ - 3);
+ tl1.log(LogLevel.info);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(LogLevel.off);
+ assert(tl1.line == __LINE__ - 3);
+ tl1.log(LogLevel.info, true);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(LogLevel.info, false);
+ assert(tl1.line == __LINE__ - 3);
+
+ auto oldunspecificLogger = sharedLog;
+ scope(exit) {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ }
+
+ () @trusted {
+ sharedLog = cast(shared) tl1;
+ }();
+
+ log();
+ assert(tl1.line == __LINE__ - 1);
+
+ log(LogLevel.info);
+ assert(tl1.line == __LINE__ - 1);
+
+ log(true);
+ assert(tl1.line == __LINE__ - 1);
+
+ log(LogLevel.warning, true);
+ assert(tl1.line == __LINE__ - 1);
+
+ trace();
+ assert(tl1.line == __LINE__ - 1);
+}
+
+@safe unittest
+{
+ import std.logger.multilogger : MultiLogger;
+
+ auto tl1 = new TestLogger;
+ auto tl2 = new TestLogger;
+
+ auto ml = new MultiLogger();
+ ml.insertLogger("one", tl1);
+ ml.insertLogger("two", tl2);
+
+ string msg = "Hello Logger World";
+ ml.log(msg);
+ int lineNumber = __LINE__ - 1;
+ assert(tl1.msg == msg);
+ assert(tl1.line == lineNumber);
+ assert(tl2.msg == msg);
+ assert(tl2.line == lineNumber);
+
+ ml.removeLogger("one");
+ ml.removeLogger("two");
+ auto n = ml.removeLogger("one");
+ assert(n is null);
+}
+
+@safe unittest
+{
+ bool errorThrown = false;
+ auto tl = new TestLogger;
+ auto dele = delegate() {
+ errorThrown = true;
+ };
+ tl.fatalHandler = dele;
+ tl.fatal();
+ assert(errorThrown);
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ import std.exception : assertThrown, assertNotThrown;
+ import std.format : format;
+
+ auto l = new TestLogger(LogLevel.all);
+ string msg = "Hello Logger World";
+ l.log(msg);
+ int lineNumber = __LINE__ - 1;
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.log(true, msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg, l.msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.log(false, msg);
+ assert(l.msg == msg);
+ assert(l.line == lineNumber, to!string(l.line));
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ l.logf(msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.logf(true, msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.logf(false, msg, "Yet");
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ Logger oldunspecificLogger;
+ () @trusted {
+ oldunspecificLogger = cast() sharedLog;
+ }();
+
+ assert(oldunspecificLogger.logLevel == LogLevel.info,
+ to!string(oldunspecificLogger.logLevel));
+
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ sharedLog = cast(shared) l;
+ }();
+
+ assert(globalLogLevel == LogLevel.all,
+ to!string(globalLogLevel));
+
+ scope(exit)
+ {
+ () @trusted {
+ sharedLog = atomicLoad(cast(shared) oldunspecificLogger);
+ }();
+ }
+
+ () @trusted {
+ assert((cast() sharedLog).logLevel == LogLevel.all);
+ }();
+
+ assert(stdThreadLocalLog.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ msg = "Another message";
+ log(msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.logLevel == LogLevel.all);
+ assert(l.line == lineNumber, to!string(l.line));
+ assert(l.msg == msg, l.msg);
+
+ log(true, msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ log(false, msg);
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ logf(msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ logf(true, msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ logf(false, msg, "Yet");
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ () @trusted {
+ assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+}
+
+@system unittest // default logger
+{
+ import std.file : deleteme, exists, remove;
+ import std.stdio : File;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ FileLogger l = new FileLogger(filename);
+ auto oldunspecificLogger = sharedLog;
+
+ sharedLog = cast(shared) l;
+
+ scope(exit)
+ {
+ remove(filename);
+ assert(!exists(filename));
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ globalLogLevel = LogLevel.critical;
+ assert(globalLogLevel == LogLevel.critical);
+
+ log(LogLevel.warning, notWritten);
+ log(LogLevel.critical, written);
+
+ l.file.flush();
+ l.file.close();
+
+ auto file = File(filename, "r");
+ assert(!file.eof);
+
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+ file.close();
+}
+
+@system unittest
+{
+ import std.file : deleteme, remove;
+ import std.stdio : File;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ remove(filename);
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ auto l = new FileLogger(filename);
+ sharedLog = cast(shared) l;
+
+ () @trusted {
+ (cast() sharedLog).logLevel = LogLevel.critical;
+ }();
+
+ log(LogLevel.error, false, notWritten);
+ log(LogLevel.critical, true, written);
+ destroy(l);
+
+ auto file = File(filename, "r");
+ auto readLine = file.readln();
+ assert(!readLine.empty, readLine);
+ assert(readLine.indexOf(written) != -1);
+ assert(readLine.indexOf(notWritten) == -1);
+ file.close();
+}
+
+@safe unittest
+{
+ import std.conv : to;
+
+ auto tl = new TestLogger(LogLevel.all);
+ int l = __LINE__;
+ tl.info("a");
+ assert(tl.line == l+1);
+ assert(tl.msg == "a");
+ assert(tl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+ l = __LINE__;
+ tl.trace("b");
+ assert(tl.msg == "b", tl.msg);
+ assert(tl.line == l+1, to!string(tl.line));
+}
+
+// testing possible log conditions
+@safe unittest
+{
+ import std.conv : to;
+ import std.format : format;
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() {};
+
+ () @trusted {
+ sharedLog = cast(shared) mem;
+ }();
+
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ int value = 0;
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ mem.logLevel = ll;
+
+ foreach (cond; [true, false])
+ {
+ foreach (condValue; [true, false])
+ {
+ foreach (memOrG; [true, false])
+ {
+ foreach (prntf; [true, false])
+ {
+ foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning,
+ LogLevel.error, LogLevel.critical,
+ LogLevel.fatal, LogLevel.off])
+ {
+ foreach (singleMulti; 0 .. 2)
+ {
+ int lineCall;
+ mem.msg = "-1";
+ if (memOrG)
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(ll2, condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(ll2, condValue,
+ "%d %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(ll2, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(ll2, "%d %d",
+ value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(ll2, condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(ll2, condValue,
+ to!string(value), value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(ll2, to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(ll2,
+ to!string(value),
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ logf(ll2, condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(ll2, condValue,
+ "%s %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ logf(ll2, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(ll2, "%s %s", value,
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ log(ll2, condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(ll2, condValue, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ log(ll2, to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(ll2, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+
+ string valueStr = to!string(value);
+ ++value;
+
+ bool ll2Off = (ll2 != LogLevel.off);
+ bool gllOff = (gll != LogLevel.off);
+ bool llOff = (ll != LogLevel.off);
+ bool condFalse = (cond ? condValue : true);
+ bool ll2VSgll = (ll2 >= gll);
+ bool ll2VSll = (ll2 >= ll);
+
+ bool shouldLog = ll2Off && gllOff && llOff
+ && condFalse && ll2VSgll && ll2VSll;
+
+ /*
+ writefln(
+ "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
+ , gll != LogLevel.off, ll2 != LogLevel.off,
+ cond ? condValue : true,
+ ll2 >= gll, ll2 >= ll, shouldLog);
+ */
+
+
+ if (shouldLog)
+ {
+ assert(mem.msg.indexOf(valueStr) != -1,
+ format(
+ "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
+ "cond(%b) condValue(%b)" ~
+ " memOrG(%b) shouldLog(%b) %s == %s" ~
+ " %b %b %b %b %b",
+ lineCall, ll2Off, gll, ll, ll2, cond,
+ condValue, memOrG, shouldLog, mem.msg,
+ valueStr, gllOff, llOff, condFalse,
+ ll2VSgll, ll2VSll
+ ));
+ }
+ else
+ {
+ assert(mem.msg.indexOf(valueStr),
+ format(
+ "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
+ "cond(%b) condValue(%b)" ~
+ " memOrG(%b) shouldLog(%b) %s == %s" ~
+ " %b %b %b %b %b",
+ lineCall, ll2Off, gll, ll, ll2, cond,
+ condValue, memOrG, shouldLog, mem.msg,
+ valueStr, gllOff, llOff, condFalse,
+ ll2VSgll, ll2VSll
+ ));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// more testing
+@safe unittest
+{
+ import std.conv : to;
+ import std.format : format;
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() {};
+
+ () @trusted {
+ sharedLog = cast(shared) mem;
+ }();
+
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ int value = 0;
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ mem.logLevel = ll;
+
+ foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ stdThreadLocalLog.logLevel = tll;
+
+ foreach (cond; [true, false])
+ {
+ foreach (condValue; [true, false])
+ {
+ foreach (memOrG; [true, false])
+ {
+ foreach (prntf; [true, false])
+ {
+ foreach (singleMulti; 0 .. 2)
+ {
+ int lineCall;
+ mem.msg = "-1";
+ if (memOrG)
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(condValue,
+ "%d %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf("%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf("%d %d",
+ value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(condValue,
+ to!string(value), value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(to!string(value),
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ logf(condValue, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(condValue, "%s %d", value,
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ logf("%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf("%s %s", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ log(condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(condValue, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ log(to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(value, to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+
+ string valueStr = to!string(value);
+ ++value;
+
+ bool gllOff = (gll != LogLevel.off);
+ bool llOff = (ll != LogLevel.off);
+ bool tllOff = (tll != LogLevel.off);
+ bool llVSgll = (ll >= gll);
+ bool tllVSll =
+ (stdThreadLocalLog.logLevel >= ll);
+ bool condFalse = (cond ? condValue : true);
+
+ bool shouldLog = gllOff && llOff
+ && (memOrG ? true : tllOff)
+ && (memOrG ?
+ (ll >= gll) :
+ (tll >= gll && tll >= ll))
+ && condFalse;
+
+ if (shouldLog)
+ {
+ assert(mem.msg.indexOf(valueStr) != -1,
+ format("\ngll(%s) ll(%s) tll(%s) " ~
+ "cond(%s) condValue(%s) " ~
+ "memOrG(%s) prntf(%s) " ~
+ "singleMulti(%s)",
+ gll, ll, tll, cond, condValue,
+ memOrG, prntf, singleMulti)
+ ~ format(" gllOff(%s) llOff(%s) " ~
+ "llVSgll(%s) tllVSll(%s) " ~
+ "tllOff(%s) condFalse(%s) "
+ ~ "shoudlLog(%s)",
+ gll != LogLevel.off,
+ ll != LogLevel.off, llVSgll,
+ tllVSll, tllOff, condFalse,
+ shouldLog)
+ ~ format("msg(%s) line(%s) " ~
+ "lineCall(%s) valueStr(%s)",
+ mem.msg, mem.line, lineCall,
+ valueStr)
+ );
+ }
+ else
+ {
+ assert(mem.msg.indexOf(valueStr) == -1,
+ format("\ngll(%s) ll(%s) tll(%s) " ~
+ "cond(%s) condValue(%s) " ~
+ "memOrG(%s) prntf(%s) " ~
+ "singleMulti(%s)",
+ gll, ll, tll, cond, condValue,
+ memOrG, prntf, singleMulti)
+ ~ format(" gllOff(%s) llOff(%s) " ~
+ "llVSgll(%s) tllVSll(%s) " ~
+ "tllOff(%s) condFalse(%s) "
+ ~ "shoudlLog(%s)",
+ gll != LogLevel.off,
+ ll != LogLevel.off, llVSgll,
+ tllVSll, tllOff, condFalse,
+ shouldLog)
+ ~ format("msg(%s) line(%s) " ~
+ "lineCall(%s) valueStr(%s)",
+ mem.msg, mem.line, lineCall,
+ valueStr)
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// testing more possible log conditions
+@safe unittest
+{
+ bool fatalLog;
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() { fatalLog = true; };
+ auto oldunspecificLogger = sharedLog;
+
+ stdThreadLocalLog.logLevel = LogLevel.all;
+
+ () @trusted {
+ sharedLog = cast(shared) mem;
+ }();
+
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ mem.logLevel = ll;
+
+ foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ stdThreadLocalLog.logLevel = tll;
+
+ foreach (cond; [true, false])
+ {
+ assert(globalLogLevel == gll);
+ assert(mem.logLevel == ll);
+
+ bool gllVSll = LogLevel.trace >= globalLogLevel;
+ bool llVSgll = ll >= globalLogLevel;
+ bool lVSll = LogLevel.trace >= ll;
+ bool gllOff = globalLogLevel != LogLevel.off;
+ bool llOff = mem.logLevel != LogLevel.off;
+ bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off;
+ bool tllVSll = tll >= ll;
+ bool tllVSgll = tll >= gll;
+ bool lVSgll = LogLevel.trace >= tll;
+
+ bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond;
+
+ mem.line = -1;
+ /*
+ writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
+ gll, ll, cond, test);
+ writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
+ gllOff, llOff, cond, test2);
+ */
+
+ mem.trace(__LINE__); int line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ trace(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.trace(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ trace(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.tracef("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ tracef("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.tracef(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ tracef(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.info >= ll;
+ lVSgll = LogLevel.info >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.info(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ info(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.info(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ info(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.infof("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ infof("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.infof(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ infof(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.warning >= ll;
+ lVSgll = LogLevel.warning >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.warning(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warning(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warning(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warning(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warningf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warningf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warningf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warningf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.critical >= ll;
+ lVSgll = LogLevel.critical >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.critical(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ critical(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.critical(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ critical(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.criticalf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ criticalf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ criticalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.fatal >= ll;
+ lVSgll = LogLevel.fatal >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.fatal(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatal(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatal(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatal(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatalf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatalf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+ }
+ }
+ }
+ }
+}
+
+// Issue #5
+@safe unittest
+{
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ auto tl = new TestLogger(LogLevel.info);
+
+ () @trusted {
+ sharedLog = cast(shared) tl;
+ }();
+
+ trace("trace");
+ assert(tl.msg.indexOf("trace") == -1);
+}
+
+// Issue #5
+@safe unittest
+{
+ import std.logger.multilogger : MultiLogger;
+ import std.string : indexOf;
+
+ stdThreadLocalLog.logLevel = LogLevel.all;
+
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldunspecificLogger);
+ globalLogLevel = LogLevel.all;
+ }
+
+ auto logger = new MultiLogger(LogLevel.error);
+
+ auto tl = new TestLogger(LogLevel.info);
+ logger.insertLogger("required", tl);
+
+ () @trusted {
+ sharedLog = cast(shared) logger;
+ }();
+
+ trace("trace");
+ assert(tl.msg.indexOf("trace") == -1);
+ info("info");
+ assert(tl.msg.indexOf("info") == -1);
+ error("error");
+ assert(tl.msg.indexOf("error") == 0);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ auto tl = new TestLogger();
+ assertThrown!Throwable(tl.fatal("fatal"));
+}
+
+// log objects with non-safe toString
+@system unittest
+{
+ struct Test
+ {
+ string toString() const @system
+ {
+ return "test";
+ }
+ }
+
+ auto tl = new TestLogger();
+ tl.info(Test.init);
+ assert(tl.msg == "test");
+}
+
+// Workaround for atomics not allowed in @safe code
+private auto trustedLoad(T)(ref shared T value) @trusted
+{
+ return atomicLoad!(MemoryOrder.acq)(value);
+}
+
+// ditto
+private void trustedStore(T)(ref shared T dst, ref T src) @trusted
+{
+ atomicStore!(MemoryOrder.rel)(dst, src);
+}
+
+// check that thread-local logging does not propagate
+// to shared logger
+@system unittest
+{
+ import core.thread, std.concurrency;
+
+ static shared logged_count = 0;
+
+ class TestLog : Logger
+ {
+ Tid tid;
+
+ this()
+ {
+ super (LogLevel.trace);
+ this.tid = thisTid;
+ }
+
+ override void writeLogMsg(ref LogEntry payload) @trusted
+ {
+ assert(thisTid == this.tid);
+ atomicOp!"+="(logged_count, 1);
+ }
+ }
+
+ class IgnoredLog : Logger
+ {
+ this()
+ {
+ super (LogLevel.trace);
+ }
+
+ override void writeLogMsg(ref LogEntry payload) @trusted
+ {
+ assert(false);
+ }
+ }
+
+ auto oldSharedLog = sharedLog;
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldSharedLog);
+ }
+
+ () @trusted {
+ sharedLog = cast(shared) new IgnoredLog;
+ }();
+
+ Thread[] spawned;
+
+ foreach (i; 0 .. 4)
+ {
+ spawned ~= new Thread({
+ stdThreadLocalLog = new TestLog;
+ trace("zzzzzzzzzz");
+ });
+ spawned[$-1].start();
+ }
+
+ foreach (t; spawned)
+ t.join();
+
+ assert(atomicOp!"=="(logged_count, 4));
+}
+
+@safe unittest
+{
+ auto dl = () @trusted {
+ return cast(FileLogger) cast() sharedLog;
+ }();
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.info);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=14940
+@safe unittest
+{
+ import std.typecons : Nullable;
+
+ Nullable!int a = 1;
+ auto l = new TestLogger();
+ l.infof("log: %s", a);
+ assert(l.msg == "log: 1");
+}
+
+// Ensure @system toString methods work
+@system unittest
+{
+ enum SystemToStringMsg = "SystemToString";
+ static struct SystemToString
+ {
+ string toString() @system
+ {
+ return SystemToStringMsg;
+ }
+ }
+
+ auto tl = new TestLogger();
+
+ SystemToString sts;
+ tl.logf("%s", sts);
+ assert(tl.msg == SystemToStringMsg);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17328
+@safe unittest
+{
+ import std.format : format;
+
+ ubyte[] data = [0];
+ string s = format("%(%02x%)", data); // format 00
+ assert(s == "00");
+
+ auto tl = new TestLogger();
+
+ tl.infof("%(%02x%)", data); // infof 000
+
+ size_t i;
+ string fs = tl.msg;
+ for (; i < s.length; ++i)
+ {
+ assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs);
+ }
+ assert(fs.length == 2);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15954
+@safe unittest
+{
+ import std.conv : to;
+ auto tl = new TestLogger();
+ tl.log("123456789".to!wstring);
+ assert(tl.msg == "123456789");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=16256
+@safe unittest
+{
+ import std.conv : to;
+ auto tl = new TestLogger();
+ tl.log("123456789"d);
+ assert(tl.msg == "123456789");
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=15517
+@system unittest
+{
+ import std.file : exists, remove, tempDir;
+ import std.path : buildPath;
+ import std.stdio : File;
+ import std.string : indexOf;
+
+ string fn = tempDir.buildPath("logfile.log");
+ if (exists(fn))
+ {
+ remove(fn);
+ }
+
+ auto oldShared = sharedLog;
+ scope(exit)
+ {
+ sharedLog = atomicLoad(oldShared);
+ if (exists(fn))
+ {
+ remove(fn);
+ }
+ }
+
+ auto ts = [ "Test log 1", "Test log 2", "Test log 3"];
+
+ auto fl = new FileLogger(fn);
+
+ () @trusted {
+ sharedLog = cast(shared) fl;
+ }();
+
+ assert(exists(fn));
+
+ foreach (t; ts)
+ {
+ log(t);
+ }
+
+ auto f = File(fn);
+ auto l = f.byLine();
+ assert(!l.empty);
+ size_t idx;
+ foreach (it; l)
+ {
+ assert(it.indexOf(ts[idx]) != -1, it);
+ ++idx;
+ }
+
+ assert(exists(fn));
+ fl.file.close();
+}
diff --git a/libphobos/src/std/logger/filelogger.d b/libphobos/src/std/logger/filelogger.d
new file mode 100644
index 00000000000..c662ca74e30
--- /dev/null
+++ b/libphobos/src/std/logger/filelogger.d
@@ -0,0 +1,272 @@
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/logger/filelogger.d)
+*/
+module std.logger.filelogger;
+
+import std.logger.core;
+import std.stdio;
+
+import std.typecons : Flag;
+
+/** An option to create $(LREF FileLogger) directory if it is non-existent.
+*/
+alias CreateFolder = Flag!"CreateFolder";
+
+/** This `Logger` implementation writes log messages to the associated
+file. The name of the file has to be passed on construction time. If the file
+is already present new log messages will be append at its end.
+*/
+class FileLogger : Logger
+{
+ import std.concurrency : Tid;
+ import std.datetime.systime : SysTime;
+ import std.format.write : formattedWrite;
+
+ /** A constructor for the `FileLogger` Logger.
+
+ Params:
+ fn = The filename of the output file of the `FileLogger`. If that
+ file can not be opened for writting an exception will be thrown.
+ lv = The `LogLevel` for the `FileLogger`. By default the
+
+ Example:
+ -------------
+ auto l1 = new FileLogger("logFile");
+ auto l2 = new FileLogger("logFile", LogLevel.fatal);
+ auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
+ -------------
+ */
+ this(const string fn, const LogLevel lv = LogLevel.all) @safe
+ {
+ this(fn, lv, CreateFolder.yes);
+ }
+
+ /** A constructor for the `FileLogger` Logger that takes a reference to
+ a `File`.
+
+ The `File` passed must be open for all the log call to the
+ `FileLogger`. If the `File` gets closed, using the `FileLogger`
+ for logging will result in undefined behaviour.
+
+ Params:
+ fn = The file used for logging.
+ lv = The `LogLevel` for the `FileLogger`. By default the
+ `LogLevel` for `FileLogger` is `LogLevel.all`.
+ createFileNameFolder = if yes and fn contains a folder name, this
+ folder will be created.
+
+ Example:
+ -------------
+ auto file = File("logFile.log", "w");
+ auto l1 = new FileLogger(file);
+ auto l2 = new FileLogger(file, LogLevel.fatal);
+ -------------
+ */
+ this(const string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
+ {
+ import std.file : exists, mkdirRecurse;
+ import std.path : dirName;
+ import std.conv : text;
+
+ super(lv);
+ this.filename = fn;
+
+ if (createFileNameFolder)
+ {
+ auto d = dirName(this.filename);
+ mkdirRecurse(d);
+ assert(exists(d), text("The folder the FileLogger should have",
+ " created in '", d,"' could not be created."));
+ }
+
+ this.file_.open(this.filename, "a");
+ }
+
+ /** A constructor for the `FileLogger` Logger that takes a reference to
+ a `File`.
+
+ The `File` passed must be open for all the log call to the
+ `FileLogger`. If the `File` gets closed, using the `FileLogger`
+ for logging will result in undefined behaviour.
+
+ Params:
+ file = The file used for logging.
+ lv = The `LogLevel` for the `FileLogger`. By default the
+ `LogLevel` for `FileLogger` is `LogLevel.all`.
+
+ Example:
+ -------------
+ auto file = File("logFile.log", "w");
+ auto l1 = new FileLogger(file);
+ auto l2 = new FileLogger(file, LogLevel.fatal);
+ -------------
+ */
+ this(File file, const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.file_ = file;
+ }
+
+ /** If the `FileLogger` is managing the `File` it logs to, this
+ method will return a reference to this File.
+ */
+ @property File file() @safe
+ {
+ return this.file_;
+ }
+
+ /* This method overrides the base class method in order to log to a file
+ without requiring heap allocated memory. Additionally, the `FileLogger`
+ local mutex is logged to serialize the log calls.
+ */
+ override protected void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp, Logger logger)
+ @safe
+ {
+ import std.string : lastIndexOf;
+ ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
+ ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
+
+ auto lt = this.file_.lockingTextWriter();
+ systimeToISOString(lt, timestamp);
+ import std.conv : to;
+ formattedWrite(lt, " [%s] %s:%u:%s ", logLevel.to!string,
+ file[fnIdx .. $], line, funcName[funIdx .. $]);
+ }
+
+ /* This methods overrides the base class method and writes the parts of
+ the log call directly to the file.
+ */
+ override protected void logMsgPart(scope const(char)[] msg)
+ {
+ formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
+ }
+
+ /* This methods overrides the base class method and finalizes the active
+ log call. This requires flushing the `File` and releasing the
+ `FileLogger` local mutex.
+ */
+ override protected void finishLogMsg()
+ {
+ this.file_.lockingTextWriter().put("\n");
+ this.file_.flush();
+ }
+
+ /* This methods overrides the base class method and delegates the
+ `LogEntry` data to the actual implementation.
+ */
+ override protected void writeLogMsg(ref LogEntry payload)
+ {
+ this.beginLogMsg(payload.file, payload.line, payload.funcName,
+ payload.prettyFuncName, payload.moduleName, payload.logLevel,
+ payload.threadId, payload.timestamp, payload.logger);
+ this.logMsgPart(payload.msg);
+ this.finishLogMsg();
+ }
+
+ /** If the `FileLogger` was constructed with a filename, this method
+ returns this filename. Otherwise an empty `string` is returned.
+ */
+ string getFilename()
+ {
+ return this.filename;
+ }
+
+ /** The `File` log messages are written to. */
+ protected File file_;
+
+ /** The filename of the `File` log messages are written to. */
+ protected string filename;
+}
+
+@system unittest
+{
+ import std.array : empty;
+ import std.file : deleteme, remove;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto l = new FileLogger(filename);
+
+ scope(exit)
+ {
+ remove(filename);
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ l.logLevel = LogLevel.critical;
+ l.log(LogLevel.warning, notWritten);
+ l.log(LogLevel.critical, written);
+ destroy(l);
+
+ auto file = File(filename, "r");
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ readLine = file.readln();
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+}
+
+@safe unittest
+{
+ import std.file : rmdirRecurse, exists, deleteme;
+ import std.path : dirName;
+
+ const string tmpFolder = dirName(deleteme);
+ const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
+ const string filename = filepath ~ "output.txt";
+ assert(!exists(filepath));
+
+ auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
+ scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
+
+ f.log("Hello World!");
+ assert(exists(filepath));
+ f.file.close();
+}
+
+@system unittest
+{
+ import std.array : empty;
+ import std.file : deleteme, remove;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto file = File(filename, "w");
+ auto l = new FileLogger(file);
+
+ scope(exit)
+ {
+ remove(filename);
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ l.logLevel = LogLevel.critical;
+ l.log(LogLevel.warning, notWritten);
+ l.log(LogLevel.critical, written);
+ file.close();
+
+ file = File(filename, "r");
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ readLine = file.readln();
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+ file.close();
+}
+
+@system unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.info);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}
diff --git a/libphobos/src/std/logger/multilogger.d b/libphobos/src/std/logger/multilogger.d
new file mode 100644
index 00000000000..eda922dee6c
--- /dev/null
+++ b/libphobos/src/std/logger/multilogger.d
@@ -0,0 +1,200 @@
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/logger/multilogger.d)
+*/
+module std.logger.multilogger;
+
+import std.logger.core;
+import std.logger.filelogger;
+
+/** This Element is stored inside the `MultiLogger` and associates a
+`Logger` to a `string`.
+*/
+struct MultiLoggerEntry
+{
+ string name; /// The name if the `Logger`
+ Logger logger; /// The stored `Logger`
+}
+
+/** MultiLogger logs to multiple `Logger`. The `Logger`s are stored in an
+`Logger[]` in their order of insertion.
+
+Every data logged to this `MultiLogger` will be distributed to all the $(D
+Logger)s inserted into it. This `MultiLogger` implementation can
+hold multiple `Logger`s with the same name. If the method `removeLogger`
+is used to remove a `Logger` only the first occurrence with that name will
+be removed.
+*/
+class MultiLogger : Logger
+{
+ /** A constructor for the `MultiLogger` Logger.
+
+ Params:
+ lv = The `LogLevel` for the `MultiLogger`. By default the
+ `LogLevel` for `MultiLogger` is `LogLevel.all`.
+
+ Example:
+ -------------
+ auto l1 = new MultiLogger(LogLevel.trace);
+ -------------
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ }
+
+ /** This member holds all `Logger`s stored in the `MultiLogger`.
+
+ When inheriting from `MultiLogger` this member can be used to gain
+ access to the stored `Logger`.
+ */
+ protected MultiLoggerEntry[] logger;
+
+ /** This method inserts a new Logger into the `MultiLogger`.
+
+ Params:
+ name = The name of the `Logger` to insert.
+ newLogger = The `Logger` to insert.
+ */
+ void insertLogger(string name, Logger newLogger) @safe
+ {
+ this.logger ~= MultiLoggerEntry(name, newLogger);
+ }
+
+ /** This method removes a Logger from the `MultiLogger`.
+
+ Params:
+ toRemove = The name of the `Logger` to remove. If the `Logger`
+ is not found `null` will be returned. Only the first occurrence of
+ a `Logger` with the given name will be removed.
+
+ Returns: The removed `Logger`.
+ */
+ Logger removeLogger(in char[] toRemove) @safe
+ {
+ import std.algorithm.mutation : copy;
+ import std.range.primitives : back, popBack;
+ for (size_t i = 0; i < this.logger.length; ++i)
+ {
+ if (this.logger[i].name == toRemove)
+ {
+ Logger ret = this.logger[i].logger;
+ this.logger[i] = this.logger.back;
+ this.logger.popBack();
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ /* The override to pass the payload to all children of the
+ `MultiLoggerBase`.
+ */
+ override protected void writeLogMsg(ref LogEntry payload) @safe
+ {
+ foreach (it; this.logger)
+ {
+ /* We don't perform any checks here to avoid race conditions.
+ Instead the child will check on its own if its log level matches
+ and assume LogLevel.all for the globalLogLevel (since we already
+ know the message passes this test).
+ */
+ it.logger.forwardMsg(payload);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.logger.nulllogger;
+ auto a = new MultiLogger;
+ auto n0 = new NullLogger();
+ auto n1 = new NullLogger();
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ auto n0_1 = a.removeLogger("zero");
+ assert(n0_1 is n0);
+ auto n = a.removeLogger("zero");
+ assert(n is null);
+
+ auto n1_1 = a.removeLogger("one");
+ assert(n1_1 is n1);
+ n = a.removeLogger("one");
+ assert(n is null);
+}
+
+@safe unittest
+{
+ auto a = new MultiLogger;
+ auto n0 = new TestLogger;
+ auto n1 = new TestLogger;
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ a.log("Hello TestLogger"); int line = __LINE__;
+ assert(n0.msg == "Hello TestLogger");
+ assert(n0.line == line);
+ assert(n1.msg == "Hello TestLogger");
+ assert(n1.line == line);
+}
+
+// Issue #16
+@system unittest
+{
+ import std.file : deleteme;
+ import std.stdio : File;
+ import std.string : indexOf;
+ string logName = deleteme ~ __FUNCTION__ ~ ".log";
+ auto logFileOutput = File(logName, "w");
+ scope(exit)
+ {
+ import std.file : remove;
+ logFileOutput.close();
+ remove(logName);
+ }
+ auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
+ auto infoLog = new TestLogger(LogLevel.info);
+
+ auto root = new MultiLogger(LogLevel.all);
+ root.insertLogger("fileLogger", traceLog);
+ root.insertLogger("stdoutLogger", infoLog);
+
+ string tMsg = "A trace message";
+ root.trace(tMsg); int line1 = __LINE__;
+
+ assert(infoLog.line != line1);
+ assert(infoLog.msg != tMsg);
+
+ string iMsg = "A info message";
+ root.info(iMsg); int line2 = __LINE__;
+
+ assert(infoLog.line == line2);
+ assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
+
+ logFileOutput.close();
+ logFileOutput = File(logName, "r");
+ assert(logFileOutput.isOpen);
+ assert(!logFileOutput.eof);
+
+ auto line = logFileOutput.readln();
+ assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg);
+ assert(!logFileOutput.eof);
+ line = logFileOutput.readln();
+ assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg);
+}
+
+@system unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.info);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}
diff --git a/libphobos/src/std/logger/nulllogger.d b/libphobos/src/std/logger/nulllogger.d
new file mode 100644
index 00000000000..fd48b8529bb
--- /dev/null
+++ b/libphobos/src/std/logger/nulllogger.d
@@ -0,0 +1,41 @@
+// Written in the D programming language.
+/**
+Source: $(PHOBOSSRC std/logger/nulllogger.d)
+*/
+module std.logger.nulllogger;
+
+import std.logger.core;
+
+/** The `NullLogger` will not process any log messages.
+
+In case of a log message with `LogLevel.fatal` nothing will happen.
+*/
+class NullLogger : Logger
+{
+ /** The default constructor for the `NullLogger`.
+
+ Independent of the parameter this Logger will never log a message.
+
+ Params:
+ lv = The `LogLevel` for the `NullLogger`. By default the `LogLevel`
+ for `NullLogger` is `LogLevel.all`.
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.fatalHandler = delegate() {};
+ }
+
+ override protected void writeLogMsg(ref LogEntry payload) @safe @nogc
+ {
+ }
+}
+
+///
+@safe unittest
+{
+ import std.logger.core : LogLevel;
+ auto nl1 = new NullLogger(LogLevel.all);
+ nl1.info("You will never read this.");
+ nl1.fatal("You will never read this, either and it will not throw");
+}
diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d
new file mode 100644
index 00000000000..4f4183c4cec
--- /dev/null
+++ b/libphobos/src/std/logger/package.d
@@ -0,0 +1,168 @@
+// Written in the D programming language.
+/**
+Implements logging facilities.
+
+Copyright: Copyright Robert "burner" Schadek 2013 --
+License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek)
+
+$(H3 Basic Logging)
+
+Message logging is a common approach to expose runtime information of a
+program. Logging should be easy, but also flexible and powerful, therefore
+`D` provides a standard interface for logging.
+
+The easiest way to create a log message is to write:
+-------------
+import std.logger;
+
+void main() {
+ log("Hello World");
+}
+-------------
+This will print a message to the `stderr` device. The message will contain
+the filename, the line number, the name of the surrounding function, the time
+and the message.
+
+More complex log call can go along the lines like:
+-------------
+log("Logging to the sharedLog with its default LogLevel");
+logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging");
+info("Logging to the sharedLog with its info LogLevel");
+warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6");
+error("Logging to the sharedLog with its error LogLevel");
+errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with");
+critical("Logging to the"," sharedLog with its error LogLevel");
+fatal("Logging to the sharedLog with its fatal LogLevel");
+
+auto fLogger = new FileLogger("NameOfTheLogFile");
+fLogger.log("Logging to the fileLogger with its default LogLevel");
+fLogger.info("Logging to the fileLogger with its default LogLevel");
+fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6");
+fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less");
+fLogger.critical("Logging to the fileLogger with its info LogLevel");
+fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6");
+fLogger.fatal("Logging to the fileLogger with its warning LogLevel");
+-------------
+Additionally, this example shows how a new `FileLogger` is created.
+Individual `Logger` and the global log functions share commonly named
+functions to log data.
+
+The names of the functions are as follows:
+$(UL
+ $(LI `log`)
+ $(LI `trace`)
+ $(LI `info`)
+ $(LI `warning`)
+ $(LI `critical`)
+ $(LI `fatal`)
+)
+The default `Logger` will by default log to `stderr` and has a default
+`LogLevel` of `LogLevel.all`. The default Logger can be accessed by
+using the property called `sharedLog`. This property is a reference to the
+current default `Logger`. This reference can be used to assign a new
+default `Logger`.
+-------------
+sharedLog = new FileLogger("New_Default_Log_File.log");
+-------------
+
+Additional `Logger` can be created by creating a new instance of the
+required `Logger`.
+
+$(H3 Logging Fundamentals)
+$(H4 LogLevel)
+The `LogLevel` of a log call can be defined in two ways. The first is by
+calling `log` and passing the `LogLevel` explicitly as the first argument.
+The second way of setting the `LogLevel` of a
+log call, is by calling either `trace`, `info`, `warning`,
+`critical`, or `fatal`. The log call will then have the respective
+`LogLevel`. If no `LogLevel` is defined the log call will use the
+current `LogLevel` of the used `Logger`. If data is logged with
+`LogLevel` `fatal` by default an `Error` will be thrown.
+This behaviour can be modified by using the member `fatalHandler` to
+assign a custom delegate to handle log call with `LogLevel` `fatal`.
+
+$(H4 Conditional Logging)
+Conditional logging can be achieved be passing a `bool` as first
+argument to a log function. If conditional logging is used the condition must
+be `true` in order to have the log message logged.
+
+In order to combine an explicit `LogLevel` passing with conditional
+logging, the `LogLevel` has to be passed as first argument followed by the
+`bool`.
+
+$(H4 Filtering Log Messages)
+Messages are logged if the `LogLevel` of the log message is greater than or
+equal to the `LogLevel` of the used `Logger` and additionally if the
+`LogLevel` of the log message is greater than or equal to the global `LogLevel`.
+If a condition is passed into the log call, this condition must be true.
+
+The global `LogLevel` is accessible by using `globalLogLevel`.
+To assign a `LogLevel` of a `Logger` use the `logLevel` property of
+the logger.
+
+$(H4 Printf Style Logging)
+If `printf`-style logging is needed add a $(B f) to the logging call, such as
+$(D myLogger.infof("Hello %s", "world");) or $(D fatalf("errno %d", 1337)).
+The additional $(B f) appended to the function name enables `printf`-style
+logging for all combinations of explicit `LogLevel` and conditional
+logging functions and methods.
+
+$(H4 Thread Local Redirection)
+Calls to the free standing log functions are not directly forwarded to the
+global `Logger` `sharedLog`. Actually, a thread local `Logger` of
+type `StdForwardLogger` processes the log call and then, by default, forwards
+the created `Logger.LogEntry` to the `sharedLog` `Logger`.
+The thread local `Logger` is accessible by the `stdThreadLocalLog`
+property. This property allows to assign user defined `Logger`. The default
+`LogLevel` of the `stdThreadLocalLog` `Logger` is `LogLevel.all`
+and it will therefore forward all messages to the `sharedLog` `Logger`.
+The `LogLevel` of the `stdThreadLocalLog` can be used to filter log
+calls before they reach the `sharedLog` `Logger`.
+
+$(H3 User Defined Logger)
+To customize the `Logger` behavior, create a new `class` that inherits from
+the abstract `Logger` `class`, and implements the `writeLogMsg`
+method.
+-------------
+class MyCustomLogger : Logger
+{
+ this(LogLevel lv) @safe
+ {
+ super(lv);
+ }
+
+ override void writeLogMsg(ref LogEntry payload)
+ {
+ // log message in my custom way
+ }
+}
+
+auto logger = new MyCustomLogger(LogLevel.info);
+logger.log("Awesome log message with LogLevel.info");
+-------------
+
+To gain more precise control over the logging process, additionally to
+overriding the `writeLogMsg` method the methods `beginLogMsg`,
+`logMsgPart` and `finishLogMsg` can be overridden.
+
+$(H3 Provided Logger)
+By default four `Logger` implementations are given. The `FileLogger`
+logs data to files. It can also be used to log to `stdout` and `stderr`
+as these devices are files as well. A `Logger` that logs to `stdout` can
+therefore be created by $(D new FileLogger(stdout)).
+The `MultiLogger` is basically an associative array of `string`s to
+`Logger`. It propagates log calls to its stored `Logger`. The
+`ArrayLogger` contains an array of `Logger` and also propagates log
+calls to its stored `Logger`. The `NullLogger` does not do anything. It
+will never log a message and will never throw on a log call with `LogLevel`
+`error`.
+
+Source: $(PHOBOSSRC std/logger/package.d)
+*/
+module std.logger;
+
+public import std.logger.core;
+public import std.logger.filelogger;
+public import std.logger.multilogger;
+public import std.logger.nulllogger;
diff --git a/libphobos/src/std/meta.d b/libphobos/src/std/meta.d
index 0075ed7edf7..2db341da85e 100644
--- a/libphobos/src/std/meta.d
+++ b/libphobos/src/std/meta.d
@@ -627,35 +627,9 @@ Evaluates to `AliasSeq!(fun!(args[0]), fun!(args[1]), ..., fun!(args[$ - 1]))`.
*/
template staticMap(alias fun, args...)
{
- version (__staticMap_simplest_but_buggy)
- {
- // @@@ BUG @@@
- // The following straightforward implementation exposes a bug.
- // See issue https://issues.dlang.org/show_bug.cgi?id=22421 and unittest below.
- alias staticMap = AliasSeq!();
- static foreach (arg; args)
- staticMap = AliasSeq!(staticMap, fun!arg);
- }
- else version (__staticMap_simple_but_slow)
- {
- // This has a performance bug. Appending to the staticMap seems to be quadratic.
- alias staticMap = AliasSeq!();
- static foreach (i; 0 .. args.length)
- staticMap = AliasSeq!(staticMap, fun!(args[i]));
- }
- else // Current best-of-breed implementation imitates quicksort
- {
- static if (args.length <= 8)
- {
- alias staticMap = AliasSeq!();
- static foreach (i; 0 .. args.length)
- staticMap = AliasSeq!(staticMap, fun!(args[i]));
- }
- else
- {
- alias staticMap = AliasSeq!(staticMap!(fun, args[0 .. $ / 2]), staticMap!(fun, args[$ / 2 .. $]));
- }
- }
+ alias staticMap = AliasSeq!();
+ static foreach (arg; args)
+ staticMap = AliasSeq!(staticMap, fun!arg);
}
///
diff --git a/libphobos/src/std/package.d b/libphobos/src/std/package.d
index bfb135b1885..e2eca7bc489 100644
--- a/libphobos/src/std/package.d
+++ b/libphobos/src/std/package.d
@@ -53,6 +53,7 @@ public import
std.getopt,
std.int128,
std.json,
+ std.logger,
std.math,
std.mathspecial,
std.meta,
diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d
index b2206ce56ba..70b87ddd59e 100644
--- a/libphobos/src/std/random.d
+++ b/libphobos/src/std/random.d
@@ -2806,7 +2806,7 @@ auto ref choice(Range)(ref Range range)
{
import std.algorithm.searching : canFind;
- class MyTestClass
+ static class MyTestClass
{
int x;
diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d
index e24abc50a07..1562d797e88 100644
--- a/libphobos/src/std/regex/package.d
+++ b/libphobos/src/std/regex/package.d
@@ -43,11 +43,16 @@ $(TR $(TD Objects) $(TD
))
$(SECTION Synopsis)
- ---
- import std.regex;
- import std.stdio;
- void main()
- {
+
+ Create a regex at runtime:
+ $(RUNNABLE_EXAMPLE
+ $(RUNNABLE_EXAMPLE_STDIN
+They met on 24/01/1970.
+7/8/99 wasn't as hot as 7/8/2022.
+)
+ ---
+ import std.regex;
+ import std.stdio;
// Print out all possible dd/mm/yy(yy) dates found in user input.
auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b");
foreach (line; stdin.byLine)
@@ -57,19 +62,24 @@ $(TR $(TD Objects) $(TD
foreach (c; matchAll(line, r))
writeln(c.hit);
}
- }
- ...
-
- // Create a static regex at compile-time, which contains fast native code.
+ ---
+ )
+ Create a static regex at compile-time, which contains fast native code:
+ $(RUNNABLE_EXAMPLE
+ ---
+ import std.regex;
auto ctr = ctRegex!(`^.*/([^/]+)/?$`);
// It works just like a normal regex:
auto c2 = matchFirst("foo/bar", ctr); // First match found here, if any
assert(!c2.empty); // Be sure to check if there is a match before examining contents!
assert(c2[1] == "bar"); // Captures is a range of submatches: 0 = full match.
-
- ...
- // multi-pattern regex
+ ---
+ )
+ Multi-pattern regex:
+ $(RUNNABLE_EXAMPLE
+ ---
+ import std.regex;
auto multi = regex([`\d+,\d+`, `([a-z]+):(\d+)`]);
auto m = "abc:43 12,34".matchAll(multi);
assert(m.front.whichPattern == 2);
@@ -77,21 +87,27 @@ $(TR $(TD Objects) $(TD
assert(m.front[2] == "43");
m.popFront();
assert(m.front.whichPattern == 1);
- assert(m.front[1] == "12");
- ...
-
+ assert(m.front[0] == "12,34");
+ ---
+ )
+ $(LREF Captures) and `opCast!bool`:
+ $(RUNNABLE_EXAMPLE
+ ---
+ import std.regex;
// The result of `matchAll/matchFirst` is directly testable with `if/assert/while`,
// e.g. test if a string consists of letters only:
assert(matchFirst("LettersOnly", `^\p{L}+$`));
- // And we can take advantage of the ability to define a variable in the $(LINK2 https://dlang.org/spec/statement.html#IfCondition `IfCondition`):
- if (const auto captures = matchFirst("At l34st one digit, but maybe more...", `((\d)(\d*))`))
+ // And we can take advantage of the ability to define a variable in the IfCondition:
+ if (const captures = matchFirst("At l34st one digit, but maybe more...", `((\d)(\d*))`))
{
assert(captures[2] == "3");
assert(captures[3] == "4");
assert(captures[1] == "34");
}
---
+ )
+ See_Also: $(LINK2 https://dlang.org/spec/statement.html#IfCondition, `IfCondition`).
$(SECTION Syntax and general information)
The general usage guideline is to keep regex complexity on the side of simplicity,
@@ -470,7 +486,7 @@ private struct CTRegexWrapper(Char)
alias getRe this;
}
-template ctRegexImpl(alias pattern, string flags=[])
+template ctRegexImpl(alias pattern, string flags="")
{
import std.regex.internal.backtracking, std.regex.internal.parser;
static immutable r = cast(immutable) regex(pattern, flags);
@@ -518,7 +534,7 @@ template ctRegexImpl(alias pattern, string flags=[])
pattern = Regular expression
flags = The _attributes (g, i, m, s and x accepted)
+/
-public enum ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).wrapper;
+public enum ctRegex(alias pattern, string flags="") = ctRegexImpl!(pattern, flags).wrapper;
enum isRegexFor(RegEx, R) = is(immutable RegEx == immutable Regex!(BasicElementOf!R))
|| is(RegEx : const(Regex!(BasicElementOf!R)))
diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d
index 5c9a2c946ba..7ed24f7f98d 100644
--- a/libphobos/src/std/string.d
+++ b/libphobos/src/std/string.d
@@ -6884,7 +6884,7 @@ if (isSomeString!S)
if (inword)
{
- if (col + 1 + (s.length - wordstart) >= columns)
+ if (col + 1 + (s.length - wordstart) > columns)
{
result ~= '\n';
result ~= indent;
@@ -6929,6 +6929,13 @@ if (isSomeString!S)
});
}
+@safe pure unittest // https://issues.dlang.org/show_bug.cgi?id=23298
+{
+ assert("1 2 3 4 5 6 7 8 9".wrap(17) == "1 2 3 4 5 6 7 8 9\n");
+ assert("1 2 3 4 5 6 7 8 9 ".wrap(17) == "1 2 3 4 5 6 7 8 9\n");
+ assert("1 2 3 4 5 6 7 8 99".wrap(17) == "1 2 3 4 5 6 7 8\n99\n");
+}
+
/******************************************
* Removes one level of indentation from a multi-line string.
*
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
index 4ecfb1051d1..c7cdc24e4d5 100644
--- a/libphobos/src/std/typecons.d
+++ b/libphobos/src/std/typecons.d
@@ -1146,7 +1146,7 @@ if (distinctFieldNames!(Specs))
}
///
- static if (Specs.length == 0) @safe unittest
+ static if (Specs.length == 0) @system unittest
{
//replace names by their position
@@ -1166,7 +1166,7 @@ if (distinctFieldNames!(Specs))
assert(t2Named.c == 3);
}
- static if (Specs.length == 0) @safe unittest
+ static if (Specs.length == 0) @system unittest
{
//check that empty translations work fine
enum string[string] a0 = null;