1 /** 2 * Core module containing types pertaining to the base Logger 3 * class and base MessageTransform class (along with a default 4 * transform, DefaultTransform) 5 */ 6 module dlog.core; 7 8 import std.conv : to; 9 import std.range : join; 10 import dlog.transform : MessageTransform; 11 import dlog.defaults; 12 import dlog.context : Context, CompilationInfo, Level; 13 import dlog.utilities : flatten; 14 15 /** 16 * Logger 17 * 18 * Represents a logger instance 19 */ 20 public class Logger 21 { 22 /* Starting transformation */ 23 private MessageTransform messageTransform; 24 25 /* The multiple argument joiner */ 26 protected string multiArgJoiner; 27 28 /** 29 * Constructs a new Logger with the default 30 * MessageTransform 31 * 32 * Params: 33 * multiArgJoiner = optional joiner for segmented prints (default is " ") 34 */ 35 this(string multiArgJoiner = " ") 36 { 37 this(new DefaultTransform(), multiArgJoiner); 38 } 39 40 /** 41 * Constructs a new Logger with the provided 42 * custom message transform 43 * Params: 44 * messageTransform = the message transform to use 45 * multiArgJoiner = optional joiner for segmented prints (default is " ") 46 */ 47 this(MessageTransform messageTransform, string multiArgJoiner = " ") 48 { 49 this.messageTransform = messageTransform; 50 this.multiArgJoiner = multiArgJoiner; 51 } 52 53 /** 54 * Given an arbitrary amount of arguments, convert each to a string 55 * and return it as an array joined by the joiner 56 * 57 * Params: 58 * segments = alias sequence 59 * Returns: a string of the argumnets 60 */ 61 public string args(TextType...)(TextType segments) 62 { 63 /* The flattened components */ 64 string[] components = flatten(segments); 65 66 /* Join all `components` into a single string */ 67 string joined = join(components, multiArgJoiner); 68 69 return joined; 70 } 71 72 73 /** 74 * Logs the given string using the default context 75 * 76 * Params: 77 * text = the string to log 78 * __FILE_FULL_PATH__ = compile time usage file 79 * __FILE__ = compile time usage file (relative) 80 * __LINE__ = compile time usage line number 81 * __MODULE__ = compile time usage module 82 * __FUNCTION__ = compile time usage function 83 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 84 */ 85 public final void log(string text, string c1 = __FILE_FULL_PATH__, 86 string c2 = __FILE__, ulong c3 = __LINE__, 87 string c4 = __MODULE__, string c5 = __FUNCTION__, 88 string c6 = __PRETTY_FUNCTION__) 89 { 90 /* Use the default context `Context` */ 91 Context defaultContext = new Context(); 92 93 /* Build up the line information */ 94 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 95 96 /* Set the line information in the context */ 97 defaultContext.setLineInfo(compilationInfo); 98 99 /* Call the log */ 100 logc(defaultContext, text, c1, c2, c3, c4, c5, c6); 101 } 102 103 /** 104 * Logs using the default context an arbitrary amount of arguments 105 * 106 * Params: 107 * segments = the arbitrary argumnets (alias sequence) 108 * __FILE_FULL_PATH__ = compile time usage file 109 * __FILE__ = compile time usage file (relative) 110 * __LINE__ = compile time usage line number 111 * __MODULE__ = compile time usage module 112 * __FUNCTION__ = compile time usage function 113 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 114 */ 115 public final void log(TextType...)(TextType segments, string c1 = __FILE_FULL_PATH__, 116 string c2 = __FILE__, ulong c3 = __LINE__, 117 string c4 = __MODULE__, string c5 = __FUNCTION__, 118 string c6 = __PRETTY_FUNCTION__) 119 { 120 /** 121 * Grab at compile-time all arguments and generate runtime code to add them to `components` 122 */ 123 string[] components = flatten(segments); 124 125 /* Join all `components` into a single string */ 126 string messageOut = join(components, multiArgJoiner); 127 128 /* Call the log (with text and default context) */ 129 log(messageOut, c1, c2, c3, c4, c5, c6); 130 } 131 132 /** 133 * Logs the given string using the provided context 134 * 135 * Params: 136 * context = the custom context to use 137 * text = the string to log 138 * __FILE_FULL_PATH__ = compile time usage file 139 * __FILE__ = compile time usage file (relative) 140 * __LINE__ = compile time usage line number 141 * __MODULE__ = compile time usage module 142 * __FUNCTION__ = compile time usage function 143 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 144 */ 145 public final void logc(Context context, string text, string c1 = __FILE_FULL_PATH__, 146 string c2 = __FILE__, ulong c3 = __LINE__, 147 string c4 = __MODULE__, string c5 = __FUNCTION__, 148 string c6 = __PRETTY_FUNCTION__) 149 { 150 /* Build up the line information */ 151 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 152 153 /* Set the line information in the context */ 154 context.setLineInfo(compilationInfo); 155 156 /* Apply the transformation on the message */ 157 string transformedMesage = messageTransform.execute(text, context); 158 159 /* Call the underlying logger implementation */ 160 logImpl(transformedMesage); 161 } 162 163 /** 164 * Logs using the default context an arbitrary amount of arguments 165 * specifically setting the context's level to ERROR 166 * 167 * Params: 168 * segments = the arbitrary argumnets (alias sequence) 169 * __FILE_FULL_PATH__ = compile time usage file 170 * __FILE__ = compile time usage file (relative) 171 * __LINE__ = compile time usage line number 172 * __MODULE__ = compile time usage module 173 * __FUNCTION__ = compile time usage function 174 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 175 */ 176 public void error(TextType...)(TextType segments, 177 string c1 = __FILE_FULL_PATH__, 178 string c2 = __FILE__, ulong c3 = __LINE__, 179 string c4 = __MODULE__, string c5 = __FUNCTION__, 180 string c6 = __PRETTY_FUNCTION__) 181 { 182 /* Use the default context `Context` */ 183 Context defaultContext = new Context(); 184 185 /* Build up the line information */ 186 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 187 188 /* Set the line information in the context */ 189 defaultContext.setLineInfo(compilationInfo); 190 191 /* Set the level to ERROR */ 192 defaultContext.setLevel(Level.ERROR); 193 194 /** 195 * Grab at compile-time all arguments and generate runtime code to add them to `components` 196 */ 197 string[] components = flatten(segments); 198 199 /* Join all `components` into a single string */ 200 string messageOut = join(components, multiArgJoiner); 201 202 /* Call the log */ 203 logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6); 204 } 205 206 /** 207 * Logs using the default context an arbitrary amount of arguments 208 * specifically setting the context's level to INFO 209 * 210 * Params: 211 * segments = the arbitrary argumnets (alias sequence) 212 * __FILE_FULL_PATH__ = compile time usage file 213 * __FILE__ = compile time usage file (relative) 214 * __LINE__ = compile time usage line number 215 * __MODULE__ = compile time usage module 216 * __FUNCTION__ = compile time usage function 217 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 218 */ 219 public void info(TextType...)(TextType segments, 220 string c1 = __FILE_FULL_PATH__, 221 string c2 = __FILE__, ulong c3 = __LINE__, 222 string c4 = __MODULE__, string c5 = __FUNCTION__, 223 string c6 = __PRETTY_FUNCTION__) 224 { 225 /* Use the default context `Context` */ 226 Context defaultContext = new Context(); 227 228 /* Build up the line information */ 229 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 230 231 /* Set the line information in the context */ 232 defaultContext.setLineInfo(compilationInfo); 233 234 /* Set the level to INFO */ 235 defaultContext.setLevel(Level.INFO); 236 237 /** 238 * Grab at compile-time all arguments and generate runtime code to add them to `components` 239 */ 240 string[] components = flatten(segments); 241 242 /* Join all `components` into a single string */ 243 string messageOut = join(components, multiArgJoiner); 244 245 /* Call the log */ 246 logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6); 247 } 248 249 /** 250 * Logs using the default context an arbitrary amount of arguments 251 * specifically setting the context's level to WARN 252 * 253 * Params: 254 * segments = the arbitrary argumnets (alias sequence) 255 * __FILE_FULL_PATH__ = compile time usage file 256 * __FILE__ = compile time usage file (relative) 257 * __LINE__ = compile time usage line number 258 * __MODULE__ = compile time usage module 259 * __FUNCTION__ = compile time usage function 260 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 261 */ 262 public void warn(TextType...)(TextType segments, 263 string c1 = __FILE_FULL_PATH__, 264 string c2 = __FILE__, ulong c3 = __LINE__, 265 string c4 = __MODULE__, string c5 = __FUNCTION__, 266 string c6 = __PRETTY_FUNCTION__) 267 { 268 /* Use the default context `Context` */ 269 Context defaultContext = new Context(); 270 271 /* Build up the line information */ 272 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 273 274 /* Set the line information in the context */ 275 defaultContext.setLineInfo(compilationInfo); 276 277 /* Set the level to WARN */ 278 defaultContext.setLevel(Level.WARN); 279 280 /** 281 * Grab at compile-time all arguments and generate runtime code to add them to `components` 282 */ 283 string[] components = flatten(segments); 284 285 /* Join all `components` into a single string */ 286 string messageOut = join(components, multiArgJoiner); 287 288 /* Call the log */ 289 logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6); 290 } 291 292 /** 293 * Logs using the default context an arbitrary amount of arguments 294 * specifically setting the context's level to DEBUG 295 * 296 * Params: 297 * segments = the arbitrary argumnets (alias sequence) 298 * __FILE_FULL_PATH__ = compile time usage file 299 * __FILE__ = compile time usage file (relative) 300 * __LINE__ = compile time usage line number 301 * __MODULE__ = compile time usage module 302 * __FUNCTION__ = compile time usage function 303 * __PRETTY_FUNCTION__ = compile time usage function (pretty) 304 */ 305 public void debug_(TextType...)(TextType segments, 306 string c1 = __FILE_FULL_PATH__, 307 string c2 = __FILE__, ulong c3 = __LINE__, 308 string c4 = __MODULE__, string c5 = __FUNCTION__, 309 string c6 = __PRETTY_FUNCTION__) 310 { 311 /* Use the default context `Context` */ 312 Context defaultContext = new Context(); 313 314 /* Build up the line information */ 315 CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6); 316 317 /* Set the line information in the context */ 318 defaultContext.setLineInfo(compilationInfo); 319 320 /* Set the level to DEBUG */ 321 defaultContext.setLevel(Level.DEBUG); 322 323 /** 324 * Grab at compile-time all arguments and generate runtime code to add them to `components` 325 */ 326 string[] components = flatten(segments); 327 328 /* Join all `components` into a single string */ 329 string messageOut = join(components, multiArgJoiner); 330 331 /* Call the log */ 332 logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6); 333 } 334 335 /* You can also call using `dbg` */ 336 public alias dbg = debug_; 337 338 /** 339 * Logging implementation, this is where the final 340 * transformed text will be transferred to and finally 341 * logged 342 * 343 * Params: 344 * message = the message to log 345 */ 346 protected abstract void logImpl(string message); 347 } 348 349 350 version(unittest) 351 { 352 import std.meta : AliasSeq; 353 import std.stdio : writeln; 354 } 355 356 /** 357 * Tests the DefaultLogger 358 */ 359 unittest 360 { 361 Logger logger = new DefaultLogger(); 362 363 alias testParameters = AliasSeq!("This is a log message", 1.1, true, [1,2,3], 'f', logger); 364 365 366 // Test various types one-by-one 367 static foreach(testParameter; testParameters) 368 { 369 logger.log(testParameter); 370 } 371 372 // Test various parameters (of various types) all at once 373 logger.log(testParameters); 374 375 // Same as above but with a custom joiner set 376 logger = new DefaultLogger("(-)"); 377 logger.log(testParameters); 378 379 writeln(); 380 } 381 382 /** 383 * Printing out some mixed data-types, also using a DEFAULT context 384 */ 385 unittest 386 { 387 Logger logger = new DefaultLogger(); 388 389 // Create a default logger with the default joiner 390 logger = new DefaultLogger(); 391 logger.log(["a", "b", "c", "d"], [1, 2], true); 392 393 writeln(); 394 } 395 396 /** 397 * Printing out some mixed data-types, also using a CUSTOM context 398 */ 399 unittest 400 { 401 Logger logger = new DefaultLogger(); 402 403 // Create a default logger with the default joiner 404 logger = new DefaultLogger(); 405 406 // Create a custom context 407 Context customContext = new Context(); 408 409 // Log with the custom context 410 logger.logc(customContext, logger.args(["an", "array"], 1, "hello", true)); 411 412 writeln(); 413 } 414 415 /** 416 * Printing out some mixed data-types, also using a DEFAULT context 417 * but also testing out the `error()`, `warn()`, `info()` and `debug()` 418 */ 419 unittest 420 { 421 Logger logger = new DefaultLogger(); 422 423 // Create a default logger with the default joiner 424 logger = new DefaultLogger(); 425 426 // Test out `error()` 427 logger.error(["woah", "LEVELS!"], 69.420); 428 429 // Test out `info()` 430 logger.info(["woah", "LEVELS!"], 69.420); 431 432 // Test out `warn()` 433 logger.warn(["woah", "LEVELS!"], 69.420); 434 435 // Test out `debug_()` 436 logger.debug_(["woah", "LEVELS!"], 69.420); 437 438 writeln(); 439 }