【问题标题】:Asp.NET MVC, custom TextWriterTraceListener does not create a fileAsp.NET MVC,自定义 TextWriterTraceListener 不创建文件
【发布时间】:2016-06-19 16:26:24
【问题描述】:

对于 MVC 应用程序自定义侦听器在使用 initializeData="CustomWeblog.txt" 参数时不会创建日志文件,但 initializeData="d:\CustomWeblog.txt" 会触发文件创建。这种行为的原因是什么?控制台应用程序为所有类型的侦听器生成文件。

自定义类:

public class CustomTextWriterTraceListener : TextWriterTraceListener 
{ 
     public CustomTextWriterTraceListener(string fileName) : base(fileName)
}

Web.config(mvc 应用程序,web.config)

<trace autoflush="true" />
<sources>
  <source name="Trace">    
      <listeners>
        <add name="TextWriterListner"
             type="System.Diagnostics.TextWriterTraceListener, WebTracing" initializeData="Weblog.txt"/>
        <!-- the file is created -->
        <add name="CustomTextWriterListner"
             type="WebTracing.CustomTextWriterTraceListener, WebTracing" initializeData="CustomWeblog.txt"/>
        <!-- the file is not created in MVC application ?! -->
        <add name="CustomTextWriterListnerAbsolutePath"
             type="WebTracing.CustomTextWriterTraceListener, WebTracing" initializeData="d:\CustomWeblog.txt"/>
        <!-- the file is created -->
      </listeners>      
  </source>
</sources>

自定义侦听器不创建日志文件。

来电者:

TraceSource obj = new TraceSource("Trace", SourceLevels.All);
obj.TraceEvent(TraceEventType.Critical,0,"This is a critical message");

我尝试添加一些额外的配置:来自this blog 和this one。但是没有成功。我应该提供绝对路径吗?是否可以通过为自定义侦听器创建单独的程序集来解决任何问题?

【问题讨论】:

  • 遇到了类似的问题。如果您检查事件日志,您可能会看到与 iis 相关的错误。通常是该进程没有在站点根目录中创建日志文件的权限。如果initializeData="CustomWeblog.txt",跟踪会做什么
  • catch(UnauthorisedAccessException) { break; } 在new StreamWriter(...) 构造函数调用上触发..
  • 没错。我必须查看原始源代码才能看到发生了什么,所以我在我的自定义侦听器中做了同样的事情。我将发布解决此问题的代码。
  • 您在答案中发布的链接与我在研究该问题时发现的来源相同。我在我的解决方案中遵循了类似的内容。

标签: c# .net asp.net-mvc configuration tracesource


【解决方案1】:

好的,最后,我已将调查切换到侦听器路径的生成方式。我在调试时注意到的是源侦听器列表包含不同的路径。

  • System.Diagnostics.TextWriterTraceListener 侦听器对象已生成完整路径;
  • WebTracing.CustomTextWriterTraceListener 只有文件名。没有生成错误。

自定义侦听吞没 UnauthorisedAccessException 异常的原因导致的不同值,以便应用程序继续工作而不通知我们权限问题。

但是存储自定义侦听器日志文件的位置是什么?他们是 生成与否?

以下指向 TextWriterTraceListener source code 的链接帮助我找出了路径。以下代码:

//initializeData="CustomWeblog.txt", so fileName == "CustomWeblog.txt" here
string fullPath = Path.GetFullPath(fileName); 
string dirPath = Path.GetDirectoryName(fullPath); 
string fileNameOnly = Path.GetFileName(fullPath);

实际存储路径取决于Project > Properties > Web > Server: IIS Express:

c:\Program Files (x86)\IIS Express\CustomWeblog.txt

我一直在调试 MVC 应用程序(以管理员身份:vs 以管理员身份运行)日志文件在该文件夹中正确生成。当我在没有管理员权限的情况下运行 VS 时,自定义侦听器根本不会创建文件。

如上所述,我执行了源代码侦听器,发现catch(UnauthorisedAccessException) { break; } 是在new StreamWriter(...) 构造函数调用时触发的。


