我需要登录:
我可以看到EntLib(5.0)有Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener
来登录滚动日志文件。
为了使日志条目看起来像一个CSV行,我可以更改Formatters.TextFormatter.Template
以在值周围加上双引号,并且还将监听器的页脚和标题更改为空,这样就不会输出它们。
在正常情况下,这会给我一个格式良好的CSV文件。但是,如果Template
中的标记值包含双引号,则不会对其进行转义。因此,日志文件将成为无效的CSV文件。
有什么方法可以解决这个问题吗?
这个问题还有其他解决办法吗?
答案 0 :(得分:2)
见http://msdn.microsoft.com/en-us/library/ff650608.aspx。 结果添加一个自定义格式化器并不难,我添加了一个CSVTextFormattter来处理按摩消息和扩展属性,这对我有用。 请注意,我使用bult-in TextFormatter来完成所有繁重的工作。
示例配置:
<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
...
<formatters>
<add type="<your namespace>.CSVTextFormatter, <your dll>"
template="{timestamp(local)},{severity},{category},{message},{property(ActivityId)},{eventid},{win32ThreadId},{threadName},{dictionary({key} - {value}{newline})}"
name="CSV Text Formatter" />
</formatters>...
</loggingConfiguration>
课程是这样的:
Public Class CSVTextFormatter
Implements ILogFormatter
Private Const csTemplateAttributeName As String = "template"
Private moTextFormatter As TextFormatter
Private Property TextFormatter() As TextFormatter
Get
Return moTextFormatter
End Get
Set(ByVal value As TextFormatter)
moTextFormatter = value
End Set
End Property
Private moConfigData As System.Collections.Specialized.NameValueCollection
Private Property ConfigData() As System.Collections.Specialized.NameValueCollection
Get
Return moConfigData
End Get
Set(ByVal value As System.Collections.Specialized.NameValueCollection)
moConfigData = value
If moConfigData.AllKeys.Contains(csTemplateAttributeName) Then
TextFormatter = New TextFormatter(moConfigData(csTemplateAttributeName))
Else
TextFormatter = New TextFormatter()
End If
End Set
End Property
Public Sub New()
TextFormatter = New TextFormatter()
End Sub
Public Sub New(ByVal configData As System.Collections.Specialized.NameValueCollection)
Me.ConfigData = configData
End Sub
Public Function Format(ByVal log As Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry) As String Implements Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.ILogFormatter.Format
Dim oLog As Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry = log.Clone()
With oLog
.Message = NormalizeToCSVValue(.Message)
For Each sKey In .ExtendedProperties.Keys
Dim sValue As String = TryCast(.ExtendedProperties(sKey), String)
If Not String.IsNullOrEmpty(sValue) Then
.ExtendedProperties(sKey) = NormalizeToCSVValue(sValue)
End If
Next
End With
Return TextFormatter.Format(oLog)
End Function
Private Shared Function NormalizeToCSVValue(ByVal text As String) As String
Dim bWrapLogText = False
Dim oQualifiers = New String() {""""}
For Each sQualifier In oQualifiers
If text.Contains(sQualifier) Then
text = text.Replace(sQualifier, String.Format("""{0}""", sQualifier))
bWrapLogText = True
End If
Next
Dim oDelimiters = New String() {",", vbLf, vbCr, vbCrLf}
If text.Contains(oDelimiters) Then
bWrapLogText = True
End If
If bWrapLogText Then
text = String.Format("""{0}""", text)
End If
Return text
End Function
End Class
答案 1 :(得分:0)
我认为除了编写自己的格式化程序之外,没有任何“银弹”解决方案。
您需要担心双引号和新行。其中任何一个都会抛弃格式化。
我认为您必须担心这些字符的唯一属性是Message,Title和您正在使用的任何ExtendedProperties。我建议在Write方法周围编写一个瘦的包装器或外观,在那里您可以转义这些属性,以确保您拥有格式正确的文件。即转义任何双引号并用空格替换新行。
答案 2 :(得分:0)
我已将代码翻译成c#并修复了限定符转义中的错误。我还添加了分号作为分隔符,因为Excel默认采用分号分隔的CSV格式。
public class CsvLogFormatter: ILogFormatter
{
private TextFormatter _formatter;
public CsvLogFormatter(string template)
{
// property Template allows 'set', but formatter still uses original template.. Must recreate formatter when template changes!
_formatter = new TextFormatter(template);
}
public string Template { get { return _formatter.Template; } }
public string Format(LogEntry log)
{
try
{
var logEntry = (LogEntry)log.Clone();
logEntry.Message = NormalizeToCsvToken(logEntry.Message);
var normalizableKeys = logEntry.ExtendedProperties.Where(l => l.Value == null || l.Value is string).ToList();
foreach (var pair in normalizableKeys)
{
logEntry.ExtendedProperties[pair.Key] = NormalizeToCsvToken((string)pair.Value);
}
return _formatter.Format(logEntry);
}
catch
{
// this redundant catch is useful for debugging exceptions in this methods (EnterpriseLibrary swallows exceptions :-/)
throw;
}
}
private static string NormalizeToCsvToken(string text)
{
var wrapLogText = false;
const string qualifier = "\"";
if (text.Contains(qualifier))
{
text = text.Replace(qualifier, qualifier + qualifier);
wrapLogText = true;
}
var delimiters = new[] { ";", ",", "\n", "\r", "\r\n" };
foreach (var delimiter in delimiters)
{
if (text.Contains(delimiter))
wrapLogText = true;
}
if (wrapLogText)
text = string.Format("\"{0}\"", text);
return text;
}
}
随意使用和增强。这是一个非常简单的解决方案,从TextFormatter派生新的Formatter而不是包装它可能会更好,但这对我来说很好('works'== Excel打开它没有任何已知问题)。
答案 3 :(得分:0)
以下代码适用于我:
[ConfigurationElementType(typeof(CustomFormatterData))]
public class CsvLogFormatter : ILogFormatter
{
private TextFormatter _formatter;
private string template = "template";
public CsvLogFormatter(NameValueCollection collection)
{
// property Template allows 'set', but formatter still uses original template.. Must recreate formatter when template changes!
_formatter = new TextFormatter(collection[template]);
}
public string Template { get { return _formatter.Template; } }
public string Format(LogEntry log)
{
try
{
var logEntry = (LogEntry)log.Clone();
logEntry.Message = NormalizeToCsvToken(logEntry.Message);
var normalizableKeys = logEntry.ExtendedProperties.Where(l => l.Value == null || l.Value is string).ToList();
foreach (var pair in normalizableKeys)
{
logEntry.ExtendedProperties[pair.Key] = NormalizeToCsvToken((string)pair.Value);
}
return _formatter.Format(logEntry);
}
catch
{
// this redundant catch is useful for debugging exceptions in this methods (EnterpriseLibrary swallows exceptions :-/)
throw;
}
}
private static string NormalizeToCsvToken(string text)
{
var wrapLogText = false;
const string qualifier = "\"";
if (text.Contains(qualifier))
{
text = text.Replace(qualifier, qualifier + qualifier);
wrapLogText = true;
}
var delimiters = new[] { ";", ",", "\n", "\r", "\r\n" };
foreach (var delimiter in delimiters)
{
if (text.Contains(delimiter))
wrapLogText = true;
}
if (wrapLogText)
text = string.Format("\"{0}\"", text);
return text;
}
}