This is an automated email from the ASF dual-hosted git repository.

florianhockmann pushed a commit to branch TINKERPOP-2471
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit dff41f3f7f06fb7d539de7c577288e84bfb5d40f
Author: Florian Hockmann <[email protected]>
AuthorDate: Wed Oct 12 14:16:45 2022 +0200

    TINKERPOP-2471 Add logging to .NET
---
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/reference/gremlin-variants.asciidoc       | 11 ++++
 docs/src/upgrade/release-3.5.x.asciidoc            | 18 ++++++
 .../Gremlin.Net.Template.csproj                    |  3 +-
 gremlin-dotnet/src/Gremlin.Net.Template/Program.cs | 22 ++++---
 .../src/Gremlin.Net/Driver/ConnectionPool.cs       | 16 ++++-
 .../src/Gremlin.Net/Driver/GremlinClient.cs        | 23 +++++--
 .../src/Gremlin.Net/Driver/IGremlinClient.cs       |  1 -
 gremlin-dotnet/src/Gremlin.Net/Driver/Log.cs       | 46 ++++++++++++++
 .../Driver/Remote/DriverRemoteConnection.cs        | 71 ++++++++++++----------
 gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj  |  3 +-
 .../src/Gremlin.Net/Process/Traversal/Bytecode.cs  |  6 ++
 .../Gremlin.Net/Process/Traversal/Instruction.cs   |  2 +-
 gremlin-dotnet/src/pom.xml                         |  2 +-
 .../Docs/Reference/GremlinVariantsTests.cs         | 15 ++++-
 .../Gremlin.Net.IntegrationTest.csproj             |  1 +
 .../Driver/ConnectionPoolTests.cs                  | 12 ++--
 .../Process/Traversal/BytecodeTests.cs             | 11 ++++
 18 files changed, 200 insertions(+), 64 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 579c005883..6496cc4e67 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -34,6 +34,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Changed `JavaTranslator` exception handling so that an 
`IllegalArgumentException` is used for cases where the method exists but the 
signature can't be discerned given the arguments supplied.
 * Dockerized all test environment for .NET, JavaScript, Python, Go, and 
Python-based tests for Console, and added Docker as a build requirement.
 * Bumped to `snakeyaml` 1.32 to fix security vulnerability.
+* Added logging in .NET.
 
 ==== Bugs
 
diff --git a/docs/src/reference/gremlin-variants.asciidoc 
b/docs/src/reference/gremlin-variants.asciidoc
index 60e5bdd406..2670e75777 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -1145,6 +1145,17 @@ attacks like CRIME/BREACH. Compression should therefore 
be turned off if the app
 server as well as data that could potentially be controlled by an untrusted 
user. Compression can be disabled via the
 `disableCompression` parameter.
 
+[[gremlin-dotnet-logging]]
+=== Logging
+
+It is possible to enable logging for the Gremlin.Net driver by providing an 
`ILoggerFactory` (from the
+`Microsoft.Extensions.Logging.Abstractions` package) to the `GremlinClient` 
constructor:
+
+[source,csharp]
+----
+include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs[tags=logging]
+----
+
 [[gremlin-dotnet-serialization]]
 === Serialization
 
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc 
b/docs/src/upgrade/release-3.5.x.asciidoc
index 2b4f659ca0..2596d8beba 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -30,7 +30,25 @@ complete list of all the modifications that are part of this 
release.
 
 === Upgrading for Users
 
+==== Gremlin.NET: Add logging
 
+The Gremlin.NET driver can now be configured for logging to get more insights 
into its internal state, like when
+a connection was closed and will therefore be replaced. It uses 
`Microsoft.Extensions.Logging` for this so all kinds
+of different .NET logging implementations can be used.
+
+The following example shows how to provide a `LoggerFactory` that is 
configured to log to the console to
+`GremlinClient`:
+
+[source,csharp]
+----
+var loggerFactory = LoggerFactory.Create(builder =>
+{
+    builder.AddConsole();
+});
+var client = new GremlinClient(new GremlinServer("localhost", 8182), 
loggerFactory: loggerFactory);
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2471[TINKERPOP-2471]
 
 == TinkerPop 3.5.4
 
