1 /++
2     Logging
3 
4     While D standard library’s `std.logger` “implements logging facilities”
5     (At the time of writing, this is its actual module description.),
6     this modules makes sense of them.
7 
8     ---
9     import socketplate.log;
10 
11     log("Hello world"); // prints a log message with text “Hello world”
12     ---
13 
14 
15     ## Log levels
16 
17     ---
18     // set the log level of `sharedLog` to *INFO*
19     // (`sharedLog` is the globally available default logger implemented in `std.logger`)
20     setLogLevel(LogLevel.info);
21 
22     logTrace("Trace/debug message");
23     logInfo("Info message");
24     logWarning("Warning message");
25     logError("(Non-fatal, uncritical) error message");
26     logCritical("(Non-fatal yet critical) error message");
27     logFatalAndCrash("Fatal error message, also crashes the program");
28     ---
29 
30 
31     ## Notable differences to `std.logger`
32 
33     $(LIST
34         * The “default” log functions applys a log level of *INFO* instead of *ALL*.
35         * Logging function names are prefixed with `log`.
36         * The “fatal”-level logging function mentions in its name that it will crash the application.
37         * The hello-world example will actually print a log message out of the box.
38     )
39  +/
40 module socketplate.log;
41 
42 import std.logger : defaultLogFunction;
43 public import std.logger : LogLevel;
44 
45 @safe:
46 
47 /// Logs a trace/debug message
48 alias logTrace = defaultLogFunction!(LogLevel.trace);
49 
50 /// Logs an informational message
51 alias logInfo = defaultLogFunction!(LogLevel.info);
52 
53 /// Logs a warning
54 alias logWarning = defaultLogFunction!(LogLevel.warning);
55 
56 /// Logs an non-critical error
57 alias logError = defaultLogFunction!(LogLevel.error);
58 
59 /// Logs a critical error
60 alias logCritical = defaultLogFunction!(LogLevel.critical);
61 
62 /// Logs a fatal error and raises an Error to halt execution by crashing the application
63 alias logFatalAndCrash = defaultLogFunction!(LogLevel.fatal);
64 
65 ///
66 alias log = logInfo;
67 
68 /++
69     Logs an exception (including a stack trace)
70  +/
71 void logException(LogLevel logLevel = LogLevel.error, LogLevel details = LogLevel.trace)(
72     Throwable exception,
73     string description = "Exception",
74     int line = __LINE__,
75     string file = __FILE__,
76     string funcName = __FUNCTION__,
77     string prettyFuncName = __PRETTY_FUNCTION__,
78     string moduleName = __MODULE__,
79 )
80 @safe nothrow
81 {
82     import std.logger : log;
83     import std.string : format;
84 
85     try
86     {
87         log(
88             logLevel,
89             line, file, funcName, prettyFuncName, moduleName,
90             format!"%s: %s"(description, exception.msg)
91         );
92 
93         try
94         {
95             log(
96                 details,
97                 line, file, funcName, prettyFuncName, moduleName,
98                 format!"Details: %s"(() @trusted { return exception.toString(); }())
99             );
100         }
101         catch (Exception ex)
102         {
103             logTrace(format!"Failed to log details: %s"(ex.msg));
104         }
105     }
106     catch (Exception)
107     {
108         // suppress
109     }
110 }
111 
112 ///
113 unittest
114 {
115     try
116     {
117         // …
118     }
119     catch (Exception ex)
120     {
121         logException(ex, "Operation XY failed.");
122     }
123 
124 }
125 
126 ///
127 unittest
128 {
129     try
130     {
131         // …
132     }
133     catch (Exception ex)
134     {
135         logException!(LogLevel.trace)(ex);
136     }
137 
138 }
139 
140 /++
141     Sets the [LogLevel] of the default logger (also known as `sharedLog`)
142  +/
143 void setLogLevel(LogLevel logLevel)
144 {
145     import std.logger : Logger, sharedLog;
146 
147     Logger l = (() @trusted { return (cast() sharedLog); })();
148     l.logLevel = logLevel;
149 }