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