diff --git 
a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj 
b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
index e7ba2b71fa..5ff2dc9fc2 100644
--- a/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
+++ b/gremlin-dotnet/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj
@@ -22,7 +22,8 @@ limitations under the License.
     <TargetFramework>net6.0</TargetFramework>
   </PropertyGroup>
 
-  <ItemGroup>
+  <ItemGroup>    
+    <PackageReference Include="Microsoft.Extensions.Logging.Console" 
Version="6.0.0" />
     <!-- We need both reference elements until this is resolved: 
https://github.com/dotnet/sdk/issues/1151 -->
     <ProjectReference Include="../Gremlin.Net/Gremlin.Net.csproj" />
 
diff --git a/gremlin-dotnet/src/Gremlin.Net.Template/Program.cs 
b/gremlin-dotnet/src/Gremlin.Net.Template/Program.cs
index 93018e7e5a..42ffb9a255 100644
--- a/gremlin-dotnet/src/Gremlin.Net.Template/Program.cs
+++ b/gremlin-dotnet/src/Gremlin.Net.Template/Program.cs
@@ -24,8 +24,7 @@
 using System;
 using Gremlin.Net.Driver;
 using Gremlin.Net.Driver.Remote;
-using Gremlin.Net.Structure;
-
+using Microsoft.Extensions.Logging;
 using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
 
 namespace Gremlin.Net.Template
@@ -37,15 +36,18 @@ namespace Gremlin.Net.Template
 
         private static void Main()
         {
-            using (var client = new GremlinClient(new 
GremlinServer(GremlinServerHostname, GremlinServerPort)))
+            var loggerFactory = LoggerFactory.Create(builder =>
+            {
+                builder.AddConsole();
+            });
+            using var connection = new DriverRemoteConnection(new 
GremlinClient(
+                new GremlinServer(GremlinServerHostname, GremlinServerPort), 
loggerFactory: loggerFactory));
+            var g = Traversal().WithRemote(connection);
+            var service = new Service(g);
+            var creators = service.FindCreatorsOfSoftware("lop");
+            foreach (var c in creators)
             {
-                var g = Traversal().WithRemote(new 
DriverRemoteConnection(client));
-                var service = new Service(g);
-                var creators = service.FindCreatorsOfSoftware("lop");
-                foreach (var c in creators)
-                {
-                    Console.WriteLine(c);
-                }
+                Console.WriteLine(c);
             }
         }
     }
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPool.cs 
b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPool.cs
index 8273561f53..e4f4a72ae2 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPool.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPool.cs
@@ -28,6 +28,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Exceptions;
 using Gremlin.Net.Process;
+using Microsoft.Extensions.Logging;
 using Polly;
 
 namespace Gremlin.Net.Driver
@@ -42,16 +43,19 @@ namespace Gremlin.Net.Driver
         private readonly ConcurrentDictionary<IConnection, byte> 
_deadConnections =
             new ConcurrentDictionary<IConnection, byte>();
         private readonly ConnectionPoolSettings _settings;
+        private readonly ILogger<ConnectionPool> _logger;
         private int _connectionIndex;
         private int _poolState;
         private const int PoolIdle = 0;
         private const int PoolPopulationInProgress = 1;
         private readonly CancellationTokenSource _cts = new 
CancellationTokenSource();
 
-        public ConnectionPool(IConnectionFactory connectionFactory, 
ConnectionPoolSettings settings)
+        public ConnectionPool(IConnectionFactory connectionFactory, 
ConnectionPoolSettings settings,
+            ILogger<ConnectionPool> logger)
         {
             _connectionFactory = connectionFactory;
             _settings = settings;
+            _logger = logger;
             ReplaceDeadConnectionsAsync().WaitUnwrap();
         }
         
