Skip to content

Commit 985f4ef

Browse files
authored
CSHARP-4716: Log informational message client-side based on detected environment (DocumentDB or CosmosDB) (#1203)
1 parent 0e2bc5f commit 985f4ef

File tree

4 files changed

+145
-2
lines changed

4 files changed

+145
-2
lines changed

src/MongoDB.Driver.Core/Core/Clusters/ClusterFactory.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
17+
using System.Linq;
1618
using Microsoft.Extensions.Logging;
1719
using MongoDB.Driver.Core.Configuration;
1820
using MongoDB.Driver.Core.Events;
21+
using MongoDB.Driver.Core.Logging;
1922
using MongoDB.Driver.Core.Misc;
2023
using MongoDB.Driver.Core.Servers;
2124

@@ -43,6 +46,8 @@ public ICluster CreateCluster()
4346
{
4447
var settings = _settings;
4548

49+
ProcessClusterEnvironment(settings);
50+
4651
bool createLoadBalancedCluster = settings.LoadBalanced;
4752
if (createLoadBalancedCluster)
4853
{
@@ -102,5 +107,35 @@ private LoadBalancedCluster CreateLoadBalancedCluster(ClusterSettings setting)
102107
{
103108
return new LoadBalancedCluster(setting, _serverFactory, _eventSubscriber, _loggerFactory);
104109
}
110+
111+
private void ProcessClusterEnvironment(ClusterSettings settings)
112+
{
113+
foreach (var (host, _) in settings.EndPoints.Select(EndPointHelper.GetHostAndPort))
114+
{
115+
if (LogIfCosmosDB(host) || LogIfDocumentDB(host))
116+
{
117+
return;
118+
}
119+
}
120+
121+
bool LogIfCosmosDB(string host) =>
122+
LogIfExternalEnvironment(host, "CosmosDB", "https://www.mongodb.com/supportability/cosmosdb", ".cosmos.azure.com");
123+
124+
bool LogIfDocumentDB(string host) =>
125+
LogIfExternalEnvironment(host, "DocumentDB", "https://www.mongodb.com/supportability/documentdb", ".docdb.amazonaws.com", ".docdb-elastic.amazonaws.com");
126+
127+
bool LogIfExternalEnvironment(string host, string environment, string documentationUrl, params string[] suffixes)
128+
{
129+
if (suffixes.Any(s => host.EndsWith(s, StringComparison.InvariantCultureIgnoreCase)))
130+
{
131+
var logger = _loggerFactory.CreateLogger<LogCategories.Client>();
132+
logger.LogInformation("You appear to be connected to a {environment} cluster. For more information regarding feature compatibility and support please visit {url}", environment, documentationUrl);
133+
134+
return true;
135+
}
136+
137+
return false;
138+
}
139+
}
105140
}
106141
}

src/MongoDB.Driver.Core/Core/Logging/LogCategories.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ namespace MongoDB.Driver.Core.Logging
1717
{
1818
internal static class LogCategories
1919
{
20-
public abstract class EventCategory { }
20+
public abstract class BaseCategory { }
21+
22+
public abstract class EventCategory : BaseCategory { }
23+
24+
public sealed class Client : BaseCategory { }
2125

2226
public sealed class Command : EventCategory { }
2327

src/MongoDB.Driver.Core/Core/Logging/LogCategoryHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ _ when __driverNamespaces.Any(n => categoryName.StartsWith(n)) => PrefixInternal
7171
return result;
7272
}
7373

74-
public static string GetCategoryName<T>() where T : LogCategories.EventCategory
74+
public static string GetCategoryName<T>() where T : LogCategories.BaseCategory
7575
{
7676
var type = typeof(T);
7777
if (!__catergories.TryGetValue(type, out var result))
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq;
17+
using FluentAssertions;
18+
using Microsoft.Extensions.Logging;
19+
using MongoDB.Driver.Core.Configuration;
20+
using MongoDB.Driver.Core.Events;
21+
using MongoDB.Driver.Core.Servers;
22+
using MongoDB.Driver.Core.TestHelpers.Logging;
23+
using Moq;
24+
using Xunit;
25+
using Xunit.Abstractions;
26+
27+
namespace MongoDB.Driver.Core.Clusters
28+
{
29+
public class ClusterFactoryTests : LoggableTestClass
30+
{
31+
private const string ExpectedCosmosDBMessage = "You appear to be connected to a CosmosDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/cosmosdb";
32+
private const string ExpectedDocumentDBMessage = "You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb";
33+
34+
public ClusterFactoryTests(ITestOutputHelper output) : base(output)
35+
{
36+
}
37+
38+
[Theory]
39+
[InlineData("mongodb://a.MONGO.COSMOS.AZURE.COM:19555", ExpectedCosmosDBMessage)]
40+
[InlineData("mongodb://a.MONGO.COSMOS.AZURE.COM:19555", ExpectedCosmosDBMessage)]
41+
[InlineData("mongodb://a.mongo.cosmos.azure.com:19555", ExpectedCosmosDBMessage)]
42+
[InlineData("mongodb://a.DOCDB-ELASTIC.AMAZONAWS.COM:27017/", ExpectedDocumentDBMessage)]
43+
[InlineData("mongodb://a.docdb-elastic.amazonaws.com:27017/", ExpectedDocumentDBMessage)]
44+
[InlineData("mongodb://a.DOCDB.AMAZONAWS.COM", ExpectedDocumentDBMessage)]
45+
[InlineData("mongodb://a.docdb.amazonaws.com", ExpectedDocumentDBMessage)]
46+
// SRV matching
47+
[InlineData("mongodb+srv://A.MONGO.COSMOS.AZURE.COM", ExpectedCosmosDBMessage)]
48+
[InlineData("mongodb+srv://a.mongo.cosmos.azure.com", ExpectedCosmosDBMessage)]
49+
[InlineData("mongodb+srv://a.DOCDB.AMAZONAWS.COM/", ExpectedDocumentDBMessage)]
50+
[InlineData("mongodb+srv://a.docdb.amazonaws.com/", ExpectedDocumentDBMessage)]
51+
[InlineData("mongodb+srv://a.DOCDB-ELASTIC.AMAZONAWS.COM/", ExpectedDocumentDBMessage)]
52+
[InlineData("mongodb+srv://a.docdb-elastic.amazonaws.com/", ExpectedDocumentDBMessage)]
53+
// Mixing internal and external hosts (unlikely in practice)
54+
[InlineData("mongodb://a.example.com:27017,b.mongo.cosmos.azure.com:19555/", ExpectedCosmosDBMessage)]
55+
[InlineData("mongodb://a.example.com:27017,b.docdb.amazonaws.com:27017/", ExpectedDocumentDBMessage)]
56+
[InlineData("mongodb://a.example.com:27017,b.docdb-elastic.amazonaws.com:27017/", ExpectedDocumentDBMessage)]
57+
// Multiple external hosts
58+
[InlineData("mongodb://a.docdb-elastic.amazonaws.com:27017,b.mongo.cosmos.azure.com:19555/", ExpectedDocumentDBMessage)]
59+
[InlineData("mongodb://a.mongo.cosmos.azure.com:19554,b.docdb-elastic.amazonaws.com:27017,c.mongo.cosmos.azure.com:19555/", ExpectedCosmosDBMessage)]
60+
public void ClusterFactory_should_log_if_external_environment_is_detected(string connectionString, string expectedMessage)
61+
{
62+
var subject = CreateSubject(connectionString);
63+
_ = subject.CreateCluster();
64+
65+
var logs = GetLogs();
66+
logs.Length.Should().Be(1);
67+
logs[0].FormattedMessage.Should().Be(expectedMessage);
68+
}
69+
70+
[Theory]
71+
[InlineData("mongodb://a.mongo.cosmos.azure.com.tld:19555")]
72+
[InlineData("mongodb://a.docdb-elastic.amazonaws.com.t")]
73+
[InlineData("mongodb://a.docdb-elastic.amazonaws.com.t,b.docdb-elastic.amazonaws.com.t")]
74+
[InlineData("mongodb+srv://a.example.com")]
75+
[InlineData("mongodb+srv://a.mongodb.net/")]
76+
[InlineData("mongodb+srv://a.mongo.cosmos.azure.com.tld/")]
77+
[InlineData("mongodb+srv://a.docdb-elastic.amazonaws.com.tld/")]
78+
public void ClusterFactory_should_not_log_if_no_external_environment_is_detected(string connectionString)
79+
{
80+
var subject = CreateSubject(connectionString);
81+
_ = subject.CreateCluster();
82+
83+
var logs = GetLogs();
84+
logs.Length.Should().Be(0);
85+
}
86+
87+
private ClusterFactory CreateSubject(string connectionString)
88+
{
89+
var parsedConnectionString = new ConnectionString(connectionString);
90+
91+
var eventSubscriberMock = Mock.Of<IEventSubscriber>();
92+
var serverFactoryMock = Mock.Of<IClusterableServerFactory>();
93+
94+
var clusterSettings = new ClusterSettings(endPoints: Optional.Enumerable(parsedConnectionString.Hosts));
95+
var clusterFactory = new ClusterFactory(clusterSettings, serverFactoryMock, eventSubscriberMock, LoggerFactory);
96+
97+
return clusterFactory;
98+
}
99+
100+
private LogEntry[] GetLogs() => Logs
101+
.Where(l => l.LogLevel == LogLevel.Information && l.Category == "MongoDB.Client")
102+
.ToArray();
103+
}
104+
}

0 commit comments

Comments
 (0)