Skip to content

Commit

Permalink
Support client activities on all target platforms (#2256)
Browse files Browse the repository at this point in the history
- Add the system diagnostics library for unsupported target platforms
- Integrate in clientfixture by adding IDisposable to support to enable/disable the activity tracing
- fix Traceable session dispose issue, do not null the disposed session
  • Loading branch information
mregen committed Aug 10, 2023
1 parent 3e174fd commit 1e535dd
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 62 deletions.
4 changes: 4 additions & 0 deletions Libraries/Opc.Ua.Client/Opc.Ua.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<PackageId>$(PackageId).Debug</PackageId>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net6.0' AND '$(TargetFramework)' != 'net7.0'">
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
Expand Down
12 changes: 7 additions & 5 deletions Libraries/Opc.Ua.Client/TraceableSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

#if NET6_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -61,7 +60,7 @@ public TraceableSession(ISession session)
/// Activity Source static instance.
/// </summary>
public static ActivitySource ActivitySource => s_activitySource.Value;
private static readonly Lazy<ActivitySource> s_activitySource = new Lazy<ActivitySource>(() => new ActivitySource(ActivitySourceName));
private static readonly Lazy<ActivitySource> s_activitySource = new Lazy<ActivitySource>(() => new ActivitySource(ActivitySourceName, "1.0.0"));

/// <summary>
/// The ISession which is being traced.
Expand Down Expand Up @@ -1806,8 +1805,12 @@ public uint NewRequestHandle()
/// </summary>
protected virtual void Dispose(bool disposing)
{
m_session.Dispose();
m_session = null;
if (disposing)
{
// note: do not null the session here,
// properties may still be accessed after dispose.
Utils.SilentDispose(m_session);
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -1910,4 +1913,3 @@ public Task<(bool, IList<ServiceResult>)> ResendDataAsync(IEnumerable<Subscripti
#endregion
}
}
#endif
2 changes: 0 additions & 2 deletions Libraries/Opc.Ua.Client/TraceableSessionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/

#if NET6_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -213,4 +212,3 @@ public override Task<ISession> RecreateAsync(ISession sessionTemplate, ITranspor
#endregion
}
}
#endif
2 changes: 1 addition & 1 deletion Stack/Opc.Ua.Core/Opc.Ua.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<Choose>
<!-- Note: Due to incompatibilities of Microsoft.Extensions Nuget packages between versions 3.x and 7.0,
use latest versions only on .NET 5/6, otherwise 3.1.x -->
<When Condition="'$(TargetFramework)' == 'net5.0' OR '$(TargetFramework)' == 'net6.0'">
<When Condition="'$(TargetFramework)' == 'net5.0' OR '$(TargetFramework)' == 'net6.0' OR '$(TargetFramework)' == 'net7.0'">
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
</ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Stack/Opc.Ua.Core/Types/BuiltIn/DiagnosticInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public sealed class DiagnosticInfo : ICloneable, IFormattable
/// <summary>
/// Limits the recursion depth for the InnerDiagnosticInfo field.
/// </summary>
public const int MaxInnerDepth = 5;
public static readonly int MaxInnerDepth = 5;

#region Constructors
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ public async Task OneTimeSetUpAsync(TextWriter writer = null)
m_server = await m_serverFixture.StartAsync(writer ?? TestContext.Out, m_pkiRoot).ConfigureAwait(false);

m_clientFixture = new ClientFixture();
m_clientFixture.UseTracing = true;
m_clientFixture.StartActivityListener();

await m_clientFixture.LoadClientConfiguration(m_pkiRoot).ConfigureAwait(false);
m_clientFixture.Config.TransportQuotas.MaxMessageSize = 4 * 1024 * 1024;
m_url = new Uri(m_uriScheme + "://localhost:" + m_serverFixture.Port.ToString());
Expand All @@ -131,6 +134,7 @@ public async Task OneTimeTearDownAsync()
m_session = null;
}
await m_serverFixture.StopAsync().ConfigureAwait(false);
Utils.SilentDispose(m_clientFixture);
}

/// <summary>
Expand Down
91 changes: 40 additions & 51 deletions Tests/Opc.Ua.Client.Tests/ClientFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@
* ======================================================================*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Opc.Ua.Configuration;
Expand All @@ -43,7 +40,7 @@ namespace Opc.Ua.Client.Tests
/// <summary>
/// Client fixture for tests.
/// </summary>
public class ClientFixture
public class ClientFixture : IDisposable
{
private const uint kDefaultOperationLimits = 5000;
private NUnitTestLogger<ClientFixture> m_traceLogger;
Expand All @@ -55,26 +52,21 @@ public class ClientFixture
public uint SessionTimeout { get; set; } = 10000;
public int OperationTimeout { get; set; } = 10000;
public int TraceMasks { get; set; } = Utils.TraceMasks.Error | Utils.TraceMasks.StackTrace | Utils.TraceMasks.Security | Utils.TraceMasks.Information;

#if NET6_0_OR_GREATER
public bool UseTracing { get; set; } = true;

public bool UseTracing { get; set; } = false;
public ISessionFactory SessionFactory => UseTracing ? TraceableSessionFactory.Instance : DefaultSessionFactory.Instance;

private ActivityListener activityListener;
#else
public ISessionFactory SessionFactory {get;} = DefaultSessionFactory.Instance;
#endif
public ActivityListener ActivityListener { get; private set; }

#region Public Methods
public void Dispose()
{
StopActivityListener();
}

/// <summary>
/// Load the default client configuration.
/// </summary>
public async Task LoadClientConfiguration(string pkiRoot = null, string clientName = "TestClient")
{
#if NET6_0_OR_GREATER
ConfigureActivityListener();
#endif
ApplicationInstance application = new ApplicationInstance {
ApplicationName = clientName
};
Expand Down Expand Up @@ -346,52 +338,49 @@ public void SetTraceOutput(TextWriter writer)
}
}

