2016-03-05 4 views
2

Для пользовательского прослушивателя MVC не создается файл журнала, когда используется параметр initializeData = «CustomWeblog.txt», но триггеры initializeData = "d: \ CustomWeblog.txt" создание файла. В чем причина такого поведения? Консольное приложение генерирует файлы для всех типов слушателей.Asp.NET MVC, пользовательский TextWriterTraceListener не создает файл

Пользовательский класс:

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> 

Cutom слушатель не создает файл журнала.

Caller:

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

Я пытался добавить некоторые дополнительные настройки: от this blog и this one. Но успеха нет. Должен ли я предоставить абсолютный путь? Есть ли способ обхода путем создания отдельной сборки для пользовательского прослушивателя?

+1

Имел аналогичный вопрос. Если вы проверите журналы событий, вы можете увидеть ошибку, связанную с iis. Обычно процесс не имеет разрешения на создание файла журнала в корне сайта. Это то, что будет делать трассировка, если 'initializeData =" CustomWeblog.txt "' – Nkosi

+1

'catch (UnauthorizedAccessException) {break; } 'запускается на' new StreamWriter (...) 'вызов конструктора .. – Spirit

+1

Это правильно. Мне пришлось посмотреть исходный исходный код, чтобы увидеть, что происходит, поэтому я сделал то же самое в своем пользовательском прослушивателе. Я отправлю код, как я решил это. – Nkosi

ответ

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 
} 

UPDATE: Вот класс утилита я написал для решения путей.

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; 
     };   
    } 
} 
+0

Можно ли добавить метод ServerPathUtility.ResolveOrCreatePath? Очень интересно, как вы обрабатываете параметр initializeData = "~/Logs/{ApplicationName} _ {DateTime: yyyy-MM-dd} .log" (относительный путь и шаблон). – Spirit

2

Итак, я переключил расследование на то, как создаются пути слушателя. Что я заметил при отладке, так это то, что список источников-слушателей содержит разные пути.

  • System.Diagnostics.TextWriterTraceListener объект-слушатель имеет полный путь, сгенерированный;
  • WebTracing.CustomTextWriterTraceListener имеет только имя файла. Сгенерированных ошибок нет.

Различные значения вызваны тем, что пользователь прослушал исключение UnauthorizedAccessException, чтобы приложение продолжало работать, не сообщая нам о проблемах с разрешениями.

Но каково место хранения файлов журнала пользовательских слушателей? Являются ли они сгенерированы или нет?

Следующая ссылка на 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); 

Фактический путь хранения зависит от проекта> Свойства> Web> Сервер: IIS Экспресс:

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

Все время я дебютировал приложение MVC (в качестве администратора: против запуска под управлением администратора) файлы журнала были правильно сгенерированы в этой папке. Когда я запускаю VS без прав администратора, пользовательские слушатели вообще не создают файлы.

Как было описано выше, я выполнил прослушиватель исходного кода и обнаружил, что catch(UnauthorisedAccessException) { break; } запускается по вызову конструктора new StreamWriter(...).

  • Почему доступ к пути запрещен?SO link
  • Что все учетные записи пользователей для IIS/ASP.NET и как они отличаются? (В разделе Практика ответа) SO link
  • Awesome video tutorial: Application pools in IIS Конфигурация пула IIS и приложений от Pragime Tech.

В качестве другого обходного вы можете объявить весь путь в initializeData="d:\CustomWeblog.txt" атрибута. Но имейте в виду, что у вас должны быть соответствующие разрешения.

Смежные вопросы