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 }