1 /** 2 * Core logging services 3 */ 4 module dlog.core; 5 6 import std.conv : to; 7 import std.range : join; 8 import dlog.transform : MessageTransform; 9 import dlog.defaults; 10 import dlog.context : Context, CompilationInfo, Level; 11 import dlog.utilities : flatten; 12 13 /** 14 * Logger 15 * 16 * Represents a logger instance 17 */ 18 public class Logger 19 { 20 /* Starting transformation */ 21 private MessageTransform messageTransform; 22 23 /** 24 * The multiple argument joiner 25 */ 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 /** 336 * Alias for debug_ 337 */ 338 public alias dbg = debug_; 339 340 /** 341 * Logging implementation, this is where the final 342 * transformed text will be transferred to and finally 343 * logged 344 * 345 * Params: 346 * message = the message to log 347 */ 348 protected abstract void logImpl(string message); 349 } 350 351 352 version(unittest) 353 { 354 import std.meta : AliasSeq; 355 import std.stdio : writeln; 356 } 357 358 /** 359 * Tests the DefaultLogger 360 */ 361 unittest 362 { 363 Logger logger = new DefaultLogger(); 364 365 alias testParameters = AliasSeq!("This is a log message", 1.1, true, [1,2,3], 'f', logger); 366 367 368 // Test various types one-by-one 369 static foreach(testParameter; testParameters) 370 { 371 logger.log(testParameter); 372 } 373 374 // Test various parameters (of various types) all at once 375 logger.log(testParameters); 376 377 // Same as above but with a custom joiner set 378 logger = new DefaultLogger("(-)"); 379 logger.log(testParameters); 380 381 writeln(); 382 } 383 384 /** 385 * Printing out some mixed data-types, also using a DEFAULT context 386 */ 387 unittest 388 { 389 Logger logger = new DefaultLogger(); 390 391 // Create a default logger with the default joiner 392 logger = new DefaultLogger(); 393 logger.log(["a", "b", "c", "d"], [1, 2], true); 394 395 writeln(); 396 } 397 398 /** 399 * Printing out some mixed data-types, also using a CUSTOM context 400 */ 401 unittest 402 { 403 Logger logger = new DefaultLogger(); 404 405 // Create a default logger with the default joiner 406 logger = new DefaultLogger(); 407 408 // Create a custom context 409 Context customContext = new Context(); 410 411 // Log with the custom context 412 logger.logc(customContext, logger.args(["an", "array"], 1, "hello", true)); 413 414 writeln(); 415 } 416 417 /** 418 * Printing out some mixed data-types, also using a DEFAULT context 419 * but also testing out the `error()`, `warn()`, `info()` and `debug()` 420 */ 421 unittest 422 { 423 Logger logger = new DefaultLogger(); 424 425 // Create a default logger with the default joiner 426 logger = new DefaultLogger(); 427 428 // Test out `error()` 429 logger.error(["woah", "LEVELS!"], 69.420); 430 431 // Test out `info()` 432 logger.info(["woah", "LEVELS!"], 69.420); 433 434 // Test out `warn()` 435 logger.warn(["woah", "LEVELS!"], 69.420); 436 437 // Test out `debug_()` 438 logger.debug_(["woah", "LEVELS!"], 69.420); 439 440 writeln(); 441 }