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