#if NET6_0_OR_GREATER
public void Cleanup()
{
activityListener = null;
}
#endif
#endregion

#region Private Methods
private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
{
if (ServiceResult.IsBad(e.Status))
{
session?.Dispose();
}
}

#if NET6_0_OR_GREATER
/// <summary>
/// Configures Activity Listener.
/// Configures Activity Listener and registers with Activity Source.
/// </summary>
private void ConfigureActivityListener(bool shouldListenToAllSources = true, bool shouldWriteStartAndStop = true)
public void StartActivityListener(bool shouldListenToAllSources = false, bool shouldWriteStartAndStop = true)
{
// Create an instance of ActivityListener and configure its properties
activityListener = new ActivityListener()
{
ActivityListener = new ActivityListener() {

// Set ShouldListenTo property to true for all activity sources
ShouldListenTo = (source) => true,
ShouldListenTo = (source) => shouldListenToAllSources || source.Name.Equals(TraceableSession.ActivitySourceName),

// Sample all data and recorded activities
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllDataAndRecorded,

// Write "Started" message when an activity starts
ActivityStarted = shouldWriteStartAndStop
? activity => Console.WriteLine("Started: {0,-15} {1,-60}", activity.OperationName, activity.Id)
: (Action<Activity>)(_ => { }),

// Write "Stopped" message along with OperationName, Id, and Duration when an activity stops
ActivityStopped = shouldWriteStartAndStop
? activity => Console.WriteLine(activity.OperationName + " : " + activity.Id + ", Duration : " + activity.Duration)
: (Action<Activity>)(_ => { }),
};

ActivitySource.AddActivityListener(activityListener);
if (shouldWriteStartAndStop)
{
ActivityListener.ActivityStarted = activity => Utils.LogInfo("Started: {0,-15} {1,-60}", activity.OperationName, activity.Id);
ActivityListener.ActivityStopped = activity => Utils.LogInfo("Stopped: {0,-15} {1,-60} Duration: {2}", activity.OperationName, activity.Id, activity.Duration);
}

ActivitySource.AddActivityListener(ActivityListener);
}

/// <summary>
/// Disposes Activity Listener and unregisters from Activity Source.
/// </summary>
public void StopActivityListener()
{
ActivityListener?.Dispose();
ActivityListener = null;
}
#endregion

#region Private Methods
private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
{
if (ServiceResult.IsBad(e.Status))
{
session?.Dispose();
}
}
#endif
#endregion
#endregion
}
}
4 changes: 4 additions & 0 deletions Tests/Opc.Ua.Client.Tests/ClientTestFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ public async Task OneTimeSetUpAsync(TextWriter writer = null, bool securityNone
}

ClientFixture = new ClientFixture();
ClientFixture.UseTracing = true;
ClientFixture.StartActivityListener();

await ClientFixture.LoadClientConfiguration(PkiRoot).ConfigureAwait(false);
ClientFixture.Config.TransportQuotas.MaxMessageSize = TransportQuotaMaxMessageSize;
ClientFixture.Config.TransportQuotas.MaxByteStringLength =
Expand Down Expand Up @@ -202,6 +205,7 @@ public async Task OneTimeTearDownAsync()
await ServerFixture.StopAsync().ConfigureAwait(false);
await Task.Delay(100).ConfigureAwait(false);
}
Utils.SilentDispose(ClientFixture);
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions Tests/Opc.Ua.Client.Tests/ReverseConnectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
* ======================================================================*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -80,6 +78,9 @@ public async Task OneTimeSetUpAsync()

// create client
ClientFixture = new ClientFixture();
ClientFixture.UseTracing = true;
ClientFixture.StartActivityListener();

await ClientFixture.LoadClientConfiguration(PkiRoot).ConfigureAwait(false);
await ClientFixture.StartReverseConnectHost().ConfigureAwait(false);
m_endpointUrl = new Uri(Utils.ReplaceLocalhost("opc.tcp://localhost:" + ServerFixture.Port.ToString()));
Expand All @@ -93,6 +94,7 @@ public async Task OneTimeSetUpAsync()
[OneTimeTearDown]
public new Task OneTimeTearDownAsync()
{
Utils.SilentDispose(ClientFixture);
return base.OneTimeTearDownAsync();
}

Expand Down

0 comments on commit 1e535dd

Please sign in to comment.