1 module mecca.log;
2 
3 // Licensed under the Boost license. Full copyright information in the AUTHORS file
4 
5 version(MeccaAlternateLogger) {
6 mixin("public import " ~ import("MeccaAlternateLogger.txt") ~ ";");
7 } else {
8 import std.stdio;
9 import std..string;
10 import std.datetime;
11 import mecca.lib.reflection: as;
12 import mecca.lib.console;
13 import mecca.lib.exception: extractStack, DefaultTraceInfoABI;
14 import mecca.log.impl;
15 
16 /// Report whether the loggin infra has been initialized
17 enum loggingInitialized = true;
18 
19 /**
20  * UDA for disabling auto tracing of a specific function
21  *
22  * Decorate functions that should not be traced with @notrace.
23  */
24 enum notrace = "notrace";
25 
26 // All implementations must define this enum to say whether logs sent find their way to the console
27 enum LogToConsole = true;
28 
29 // UDA for modifying a variable formatting (currently ignored)
30 struct FMT {
31     immutable string __log_customFormatString;
32 }
33 
34 /*
35    These functions are mostly placeholders. Since we'd sometimes want to replace them with functions that
36    do binary logging, the format is part of the function's template.
37  */
38 
39 enum LEVEL_DEBUG     = FG.grey;
40 enum LEVEL_INFO      = FG.green;
41 enum LEVEL_WARN      = FG.iyellow;
42 enum LEVEL_ERROR     = FG.ired;
43 enum LEVEL_EXCEPTION = FG.iwhite | BG.red;
44 enum LEVEL_META = FG.iwhite | BG.magenta;
45 enum LEVEL_BT        = FG.red;
46 
47 private void internalLogOutput(ANSI level, T...)(string fmt, string file, size_t line, T args) nothrow @trusted @nogc {
48     as!"nothrow @nogc"({
49         auto t = Clock.currTime();
50         auto loc = "%s:%s".format(file.split("/")[$-1], line);
51         writefln(FG.grey("%02d:%02d:%02d.%03d") ~ "\u2502" ~ FG.cyan("%s") ~ "\u2502" ~ FG.grey("%-20s") ~ "\u2502" ~ level(fmt),
52             t.hour, t.minute, t.second, t.fracSecs.total!"msecs", logSource, loc[$ > 20 ? $ - 20 : 0 .. $], args);
53     });
54 }
55 
56 void DEBUG(string fmt, string file = __FILE_FULL_PATH__, string mod = __MODULE__, int line = __LINE__, T...)(T args) nothrow @safe @nogc {
57     internalLogOutput!LEVEL_DEBUG(fmt, file, line, args);
58 }
59 
60 void INFO(string fmt, string file = __FILE_FULL_PATH__, string mod = __MODULE__, int line = __LINE__, T...)(T args) nothrow @safe @nogc {
61     internalLogOutput!LEVEL_INFO(fmt, file, line, args);
62 }
63 
64 void WARN(string fmt, string file = __FILE_FULL_PATH__, string mod = __MODULE__, int line = __LINE__, T...)(T args) nothrow @safe @nogc {
65     internalLogOutput!LEVEL_WARN(fmt, file, line, args);
66 }
67 
68 void ERROR(string fmt, string file = __FILE_FULL_PATH__, string mod = __MODULE__, int line = __LINE__, T...)(T args) nothrow @safe @nogc {
69     internalLogOutput!LEVEL_ERROR(fmt, file, line, args);
70 }
71 
72 void LOG_EXCEPTION(Throwable ex) nothrow @trusted @nogc {
73     internalLogOutput!LEVEL_EXCEPTION("%s@%s(%s): %s", ex.file, ex.line, typeid(ex).name, ex.file, ex.line, ex.msg);
74     if (ex.info) {
75         foreach( ptr; DefaultTraceInfoABI.extract(ex.info).frames ) {
76             as!"nothrow @nogc"({ writefln("\t0x%x", ptr); });
77         }
78     }
79 }
80 
81 void META(string fmt, string file = __FILE_FULL_PATH__, string mod = __MODULE__, int line = __LINE__, T...)(T args) nothrow @safe @nogc {
82     internalLogOutput!LEVEL_META(fmt, file, line, args);
83 }
84 
85 void LOG_TRACEBACK(void*[] bt, string msg, string file = __FILE_FULL_PATH__, size_t line = __LINE__) nothrow @trusted @nogc
86 {
87     internalLogOutput!LEVEL_BT(msg, file, line);
88     foreach( ptr; bt ) {
89         as!"nothrow @nogc"({ writefln("\t0x%x", ptr); });
90     }
91 }
92 
93 /* thread local */ static void*[128] btBuffer = void;
94 
95 void dumpStackTrace(string msg = "Backtrace:", string file = __FILE_FULL_PATH__, size_t line = __LINE__) nothrow @trusted @nogc {
96     auto bt = extractStack(btBuffer);
97     LOG_TRACEBACK(bt, msg, file, line);
98 }
99 
100 void flushLog() nothrow @trusted @nogc {
101     as!"nothrow @nogc"({ stdout.flush(); });
102 }
103 
104 unittest {
105     DEBUG!"Just some debug info %s"(42);
106     INFO!"Event worthy of run time mention %s"(100);
107     WARN!"Take heed, %s traveller, for something strange is a%s"("weary", "foot");
108     ERROR!"2b || !2b == %s"('?');
109     dumpStackTrace("Where am I?");
110     try {
111         throw new Exception("inception");
112     }
113     catch (Exception ex) {
114         LOG_EXCEPTION(ex);
115     }
116 }
117 }