diff --git a/doc/EnvironmentVariables.md b/doc/EnvironmentVariables.md
index d917dc89674..7fbc9bca78a 100644
--- a/doc/EnvironmentVariables.md
+++ b/doc/EnvironmentVariables.md
@@ -16,6 +16,7 @@ You can set Edge Agent and Edge Hub environment variables in the Azure portal. I
| DisableDeviceAnalyticsMetadata | Whether to disable sending [basic metadata](https://github.com/Azure/azure-iotedge/blob/main/README.md#telemetry) about the device to Microsoft. | bool | false |
| EnableK8sServiceCallTracing | Whether to enable logging for K8s requests that the Agent makes | bool | false |
| EnableOrphanedIdentityCleanup | Whether to enable removal of any orphaned identities
NOTE: There is an issue with the `managedBy` tag for module identities that **WILL** cause this logic to remove **Host-Level** modules (i.e. managed by something other than `IotEdge`) | bool | false |
+| EnableSdkDebugLogs | If set, emits SDK client events to logs. Note that this method will substantially slow down execution. | bool | false |
| Https_Proxy | Address of the proxy to use for outbound HTTPS requests | string | |
| IntensiveCareTimeInMinutes | Time span for a module to be running before considered completely healthy (restart time / count cleared) | int32 | |
| K8sNamespace | K8s namespace to use for deploying modules | string | |
@@ -66,6 +67,7 @@ You can set Edge Agent and Edge Hub environment variables in the Azure portal. I
| ConnectivityCheckFrequencySecs | Interval at which EdgeHub will ping upstream to ensure connectivity is still present | int32 | 300 |
| DeviceScopeCacheRefreshRateSecs | Interval at which leaf and module identities are refreshed from upstream | int32 | 3600 |
| EnableRoutingLogging | Whether message routing logs should be enabled | bool | false |
+| EnableSdkDebugLogs | If set, emits SDK client events to logs. Note that this method will substantially slow down execution. | bool | false |
| EncryptTwinStore | Whether to encrypt the twin data before persisting to disk | bool | true |
| Https_Proxy | Address of the proxy to use for outbound HTTPS requests | string | |
| HttpSettings__Enabled | Whether the HTTP server should be enabled | bool | true |
diff --git a/doc/devguide.md b/doc/devguide.md
index e06c877d00d..b9af6c50612 100644
--- a/doc/devguide.md
+++ b/doc/devguide.md
@@ -11,7 +11,7 @@ Make sure the following dependencies are installed in your environment before yo
| Dependency | Notes |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| .NET 6.0 | Installation instructions [here](https://www.microsoft.com/net/core). |
+| .NET 8.0 | Installation instructions [here](https://www.microsoft.com/net/core). |
| Java | Not needed if building in VS IDE (Windows). Otherwise, a JRE is required to compile the Antlr4 grammar files into C# classes, and `java` must be on your path. |
## Build
diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/ConsoleEventListener.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/ConsoleEventListener.cs
new file mode 100644
index 00000000000..e3559e107d3
--- /dev/null
+++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/ConsoleEventListener.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft. All rights reserved.
+//----------------------
+//
+// Used to trick stylecop into not processing this file
+//
+//----------------------
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.Logging;
+using System;
+using System.Diagnostics.Tracing;
+using System.Globalization;
+using System.Linq;
+
+namespace Microsoft.Azure.Devices.Logging
+{
+ ///
+ /// Prints SDK events to Console output - the log level is set to TRACE
+ /// NOTE: keep in sync with edge-hub/src/Microsoft.Azure.Devices.Edge.Hub.Core/ConsoleEventListener.cs
+ ///
+ public sealed class ConsoleEventListener : EventListener
+ {
+ private readonly string[] _eventFilters;
+ private readonly ILogger _logger;
+ private readonly object _lock = new object();
+
+ public ConsoleEventListener(string filter, ILogger logger)
+ {
+ _eventFilters = new string[1];
+ _eventFilters[0] = filter ?? throw new ArgumentNullException(nameof(filter));
+ _logger = logger;
+
+ InitializeEventSources();
+ }
+
+ public ConsoleEventListener(string[] filters, ILogger logger)
+ {
+ _eventFilters = filters ?? throw new ArgumentNullException(nameof(filters));
+ if (_eventFilters.Length == 0) throw new ArgumentException("Filters cannot be empty", nameof(filters));
+
+ foreach (string filter in _eventFilters)
+ {
+ if (string.IsNullOrWhiteSpace(filter))
+ {
+ throw new ArgumentNullException(nameof(filters));
+ }
+ }
+ _logger = logger;
+
+ InitializeEventSources();
+ }
+
+ private void InitializeEventSources()
+ {
+ foreach (EventSource source in EventSource.GetSources())
+ {
+ EnableEvents(source, EventLevel.LogAlways);
+ }
+ }
+
+ protected override void OnEventSourceCreated(EventSource eventSource)
+ {
+ base.OnEventSourceCreated(eventSource);
+ EnableEvents(
+ eventSource,
+ EventLevel.LogAlways
+#if !NET451
+ , EventKeywords.All
+#endif
+ );
+ }
+
+ protected override void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ if (_eventFilters == null) return;
+
+ lock (_lock)
+ {
+ if (_eventFilters.Any(ef => eventData.EventSource.Name.StartsWith(ef, StringComparison.Ordinal)))
+ {
+ string eventIdent;
+#if NET451
+ // net451 doesn't have EventName, so we'll settle for EventId
+ eventIdent = eventData.EventId.ToString(CultureInfo.InvariantCulture);
+#else
+ eventIdent = eventData.EventName;
+#endif
+ string text = $"{DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff", CultureInfo.InvariantCulture)} [SDK] [{eventData.EventSource.Name}-{eventIdent}]{(eventData.Payload != null ? $" ({string.Join(", ", eventData.Payload)})." : "")}";
+ _logger.LogInformation(text);
+ }
+ }
+ }
+ }
+}
+
diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs
index 415b415f863..658f82212f6 100644
--- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs
+++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Service/Program.cs
@@ -4,6 +4,7 @@ namespace Microsoft.Azure.Devices.Edge.Agent.Service
using System;
using System.Collections;
using System.Collections.Generic;
+ using System.Diagnostics.Tracing;
using System.IO;
using System.Linq;
using System.Net;
@@ -21,6 +22,7 @@ namespace Microsoft.Azure.Devices.Edge.Agent.Service
using Microsoft.Azure.Devices.Edge.Storage;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Azure.Devices.Edge.Util.Metrics;
+ using Microsoft.Azure.Devices.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Constants = Microsoft.Azure.Devices.Edge.Agent.Core.Constants;
@@ -51,7 +53,18 @@ public static int Main()
// Bring up the logger before anything else so we can log errors ASAP
logger = SetupLogger(configuration);
- return MainAsync(configuration, logger).Result;
+ if (configuration.GetValue("EnableSdkDebugLogs", false))
+ {
+ // Enable SDK debug logs, see ConsoleEventListener for details.
+ string[] eventFilter = new string[] { "DotNetty-Default", "Microsoft-Azure-Devices", "Azure-Core", "Azure-Identity" };
+ using var sdk = new ConsoleEventListener(eventFilter, logger);
+
+ return MainAsync(configuration, logger).Result;
+ }
+ else
+ {
+ return MainAsync(configuration, logger).Result;
+ }
}
catch (Exception ex)
{
diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/ConsoleEventListener.cs b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/ConsoleEventListener.cs
new file mode 100644
index 00000000000..d646f338f3e
--- /dev/null
+++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Core/ConsoleEventListener.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Microsoft. All rights reserved.
+//----------------------
+//
+// Used to trick stylecop into not processing this file
+//
+//----------------------
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.Logging;
+using System;
+using System.Diagnostics.Tracing;
+using System.Globalization;
+using System.Linq;
+
+namespace Microsoft.Azure.Devices.Logging
+{
+ ///
+ /// Prints SDK events to Console output - the log level is set to TRACE
+ /// NOTE: Keep in sync with edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/ConsoleEventListener.cs
+ ///
+ public sealed class ConsoleEventListener : EventListener
+ {
+ private readonly string[] _eventFilters;
+ private readonly ILogger _logger;
+ private readonly object _lock = new object();
+
+ public ConsoleEventListener(string filter, ILogger logger)
+ {
+ _eventFilters = new string[1];
+ _eventFilters[0] = filter ?? throw new ArgumentNullException(nameof(filter));
+ _logger = logger;
+
+ InitializeEventSources();
+ }
+
+ public ConsoleEventListener(string[] filters, ILogger logger)
+ {
+ _eventFilters = filters ?? throw new ArgumentNullException(nameof(filters));
+ if (_eventFilters.Length == 0) throw new ArgumentException("Filters cannot be empty", nameof(filters));
+
+ foreach (string filter in _eventFilters)
+ {
+ if (string.IsNullOrWhiteSpace(filter))
+ {
+ throw new ArgumentNullException(nameof(filters));
+ }
+ }
+ _logger = logger;
+
+ InitializeEventSources();
+ }
+
+ private void InitializeEventSources()
+ {
+ foreach (EventSource source in EventSource.GetSources())
+ {
+ EnableEvents(source, EventLevel.LogAlways);
+ }
+ }
+
+ protected override void OnEventSourceCreated(EventSource eventSource)
+ {
+ base.OnEventSourceCreated(eventSource);
+ EnableEvents(
+ eventSource,
+ EventLevel.LogAlways
+#if !NET451
+ , EventKeywords.All
+#endif
+ );
+ }
+
+ protected override void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ if (_eventFilters == null) return;
+
+ lock (_lock)
+ {
+ if (_eventFilters.Any(ef => eventData.EventSource.Name.StartsWith(ef, StringComparison.Ordinal)))
+ {
+ string eventIdent;
+#if NET451
+ // net451 doesn't have EventName, so we'll settle for EventId
+ eventIdent = eventData.EventId.ToString(CultureInfo.InvariantCulture);
+#else
+ eventIdent = eventData.EventName;
+#endif
+ string text = $"{DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff", CultureInfo.InvariantCulture)} [SDK] [{eventData.EventSource.Name}-{eventIdent}]{(eventData.Payload != null ? $" ({string.Join(", ", eventData.Payload)})." : "")}";
+ _logger.LogInformation(text);
+ }
+ }
+ }
+ }
+}
+
diff --git a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
index c925050f10c..76a31c9a8a4 100644
--- a/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
+++ b/edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Service/Program.cs
@@ -3,6 +3,7 @@ namespace Microsoft.Azure.Devices.Edge.Hub.Service
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Tracing;
using System.Linq;
using System.Security.Authentication;
using System.Threading;
@@ -20,6 +21,7 @@ namespace Microsoft.Azure.Devices.Edge.Hub.Service
using Microsoft.Azure.Devices.Edge.Storage;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Azure.Devices.Edge.Util.Metrics;
+ using Microsoft.Azure.Devices.Logging;
using Microsoft.Azure.Devices.Routing.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@@ -52,7 +54,18 @@ public static int Main()
logger = Logger.Factory.CreateLogger("EdgeHub");
- return MainAsync(configuration, logger).Result;
+ if (configuration.GetValue("EnableSdkDebugLogs", false))
+ {
+ // Enable SDK debug logs, see ConsoleEventListener for details
+ string[] eventFilter = new string[] { "DotNetty-Default", "Microsoft-Azure-Devices", "Azure-Core", "Azure-Identity" };
+ using var sdk = new ConsoleEventListener(eventFilter, logger);
+
+ return MainAsync(configuration, logger).Result;
+ }
+ else
+ {
+ return MainAsync(configuration, logger).Result;
+ }
}
catch (Exception ex)
{