1 module geario.logging.logger; 2 3 import geario.logging.config; 4 5 @safe: 6 public: 7 8 @property @system Logger log() 9 { 10 if (_defaultGlobalLogger is null) 11 { 12 _defaultGlobalLogger = new Logger; 13 } 14 15 return _defaultGlobalLogger; 16 } 17 18 /** 19 * Create loggers 20 * 21 * Throws: ConfException, LogCreateException, Exception 22 */ 23 @trusted 24 void create(string loggerName) 25 { 26 if (loggerName in _loggers) 27 { 28 throw new LoggerCreateException("Creating logger error. Logger with name: " ~ loggerName ~ " already created"); 29 } 30 31 _loggers[loggerName] = new Logger(loggerName); 32 } 33 34 /** 35 * Delete logger 36 * 37 * Throws: Exception 38 */ 39 @trusted 40 void remove(immutable string loggerName) 41 { 42 synchronized (lock) 43 { 44 _loggers.remove(loggerName); 45 } 46 } 47 48 /** 49 * Get created logger 50 * 51 * Throws: LogException 52 */ 53 @trusted 54 Logger get(immutable string loggerName) 55 { 56 if (loggerName in _loggers) 57 { 58 return _loggers[loggerName]; 59 } 60 else 61 { 62 throw new LoggerException("Getting logger error. Logger with name: " ~ loggerName ~ " not created"); 63 } 64 } 65 66 /** 67 * Check Logger 68 */ 69 @trusted 70 bool isCreated(immutable string loggerName) nothrow 71 { 72 return (loggerName in _loggers) ? true : false; 73 } 74 75 /** 76 * Set file for save loggers exception information 77 * 78 * Throws: Exception 79 */ 80 @trusted 81 void setErrorFile(immutable string file) 82 { 83 synchronized (lock) 84 { 85 static import geario.logging.storage; 86 geario.logging.storage.createPath(file); 87 errorFile = File(file, "a"); 88 } 89 } 90 91 /* 92 * Logger implementation 93 */ 94 class Logger 95 { 96 97 public: 98 /* 99 * Write message with level "trace" to logger 100 */ 101 void trace(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 102 { 103 putMsg(file, line, Level.Trace, msg); 104 } 105 106 /* 107 * Write message with level "info" to logger 108 */ 109 void info(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 110 { 111 putMsg(file, line, Level.Info, msg); 112 } 113 114 /* 115 * Write message with level "warn" to logger 116 */ 117 void warn(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 118 { 119 putMsg(file, line, Level.Warn, msg); 120 } 121 122 /* 123 * Write message with level "error" to logger 124 */ 125 void error(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 126 { 127 putMsg(file, line, Level.Error, msg); 128 } 129 130 /* 131 * Write message with level "critical" to logger 132 */ 133 void critical(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 134 { 135 putMsg(file, line, Level.Critical, msg); 136 } 137 138 /* 139 * Write message with level "fatal" to logger 140 */ 141 void fatal(string file = __FILE__ , size_t line = __LINE__ , M...)(lazy const M msg) nothrow 142 { 143 putMsg(file, line, Level.Fatal, msg); 144 } 145 146 void setLevel(string level) @system 147 { 148 _level = level.toLevel; 149 150 _config.level = level; 151 } 152 153 void setFilename(string filename) 154 { 155 _config.filename = filename; 156 _config.appenderType = "FileAppender"; 157 } 158 159 void setEncoder(Encoder encoder) 160 { 161 _encoder = encoder; 162 } 163 164 void setRolling() 165 {} 166 167 void setMaxSize(uint maxSize) 168 { 169 _config.maxSize = maxSize; 170 } 171 172 void setMaxHistory(uint maxHistory) 173 { 174 _config.maxHistory = maxHistory; 175 } 176 177 @system: 178 private: 179 180 /* Name */ 181 string _name; 182 183 /* Config */ 184 LoggerConfig _config; 185 186 /* Level */ 187 Level _level; 188 189 /* Appender */ 190 Appender _appender; 191 192 /* Encoder */ 193 Encoder _encoder; 194 195 /* 196 * Level getter in string type 197 */ 198 // public immutable (string) level() 199 // { 200 // return _level.levelToString(); 201 // } 202 203 /* 204 * Create logger impl 205 * 206 * Throws: LogCreateException, ConfException 207 */ 208 this() 209 { 210 } 211 212 /* 213 * Create logger impl 214 * 215 * Throws: LogCreateException, ConfException 216 */ 217 this(string filename, string level = "trace") 218 { 219 _config.filename = filename; 220 221 this.setLevel(level); 222 } 223 224 Appender appender() 225 { 226 if (_appender is null) 227 { 228 _appender = createAppender(_config); 229 } 230 231 return _appender; 232 } 233 234 Encoder encoder() 235 { 236 if (_encoder is null) 237 { 238 _encoder = new DefaultEncoder; 239 } 240 241 return _encoder; 242 } 243 244 /* 245 * Extract logger type from bundle 246 * 247 * Throws: BundleException, LogCreateException 248 */ 249 @trusted /* Object.factory is system */ 250 Appender createAppender(LoggerConfig config) 251 { 252 AppenderFactory f = cast(AppenderFactory)Object.factory("geario.logging.appender." ~ config.appenderType ~ "Factory"); 253 254 if (f is null) 255 { 256 throw new LoggerCreateException("Error create log appender: " ~ config.appenderType ~ " is Illegal appender type."); 257 } 258 259 return f.factory(config); 260 } 261 262 /* 263 * Encode message and put to appender 264 */ 265 @trusted 266 void putMsg(M...)(string file, size_t line, Level level, lazy M msg) nothrow 267 { 268 if (level >= _level) 269 { 270 string emsg; 271 272 try 273 { 274 import std.format; 275 import std.conv : to; 276 277 auto fmsg = format(msg[0].to!string, msg[1 .. $]); 278 emsg = encoder().encode(file, line, level, fmsg); 279 } 280 catch (Exception e) 281 { 282 try 283 { 284 emsg = encoder().encode(file, line, Level.Error, "Error in encoding log message: " ~ e.msg); 285 } 286 catch (Exception ee) 287 { 288 fixException(ee); 289 return; 290 } 291 } 292 293 try 294 { 295 appender.append(level, emsg); 296 } 297 catch (Exception e) 298 { 299 fixException(e); 300 } 301 } 302 } 303 304 /** 305 * Logger exeption handler 306 */ 307 @trusted 308 void fixException (Exception e) nothrow 309 { 310 try 311 { 312 synchronized(lock) 313 { 314 errorFile.writeln("Error to work with log Exception-> " ~ e.msg); 315 } 316 } 317 catch(Exception e){} 318 } 319 } 320 321 @system: 322 private: 323 324 325 import core.sync.mutex; 326 import std.stdio; 327 import geario.logging.appender; 328 329 /* Mutex use for block work with loggers pool */ 330 __gshared Mutex lock; 331 /* Save loggers by names in pool */ 332 __gshared Logger[immutable string] _loggers; 333 /* Save loggers errors in file */ 334 __gshared File errorFile; 335 /* Global logger object */ 336 __gshared Logger _defaultGlobalLogger; 337 338 shared static this() 339 { 340 lock = new Mutex(); 341 } 342 343 interface Encoder 344 { 345 string encode (string file, size_t line, immutable Level level, const string message); 346 } 347 348 class DefaultEncoder : Encoder 349 { 350 import std.datetime; 351 352 /** 353 * Do make message finish string 354 * 355 * Throws: Exception 356 */ 357 string encode(string file, size_t line, immutable Level level, const string message) 358 { 359 import std.string : format; 360 import std.conv : to; 361 362 import geario.util.DateTime : date; 363 import geario.util.ThreadHelper : GetTid; 364 365 string strLevel = "[" ~ levelToViewString(level) ~ "]"; 366 return format("%-19s %s %-7s %s - %s:%d", date("Y-m-d H:i:s", Clock.currTime.toUnixTime()), GetTid().to!string, strLevel, message, file, line); 367 } 368 }