1 module geario.logging.appender;
2 
3 @system:
4 package:
5 
6 import geario.logging.config;
7 
8 /**
9  * Appender Creating interface
10  *
11  * Use by Logger for create new Appender
12  *
13  * ====================================================================================
14  */
15 interface AppenderFactory
16 {
17     Appender factory(LoggerConfig config);
18 }
19 
20 
21 /**
22  * Accept messages and publicate it in target
23  */
24 abstract class Appender
25 {
26     /**
27      * Append new message
28      */
29     void append(Level level, string message);
30 }
31 
32 
33 /**
34  * Factory for NullAppender
35  *
36  * ====================================================================================
37  */
38 class NullAppenderFactory:AppenderFactory
39 {
40     override Appender factory(LoggerConfig config)
41     {
42         return new NullAppender();
43     }
44 }
45 
46 
47 /**
48  * Only Accept messages
49  */
50 class NullAppender:Appender
51 {
52     /**
53      * Append new message and do nothing
54      */
55     override void append(Level level, string message) nothrow pure {}
56 }
57 
58 
59 /**
60  * Factory for ConsoleAppender
61  *
62  * ====================================================================================
63  */
64 class ConsoleAppenderFactory:AppenderFactory
65 {
66     override Appender factory(LoggerConfig config)
67     {
68         return new ConsoleAppender();
69     }
70 }
71 
72 
73 /**
74  * Accept messages and publicate it on console
75  */
76 class ConsoleAppender:Appender
77 {
78     /**
79      * Append new message and print it to console
80      */
81     @trusted /* writefln is system */
82     override void append(Level level, string message)
83     {
84         import colorize : fg, color, cwriteln, cwritefln;
85 
86         fg c;
87 
88         switch (level)
89         {
90             case level.Warn:
91                 c = fg.yellow;
92                 break;
93             case Level.Error:
94             case Level.Fatal:
95                 c = fg.red;
96                 break;
97             case Level.Info:
98                 c = fg.green;
99                 break;
100             default:
101                 c = fg.init;
102         }
103 
104         cwriteln(message.color(c));
105     }
106 }
107 
108 
109 /**
110  * Factory for FileAppender
111  *
112  * ====================================================================================
113  */
114 class FileAppenderFactory:AppenderFactory
115 {
116     override Appender factory(LoggerConfig config)
117     {
118         return new FileAppender(config);
119     }
120 }
121 
122 
123 /**
124  * Accept messages and publicate it in file
125  */
126 class FileAppender:Appender
127 {
128     import std.concurrency;
129 
130     /* Tid for appender activity */
131     Tid activity;
132 
133     /**
134      * Create Appender
135      */
136     @trusted
137     this(LoggerConfig config)
138     {
139         activity = spawn(&fileAppenderActivityStart, config);
140     }
141 
142     /**
143      * Append new message and send it to file
144      */
145     @trusted
146     override void append(Level level, string message)
147     {
148         activity.send(message);
149     }
150 }
151 
152 
153 /**
154  * Start new thread for file log activity
155  */
156 @system
157 void fileAppenderActivityStart(LoggerConfig config) nothrow
158 {
159     try
160     {
161         new FileAppenderActivity(config).run();
162     }
163     catch (Exception e)
164     {
165         try
166         {
167             import std.stdio;
168             writeln("FileAppenderActivity exception: " ~ e.msg);
169         }
170         catch (Exception ioe){}
171     }
172 }
173 
174 
175 /**
176  * Logger FileAppender activity
177  *
178  * Write log message to file from one thread
179  */
180 class FileAppenderActivity
181 {
182     import geario.logging.storage;
183     import std.concurrency;
184     import std.datetime;
185 
186     /* Max flush period to write to file */
187     enum logFileWriteFlushPeriod = 100; // ms
188 
189     /* Activity working status */
190     enum AppenderWorkStatus {WORKING, STOPPING}
191     private auto workStatus = AppenderWorkStatus.WORKING;
192 
193     long startFlushTime;
194 
195     /* Max flush period to write to file */
196     FileStorage storage;
197 
198     /**
199      * Primary constructor
200      *
201      * Save config path and name
202      */
203     this(LoggerConfig config)
204     {
205         storage = FileStorage(config);
206         startFlushTime = Clock.currStdTime();
207     }
208 
209     /**
210      * Entry point for start module work
211      */
212     @system
213     void run()
214     {
215         /**
216          * Main activity cycle
217          */
218         while (workStatus == AppenderWorkStatus.WORKING)
219         {
220             try
221             {
222                 workCycle();
223             }
224             catch (Exception e)
225             {
226                 import std.stdio;
227                 writeln("FileAppenderActivity workcycle exception: " ~ e.msg);
228             }
229         }
230     }
231 
232     /**
233      * Activity main cycle
234      */
235     @trusted
236     private void workCycle()
237     {
238         receiveTimeout(
239             100.msecs,
240             (string msg)
241             {
242                 storage.saveMsg(msg);
243             },
244             (OwnerTerminated e){workStatus = AppenderWorkStatus.STOPPING;},
245             (Variant any){}
246         );
247 
248         if (logFileWriteFlushPeriod <= (Clock.currStdTime() - startFlushTime)/(1000*10))
249         {
250             storage.flush;
251             startFlushTime = Clock.currStdTime();
252         }
253     }
254 }