1 /** 2 * The main module of SLF4D, which publicly imports all of the components that 3 * are typically needed in an application for logging. It also defines the 4 * global shared `loggerFactory` from which all application loggers are 5 * obtained. 6 */ 7 module slf4d; 8 9 public import slf4d.logger; 10 public import slf4d.factory; 11 public import slf4d.handler; 12 public import slf4d.provider; 13 public import slf4d.writer; 14 public import slf4d.level; 15 public import slf4d.log_functions; 16 17 // Below the public imports, we define some common global state for the SLF4D 18 // logging system. 19 20 import core.atomic; 21 22 /** 23 * The logging provider. It defaults to the default provider included in the 24 * box with SLF4D, but it's intended for applications to change this via the 25 * `configureLoggingProvider` function on application startup. 26 */ 27 private shared LoggingProvider loggingProvider; 28 29 /** 30 * An internal flag that's set once the logging provider is configured, and 31 * used to issue warnings if the provider is re-configured when it shouldn't be. 32 */ 33 private shared bool loggingProviderSet = false; 34 35 /** 36 * Configures SLF4D to use the given logging provider. Call this once on 37 * application startup. In order to improve runtime performance, access to this 38 * provider is **not** synchronized, so attempts to reconfigure the logging 39 * provider after startup may lead to undefined behavior. 40 * Params: 41 * provider = The logging provider to use. If `null` is given then SFL4D will 42 * use its built-in `NoOpProvider` from `slf4d.noop_provider`. 43 */ 44 public void configureLoggingProvider(shared LoggingProvider provider) { 45 if (atomicLoad(loggingProviderSet)) { 46 Logger logger = getLogger(); 47 static immutable string fmt = "The SLF4D logging provider has already been " ~ 48 "configured with the provider %s. Re-configuring the logging provider " ~ 49 "after it has already been configured is not supported, and may have " ~ 50 "unintended consequences."; 51 logger.warnF!(fmt)(loggingProvider); 52 } 53 if (provider is null) { 54 import slf4d.noop_provider : NoOpProvider; 55 loggingProvider = new shared NoOpProvider(); 56 } else { 57 loggingProvider = provider; 58 } 59 atomicStore(loggingProviderSet, true); 60 } 61 62 /** 63 * Gets the global shared logging provider instance. If no provider has been 64 * explicitly configured via `configureLoggingProvider`, then SLF4D's default 65 * provider will be used (or testing provider for unit tests). 66 * Returns: The logging provider. 67 */ 68 public shared(LoggingProvider) getLoggingProvider() { 69 if (loggingProvider is null) { 70 import slf4d.default_provider : DefaultProvider; 71 loggingProvider = new DefaultProvider(); 72 } 73 return loggingProvider; 74 } 75 76 /** 77 * Gets the global shared logger factory instance. If no provider has been 78 * explicitly configured, the `slf4d.default_provider` module's 79 * `DefaultProvider` is used. 80 * Returns: The logger factory. 81 */ 82 public shared(LoggerFactory) getLoggerFactory() { 83 return getLoggingProvider().getLoggerFactory(); 84 } 85 86 /** 87 * Gets a Logger using the configured logger factory. This is a shortcut for 88 * `getLoggerFactory().getLogger()`. 89 * Params: 90 * name = The name of the logger. Defaults to the name of the module where 91 * this function is invoked. 92 * Returns: The logger. 93 */ 94 public Logger getLogger(string name = __MODULE__) { 95 return getLoggerFactory().getLogger(name); 96 } 97 98 /** 99 * Resets the logging state. Call this after your test, if you configured 100 * the global shared logging provider. 101 * 102 * Warning: This function is intended for use in tests, and should NOT be used 103 * in your normal application's logic. 104 */ 105 public void resetLoggingState() { 106 loggingProvider = null; 107 atomicStore(loggingProviderSet, false); 108 } 109 110 unittest { 111 /** 112 * Asserts that the logging state is not yet initialized. 113 */ 114 void assertNotInitialized() { 115 import std.format : format; 116 assert( 117 loggingProvider is null, 118 format!"loggingProvider is not null when it shouldn't have been initialized yet: %s"(loggingProvider) 119 ); 120 assert( 121 loggingProviderSet == false, 122 "loggingProviderSet is true the logging provider hasn't been configured yet." 123 ); 124 } 125 126 /** 127 * Asserts that the logging state has been initialized with a given 128 * provider class. 129 */ 130 void assertInitialized(ProviderClass)() { 131 import std.format : format; 132 import std.traits; 133 assert( 134 loggingProvider !is null, 135 "loggingProvider is null when it should have been initialized." 136 ); 137 assert( 138 loggingProviderSet, 139 "loggingProviderSet is false when the logging provider should have been initialized." 140 ); 141 assert( 142 cast(ProviderClass) loggingProvider, 143 format!"loggingProvider is not of the expected class %s, instead it is %s"( 144 fullyQualifiedName!ProviderClass, 145 loggingProvider 146 ) 147 ); 148 } 149 150 import slf4d.test; 151 synchronized(loggingTestingMutex) { 152 // Test that a warning is issued if the provider is configured more than once. 153 assertNotInitialized(); 154 auto provider = new TestingLoggingProvider(); 155 configureLoggingProvider(provider); 156 assert(loggingProviderSet == true, "loggingProviderSet is not true after configuring the logging provider."); 157 // Now try and configure it again. A warning message should be produced. 158 configureLoggingProvider(provider); 159 assertInitialized!TestingLoggingProvider(); 160 assert(provider.messageCount == 1 && provider.messageCount(Levels.WARN) == 1); 161 resetLoggingState(); 162 assertNotInitialized(); 163 164 // Test that if `null` is given, the NoOpProvider is used. 165 assertNotInitialized(); 166 configureLoggingProvider(null); 167 import slf4d.noop_provider : NoOpProvider; 168 assertInitialized!NoOpProvider(); 169 resetLoggingState(); 170 } 171 } 172 173 /** 174 * An exception that may be thrown by certain logging features. Within SLF4D, 175 * documentation will be provided to declare whenever a function may throw this 176 * exception. 177 */ 178 public class LoggingException : Exception { 179 /** 180 * Constructs this exception with a message and throwable cause. 181 * Params: 182 * msg = The message to provide. 183 * next = The throwable that caused this exception. 184 */ 185 this(string msg, Throwable next) { 186 super(msg, next); 187 } 188 189 /** 190 * Constructs this exception with a message. 191 * Params: 192 * msg = The message to provide. 193 */ 194 this(string msg) { 195 super(msg); 196 } 197 }