MessageLoggingPolicy
A PipelinePolicy used by a ClientPipeline to
log request and response information.
using System.ClientModel.Internal;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace System.ClientModel.Primitives
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public class MessageLoggingPolicy : PipelinePolicy
{
[System.Runtime.CompilerServices.Nullable(0)]
private class MaxLengthStream : MemoryStream
{
private int _bytesLeft;
public MaxLengthStream(int maxLength)
{
_bytesLeft = maxLength;
}
public override void Write(byte[] buffer, int offset, int count)
{
DecrementLength(ref count);
if (count > 0)
base.Write(buffer, offset, count);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (count <= 0)
return Task.CompletedTask;
return base.WriteAsync(buffer, offset, count, cancellationToken);
}
private void DecrementLength(ref int count)
{
int num = count = Math.Min(count, _bytesLeft);
_bytesLeft -= count;
}
}
[System.Runtime.CompilerServices.Nullable(0)]
private class LoggingStream : Stream
{
private readonly string _requestId;
private int _remainingBytesToLog;
private readonly Stream _originalStream;
private readonly bool _error;
[System.Runtime.CompilerServices.Nullable(2)]
private readonly Encoding _textEncoding;
private int _blockNumber;
private readonly PipelineMessageLogger _messageLogger;
public override bool CanRead => _originalStream.CanRead;
public override bool CanSeek => _originalStream.CanSeek;
public override long Length => _originalStream.Length;
public override long Position {
get {
return _originalStream.Position;
}
set {
_originalStream.Position = value;
}
}
public override bool CanWrite => false;
public LoggingStream(PipelineMessageLogger messageLogger, string requestId, int maxLoggedBytes, Stream originalStream, bool error, [System.Runtime.CompilerServices.Nullable(2)] Encoding textEncoding)
{
_requestId = requestId;
_remainingBytesToLog = maxLoggedBytes;
_originalStream = originalStream;
_error = error;
_textEncoding = textEncoding;
_messageLogger = messageLogger;
}
public override int Read(byte[] buffer, int offset, int count)
{
int num = _originalStream.Read(buffer, offset, count);
LogBuffer(buffer, offset, num);
return num;
}
[AsyncStateMachine(typeof(<ReadAsync>d__9))]
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
<ReadAsync>d__9 stateMachine = default(<ReadAsync>d__9);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
stateMachine.<>4__this = this;
stateMachine.buffer = buffer;
stateMachine.offset = offset;
stateMachine.count = count;
stateMachine.cancellationToken = cancellationToken;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("This stream does not support seek operations.");
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("This stream is read-only.");
}
public override void SetLength(long value)
{
throw new NotSupportedException("This stream is read-only.");
}
public override void Flush()
{
_originalStream.Flush();
}
public override void Close()
{
_originalStream.Close();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_originalStream.Dispose();
}
[System.Runtime.CompilerServices.NullableContext(0)]
private void LogMemory(Memory<byte> memory, int numBytesReadIntoMemory)
{
int num = Math.Min(numBytesReadIntoMemory, _remainingBytesToLog);
_remainingBytesToLog -= num;
if (num != 0) {
byte[] array = new byte[num];
memory.Slice(0, num).Span.CopyTo(array);
if (_error)
_messageLogger.LogErrorResponseContentBlock(_requestId, _blockNumber, array, _textEncoding);
else
_messageLogger.LogResponseContentBlock(_requestId, _blockNumber, array, _textEncoding);
_blockNumber++;
}
}
private void LogBuffer(byte[] buffer, int offset, int numBytesReadIntoBuffer)
{
int num = Math.Min(numBytesReadIntoBuffer, _remainingBytesToLog);
_remainingBytesToLog -= num;
if (num != 0 && buffer != null) {
byte[] array;
if (num == numBytesReadIntoBuffer && offset == 0)
array = buffer;
else {
array = new byte[num];
Buffer.BlockCopy(buffer, offset, array, 0, num);
}
if (_error)
_messageLogger.LogErrorResponseContentBlock(_requestId, _blockNumber, array, _textEncoding);
else
_messageLogger.LogResponseContentBlock(_requestId, _blockNumber, array, _textEncoding);
_blockNumber++;
}
}
}
private readonly ClientLoggingOptions _loggingOptions;
[System.Runtime.CompilerServices.Nullable(2)]
private PipelineMessageLogger _messageLogger;
private readonly string _clientAssembly = typeof(MessageLoggingPolicy).Assembly.GetName().Name;
public static MessageLoggingPolicy Default { get; } = new MessageLoggingPolicy(null);
private bool _enableMessageContentLogging => _loggingOptions.EnableMessageContentLogging.GetValueOrDefault();
private int _maxLength => _loggingOptions.MessageContentSizeLimit.GetValueOrDefault(4096);
[System.Runtime.CompilerServices.NullableContext(2)]
public MessageLoggingPolicy(ClientLoggingOptions options = null)
{
_loggingOptions = (options ?? new ClientLoggingOptions());
}
public sealed override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
{
ProcessSyncOrAsync(message, pipeline, currentIndex, false).EnsureCompleted();
}
[AsyncStateMachine(typeof(<ProcessAsync>d__12))]
public sealed override ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
{
<ProcessAsync>d__12 stateMachine = default(<ProcessAsync>d__12);
stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.message = message;
stateMachine.pipeline = pipeline;
stateMachine.currentIndex = currentIndex;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[AsyncStateMachine(typeof(<ProcessSyncOrAsync>d__13))]
private ValueTask ProcessSyncOrAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex, bool async)
{
<ProcessSyncOrAsync>d__13 stateMachine = default(<ProcessSyncOrAsync>d__13);
stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.message = message;
stateMachine.pipeline = pipeline;
stateMachine.currentIndex = currentIndex;
stateMachine.async = async;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
}
}