1 /*******************************************************************************
2 
3     Application extension to parse configuration files for the logging system.
4 
5     Copyright:
6         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module ocean.util.app.ext.LogExt;
17 
18 
19 
20 
21 import ocean.core.TypeConvert;
22 
23 import ocean.util.app.model.ExtensibleClassMixin;
24 import ocean.util.app.Application;
25 import ocean.util.app.ext.model.IConfigExtExtension;
26 import ocean.util.app.ext.model.ILogExtExtension;
27 import ocean.util.app.ext.ConfigExt;
28 
29 import ocean.util.app.ext.ReopenableFilesExt;
30 
31 import ocean.util.config.ConfigFiller;
32 import ocean.util.config.ConfigParser;
33 import LogUtil = ocean.util.log.Config;
34 import ConfigFiller = ocean.util.config.ConfigFiller;
35 
36 import ocean.meta.types.Qualifiers;
37 import ocean.io.device.File;
38 
39 import ocean.util.log.Appender;
40 
41 
42 /*******************************************************************************
43 
44     Application extension to parse configuration files for the logging system.
45 
46     This extension is an extension itself, providing new hooks via
47     ILogExtExtension.
48 
49 *******************************************************************************/
50 
51 class LogExt : IConfigExtExtension
52 {
53     /***************************************************************************
54 
55         Adds a list of extensions (this.extensions) and methods to handle them.
56         See ExtensibleClassMixin documentation for details.
57 
58     ***************************************************************************/
59 
60     mixin ExtensibleClassMixin!(ILogExtExtension);
61 
62 
63     /***************************************************************************
64 
65         True if the InsertConsole appender should be used instead of the regular
66         one. The InsertConsole appender is needed when using the AppStatus
67         module.
68 
69     ***************************************************************************/
70 
71     public bool use_insert_appender;
72 
73 
74     /// If set, will be used by `makeLayout` to map a name to a `Layout`
75     private Appender.Layout delegate (cstring name) layout_maker;
76 
77 
78     /***************************************************************************
79 
80         Constructor.
81 
82         Params:
83             use_insert_appender = true if the InsertConsole appender should be
84                                   used (needed when using the AppStatus module)
85 
86     ***************************************************************************/
87 
88     this ( bool use_insert_appender )
89     {
90         this(null, use_insert_appender);
91     }
92 
93 
94     /***************************************************************************
95 
96         Constructor.
97 
98         Params:
99             make_layout         = A delegate that instantiates an
100                                   `Appender.Layout` from a name. If null,
101                                   defaults to `ocean.util.Config: newLayout`.
102             use_insert_appender = true if the InsertConsole appender should be
103                                   used (needed when using the AppStatus module)
104 
105     ***************************************************************************/
106 
107     public this ( scope Appender.Layout delegate (cstring name) make_layout = null,
108                   bool use_insert_appender = false )
109     {
110         this.layout_maker = make_layout is null ? &this.makeLayoutDefault
111             : make_layout;
112 
113         this.use_insert_appender = use_insert_appender;
114     }
115 
116     /***************************************************************************
117 
118         Extension order. This extension uses -1_000 because it should be
119         called early, but after the ConfigExt extension.
120 
121         Returns:
122             the extension order
123 
124     ***************************************************************************/
125 
126     public override int order ( )
127     {
128         return -1_000;
129     }
130 
131 
132     /***************************************************************************
133 
134         Parse the configuration file options to set up the loggers.
135 
136         Params:
137             app = the application instance
138             config = configuration instance
139 
140     ***************************************************************************/
141 
142     public override void processConfig ( IApplication app, ConfigParser config )
143     {
144         auto conf_ext = (cast(Application)app).getExtension!(ConfigExt);
145 
146         foreach (ext; this.extensions)
147         {
148             ext.preConfigureLoggers(app, config, conf_ext.loose_config_parsing,
149                     this.use_insert_appender);
150         }
151 
152         auto log_config = ConfigFiller.iterate!(LogUtil.Config)("LOG", config);
153         auto log_meta_config = ConfigFiller.fill!(LogUtil.MetaConfig)("LOG", config);
154 
155         enable_loose_parsing(conf_ext.loose_config_parsing);
156 
157         // tracks already registered files to prevent double registration
158         File[cstring] registered_files;
159 
160         Appender appender ( istring file, LogUtil.Layout layout )
161         {
162             if (!(file in registered_files))
163             {
164                 auto stream = new File(file, File.WriteAppending);
165                 if ( auto reopenable_files_ext =
166                     (cast(Application)app).getExtension!(ReopenableFilesExt) )
167                 {
168                     reopenable_files_ext.register(stream);
169                 }
170                 registered_files[file] = stream;
171             }
172 
173             return new AppendStream(registered_files[file], true, layout);
174         }
175 
176         LogUtil.configureNewLoggers(log_config, log_meta_config, &appender,
177             this.layout_maker, this.use_insert_appender);
178 
179         foreach (ext; this.extensions)
180         {
181             ext.postConfigureLoggers(app, config, conf_ext.loose_config_parsing,
182                     this.use_insert_appender);
183         }
184     }
185 
186 
187     /***************************************************************************
188 
189         Unused IConfigExtExtension methods.
190 
191         We just need to provide an "empty" implementation to satisfy the
192         interface.
193 
194         Params:
195             app = the application instance
196             config = configuration instance
197 
198     ***************************************************************************/
199 
200     public override void preParseConfig ( IApplication app, ConfigParser config )
201     {
202         // Unused
203     }
204 
205 
206     /***************************************************************************
207 
208         Function to filter the list of configuration files to parse.
209         Only present to satisfy the interface
210 
211         Params:
212             app = the application instance
213             config = configuration instance
214             files = current list of configuration files to parse
215 
216         Returns:
217             new list of configuration files to parse
218 
219     ***************************************************************************/
220 
221     public override istring[] filterConfigFiles ( IApplication app,
222                                          ConfigParser config,
223                                          istring[] files )
224     {
225         // Unused
226         return files;
227     }
228 
229 
230     /***************************************************************************
231 
232         Default for layout_maker. DMD complains because `LogUtil.newLayout`
233         is a function, not a delegate.
234 
235         Params:
236             name = name of the Layout to instantiate.
237 
238         Throws:
239             `Exception` if it cannot match the name
240 
241         Returns:
242             A new Layout instance matching name
243 
244     ***************************************************************************/
245 
246     private Appender.Layout makeLayoutDefault ( cstring name )
247     {
248         return LogUtil.newLayout(name);
249     }
250 }