Asp.NET MVC,自定义TextWriterTraceListener不创建文件
Asp.NET MVC, custom TextWriterTraceListener does not create a file
对于 MVC 应用程序,自定义侦听器在使用 initializeData="CustomWeblog.txt" 参数时不会创建日志文件,但 initializeData="d:\CustomWeblog.txt" 会触发文件创建。这种行为的原因是什么?控制台应用程序为所有类型的侦听器生成文件。
自定义 class:
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 侦听器不创建日志文件。
来电者:
TraceSource obj = new TraceSource("Trace", SourceLevels.All);
obj.TraceEvent(TraceEventType.Critical,0,"This is a critical message");
我尝试添加一些额外的配置:来自 this blog and this one。但是没有成功。我应该提供绝对路径吗?通过为自定义侦听器创建单独的程序集是否有任何解决方法?
好的,最后,我已将调查切换到生成侦听器路径的方式。我在调试时注意到的是源侦听器列表包含不同的路径。
- System.Diagnostics.TextWriterTraceListener 侦听器对象已生成完整路径;
- WebTracing.CustomTextWriterTraceListener 只有文件名。没有生成错误。
由于自定义侦听吞没了 UnauthorisedAccessException 异常的原因导致的不同值,因此应用程序继续工作而不通知我们权限问题。
But what is the place of storing Custom listener log files? Are they
generated or not?
TextWriterTraceListener source code 的以下 link 帮助我找出路径。以下代码:
//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 Express:
c:\Program Files (x86)\IIS Express\CustomWeblog.txt
我一直在调试 MVC 应用程序 (作为管理员:vs 运行 作为管理员) 日志文件在该文件夹中正确生成。当我 运行 在没有管理员权限的情况下使用 VS 时,自定义侦听器根本不创建文件。
如上所述,我执行了源代码侦听器,发现 catch(UnauthorisedAccessException) { break; }
在 new StreamWriter(...)
构造函数调用时触发。
- 为什么访问路径被拒绝? SO link
- IIS/ASP.NET 的所有用户帐户是什么?它们有何不同? (答案的练习部分)SO link
- Pragime Tech 关于 IIS 和应用程序池配置的精彩 video tutorial: Application pools in IIS。
作为另一种解决方法,您可以在 initializeData="d:\CustomWeblog.txt"
属性中声明整个路径。但请记住,您必须拥有适当的权限。
当我遇到您描述的相同问题时,我正在尝试创建自己的滚动文本编写器跟踪侦听器。长话短说,毕竟这里的 运行 是我想出来的。
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
}
更新:这是我为解析路径而编写的实用程序class。
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;
}
}
所以这个实用程序 class 允许我解析相对路径并自定义格式。例如,如果您查看代码,您会发现此路径中不存在应用程序名称 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;
};
}
}
对于 MVC 应用程序,自定义侦听器在使用 initializeData="CustomWeblog.txt" 参数时不会创建日志文件,但 initializeData="d:\CustomWeblog.txt" 会触发文件创建。这种行为的原因是什么?控制台应用程序为所有类型的侦听器生成文件。
自定义 class:
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 侦听器不创建日志文件。
来电者:
TraceSource obj = new TraceSource("Trace", SourceLevels.All);
obj.TraceEvent(TraceEventType.Critical,0,"This is a critical message");
我尝试添加一些额外的配置:来自 this blog and this one。但是没有成功。我应该提供绝对路径吗?通过为自定义侦听器创建单独的程序集是否有任何解决方法?
好的,最后,我已将调查切换到生成侦听器路径的方式。我在调试时注意到的是源侦听器列表包含不同的路径。
- System.Diagnostics.TextWriterTraceListener 侦听器对象已生成完整路径;
- WebTracing.CustomTextWriterTraceListener 只有文件名。没有生成错误。
由于自定义侦听吞没了 UnauthorisedAccessException 异常的原因导致的不同值,因此应用程序继续工作而不通知我们权限问题。
But what is the place of storing Custom listener log files? Are they generated or not?
TextWriterTraceListener source code 的以下 link 帮助我找出路径。以下代码:
//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 Express:
c:\Program Files (x86)\IIS Express\CustomWeblog.txt
我一直在调试 MVC 应用程序 (作为管理员:vs 运行 作为管理员) 日志文件在该文件夹中正确生成。当我 运行 在没有管理员权限的情况下使用 VS 时,自定义侦听器根本不创建文件。
如上所述,我执行了源代码侦听器,发现 catch(UnauthorisedAccessException) { break; }
在 new StreamWriter(...)
构造函数调用时触发。
- 为什么访问路径被拒绝? SO link
- IIS/ASP.NET 的所有用户帐户是什么?它们有何不同? (答案的练习部分)SO link
- Pragime Tech 关于 IIS 和应用程序池配置的精彩 video tutorial: Application pools in IIS。
作为另一种解决方法,您可以在 initializeData="d:\CustomWeblog.txt"
属性中声明整个路径。但请记住,您必须拥有适当的权限。
当我遇到您描述的相同问题时,我正在尝试创建自己的滚动文本编写器跟踪侦听器。长话短说,毕竟这里的 运行 是我想出来的。
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
}
更新:这是我为解析路径而编写的实用程序class。
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;
}
}
所以这个实用程序 class 允许我解析相对路径并自定义格式。例如,如果您查看代码,您会发现此路径中不存在应用程序名称 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;
};
}
}