作为另一种解决方法,您可以在initializeData="d:\CustomWeblog.txt" 属性中声明整个路径。但请记住,您必须拥有适当的权限。

【讨论】:

    【解决方案2】:

    当我遇到您描述的相同问题时,我正在尝试创建自己的滚动文本编写器跟踪侦听器。长话短说,毕竟跑来跑去是我想出来的。

    public class RollingTextWriterTraceListener : TextWriterTraceListener {
        string fileName;
        private static string[] _supportedAttributes = new string[] 
            { 
                "template", "Template", 
                "convertWriteToEvent", "ConvertWriteToEvent",
                "addtoarchive","addToArchive","AddToArchive",
            };
    
        public RollingTextWriterTraceListener(string fileName)
            : base() {
            this.fileName = fileName;
        }
        /// <summary>
        /// This makes sure that the writer exists to be written to.
        /// </summary>
        private void ensureWriter() {
            //Resolve file name given. relative paths (if present) are resolved to full paths.
            // Also allows for paths like this: initializeData="~/Logs/{ApplicationName}_{DateTime:yyyy-MM-dd}.log"
            var logFileFullPath = ServerPathUtility.ResolvePhysicalPath(fileName);
            var writer = base.Writer;
            if (writer == null && createWriter(logFileFullPath)) {
                writer = base.Writer;
            }
            if (!File.Exists(logFileFullPath)) {
                if (writer != null) {
                    try {
                        writer.Flush();
                        writer.Close();
                        writer.Dispose();
                    } catch (ObjectDisposedException) { }
                }
                createWriter(logFileFullPath);
            }
            //Custom code to package the previous log file(s) into a zip file.
            if (AddToArchive) {
                TextFileArchiveHelper.Archive(logFileFullPath);
            }
        }
    
        bool createWriter(string logFileFullPath) {
            try {
                logFileFullPath = ServerPathUtility.ResolveOrCreatePath(logFileFullPath);
                var writer = new StreamWriter(logFileFullPath, true);
                base.Writer = writer;
                return true;
            } catch (IOException) {
                //locked as already in use
                return false;
            } catch (UnauthorizedAccessException) {
                //ERROR_ACCESS_DENIED, mostly ACL issues
                return false;
            }
        }
    
        /// <summary>
        /// Get the add to archive flag
        /// </summary>
        public bool AddToArchive {
            get {
                // Default behaviour is not to add to archive.
                var addToArchive = false;
                var key = Attributes.Keys.Cast<string>().
                    FirstOrDefault(s => string.Equals(s, "addtoarchive", StringComparison.InvariantCultureIgnoreCase));
                if (!string.IsNullOrWhiteSpace(key)) {
                    bool.TryParse(Attributes[key], out addToArchive);
                }
                return addToArchive;
            }
        }
    
        #region Overrides
        /// <summary>
        /// Allowed attributes for this trace listener.
        /// </summary>
        protected override string[] GetSupportedAttributes() {
            return _supportedAttributes;
        }
    
        public override void Flush() {
            ensureWriter();
            base.Flush();
        }
    
        public override void Write(string message) {
            ensureWriter();
            base.Write(message);
        }
    
        public override void WriteLine(string message) {
            ensureWriter();
            base.WriteLine(message);
        }
        #endregion
    }
    

    更新:这是我为解析路径编写的实用程序类。

    public static class ServerPathUtility {
    
        public static string ResolveOrCreatePath(string pathToReplace) {
            string rootedFileName = ResolvePhysicalPath(pathToReplace);
            FileInfo fi = new FileInfo(rootedFileName);
            try {
                DirectoryInfo di = new DirectoryInfo(fi.DirectoryName);
                if (!di.Exists) {
                    di.Create();
                }
                if (!fi.Exists) {
                    fi.CreateText().Close();
                }
            } catch {
                // NO-OP
                // TODO: Review what should be done here.
            }
            return fi.FullName;
        }
    
        public static string ResolvePhysicalPath(string pathToReplace) {
            string rootedPath = ResolveFormat(pathToReplace);
            if (rootedPath.StartsWith("~") || rootedPath.StartsWith("/")) {
                rootedPath = System.Web.Hosting.HostingEnvironment.MapPath(rootedPath);
            } else if (!Path.IsPathRooted(rootedPath)) {
                rootedPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, rootedPath);
            }
            return rootedPath;
        }
    
        public static string ResolveFormat(string format) {
            string result = format;
    
            try {
                result = ExpandApplicationVariables(format);
            } catch (System.Security.SecurityException) {
                // Log?
            }
    
            try {
                string variables = Environment.ExpandEnvironmentVariables(result);
                // If an Environment Variable is not found then remove any invalid tokens
                Regex filter = new Regex("%(.*?)%", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
    
                string filePath = filter.Replace(variables, "");
    
                if (Path.GetDirectoryName(filePath) == null) {
                    filePath = Path.GetFileName(filePath);
                }
                result = filePath;
            } catch (System.Security.SecurityException) {
                // Log?
            }
    
            return result;
        }
    
        public static string ExpandApplicationVariables(string input) {
            var filter = new Regex("{(.*?)}", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
            var result = filter.Replace(input, evaluateMatch());
            return result;
        }
    
        private static MatchEvaluator evaluateMatch() {
            return match => {
                var variableName = match.Value;
                var value = GetApplicationVariable(variableName);
                return value;
            };
        }
    
        public static string GetApplicationVariable(string variable) {
            string value = string.Empty;
            variable = variable.Replace("{", "").Replace("}", "");
            var parts = variable.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
            variable = parts[0];
            var parameter = string.Empty;
            if (parts.Length > 1) {
                parameter = string.Join("", parts.Skip(1));
            }
    
            Func<string, string> resolve = null;
            value = VariableResolutionStrategies.TryGetValue(variable.ToUpperInvariant(), out resolve) && resolve != null
                ? resolve(parameter)
                : string.Empty;
    
            return value;
        }
    
        public static readonly IDictionary<string, Func<string, string>> VariableResolutionStrategies =
            new Dictionary<string, Func<string, string>> {
                {"MACHINENAME", p => Environment.MachineName },
                {"APPDOMAIN", p => AppDomain.CurrentDomain.FriendlyName },
                {"DATETIME", getDate},
                {"DATE", getDate},
                {"UTCDATETIME", getUtcDate},
                {"UTCDATE", getUtcDate},
            };
    
        static string getDate(string format = "yyyy-MM-dd") {
            var value = string.Empty;
            if (string.IsNullOrWhiteSpace(format))
                format = "yyyy-MM-dd";
            value = DateTime.Now.ToString(format);
            return value;
        }
    
        static string getUtcDate(string format = "yyyy-MM-dd") {
            var value = string.Empty;
            if (string.IsNullOrWhiteSpace(format))
                format = "yyyy-MM-dd";
            value = DateTime.Now.ToString(format);
            return value;
        }
    }
    

    所以这个实用程序类允许我解析相对路径并自定义格式。例如,如果您查看代码,您会发现此路径中不存在应用程序名称 ApplicationName 变量

    "~/Logs/{ApplicationName}_{DateTime:yyyy-MM-dd}.log"
    

    我可以在应用程序启动时配置它以及我想添加的任何其他变量

    public partial class Startup {
        public void Configuration(IAppBuilder app) {
            //... Code removed for brevity           
            // Add APPLICATIONNAME name to path Utility
            ServerPathUtility.VariableResolutionStrategies["APPLICATIONNAME"] = p => {
                var assembly = System.Reflection.Assembly.GetExecutingAssembly();
                if (assembly != null)
                    return assembly.GetName().Name;
                return string.Empty;
            };           
        }
    }
    

    【讨论】:

    • 是否可以添加 ServerPathUtility.ResolveOrCreatePath 方法?您如何处理 initializeData="~/Logs/{ApplicationName}_{DateTime:yyyy-MM-dd}.log" 参数(相对路径和模板)非常有趣。
    猜你喜欢
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    相关资源
    最近更新 更多