Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BREAKING CHANGE] DefaultTracing: Removes DefaultTraceListener to disable tracing by default #2926

Merged
merged 12 commits into from
Dec 10, 2021
32 changes: 31 additions & 1 deletion Microsoft.Azure.Cosmos/src/CosmosClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Threading;
Expand Down Expand Up @@ -96,6 +97,9 @@ namespace Microsoft.Azure.Cosmos
/// <seealso href="https://docs.microsoft.com/azure/cosmos-db/request-units">Request Units</seealso>
public class CosmosClient : IDisposable
{
private static readonly object defaultTraceLockObject = new object();
private static bool enableDefaultTrace = false;

private readonly string DatabaseRootUri = Paths.Databases_Root;
private ConsistencyLevel? accountConsistencyLevel;
private bool isDisposed = false;
Expand All @@ -119,6 +123,11 @@ static CosmosClient()
// NOTE: Native ServiceInteropWrapper.AssembliesExist has appsettings dependency which are proofed for CTL (native dll entry) scenarios.
// Revert of this depends on handling such in direct assembly
ServiceInteropWrapper.AssembliesExist = new Lazy<bool>(() => true);

if (Debugger.IsAttached)
{
CosmosClient.EnableDefaultTrace();
}
j82w marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
Expand Down Expand Up @@ -257,7 +266,7 @@ internal CosmosClient(
this.ClientContext = ClientContextCore.Create(
this,
clientOptions);

this.ClientConfigurationTraceDatum = new ClientConfigurationTraceDatum(this.ClientContext, DateTime.UtcNow);
}

Expand Down Expand Up @@ -394,6 +403,27 @@ public static async Task<CosmosClient> CreateAndInitializeAsync(string accountEn
return cosmosClient;
}

/// <summary>
/// Enable the default trace listener by setting the TraceSource and SourceSwitch
/// Default TraceSource name DocDBTrace
/// Default SourceSwitch value Information
/// </summary>
/// <remarks>
/// This is enabled by default when Debugger.IsAttached is true. This makes it
/// easier to troubleshoot issues while debugging in Visual Studio.
/// </remarks>
public static void EnableDefaultTrace()
{
lock (CosmosClient.defaultTraceLockObject)
{
if (!CosmosClient.enableDefaultTrace)
{
Microsoft.Azure.Cosmos.Core.Trace.DefaultTrace.InitEventListener();
j82w marked this conversation as resolved.
Show resolved Hide resolved
CosmosClient.enableDefaultTrace = true;
}
}
}

/// <summary>
/// Used for unit testing only.
/// </summary>
Expand Down
2 changes: 0 additions & 2 deletions Microsoft.Azure.Cosmos/src/DocumentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,6 @@ internal virtual void Initialize(Uri serviceEndpoint,
throw new ArgumentNullException("serviceEndpoint");
}

DefaultTrace.InitEventListener();

this.queryPartitionProvider = new AsyncLazy<QueryPartitionProvider>(async () =>
{
await this.EnsureValidClientAsync(NoOpTrace.Singleton);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2188,6 +2188,11 @@
"Type": "Method",
"Attributes": [],
"MethodInfo": "Void Dispose();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:True;"
},
"Void EnableDefaultTrace()": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "Void EnableDefaultTrace();IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
}
},
"NestedTypes": {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Tests
{
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Trace;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

[TestClass]
public class DefaultTracingTests
{
[TestMethod]
public async Task DefaultTracingDisabledByDefault()
{
FieldInfo enabledField = typeof(CosmosClient).GetField("enableDefaultTrace", BindingFlags.Static | BindingFlags.NonPublic);

bool ccosmosClientAlreadyEnabledTrace = (bool)enabledField.GetValue(null);
if (ccosmosClientAlreadyEnabledTrace)
{
// A previous test enabled the traces. Reset back to the default state.
enabledField.SetValue(null, false);
DefaultTrace.TraceSource.Switch.Level = SourceLevels.Off;
DefaultTrace.TraceSource.Listeners.Clear();
FieldInfo defaultTraceIsListenerAdded = typeof(DefaultTrace).GetField("IsListenerAdded", BindingFlags.Static | BindingFlags.NonPublic);
defaultTraceIsListenerAdded.SetValue(null, false);
}

Assert.AreEqual(SourceLevels.Off, DefaultTrace.TraceSource.Switch.Level, $"The trace is already enabled.");

await this.ValidateTraceAsync(false);

Assert.AreEqual(SourceLevels.Off, DefaultTrace.TraceSource.Switch.Level, $"The trace got enabled.");
}

[TestMethod]
public async Task DefaultTracingEnableTest()
{
CosmosClient.EnableDefaultTrace();
await this.ValidateTraceAsync(true);
}

private async Task ValidateTraceAsync(
bool isTraceEnabled)
{
TestTraceListener testTraceListener = new TestTraceListener();
DefaultTrace.TraceSource.Listeners.Add(testTraceListener);

Mock<IHttpHandler> mockHttpHandler = new Mock<IHttpHandler>();
mockHttpHandler.Setup(x => x.SendAsync(
It.IsAny<HttpRequestMessage>(),
It.IsAny<CancellationToken>())).Throws(new InvalidOperationException("Test exception that won't be retried"));

using CosmosClient cosmosClient = new CosmosClient(
"https://localhost:8081",
Convert.ToBase64String(Guid.NewGuid().ToByteArray()),
new CosmosClientOptions()
{
HttpClientFactory = () => new HttpClient(new HttpHandlerHelper(mockHttpHandler.Object)),
});

try
{
await cosmosClient.GetDatabase("randomDb").ReadAsync();
Assert.Fail("Should throw exception");
}
catch (InvalidOperationException ex)
{
}

if (isTraceEnabled)
{
Assert.IsTrue(testTraceListener.IsTraceWritten);
}
else
{
Assert.IsFalse(testTraceListener.IsTraceWritten);
}
}

private class TestTraceListener : TraceListener
{
public bool IsTraceWritten = false;
public bool WriteTraceToConsole = false;

public override bool IsThreadSafe => true;
public override void Write(string message)
{
this.IsTraceWritten = true;
if (this.WriteTraceToConsole)
{
Logger.LogLine("Trace.Write:" + message);
}
}

public override void WriteLine(string message)
{
this.IsTraceWritten = true;
if (this.WriteTraceToConsole)
{
Logger.LogLine("Trace.WriteLine:" + message);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ private class TestTraceListener : TraceListener
[Owner("kraman")]
public async Task TestOpenAsyncFailFast()
{
CosmosClient.EnableDefaultTrace();

const string accountEndpoint = "https://veryrandomurl123456789.documents.azure.com:443/";

bool failedToResolve = false;
Expand All @@ -60,7 +62,7 @@ void TraceHandler(string message)
{
Assert.IsFalse(failedToResolve, "Failure to resolve should happen only once.");
failedToResolve = true;
didNotRetryMessage = message.Substring(failedToResolveMessage.Length).Split('\n')[0];
didNotRetryMessage = message[failedToResolveMessage.Length..].Split('\n')[0];
}

if (failedToResolve && message.Contains("NOT be retried") && message.Contains(didNotRetryMessage))
Expand Down