@@ -60,7 +64,11 @@ namespace Gremlin.Net.Driver
         public IConnection GetAvailableConnection()
         {
             var connection = Policy.Handle<ServerUnavailableException>()
-                .WaitAndRetry(_settings.ReconnectionAttempts, 
ComputeRetrySleepDuration)
+                .WaitAndRetry(_settings.ReconnectionAttempts, 
ComputeRetrySleepDuration, onRetry:
+                    (_, _, retryCount, _) =>
+                    {
+                        _logger.CouldNotGetConnectionFromPool(retryCount, 
_settings.ReconnectionAttempts);
+                    })
                 .Execute(GetConnectionFromPool);
 
             return ProxiedConnection(connection);
@@ -116,10 +124,11 @@ namespace Gremlin.Net.Driver
 
             _deadConnections.Clear();
         }
-        
+
         private async Task FillPoolAsync()
         {
             var nrConnectionsToCreate = _settings.PoolSize - 
_connections.Count;
+            _logger.FillingPool(nrConnectionsToCreate);
             var connectionCreationTasks = new 
List<Task<IConnection>>(nrConnectionsToCreate);
             try
             {
@@ -218,6 +227,7 @@ namespace Gremlin.Net.Driver
         
         private void RemoveConnectionFromPool(IConnection connection)
         {
+            _logger.RemovingClosedConnectionFromPool();
             _deadConnections.TryAdd(connection, 0);
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs 
b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
index 149622e78d..ed801ce4e4 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
@@ -27,6 +27,8 @@ using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
 using Gremlin.Net.Structure.IO;
 using Gremlin.Net.Structure.IO.GraphSON;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
 
 namespace Gremlin.Net.Driver
 {
@@ -36,6 +38,8 @@ namespace Gremlin.Net.Driver
     public class GremlinClient : IGremlinClient
     {
         private readonly ConnectionPool _connectionPool;
+        
+        internal ILoggerFactory LoggerFactory { get; }
 
         /// <summary>
         ///     Initializes a new instance of the <see cref="GremlinClient" /> 
class for the specified Gremlin Server.
@@ -118,8 +122,11 @@ namespace Gremlin.Net.Driver
                     connectionPoolSettings = new ConnectionPoolSettings 
{PoolSize = 1};
                 }
             }
-            _connectionPool =
-                new ConnectionPool(connectionFactory, connectionPoolSettings 
?? new ConnectionPoolSettings());
+
+            LoggerFactory = NullLoggerFactory.Instance;
+
+            _connectionPool = new ConnectionPool(connectionFactory,
+                connectionPoolSettings ?? new ConnectionPoolSettings(), 
LoggerFactory.CreateLogger<ConnectionPool>());
         }
 
         private static void VerifyGraphSONArgumentTypeForMimeType<T>(object 
argument, string argumentName,
@@ -149,15 +156,16 @@ namespace Gremlin.Net.Driver
         /// <param name="disableCompression">
         ///     Whether to disable compression. Compression is only supported 
since .NET 6.
         ///     There it is also enabled by default.
-        ///
+        /// 
         ///     Note that compression might make your application susceptible 
to attacks like CRIME/BREACH. Compression
         ///     should therefore be turned off if your application sends 
sensitive data to the server as well as data
         ///     that could potentially be controlled by an untrusted user.
         /// </param>
+        /// <param name="loggerFactory">A factory to create loggers. If not 
provided, then nothing will be logged.</param>
         public GremlinClient(GremlinServer gremlinServer, IMessageSerializer 
messageSerializer = null,
             ConnectionPoolSettings connectionPoolSettings = null,
             Action<ClientWebSocketOptions> webSocketConfiguration = null, 
string sessionId = null,
-            bool disableCompression = false)
+            bool disableCompression = false, ILoggerFactory loggerFactory = 
null)
         {
             messageSerializer ??= new GraphSON3MessageSerializer();
             var webSocketSettings = new WebSocketSettings
@@ -184,8 +192,11 @@ namespace Gremlin.Net.Driver
                     connectionPoolSettings = new ConnectionPoolSettings 
{PoolSize = 1};
                 }
             }
-            _connectionPool =
-                new ConnectionPool(connectionFactory, connectionPoolSettings 
?? new ConnectionPoolSettings());
+
+            LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
+
+            _connectionPool = new ConnectionPool(connectionFactory,
+                connectionPoolSettings ?? new ConnectionPoolSettings(), 
LoggerFactory.CreateLogger<ConnectionPool>());
         }
 
         /// <summary>
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/IGremlinClient.cs 
b/gremlin-dotnet/src/Gremlin.Net/Driver/IGremlinClient.cs
index 9bef4be409..97baca25d5 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/IGremlinClient.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/IGremlinClient.cs
@@ -22,7 +22,6 @@
 #endregion
 
 using System;
