1 /******************************************************************************* 2 3 Application extension to parse configuration for the stats output. 4 5 This extension writes to a file the list of value and identifier in 6 an easily parseable way. It also has the option to send metrics 7 directly to a Collectd unix socket. 8 9 Should one want to use the Collectd socket, several additional configuration 10 values need to be provided: 11 - path to the unix socket (which enables the option); 12 - application name: If not provided, default to the name passed to the 13 application framework; 14 - application instance: Optional, no default value 15 - hostname: If not provided, the value of `gethostname` (2) will be used; 16 - default type: A convention on the type of the 'application stats', 17 which will be used when calling `StatsLog.add`. 18 Defaults to `application_name ~ "_stats"`. 19 20 Copyright: 21 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 22 All rights reserved. 23 24 License: 25 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 26 Alternatively, this file may be distributed under the terms of the Tango 27 3-Clause BSD License (see LICENSE_BSD.txt for details). 28 29 *******************************************************************************/ 30 31 module ocean.util.app.ext.StatsExt; 32 33 34 35 36 import core.sys.posix.unistd : gethostname; 37 38 import ocean.core.Enforce; 39 import ocean.core.TypeConvert; 40 import ocean.stdc.gnu.string; 41 import ocean.sys.ErrnoException; 42 43 import ocean.util.app.model.ExtensibleClassMixin; 44 import ocean.util.app.Application; 45 import ocean.util.app.ext.model.IConfigExtExtension; 46 import ocean.util.app.ext.model.ILogExtExtension; 47 import ocean.util.app.ext.ConfigExt; 48 49 import ocean.util.app.ext.ReopenableFilesExt; 50 51 import ocean.util.config.ConfigParser; 52 import ocean.util.log.Appender; 53 import ocean.util.log.Stats; 54 import ConfigFiller = ocean.util.config.ConfigFiller; 55 56 import ocean.meta.types.Qualifiers; 57 import ocean.core.Verify; 58 import ocean.io.device.File; 59 60 61 /******************************************************************************* 62 63 Application extension to parse configuration files for the stats output. 64 65 *******************************************************************************/ 66 67 class StatsExt : IConfigExtExtension 68 { 69 /*************************************************************************** 70 71 Stats Log instance 72 73 ***************************************************************************/ 74 75 public StatsLog stats_log; 76 77 78 /// Config instance for creating the default StatsLog. 79 public StatsLog.Config config; 80 81 82 /*************************************************************************** 83 84 Extension order. This extension uses -500 because it should be 85 called early, but after the LogExt extension. 86 87 Returns: 88 the extension order 89 90 ***************************************************************************/ 91 92 public override int order ( ) 93 { 94 return -500; 95 } 96 97 98 /*************************************************************************** 99 100 Parse the configuration file options to set up the stats log. 101 102 Params: 103 app = the application instance 104 parser = configuration instance 105 106 ***************************************************************************/ 107 108 public override void processConfig ( IApplication app, ConfigParser parser ) 109 { 110 ConfigFiller.fill!(StatsLog.Config)("STATS", this.config, parser); 111 112 if (!this.config.app_name.length) 113 this.config.app_name = app.name; 114 if (!this.config.hostname.length) 115 this.config.hostname = getHostName(); 116 if (!this.config.default_type.length) 117 this.config.default_type = this.config.app_name ~ "_stats"; 118 119 verify(this.config.app_name.length != 0); 120 121 this.stats_log = this.newStatsLog(app, this.config); 122 } 123 124 125 /*************************************************************************** 126 127 Creates a new stats log instance according to the provided config 128 settings. If the reopenable files extension exists, the log file is 129 registered with it. 130 131 Params: 132 app = the application instance 133 stats_config = stats log configuration instance 134 135 Returns: 136 new, configured StatsLog instance 137 138 ***************************************************************************/ 139 140 static public StatsLog newStatsLog ( IApplication app, 141 StatsLog.Config stats_config ) 142 { 143 Appender newAppender ( istring file, Appender.Layout layout ) 144 { 145 auto stream = new File(file, File.WriteAppending); 146 147 if ( auto reopenable_files_ext = 148 (cast(Application)app).getExtension!(ReopenableFilesExt) ) 149 { 150 reopenable_files_ext.register(stream); 151 } 152 153 return new AppendStream(stream, true, layout); 154 } 155 156 return new StatsLog(stats_config, &newAppender, stats_config.file_name); 157 } 158 159 /*************************************************************************** 160 161 Unused IConfigExtExtension method. 162 163 We just need to provide an "empty" implementation to satisfy the 164 interface. 165 166 Params: 167 app = the application instance 168 config = configuration instance 169 170 ***************************************************************************/ 171 172 public override void preParseConfig ( IApplication app, ConfigParser config ) 173 { 174 // Unused 175 } 176 177 178 /*************************************************************************** 179 180 Unused IConfigExtExtension method. 181 182 We just need to provide an "empty" implementation to satisfy the 183 interface. 184 185 Params: 186 app = the application instance 187 config = configuration instance 188 files = current list of configuration files to parse 189 190 Returns: 191 new list of configuration files to parse 192 193 ***************************************************************************/ 194 195 public override istring[] filterConfigFiles ( IApplication app, 196 ConfigParser config, 197 istring[] files ) 198 { 199 // Unused 200 return files; 201 } 202 } 203 204 205 /******************************************************************************* 206 207 An helper function to get the hostname, used by Collectd 208 209 Returns: 210 A string representing the hostname 211 212 *******************************************************************************/ 213 214 private istring getHostName () 215 { 216 // SuSv2 ensure that hostname are <= 255 bytes 217 // On Linux, they are <= HOST_MAX_NAME which has been 64 bytes 218 // for almost all of Linux lifetime 219 char[256] buffer; 220 enforce!(ErrnoException)(gethostname(buffer.ptr, buffer.length) == 0); 221 return idup(buffer[0 .. strnlen(buffer.ptr, buffer.length)]); 222 223 }