/Dependencies/log4net/Config/XmlConfiguratorAttribute.cs
C# | 469 lines | 242 code | 40 blank | 187 comment | 42 complexity | 2af034ca1d20366935230842e78fba98 MD5 | raw file
1#region Apache License 2// 3// Licensed to the Apache Software Foundation (ASF) under one or more 4// contributor license agreements. See the NOTICE file distributed with 5// this work for additional information regarding copyright ownership. 6// The ASF licenses this file to you under the Apache License, Version 2.0 7// (the "License"); you may not use this file except in compliance with 8// the License. You may obtain a copy of the License at 9// 10// http://www.apache.org/licenses/LICENSE-2.0 11// 12// Unless required by applicable law or agreed to in writing, software 13// distributed under the License is distributed on an "AS IS" BASIS, 14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15// See the License for the specific language governing permissions and 16// limitations under the License. 17// 18#endregion 19 20// .NET Compact Framework 1.0 has no support for reading assembly attributes 21#if !NETCF 22 23using System; 24using System.Collections; 25using System.Reflection; 26using System.IO; 27 28using log4net.Util; 29using log4net.Repository; 30using log4net.Repository.Hierarchy; 31 32namespace log4net.Config 33{ 34 /// <summary> 35 /// Assembly level attribute to configure the <see cref="XmlConfigurator"/>. 36 /// </summary> 37 /// <remarks> 38 /// <para> 39 /// This attribute may only be used at the assembly scope and can only 40 /// be used once per assembly. 41 /// </para> 42 /// <para> 43 /// Use this attribute to configure the <see cref="XmlConfigurator"/> 44 /// without calling one of the <see cref="XmlConfigurator.Configure()"/> 45 /// methods. 46 /// </para> 47 /// <para> 48 /// If neither of the <see cref="ConfigFile"/> or <see cref="ConfigFileExtension"/> 49 /// properties are set the configuration is loaded from the application's .config file. 50 /// If set the <see cref="ConfigFile"/> property takes priority over the 51 /// <see cref="ConfigFileExtension"/> property. The <see cref="ConfigFile"/> property 52 /// specifies a path to a file to load the config from. The path is relative to the 53 /// application's base directory; <see cref="AppDomain.BaseDirectory"/>. 54 /// The <see cref="ConfigFileExtension"/> property is used as a postfix to the assembly file name. 55 /// The config file must be located in the application's base directory; <see cref="AppDomain.BaseDirectory"/>. 56 /// For example in a console application setting the <see cref="ConfigFileExtension"/> to 57 /// <c>config</c> has the same effect as not specifying the <see cref="ConfigFile"/> or 58 /// <see cref="ConfigFileExtension"/> properties. 59 /// </para> 60 /// <para> 61 /// The <see cref="Watch"/> property can be set to cause the <see cref="XmlConfigurator"/> 62 /// to watch the configuration file for changes. 63 /// </para> 64 /// <note> 65 /// <para> 66 /// Log4net will only look for assembly level configuration attributes once. 67 /// When using the log4net assembly level attributes to control the configuration 68 /// of log4net you must ensure that the first call to any of the 69 /// <see cref="log4net.Core.LoggerManager"/> methods is made from the assembly with the configuration 70 /// attributes. 71 /// </para> 72 /// <para> 73 /// If you cannot guarantee the order in which log4net calls will be made from 74 /// different assemblies you must use programmatic configuration instead, i.e. 75 /// call the <see cref="XmlConfigurator.Configure()"/> method directly. 76 /// </para> 77 /// </note> 78 /// </remarks> 79 /// <author>Nicko Cadell</author> 80 /// <author>Gert Driesen</author> 81 [AttributeUsage(AttributeTargets.Assembly)] 82 [Serializable] 83 public /*sealed*/ class XmlConfiguratorAttribute : ConfiguratorAttribute 84 { 85 // 86 // Class is not sealed because DOMConfiguratorAttribute extends it while it is obsoleted 87 // 88 89 /// <summary> 90 /// Default constructor 91 /// </summary> 92 /// <remarks> 93 /// <para> 94 /// Default constructor 95 /// </para> 96 /// </remarks> 97 public XmlConfiguratorAttribute() : base(0) /* configurator priority 0 */ 98 { 99 } 100 101 #region Public Instance Properties 102 103 /// <summary> 104 /// Gets or sets the filename of the configuration file. 105 /// </summary> 106 /// <value> 107 /// The filename of the configuration file. 108 /// </value> 109 /// <remarks> 110 /// <para> 111 /// If specified, this is the name of the configuration file to use with 112 /// the <see cref="XmlConfigurator"/>. This file path is relative to the 113 /// <b>application base</b> directory (<see cref="AppDomain.BaseDirectory"/>). 114 /// </para> 115 /// <para> 116 /// The <see cref="ConfigFile"/> takes priority over the <see cref="ConfigFileExtension"/>. 117 /// </para> 118 /// </remarks> 119 public string ConfigFile 120 { 121 get { return m_configFile; } 122 set { m_configFile = value; } 123 } 124 125 /// <summary> 126 /// Gets or sets the extension of the configuration file. 127 /// </summary> 128 /// <value> 129 /// The extension of the configuration file. 130 /// </value> 131 /// <remarks> 132 /// <para> 133 /// If specified this is the extension for the configuration file. 134 /// The path to the config file is built by using the <b>application 135 /// base</b> directory (<see cref="AppDomain.BaseDirectory"/>), 136 /// the <b>assembly file name</b> and the config file extension. 137 /// </para> 138 /// <para> 139 /// If the <see cref="ConfigFileExtension"/> is set to <c>MyExt</c> then 140 /// possible config file names would be: <c>MyConsoleApp.exe.MyExt</c> or 141 /// <c>MyClassLibrary.dll.MyExt</c>. 142 /// </para> 143 /// <para> 144 /// The <see cref="ConfigFile"/> takes priority over the <see cref="ConfigFileExtension"/>. 145 /// </para> 146 /// </remarks> 147 public string ConfigFileExtension 148 { 149 get { return m_configFileExtension; } 150 set { m_configFileExtension = value; } 151 } 152 153 /// <summary> 154 /// Gets or sets a value indicating whether to watch the configuration file. 155 /// </summary> 156 /// <value> 157 /// <c>true</c> if the configuration should be watched, <c>false</c> otherwise. 158 /// </value> 159 /// <remarks> 160 /// <para> 161 /// If this flag is specified and set to <c>true</c> then the framework 162 /// will watch the configuration file and will reload the config each time 163 /// the file is modified. 164 /// </para> 165 /// <para> 166 /// The config file can only be watched if it is loaded from local disk. 167 /// In a No-Touch (Smart Client) deployment where the application is downloaded 168 /// from a web server the config file may not reside on the local disk 169 /// and therefore it may not be able to watch it. 170 /// </para> 171 /// <note> 172 /// Watching configuration is not supported on the SSCLI. 173 /// </note> 174 /// </remarks> 175 public bool Watch 176 { 177 get { return m_configureAndWatch; } 178 set { m_configureAndWatch = value; } 179 } 180 181 #endregion Public Instance Properties 182 183 #region Override ConfiguratorAttribute 184 185 /// <summary> 186 /// Configures the <see cref="ILoggerRepository"/> for the specified assembly. 187 /// </summary> 188 /// <param name="sourceAssembly">The assembly that this attribute was defined on.</param> 189 /// <param name="targetRepository">The repository to configure.</param> 190 /// <remarks> 191 /// <para> 192 /// Configure the repository using the <see cref="XmlConfigurator"/>. 193 /// The <paramref name="targetRepository"/> specified must extend the <see cref="Hierarchy"/> 194 /// class otherwise the <see cref="XmlConfigurator"/> will not be able to 195 /// configure it. 196 /// </para> 197 /// </remarks> 198 /// <exception cref="ArgumentOutOfRangeException">The <paramref name="targetRepository" /> does not extend <see cref="Hierarchy"/>.</exception> 199 override public void Configure(Assembly sourceAssembly, ILoggerRepository targetRepository) 200 { 201 IList configurationMessages = new ArrayList(); 202 203 using (new LogLog.LogReceivedAdapter(configurationMessages)) 204 { 205 string applicationBaseDirectory = null; 206 try 207 { 208 applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; 209 } 210 catch 211 { 212 // Ignore this exception because it is only thrown when ApplicationBaseDirectory is a file 213 // and the application does not have PathDiscovery permission 214 } 215 216 if (applicationBaseDirectory == null || (new Uri(applicationBaseDirectory)).IsFile) 217 { 218 ConfigureFromFile(sourceAssembly, targetRepository); 219 } 220 else 221 { 222 ConfigureFromUri(sourceAssembly, targetRepository); 223 } 224 } 225 226 targetRepository.ConfigurationMessages = configurationMessages; 227 } 228 229 #endregion 230 231 /// <summary> 232 /// Attempt to load configuration from the local file system 233 /// </summary> 234 /// <param name="sourceAssembly">The assembly that this attribute was defined on.</param> 235 /// <param name="targetRepository">The repository to configure.</param> 236 private void ConfigureFromFile(Assembly sourceAssembly, ILoggerRepository targetRepository) 237 { 238 // Work out the full path to the config file 239 string fullPath2ConfigFile = null; 240 241 // Select the config file 242 if (m_configFile == null || m_configFile.Length == 0) 243 { 244 if (m_configFileExtension == null || m_configFileExtension.Length == 0) 245 { 246 // Use the default .config file for the AppDomain 247 try 248 { 249 fullPath2ConfigFile = SystemInfo.ConfigurationFileLocation; 250 } 251 catch(Exception ex) 252 { 253 LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when ConfigFile and ConfigFileExtension properties are not set.", ex); 254 } 255 } 256 else 257 { 258 // Force the extension to start with a '.' 259 if (m_configFileExtension[0] != '.') 260 { 261 m_configFileExtension = "." + m_configFileExtension; 262 } 263 264 string applicationBaseDirectory = null; 265 try 266 { 267 applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; 268 } 269 catch(Exception ex) 270 { 271 LogLog.Error(declaringType, "Exception getting ApplicationBaseDirectory. Must be able to resolve ApplicationBaseDirectory and AssemblyFileName when ConfigFileExtension property is set.", ex); 272 } 273 274 if (applicationBaseDirectory != null) 275 { 276 fullPath2ConfigFile = Path.Combine(applicationBaseDirectory, SystemInfo.AssemblyFileName(sourceAssembly) + m_configFileExtension); 277 } 278 } 279 } 280 else 281 { 282 string applicationBaseDirectory = null; 283 try 284 { 285 applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; 286 } 287 catch(Exception ex) 288 { 289 LogLog.Warn(declaringType, "Exception getting ApplicationBaseDirectory. ConfigFile property path ["+m_configFile+"] will be treated as an absolute path.", ex); 290 } 291 292 if (applicationBaseDirectory != null) 293 { 294 // Just the base dir + the config file 295 fullPath2ConfigFile = Path.Combine(applicationBaseDirectory, m_configFile); 296 } 297 else 298 { 299 fullPath2ConfigFile = m_configFile; 300 } 301 } 302 303 if (fullPath2ConfigFile != null) 304 { 305 ConfigureFromFile(targetRepository, new FileInfo(fullPath2ConfigFile)); 306 } 307 } 308 309 /// <summary> 310 /// Configure the specified repository using a <see cref="FileInfo"/> 311 /// </summary> 312 /// <param name="targetRepository">The repository to configure.</param> 313 /// <param name="configFile">the FileInfo pointing to the config file</param> 314 private void ConfigureFromFile(ILoggerRepository targetRepository, FileInfo configFile) 315 { 316#if (SSCLI) 317 if (m_configureAndWatch) 318 { 319 LogLog.Warn(declaringType, "XmlConfiguratorAttribute: Unable to watch config file not supported on SSCLI"); 320 } 321 XmlConfigurator.Configure(targetRepository, configFile); 322#else 323 // Do we configure just once or do we configure and then watch? 324 if (m_configureAndWatch) 325 { 326 XmlConfigurator.ConfigureAndWatch(targetRepository, configFile); 327 } 328 else 329 { 330 XmlConfigurator.Configure(targetRepository, configFile); 331 } 332#endif 333 } 334 335 /// <summary> 336 /// Attempt to load configuration from a URI 337 /// </summary> 338 /// <param name="sourceAssembly">The assembly that this attribute was defined on.</param> 339 /// <param name="targetRepository">The repository to configure.</param> 340 private void ConfigureFromUri(Assembly sourceAssembly, ILoggerRepository targetRepository) 341 { 342 // Work out the full path to the config file 343 Uri fullPath2ConfigFile = null; 344 345 // Select the config file 346 if (m_configFile == null || m_configFile.Length == 0) 347 { 348 if (m_configFileExtension == null || m_configFileExtension.Length == 0) 349 { 350 string systemConfigFilePath = null; 351 try 352 { 353 systemConfigFilePath = SystemInfo.ConfigurationFileLocation; 354 } 355 catch(Exception ex) 356 { 357 LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when ConfigFile and ConfigFileExtension properties are not set.", ex); 358 } 359 360 if (systemConfigFilePath != null) 361 { 362 Uri systemConfigFileUri = new Uri(systemConfigFilePath); 363 364 // Use the default .config file for the AppDomain 365 fullPath2ConfigFile = systemConfigFileUri; 366 } 367 } 368 else 369 { 370 // Force the extension to start with a '.' 371 if (m_configFileExtension[0] != '.') 372 { 373 m_configFileExtension = "." + m_configFileExtension; 374 } 375 376 string systemConfigFilePath = null; 377 try 378 { 379 systemConfigFilePath = SystemInfo.ConfigurationFileLocation; 380 } 381 catch(Exception ex) 382 { 383 LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when the ConfigFile property are not set.", ex); 384 } 385 386 if (systemConfigFilePath != null) 387 { 388 UriBuilder builder = new UriBuilder(new Uri(systemConfigFilePath)); 389 390 // Remove the current extension from the systemConfigFileUri path 391 string path = builder.Path; 392 int startOfExtension = path.LastIndexOf("."); 393 if (startOfExtension >= 0) 394 { 395 path = path.Substring(0, startOfExtension); 396 } 397 path += m_configFileExtension; 398 399 builder.Path = path; 400 fullPath2ConfigFile = builder.Uri; 401 } 402 } 403 } 404 else 405 { 406 string applicationBaseDirectory = null; 407 try 408 { 409 applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; 410 } 411 catch(Exception ex) 412 { 413 LogLog.Warn(declaringType, "Exception getting ApplicationBaseDirectory. ConfigFile property path ["+m_configFile+"] will be treated as an absolute URI.", ex); 414 } 415 416 if (applicationBaseDirectory != null) 417 { 418 // Just the base dir + the config file 419 fullPath2ConfigFile = new Uri(new Uri(applicationBaseDirectory), m_configFile); 420 } 421 else 422 { 423 fullPath2ConfigFile = new Uri(m_configFile); 424 } 425 } 426 427 if (fullPath2ConfigFile != null) 428 { 429 if (fullPath2ConfigFile.IsFile) 430 { 431 // The m_configFile could be an absolute local path, therefore we have to be 432 // prepared to switch back to using FileInfos here 433 ConfigureFromFile(targetRepository, new FileInfo(fullPath2ConfigFile.LocalPath)); 434 } 435 else 436 { 437 if (m_configureAndWatch) 438 { 439 LogLog.Warn(declaringType, "XmlConfiguratorAttribute: Unable to watch config file loaded from a URI"); 440 } 441 XmlConfigurator.Configure(targetRepository, fullPath2ConfigFile); 442 } 443 } 444 } 445 446 #region Private Instance Fields 447 448 private string m_configFile = null; 449 private string m_configFileExtension = null; 450 private bool m_configureAndWatch = false; 451 452 #endregion Private Instance Fields 453 454 #region Private Static Fields 455 456 /// <summary> 457 /// The fully qualified type of the XmlConfiguratorAttribute class. 458 /// </summary> 459 /// <remarks> 460 /// Used by the internal logger to record the Type of the 461 /// log message. 462 /// </remarks> 463 private readonly static Type declaringType = typeof(XmlConfiguratorAttribute); 464 465 #endregion Private Static Fields 466 } 467} 468 469#endif // !NETCF