-using System.Collections.Generic;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Log.cs 
b/gremlin-dotnet/src/Gremlin.Net/Driver/Log.cs
new file mode 100644
index 0000000000..ae9c712f82
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Log.cs
@@ -0,0 +1,46 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using Gremlin.Net.Process.Traversal;
+using Microsoft.Extensions.Logging;
+
+namespace Gremlin.Net.Driver
+{
+    internal static partial class Log
+    {
+        [LoggerMessage(20000, LogLevel.Information, "Creating {nrConnections} 
new connections")]
+        public static partial void FillingPool(this ILogger logger, int 
nrConnections);
+
+        [LoggerMessage(20001, LogLevel.Warning,
+            "Could not get a connection from the pool. Will try again. Retry 
{retryCount} of {maxRetries}")]
+        public static partial void CouldNotGetConnectionFromPool(this ILogger 
logger, int retryCount, int maxRetries);
+
+        [LoggerMessage(20002, LogLevel.Information,
+            "A connection was closed. Removing it from the pool so it can be 
replaced.")]
+        public static partial void RemovingClosedConnectionFromPool(this 
ILogger logger);
+        
+        [LoggerMessage(20003, LogLevel.Debug, "Submitting Bytecode {bytecode} 
for request: {requestId}")]
+        public static partial void SubmittingBytecode(this ILogger logger, 
Bytecode bytecode, Guid requestId);
+    }
+}
\ No newline at end of file
diff --git 
a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs 
b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
index 2f32f778ba..761a85b74a 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs
@@ -28,6 +28,8 @@ using Gremlin.Net.Driver.Messages;
 using Gremlin.Net.Process.Remote;
 using Gremlin.Net.Process.Traversal;
 using Gremlin.Net.Process.Traversal.Strategy.Decoration;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
 
 namespace Gremlin.Net.Driver.Remote
 {
@@ -38,68 +40,69 @@ namespace Gremlin.Net.Driver.Remote
     {
         private readonly IGremlinClient _client;
         private readonly string _traversalSource;
-        
+        private readonly ILogger<DriverRemoteConnection> _logger;
+
         /// <summary>
         /// Filter on these keys provided to OptionsStrategy and apply them to 
the request. Note that
         /// "scriptEvaluationTimeout" was deprecated in 3.3.9 but still 
supported in server implementations and will
         /// be removed in later versions. 
         /// </summary>
-        private readonly List<String> _allowedKeys = new List<string> 
-                    {Tokens.ArgsEvalTimeout, "scriptEvaluationTimeout", 
Tokens.ArgsBatchSize, 
-                     Tokens.RequestId, Tokens.ArgsUserAgent};
+        private readonly List<string> _allowedKeys = new()
+        {
+            Tokens.ArgsEvalTimeout, "scriptEvaluationTimeout", 
Tokens.ArgsBatchSize,
+            Tokens.RequestId, Tokens.ArgsUserAgent
+        };
 
         private readonly string _sessionId;
         private string Processor => IsSessionBound ? Tokens.ProcessorSession : 
Tokens.ProcessorTraversal;
 
         /// <inheritdoc />
         public bool IsSessionBound => _sessionId != null;
-
-        /// <summary>
-        ///     Initializes a new <see cref="IRemoteConnection" /> using "g" 
as the default remote TraversalSource name.
-        /// </summary>
-        /// <param name="host">The host to connect to.</param>
-        /// <param name="port">The port to connect to.</param>
-        /// <exception cref="ArgumentNullException">Thrown when client is 
null.</exception>
-        public DriverRemoteConnection(string host, int port):this(host, port, 
"g")
-        {
-        }
-
+        
         /// <summary>
         ///     Initializes a new <see cref="IRemoteConnection" />.
         /// </summary>
         /// <param name="host">The host to connect to.</param>
         /// <param name="port">The port to connect to.</param>
         /// <param name="traversalSource">The name of the traversal source on 
the server to bind to.</param>
+        /// <param name="loggerFactory">A factory to create loggers. If not 
provided, then nothing will be logged.</param>
         /// <exception cref="ArgumentNullException">Thrown when client is 
null.</exception>
-        public DriverRemoteConnection(string host, int port, string 
traversalSource):this(new GremlinClient(new GremlinServer(host, port)), 
traversalSource)
+        public DriverRemoteConnection(string host, int port, string 
traversalSource = "g",
+            ILoggerFactory loggerFactory = null) : this(
+            new GremlinClient(new GremlinServer(host, port), loggerFactory: 
loggerFactory), traversalSource,
+            logger: loggerFactory != null
+                ? loggerFactory.CreateLogger<DriverRemoteConnection>()
+                : NullLogger<DriverRemoteConnection>.Instance)
         {
         }
 
         /// <summary>
-        ///     Initializes a new <see cref="IRemoteConnection" /> using "g" 
as the default remote TraversalSource name.
+        ///     Initializes a new <see cref="IRemoteConnection" />.
         /// </summary>
         /// <param name="client">The <see cref="IGremlinClient" /> that will 
be used for the connection.</param>
-        /// <exception cref="ArgumentNullException">Thrown when client is 
null.</exception>
-        public DriverRemoteConnection(IGremlinClient client):this(client, "g")
+        /// <param name="traversalSource">The name of the traversal source on 
the server to bind to.</param>
+        /// <exception cref="ArgumentNullException">Thrown when client or the 
traversalSource is null.</exception>
+        public DriverRemoteConnection(IGremlinClient client, string 
traversalSource = "g")
+            : this(client, traversalSource, null)
         {
         }
 
-        /// <summary>
-        ///     Initializes a new <see cref="IRemoteConnection" />.
-        /// </summary>
-        /// <param name="client">The <see cref="IGremlinClient" /> that will 
be used for the connection.</param>
-        /// <param name="traversalSource">The name of the traversal source on 
the server to bind to.</param>
-        /// <exception cref="ArgumentNullException">Thrown when client is 
null.</exception>
-        public DriverRemoteConnection(IGremlinClient client, string 
traversalSource)
+        private DriverRemoteConnection(IGremlinClient client, string 
traversalSource, string sessionId = null,
+            ILogger<DriverRemoteConnection> logger = null)
         {
             _client = client ?? throw new 
ArgumentNullException(nameof(client));
             _traversalSource = traversalSource ?? throw new 
ArgumentNullException(nameof(traversalSource));
-        }
 
-        private DriverRemoteConnection(IGremlinClient client, string 
traversalSource, Guid sessionId)
-            : this(client, traversalSource)
-        {
-            _sessionId = sessionId.ToString();
+            if (logger == null)
+            {
+                var loggerFactory = client is GremlinClient gremlinClient
+                    ? gremlinClient.LoggerFactory
+                    : NullLoggerFactory.Instance;
+
+                logger = loggerFactory.CreateLogger<DriverRemoteConnection>();
+            }
+            _logger = logger;
+            _sessionId = sessionId;
         }
 
         /// <summary>
@@ -116,6 +119,8 @@ namespace Gremlin.Net.Driver.Remote
 
         private async Task<IEnumerable<Traverser>> SubmitBytecodeAsync(Guid 
requestid, Bytecode bytecode)
         {
+            _logger.SubmittingBytecode(bytecode, requestid);
+            
             var requestMsg =
                 RequestMessage.Build(Tokens.OpsBytecode)
                     .Processor(Processor)
@@ -141,14 +146,14 @@ namespace Gremlin.Net.Driver.Remote
                     }
                 }
             }
-            
+
             return await 
_client.SubmitAsync<Traverser>(requestMsg.Create()).ConfigureAwait(false);
         }
 
         /// <inheritdoc />
         public RemoteTransaction Tx(GraphTraversalSource g)
         {
-            var session = new DriverRemoteConnection(_client, 
_traversalSource, Guid.NewGuid());
+            var session = new DriverRemoteConnection(_client, 
_traversalSource, Guid.NewGuid().ToString(), _logger);
             return new RemoteTransaction(session, g);
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj 
b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
index 9110d4de92..e16d088f73 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
+++ b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj
@@ -21,7 +21,7 @@ limitations under the License.
     <TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <LangVersion>8</LangVersion>
+    <LangVersion>9</LangVersion>
   </PropertyGroup>
 
   <PropertyGroup Label="Package">
@@ -71,6 +71,7 @@ NOTE that versions suffixed with "-rc" are considered release 
candidates (i.e. p
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" 
Version="6.0.2" />
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" 
PrivateAssets="All" />
     <PackageReference Include="System.Text.Json" Version="6.0.6" />
     <PackageReference Include="Polly" Version="7.2.3" />
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs 
b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
index cd53a1c6c5..e692ed23e2 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
@@ -168,5 +168,11 @@ namespace Gremlin.Net.Process.Traversal
         {
             return type.IsConstructedGenericType && 
type.GetGenericTypeDefinition() == typeof(HashSet<>);
         }
+
+        /// <inheritdoc />
+        public override string ToString()
+        {
+            return $"[[{string.Join(", ", SourceInstructions)}], 
[{string.Join(", ", StepInstructions)}]]";
+        }
     }
 }
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs 
b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs
index 1063b33749..b99343ab0b 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs
@@ -57,7 +57,7 @@ namespace Gremlin.Net.Process.Traversal
         /// </summary>
         public override string ToString()
         {
-            return OperatorName + " [" + String.Join(",", Arguments) + "]";
+            return $"{OperatorName}({string.Join(", ", Arguments)})";
         }
 
         /// <inheritdoc />
diff --git a/gremlin-dotnet/src/pom.xml b/gremlin-dotnet/src/pom.xml
index 264196baff..e5ed5985ce 100644
--- a/gremlin-dotnet/src/pom.xml
+++ b/gremlin-dotnet/src/pom.xml
@@ -61,7 +61,7 @@ limitations under the License.
                                     
file.write(file.getText("UTF-8").replaceFirst(/&lt;Version&gt;(.*)&lt;\/Version&gt;/,
 "&lt;Version&gt;" + mavenVersion + "&lt;/Version&gt;"))
 
                                     file = new 
File(platformAgnosticBaseDirPath + 
"/Gremlin.Net.Template/Gremlin.Net.Template.csproj")
-                                    
file.write(file.getText("UTF-8").replaceFirst(/Version="(.*)"/, "Version=\"" + 
mavenVersion + "\""))
+                                    
file.write(file.getText("UTF-8").replaceFirst(/Gremlin\.Net\" Version="(.*)"/, 
"Gremlin.Net\" Version=\"" + mavenVersion + "\""))
 
                                     file = new 
File(platformAgnosticBaseDirPath + 
"/Gremlin.Net.Template/Gremlin.Net.Template.nuspec")
                                     
file.write(file.getText("UTF-8").replaceFirst(/&lt;version&gt;(.*)&lt;\/version&gt;/,
 "&lt;version&gt;" + mavenVersion + "&lt;/version&gt;"))
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
index fb8479f223..4b277c4f8d 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
@@ -32,6 +32,7 @@ using Gremlin.Net.Process.Traversal.Step.Util;
 using Gremlin.Net.Process.Traversal.Strategy.Decoration;
 using Gremlin.Net.Structure.IO.GraphBinary;
 using Gremlin.Net.Structure.IO.GraphSON;
+using Microsoft.Extensions.Logging;
 using Xunit;
 // tag::commonImports[]
 using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
@@ -58,11 +59,23 @@ namespace Gremlin.Net.IntegrationTest.Docs.Reference
         public void ConnectingTest()
         {
 // tag::connecting[]
-var remoteConnection = new DriverRemoteConnection(new GremlinClient(new 
GremlinServer("localhost", 8182)), "g");
+using var remoteConnection = new DriverRemoteConnection(new GremlinClient(new 
GremlinServer("localhost", 8182)), "g");
 var g = Traversal().WithRemote(remoteConnection);
 // end::connecting[]
         }
         
+        [Fact(Skip="No Server under localhost")]
+        public void LoggingTest()
+        {
+// tag::logging[]
+            var loggerFactory = LoggerFactory.Create(builder =>
+            {
+                builder.AddConsole();
+            });
+            var client = new GremlinClient(new GremlinServer("localhost", 
8182), loggerFactory: loggerFactory);
+// end::logging[]
+        }
+        
         [Fact(Skip="No Server under localhost")]
         public void SerializationGraphBinaryTest()
         {
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gremlin.Net.IntegrationTest.csproj
 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gremlin.Net.IntegrationTest.csproj
index 697ae87856..ce3889cb8a 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gremlin.Net.IntegrationTest.csproj
+++ 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gremlin.Net.IntegrationTest.csproj
@@ -12,6 +12,7 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="gherkin" Version="23.0.1" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Console" 
Version="6.0.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
     <PackageReference Include="xunit" Version="2.4.2" />
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/ConnectionPoolTests.cs 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/ConnectionPoolTests.cs
index 53e3506c22..9db8e91aa3 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/ConnectionPoolTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/ConnectionPoolTests.cs
@@ -27,6 +27,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver;
 using Gremlin.Net.Driver.Exceptions;
+using Microsoft.Extensions.Logging.Abstractions;
 using Moq;
 using Xunit;
 
@@ -389,12 +390,11 @@ namespace Gremlin.Net.UnitTest.Driver
         private static ConnectionPool CreateConnectionPool(IConnectionFactory 
connectionFactory, int poolSize = 2,
             int reconnectionAttempts = 1)
         {
-            return new ConnectionPool(connectionFactory,
-                new ConnectionPoolSettings
-                {
-                    PoolSize = poolSize, ReconnectionAttempts = 
reconnectionAttempts,
-                    ReconnectionBaseDelay = TimeSpan.FromMilliseconds(10)   // 
let the tests execute fast
-                });
+            return new ConnectionPool(connectionFactory, new 
ConnectionPoolSettings
+            {
+                PoolSize = poolSize, ReconnectionAttempts = 
reconnectionAttempts,
+                ReconnectionBaseDelay = TimeSpan.FromMilliseconds(10) // let 
the tests execute fast
+            }, NullLogger<ConnectionPool>.Instance);
         }
     }
 }
\ No newline at end of file
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs
index 64f2f87c77..5f42b4e648 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs
@@ -172,5 +172,16 @@ namespace Gremlin.Net.UnitTest.Process.Traversal
             var arg = bytecode.SourceInstructions[0].Arguments[0] as 
ISet<object>;
             Assert.Equal(new Binding("setVariable", "setValue"), 
arg.ToList()[1]);
         }
+
+        [Fact]
+        public void ShouldIncludeStepAndSourceInstructionsForToString()
+        {
+            var bytecode = new Bytecode();
+            bytecode.AddSource("source", 1, 2);
+            bytecode.AddStep("step1", 9, 8);
+            bytecode.AddStep("step2", 0);
+            
+            Assert.Equal("[[source(1, 2)], [step1(9, 8), step2(0)]]", 
bytecode.ToString());
+        }
     }
 }
\ No newline at end of file

Reply via email to