|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace System.Diagnostics
{
/// <devdoc>
/// <para>Provides the <see langword='abstract '/>base class for the listeners who
/// monitor trace and debug output.</para>
/// </devdoc>
public abstract class TraceListener : MarshalByRefObject, IDisposable
{
private int _indentLevel;
private int _indentSize = 4;
private TraceOptions _traceOptions = TraceOptions.None;
private bool _needIndent = true;
private StringDictionary? _attributes;
private string? _listenerName;
private TraceFilter? _filter;
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Diagnostics.TraceListener'/> class.</para>
/// </devdoc>
protected TraceListener()
{
}
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Diagnostics.TraceListener'/> class using the specified name as the
/// listener.</para>
/// </devdoc>
protected TraceListener(string? name)
{
_listenerName = name;
}
public StringDictionary Attributes => _attributes ??= new StringDictionary();
/// <devdoc>
/// <para> Gets or sets a name for this <see cref='System.Diagnostics.TraceListener'/>.</para>
/// </devdoc>
[AllowNull]
public virtual string Name
{
get { return _listenerName ?? ""; }
set { _listenerName = value; }
}
public virtual bool IsThreadSafe
{
get { return false; }
}
/// <devdoc>
/// </devdoc>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <devdoc>
/// </devdoc>
protected virtual void Dispose(bool disposing)
{
return;
}
/// <devdoc>
/// <para>When overridden in a derived class, flushes the output buffer.</para>
/// </devdoc>
public virtual void Flush()
{
return;
}
/// <devdoc>
/// <para>Gets or sets the indent level.</para>
/// </devdoc>
public int IndentLevel
{
get
{
return _indentLevel;
}
set
{
_indentLevel = (value < 0) ? 0 : value;
}
}
/// <devdoc>
/// <para>Gets or sets the number of spaces in an indent.</para>
/// </devdoc>
public int IndentSize
{
get
{
return _indentSize;
}
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(IndentSize), value, SR.TraceListenerIndentSize);
_indentSize = value;
}
}
public TraceFilter? Filter
{
get
{
return _filter;
}
set
{
_filter = value;
}
}
/// <devdoc>
/// <para>Gets or sets a value indicating whether an indent is needed.</para>
/// </devdoc>
protected bool NeedIndent
{
get
{
return _needIndent;
}
set
{
_needIndent = value;
}
}
public TraceOptions TraceOutputOptions
{
get { return _traceOptions; }
set
{
if (((int)value >> 6) != 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_traceOptions = value;
}
}
/// <devdoc>
/// <para>When overridden in a derived class, closes the output stream
/// so that it no longer receives tracing or debugging output.</para>
/// </devdoc>
public virtual void Close()
{
return;
}
protected internal virtual string[]? GetSupportedAttributes()
{
return null;
}
public virtual void TraceTransfer(TraceEventCache? eventCache, string source, int id, string? message, Guid relatedActivityId)
{
TraceEvent(eventCache, source, TraceEventType.Transfer, id, string.Create(null, stackalloc char[256], $"{message}, relatedActivityId={relatedActivityId}"));
}
/// <devdoc>
/// <para>Emits or displays a message for an assertion that always fails.</para>
/// </devdoc>
public virtual void Fail(string? message)
{
Fail(message, null);
}
/// <devdoc>
/// <para>Emits or displays messages for an assertion that always fails.</para>
/// </devdoc>
public virtual void Fail(string? message, string? detailMessage)
{
WriteLine(detailMessage is null ?
$"{SR.TraceListenerFail} {message}" :
$"{SR.TraceListenerFail} {message} {detailMessage}");
}
/// <devdoc>
/// <para>When overridden in a derived class, writes the specified
/// message to the listener you specify in the derived class.</para>
/// </devdoc>
public abstract void Write(string? message);
/// <devdoc>
/// <para>Writes the name of the <paramref name="o"/> parameter to the listener you specify when you inherit from the <see cref='System.Diagnostics.TraceListener'/>
/// class.</para>
/// </devdoc>
public virtual void Write(object? o)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, null, null, o))
return;
if (o == null) return;
Write(o.ToString());
}
/// <devdoc>
/// <para>Writes a category name and a message to the listener you specify when you
/// inherit from the <see cref='System.Diagnostics.TraceListener'/>
/// class.</para>
/// </devdoc>
public virtual void Write(string? message, string? category)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, message))
return;
if (category == null)
Write(message);
else
Write(category + ": " + (message ?? string.Empty));
}
/// <devdoc>
/// <para>Writes a category name and the name of the <paramref name="o"/> parameter to the listener you
/// specify when you inherit from the <see cref='System.Diagnostics.TraceListener'/>
/// class.</para>
/// </devdoc>
public virtual void Write(object? o, string? category)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, category, null, o))
return;
if (category == null)
Write(o);
else
Write(o == null ? "" : o.ToString(), category);
}
/// <devdoc>
/// <para>Writes the indent to the listener you specify when you
/// inherit from the <see cref='System.Diagnostics.TraceListener'/>
/// class, and resets the <see cref='TraceListener.NeedIndent'/> property to <see langword='false'/>.</para>
/// </devdoc>
protected virtual void WriteIndent()
{
NeedIndent = false;
for (int i = 0; i < _indentLevel; i++)
{
if (_indentSize == 4)
Write(" ");
else
{
for (int j = 0; j < _indentSize; j++)
{
Write(" ");
}
}
}
}
/// <devdoc>
/// <para>When overridden in a derived class, writes a message to the listener you specify in
/// the derived class, followed by a line terminator. The default line terminator is a carriage return followed
/// by a line feed (\r\n).</para>
/// </devdoc>
public abstract void WriteLine(string? message);
/// <devdoc>
/// <para>Writes the name of the <paramref name="o"/> parameter to the listener you specify when you inherit from the <see cref='System.Diagnostics.TraceListener'/> class, followed by a line terminator. The default line terminator is a
/// carriage return followed by a line feed
/// (\r\n).</para>
/// </devdoc>
public virtual void WriteLine(object? o)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, null, null, o))
return;
WriteLine(o == null ? "" : o.ToString());
}
/// <devdoc>
/// <para>Writes a category name and a message to the listener you specify when you
/// inherit from the <see cref='System.Diagnostics.TraceListener'/> class,
/// followed by a line terminator. The default line terminator is a carriage return followed by a line feed (\r\n).</para>
/// </devdoc>
public virtual void WriteLine(string? message, string? category)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, message))
return;
if (category == null)
WriteLine(message);
else
WriteLine(category + ": " + (message ?? string.Empty));
}
/// <devdoc>
/// <para>Writes a category
/// name and the name of the <paramref name="o"/>parameter to the listener you
/// specify when you inherit from the <see cref='System.Diagnostics.TraceListener'/>
/// class, followed by a line terminator. The default line terminator is a carriage
/// return followed by a line feed (\r\n).</para>
/// </devdoc>
public virtual void WriteLine(object? o, string? category)
{
if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, category, null, o))
return;
WriteLine(o == null ? "" : o.ToString(), category);
}
// new write methods used by TraceSource
public virtual void TraceData(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, object? data)
{
if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data))
return;
WriteHeader(source, eventType, id);
string? datastring = string.Empty;
if (data != null)
datastring = data.ToString();
WriteLine(datastring);
WriteFooter(eventCache);
}
public virtual void TraceData(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, params object?[]? data)
{
if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, null, null, null, data))
return;
WriteHeader(source, eventType, id);
WriteLine(data != null ? string.Join(", ", data) : string.Empty);
WriteFooter(eventCache);
}
public virtual void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id)
{
TraceEvent(eventCache, source, eventType, id, string.Empty);
}
// All other TraceEvent methods come through this one.
public virtual void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, string? message)
{
if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message))
return;
WriteHeader(source, eventType, id);
WriteLine(message);
WriteFooter(eventCache);
}
public virtual void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? format, params object?[]? args)
{
if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, format, args))
return;
WriteHeader(source, eventType, id);
if (args != null)
WriteLine(string.Format(CultureInfo.InvariantCulture, format!, args));
else
WriteLine(format);
WriteFooter(eventCache);
}
private void WriteHeader(string source, TraceEventType eventType, int id)
{
Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[256], $"{source} {eventType}: {id} : "));
}
private void WriteFooter(TraceEventCache? eventCache)
{
if (eventCache == null)
return;
_indentLevel++;
if (IsEnabled(TraceOptions.ProcessId))
WriteLine("ProcessId=" + eventCache.ProcessId);
if (IsEnabled(TraceOptions.LogicalOperationStack))
{
Write("LogicalOperationStack=");
Stack operationStack = eventCache.LogicalOperationStack;
bool first = true;
foreach (object obj in operationStack)
{
if (!first)
{
Write(", ");
}
else
{
first = false;
}
Write(obj.ToString());
}
WriteLine(string.Empty);
}
Span<char> stackBuffer = stackalloc char[128];
if (IsEnabled(TraceOptions.ThreadId))
WriteLine("ThreadId=" + eventCache.ThreadId);
if (IsEnabled(TraceOptions.DateTime))
WriteLine(string.Create(null, stackBuffer, $"DateTime={eventCache.DateTime:o}"));
if (IsEnabled(TraceOptions.Timestamp))
WriteLine(string.Create(null, stackBuffer, $"Timestamp={eventCache.Timestamp}"));
if (IsEnabled(TraceOptions.Callstack))
WriteLine("Callstack=" + eventCache.Callstack);
_indentLevel--;
}
internal bool IsEnabled(TraceOptions opts)
{
return (opts & TraceOutputOptions) != 0;
}
}
}
|