Webinsights - Sending Diagnostics Data from Microservice to Diagnostics Backend (#236)
* Adding customer insights for different simulation events (start, stop, error)
This commit is contained in:
Родитель
a290ef3027
Коммит
27ddbbeb67
|
@ -19,16 +19,19 @@ namespace Services.Test
|
|||
|
||||
private readonly Mock<IStorageAdapterClient> storage;
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
private readonly CustomDeviceModels target;
|
||||
|
||||
public CustomDeviceModelsTest()
|
||||
{
|
||||
this.storage = new Mock<IStorageAdapterClient>();
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
|
||||
this.target = new CustomDeviceModels(
|
||||
this.storage.Object,
|
||||
this.logger.Object);
|
||||
this.logger.Object,
|
||||
this.diagnosticsLogger.Object);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Services.Test
|
|||
private readonly Mock<IRegistryManager> registry;
|
||||
private readonly Mock<IDeviceClientWrapper> deviceClient;
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
|
||||
public DevicesTest(ITestOutputHelper log)
|
||||
{
|
||||
|
@ -36,13 +37,15 @@ namespace Services.Test
|
|||
this.registry = new Mock<IRegistryManager>();
|
||||
this.deviceClient = new Mock<IDeviceClientWrapper>();
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
|
||||
this.target = new Devices(
|
||||
this.config.Object,
|
||||
this.connectionStringManager.Object,
|
||||
this.registry.Object,
|
||||
this.deviceClient.Object,
|
||||
this.logger.Object);
|
||||
this.logger.Object,
|
||||
this.diagnosticsLogger.Object);
|
||||
|
||||
this.connectionStringManager
|
||||
.Setup(x => x.GetIotHubConnectionString())
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Http;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
|
||||
using Moq;
|
||||
using Services.Test.helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Services.Test.Diagnostics
|
||||
{
|
||||
public class DiagnosticsLoggerTest
|
||||
{
|
||||
private const string DIAGNOSTICS_SERVICE_URL = @"http://diagnostics";
|
||||
|
||||
private readonly Mock<IHttpClient> mockHttpClient;
|
||||
private DiagnosticsLogger target;
|
||||
|
||||
public DiagnosticsLoggerTest()
|
||||
{
|
||||
this.mockHttpClient = new Mock<IHttpClient>();
|
||||
|
||||
this.target = new DiagnosticsLogger(
|
||||
this.mockHttpClient.Object,
|
||||
new ServicesConfig
|
||||
{
|
||||
DiagnosticsEndpointUrl = DIAGNOSTICS_SERVICE_URL
|
||||
});
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
public void ShouldLogServiceStart()
|
||||
{
|
||||
// Act
|
||||
target.LogServiceStartAsync("test").Wait(Constants.TEST_TIMEOUT);
|
||||
|
||||
// Assert - Checking if the httpcall is made just once
|
||||
this.mockHttpClient.Verify(x => x.PostAsync(It.IsAny<HttpRequest>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
public void ShouldLogServiceHeartbeat()
|
||||
{
|
||||
// Act
|
||||
target.LogServiceHeartbeatAsync().Wait(Constants.TEST_TIMEOUT);
|
||||
|
||||
// Assert - Checking if the httpcall is made just once
|
||||
this.mockHttpClient.Verify(x => x.PostAsync(It.IsAny<HttpRequest>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
public void ShouldLogServiceError()
|
||||
{
|
||||
// Act
|
||||
// Logging service error sending just a message string
|
||||
target.LogServiceErrorAsync("testmessage").Wait(Constants.TEST_TIMEOUT);
|
||||
// Logging service error along with an exception
|
||||
target.LogServiceErrorAsync("testmessage", new System.Exception().Message).Wait(Constants.TEST_TIMEOUT);
|
||||
// Logging service error along with an object
|
||||
target.LogServiceErrorAsync("testmessage", new { Test = "test" }).Wait(Constants.TEST_TIMEOUT);
|
||||
|
||||
// Assert - Checking if the httpcall is made exactly 3 times one for each type of service error
|
||||
this.mockHttpClient.Verify(x => x.PostAsync(It.IsAny<HttpRequest>()), Times.Exactly(3));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,14 +13,16 @@ namespace Services.Test
|
|||
public class IotHubConnectionStringManagerTest
|
||||
{
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
private readonly IServicesConfig config;
|
||||
private readonly IotHubConnectionStringManager target;
|
||||
|
||||
public IotHubConnectionStringManagerTest()
|
||||
{
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
this.config = new ServicesConfig();
|
||||
this.target = new IotHubConnectionStringManager(this.config, this.logger.Object);
|
||||
this.target = new IotHubConnectionStringManager(this.config, this.diagnosticsLogger.Object, this.logger.Object);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
|
|
|
@ -29,6 +29,7 @@ namespace Services.Test
|
|||
private readonly Mock<IStorageAdapterClient> storage;
|
||||
private readonly Mock<IDevices> devices;
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
private readonly Mock<IIotHubConnectionStringManager> connStringManager;
|
||||
private readonly Simulations target;
|
||||
private readonly List<DeviceModel> models;
|
||||
|
@ -40,6 +41,7 @@ namespace Services.Test
|
|||
this.deviceModels = new Mock<IDeviceModels>();
|
||||
this.storage = new Mock<IStorageAdapterClient>();
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
this.devices = new Mock<IDevices>();
|
||||
this.connStringManager = new Mock<IIotHubConnectionStringManager>();
|
||||
this.models = new List<DeviceModel>
|
||||
|
@ -50,7 +52,7 @@ namespace Services.Test
|
|||
new DeviceModel { Id = "AA" }
|
||||
};
|
||||
|
||||
this.target = new Simulations(this.deviceModels.Object, this.storage.Object, this.connStringManager.Object, this.devices.Object, this.logger.Object);
|
||||
this.target = new Simulations(this.deviceModels.Object, this.storage.Object, this.connStringManager.Object, this.devices.Object, this.logger.Object, this.diagnosticsLogger.Object);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Services.Test
|
|||
public class StockDeviceModelTest
|
||||
{
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
private readonly Mock<IServicesConfig> config;
|
||||
private readonly StockDeviceModels target;
|
||||
|
||||
|
@ -24,10 +25,12 @@ namespace Services.Test
|
|||
{
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.config = new Mock<IServicesConfig>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
|
||||
this.target = new StockDeviceModels(
|
||||
this.config.Object,
|
||||
this.logger.Object);
|
||||
this.logger.Object,
|
||||
this.diagnosticsLogger.Object);
|
||||
}
|
||||
|
||||
[Fact, Trait(Constants.TYPE, Constants.UNIT_TEST)]
|
||||
|
|
|
@ -45,13 +45,16 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
|
||||
private readonly IStorageAdapterClient storage;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
|
||||
public CustomDeviceModels(
|
||||
IStorageAdapterClient storage,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.storage = storage;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -67,8 +70,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to load device models from storage", e);
|
||||
throw new ExternalDependencyException("Unable to load device models from storage", e);
|
||||
var msg = "Unable to load device models from storage";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new ExternalDependencyException(msg, e);
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -86,8 +91,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to parse device models loaded from storage", e);
|
||||
throw new ExternalDependencyException("Unable to parse device models loaded from storage", e);
|
||||
var msg = "Unable to parse device models loaded from storage";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new ExternalDependencyException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,9 +120,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to load device model from storage",
|
||||
var msg = "Unable to load device model from storage";
|
||||
this.log.Error(msg,
|
||||
() => new { id, e.Message, Exception = e });
|
||||
throw new ExternalDependencyException("Unable to load device model from storage", e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg,
|
||||
new { id, e.Message, Exception = e.Message });
|
||||
throw new ExternalDependencyException(msg, e);
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -161,10 +171,13 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Failed to insert new device model into storage",
|
||||
var msg = "Failed to insert new device model into storage";
|
||||
this.log.Error(msg,
|
||||
() => new { deviceModel, generateId, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg,
|
||||
new { deviceModel, generateId, e.Message });
|
||||
throw new ExternalDependencyException(
|
||||
"Failed to insert new device model into storage", e);
|
||||
msg, e);
|
||||
}
|
||||
|
||||
return deviceModel;
|
||||
|
@ -201,8 +214,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
else
|
||||
{
|
||||
this.log.Error("Invalid ETag.", () => new { CurrentETag = item.ETag, ETagProvided = eTag });
|
||||
throw new ConflictingResourceException("Invalid ETag. Device Model ETag is:'" + item.ETag + "'.");
|
||||
var msg = "Invalid ETag.";
|
||||
this.log.Error(msg, () => new { CurrentETag = item.ETag, ETagProvided = eTag });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { CurrentETag = item.ETag, ETagProvided = eTag });
|
||||
throw new ConflictingResourceException(msg + "Device Model ETag is:'" + item.ETag + "'.");
|
||||
}
|
||||
}
|
||||
catch (ConflictingResourceException)
|
||||
|
@ -218,7 +233,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
this.log.Error("Something went wrong while upserting the device model.", () => new { deviceModel });
|
||||
var msg = "Something went wrong while upserting the device model.";
|
||||
this.log.Error(msg, () => new { deviceModel });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { deviceModel });
|
||||
throw new ExternalDependencyException("Failed to upsert: " + exception.Message, exception);
|
||||
}
|
||||
|
||||
|
@ -236,7 +253,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Something went wrong while deleting the device model.", () => new { id, e });
|
||||
var msg = "Something went wrong while deleting the device model.";
|
||||
this.log.Error(msg, () => new { id, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { id, e.Message });
|
||||
throw new ExternalDependencyException("Failed to delete the device model", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
{
|
||||
private readonly IDeviceClientWrapper client;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
private readonly IScriptInterpreter scriptInterpreter;
|
||||
private IDictionary<string, Script> cloudToDeviceMethods;
|
||||
private ISmartDictionary deviceState;
|
||||
|
@ -36,10 +37,12 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
public DeviceMethods(
|
||||
IDeviceClientWrapper client,
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger,
|
||||
IScriptInterpreter scriptInterpreter)
|
||||
{
|
||||
this.client = client;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
this.scriptInterpreter = scriptInterpreter;
|
||||
this.deviceId = string.Empty;
|
||||
this.isRegistered = false;
|
||||
|
@ -106,7 +109,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Failed executing method.", () => new { methodRequest, e });
|
||||
var msg = "Failed executing method.";
|
||||
this.log.Error(msg, () => new { methodRequest, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { methodRequest, e.Message });
|
||||
return Task.FromResult(new MethodResponse((int) HttpStatusCode.InternalServerError));
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +156,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Error while executing method for device",
|
||||
var msg = "Error while executing method for device";
|
||||
this.log.Error(msg,
|
||||
() => new
|
||||
{
|
||||
this.deviceId,
|
||||
|
@ -159,6 +165,14 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
methodRequest.DataAsJson,
|
||||
e
|
||||
});
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg,
|
||||
new
|
||||
{
|
||||
this.deviceId,
|
||||
methodName = methodRequest.Name,
|
||||
methodRequest.DataAsJson,
|
||||
e.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
|
||||
private readonly IIotHubConnectionStringManager connectionStringManager;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
private readonly IServicesConfig config;
|
||||
private readonly IDeviceClientWrapper deviceClient;
|
||||
|
||||
|
@ -90,7 +91,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
IIotHubConnectionStringManager connStringManager,
|
||||
IRegistryManager registryManager,
|
||||
IDeviceClientWrapper deviceClient,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.config = config;
|
||||
this.connectionStringManager = connStringManager;
|
||||
|
@ -98,6 +100,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
this.registry = registryManager;
|
||||
this.deviceClient = deviceClient;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
this.twinReadsWritesEnabled = config.TwinReadWriteEnabled;
|
||||
this.setupDone = false;
|
||||
}
|
||||
|
@ -129,7 +132,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("IoT Hub connection setup failed", e);
|
||||
var msg = "IoT Hub connection setup failed";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +145,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
this.CheckSetup();
|
||||
|
||||
var sdkClient = this.GetDeviceSdkClient(device, protocol);
|
||||
var methods = new DeviceMethods(sdkClient, this.log, scriptInterpreter);
|
||||
var methods = new DeviceMethods(sdkClient, this.log, this.diagnosticsLogger, scriptInterpreter);
|
||||
|
||||
return new DeviceClient(
|
||||
device.Id,
|
||||
|
@ -192,8 +197,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
catch (Exception e)
|
||||
{
|
||||
var timeSpentMsecs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start;
|
||||
this.log.Error("Unable to fetch the IoT device", () => new { timeSpentMsecs, deviceId, e });
|
||||
throw new ExternalDependencyException("Unable to fetch the IoT device");
|
||||
var msg = "Unable to fetch the IoT device";
|
||||
this.log.Error(msg, () => new { timeSpentMsecs, deviceId, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { timeSpentMsecs, deviceId, e.Message });
|
||||
throw new ExternalDependencyException(msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -217,8 +224,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
catch (Exception e)
|
||||
{
|
||||
var timeSpentMsecs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start;
|
||||
this.log.Error("Unable to create the device", () => new { timeSpentMsecs, deviceId, e });
|
||||
throw new ExternalDependencyException("Unable to create the device", e);
|
||||
var msg = "Unable to create the device";
|
||||
this.log.Error(msg, () => new { timeSpentMsecs, deviceId, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { timeSpentMsecs, deviceId, e.Message });
|
||||
throw new ExternalDependencyException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,17 +342,23 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (TooManyDevicesException error)
|
||||
{
|
||||
this.log.Error("Failed to delete devices, the batch is too big", error);
|
||||
var msg = "Failed to delete devices, the batch is too big";
|
||||
this.log.Error(msg, error);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, error.Message);
|
||||
throw;
|
||||
}
|
||||
catch (IotHubCommunicationException error)
|
||||
{
|
||||
this.log.Error("Failed to delete devices (IotHubCommunicationException)", () => new { error.InnerException, error });
|
||||
var msg = "Failed to delete devices (IotHubCommunicationException)";
|
||||
this.log.Error(msg, () => new { error.InnerException, error });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { error.Message });
|
||||
throw;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
this.log.Error("Failed to delete devices", error);
|
||||
var msg = "Failed to delete devices";
|
||||
this.log.Error(msg, error);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, error.Message); ;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Http;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics
|
||||
{
|
||||
public interface IDiagnosticsLogger
|
||||
{
|
||||
Task LogServiceStartAsync(string message);
|
||||
|
||||
Task LogServiceHeartbeatAsync();
|
||||
|
||||
Task LogServiceErrorAsync(
|
||||
string message,
|
||||
string exceptionMessage = "",
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0);
|
||||
|
||||
Task LogServiceErrorAsync(
|
||||
string message,
|
||||
object data,
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0);
|
||||
}
|
||||
|
||||
public class DiagnosticsLogger : IDiagnosticsLogger
|
||||
{
|
||||
public struct JsonStruct
|
||||
{
|
||||
[JsonProperty(PropertyName = "EventType")]
|
||||
public string EventType;
|
||||
|
||||
[JsonProperty(PropertyName = "EventProperties")]
|
||||
public Dictionary<string, object> EventProperties;
|
||||
|
||||
public JsonStruct(string eventType, Dictionary<string, object> eventProps)
|
||||
{
|
||||
this.EventType = eventType;
|
||||
this.EventProperties = eventProps;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IHttpClient httpClient;
|
||||
private readonly IServicesConfig servicesConfig;
|
||||
private readonly string diagnosticsEndpoint = string.Empty;
|
||||
|
||||
private const string SERVICE_ERROR_EVENT = "ServiceError";
|
||||
private const string SERVICE_START_EVENT = "ServiceStart";
|
||||
private const string SERVICE_HEARTBEAT_EVENT = "ServiceHeartbeat";
|
||||
|
||||
public DiagnosticsLogger(IHttpClient httpClient, IServicesConfig servicesConfig)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.servicesConfig = servicesConfig;
|
||||
this.diagnosticsEndpoint = this.servicesConfig.DiagnosticsEndpointUrl;
|
||||
}
|
||||
|
||||
public async Task LogServiceStartAsync(string message)
|
||||
{
|
||||
JsonStruct jsonStruct = new JsonStruct(SERVICE_START_EVENT + message, null);
|
||||
await this.httpClient.PostAsync(this.PrepareRequest(this.diagnosticsEndpoint, jsonStruct));
|
||||
}
|
||||
|
||||
public async Task LogServiceHeartbeatAsync()
|
||||
{
|
||||
JsonStruct jsonStruct = new JsonStruct(SERVICE_HEARTBEAT_EVENT, null);
|
||||
await this.httpClient.PostAsync(this.PrepareRequest(this.diagnosticsEndpoint, jsonStruct));
|
||||
}
|
||||
|
||||
public async Task LogServiceErrorAsync(
|
||||
string message,
|
||||
string exceptionMessage = "",
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
JsonStruct jsonStruct = this.ConvertServiceErrorToJson(message, exceptionMessage, null, callerName, filePath, lineNumber);
|
||||
await this.httpClient.PostAsync(this.PrepareRequest(this.diagnosticsEndpoint, jsonStruct));
|
||||
}
|
||||
|
||||
public async Task LogServiceErrorAsync(
|
||||
string message,
|
||||
object data,
|
||||
[CallerMemberName] string callerName = "",
|
||||
[CallerFilePath] string filePath = "",
|
||||
[CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
JsonStruct jsonStruct = this.ConvertServiceErrorToJson(message, "", data, callerName, filePath, lineNumber);
|
||||
await this.httpClient.PostAsync(this.PrepareRequest(this.diagnosticsEndpoint, jsonStruct));
|
||||
}
|
||||
|
||||
private JsonStruct ConvertServiceErrorToJson(string message,
|
||||
string exceptionMessage,
|
||||
object data,
|
||||
string callerName = "",
|
||||
string filePath = "",
|
||||
int lineNumber = 0)
|
||||
{
|
||||
var eventProps = new Dictionary<string, object>
|
||||
{
|
||||
{ "message", message + $"(CallerMemberName = '{callerName}', CallerFilePath = '{filePath}', lineNumber = '{lineNumber}')" }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(exceptionMessage))
|
||||
{
|
||||
eventProps.Add("ExceptionMessage", exceptionMessage);
|
||||
}
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
eventProps.Add("data", data);
|
||||
}
|
||||
|
||||
return new JsonStruct(SERVICE_ERROR_EVENT, eventProps);
|
||||
}
|
||||
|
||||
private HttpRequest PrepareRequest(string path, object obj = null)
|
||||
{
|
||||
var request = new HttpRequest();
|
||||
request.SetUriFromString(path);
|
||||
request.SetContent(obj);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,14 +33,17 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
|
||||
private readonly IServicesConfig config;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
|
||||
public IotHubConnectionStringManager(
|
||||
IServicesConfig config,
|
||||
IDiagnosticsLogger diagnosticsLogger,
|
||||
ILogger logger)
|
||||
{
|
||||
this.config = config;
|
||||
this.connStringFilePath = config.IoTHubDataFolder + CONNSTRING_FILE_NAME;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -139,6 +142,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
"The correct format is: HostName=[hubname];SharedAccessKeyName=" +
|
||||
"[iothubowner or service];SharedAccessKey=[null or valid key]";
|
||||
this.log.Error(message);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message);
|
||||
throw new InvalidIotHubConnectionStringFormatException(message);
|
||||
}
|
||||
|
||||
|
@ -177,10 +181,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string message = "Could not connect to IotHub with the connection " +
|
||||
var message = "Could not connect to IotHub with the connection " +
|
||||
"string provided. Check that the key is valid and " +
|
||||
"that the hub exists.";
|
||||
this.log.Error(message, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message, e.Message);
|
||||
throw new IotHubConnectionException(message, e);
|
||||
}
|
||||
}
|
||||
|
@ -195,10 +200,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string message = "Could not read devices with the Iot Hub connection " +
|
||||
var message = "Could not read devices with the Iot Hub connection " +
|
||||
"string provided. Check that the policy for the key allows " +
|
||||
"`Registry Read/Write` and `Service Connect` permissions.";
|
||||
this.log.Error(message, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message, e.Message);
|
||||
throw new IotHubConnectionException(message, e);
|
||||
}
|
||||
}
|
||||
|
@ -217,10 +223,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string message = "Could not create devices with the Iot Hub connection " +
|
||||
var message = "Could not create devices with the Iot Hub connection " +
|
||||
"string provided. Check that the policy for the key allows " +
|
||||
"`Registry Read/Write` and `Service Connect` permissions.";
|
||||
this.log.Error(message, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message, e.Message);
|
||||
throw new IotHubConnectionException(message, e);
|
||||
}
|
||||
|
||||
|
@ -237,9 +244,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string message = "Could not delete test device from IotHub. Attempt " +
|
||||
var message = "Could not delete test device from IotHub. Attempt " +
|
||||
deleteRetryCount + 1 + " of " + MAX_DELETE_RETRY;
|
||||
this.log.Error(message, () => new { testDeviceId, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message, new { testDeviceId, e.Message });
|
||||
throw new IotHubConnectionException(message, e);
|
||||
}
|
||||
|
||||
|
@ -250,8 +258,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
|
||||
if (response != null)
|
||||
{
|
||||
string message = "Could not delete test device from IotHub.";
|
||||
var message = "Could not delete test device from IotHub.";
|
||||
this.log.Error(message, () => new { testDeviceId });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message, new { testDeviceId });
|
||||
throw new IotHubConnectionException(message);
|
||||
}
|
||||
}
|
||||
|
@ -271,9 +280,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
string msg = "Unable to use default IoT Hub. Check that the " +
|
||||
var msg = "Unable to use default IoT Hub. Check that the " +
|
||||
"pre-provisioned hub exists and has the correct permissions.";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new IotHubConnectionException(msg, e);
|
||||
}
|
||||
|
||||
|
@ -284,8 +294,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to delete connection string file.",
|
||||
var msg = "Unable to delete connection string file";
|
||||
this.log.Error(msg,
|
||||
() => new { this.connStringFilePath, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg,
|
||||
new { this.connStringFilePath, e.Message });
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -338,8 +351,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to write connection string to file.",
|
||||
var msg = "Unable to write connection string to file";
|
||||
this.log.Error(msg,
|
||||
() => new { this.connStringFilePath, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg,
|
||||
new { this.connStringFilePath, e.Message });
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -361,8 +377,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.IotHub
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to read connection string from file.",
|
||||
var message = "Unable to read connection string from file";
|
||||
this.log.Error(message,
|
||||
() => new { this.connStringFilePath, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(message,
|
||||
new { this.connStringFilePath, e.Message });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime
|
|||
StorageConfig SimulationsStorage { get; set; }
|
||||
StorageConfig DevicesStorage { get; set; }
|
||||
StorageConfig PartitionsStorage { get; set; }
|
||||
string DiagnosticsEndpointUrl { get; }
|
||||
}
|
||||
|
||||
// TODO: test Windows/Linux folder separator
|
||||
|
@ -65,6 +66,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime
|
|||
|
||||
public int StorageAdapterApiTimeout { get; set; }
|
||||
|
||||
public string DiagnosticsEndpointUrl { get; set; }
|
||||
|
||||
public bool TwinReadWriteEnabled { get; set; }
|
||||
|
||||
public StorageConfig MainStorage { get; set; }
|
||||
|
|
|
@ -69,19 +69,22 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
private readonly IIotHubConnectionStringManager connectionStringManager;
|
||||
private readonly IDevices devices;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
|
||||
public Simulations(
|
||||
IDeviceModels deviceModels,
|
||||
IStorageAdapterClient storage,
|
||||
IIotHubConnectionStringManager connectionStringManager,
|
||||
IDevices devices,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.deviceModels = deviceModels;
|
||||
this.storage = storage;
|
||||
this.connectionStringManager = connectionStringManager;
|
||||
this.devices = devices;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -209,8 +212,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
|
||||
if (simulation.ETag != existingSimulation.ETag)
|
||||
{
|
||||
this.log.Error("Invalid ETag. Running simulation ETag is:'", () => new { simulation });
|
||||
throw new ResourceOutOfDateException("Invalid ETag. Running simulation ETag is:'" + simulation.ETag + "'.");
|
||||
var msg = "Invalid ETag. Running simulation ETag is:'";
|
||||
this.log.Error(msg, () => new { simulation });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { simulation.Id, simulation.Name });
|
||||
throw new ResourceOutOfDateException(msg + simulation.ETag + "'.");
|
||||
}
|
||||
|
||||
simulation.Created = existingSimulation.Created;
|
||||
|
|
|
@ -24,16 +24,19 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
|
||||
private readonly IServicesConfig config;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
|
||||
private List<string> deviceModelFiles;
|
||||
private List<DeviceModel> deviceModels;
|
||||
|
||||
public StockDeviceModels(
|
||||
IServicesConfig config,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.config = config;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
this.deviceModelFiles = null;
|
||||
this.deviceModels = null;
|
||||
}
|
||||
|
@ -52,8 +55,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to load Device Model files", e);
|
||||
|
||||
var msg = "Unable to load Device Model files";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -68,8 +72,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Unable to parse Device Model files", e);
|
||||
throw new InvalidConfigurationException("Unable to parse Device Model files: " + e.Message, e);
|
||||
var msg = "Unable to parse Device Model files";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new InvalidConfigurationException(msg + e.Message, e);
|
||||
}
|
||||
|
||||
return this.deviceModels;
|
||||
|
|
|
@ -33,18 +33,21 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.StorageAdapter
|
|||
|
||||
private readonly IHttpClient httpClient;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
private readonly string serviceUri;
|
||||
private readonly int timeout;
|
||||
|
||||
public StorageAdapterClient(
|
||||
IHttpClient httpClient,
|
||||
IServicesConfig config,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.log = logger;
|
||||
this.serviceUri = config.StorageAdapterApiUrl;
|
||||
this.timeout = config.StorageAdapterApiTimeout;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
}
|
||||
|
||||
public async Task<Tuple<bool, string>> PingAsync()
|
||||
|
@ -68,7 +71,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.StorageAdapter
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Storage adapter check failed", e);
|
||||
var msg= "Storage adapter check failed";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
message = e.Message;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace SimulationAgent.Test
|
|||
private readonly Mock<IRateLimitingConfig> ratingConfig;
|
||||
private readonly Mock<IConcurrencyConfig> concurrencyConfig;
|
||||
private readonly Mock<ILogger> logger;
|
||||
private readonly Mock<IDiagnosticsLogger> diagnosticsLogger;
|
||||
private readonly Mock<IDeviceModels> deviceModels;
|
||||
private readonly Mock<IDeviceModelsGeneration> deviceModelsOverriding;
|
||||
private readonly Mock<IDevices> devices;
|
||||
|
@ -44,6 +45,7 @@ namespace SimulationAgent.Test
|
|||
this.ratingConfig = new Mock<IRateLimitingConfig>();
|
||||
this.concurrencyConfig = new Mock<IConcurrencyConfig>();
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.diagnosticsLogger = new Mock<IDiagnosticsLogger>();
|
||||
this.deviceModels = new Mock<IDeviceModels>();
|
||||
this.deviceModelsOverriding = new Mock<IDeviceModelsGeneration>();
|
||||
this.devices = new Mock<IDevices>();
|
||||
|
@ -61,6 +63,7 @@ namespace SimulationAgent.Test
|
|||
this.rateLimiting.Object,
|
||||
this.concurrencyConfig.Object,
|
||||
this.logger.Object,
|
||||
this.diagnosticsLogger.Object,
|
||||
this.deviceModels.Object,
|
||||
this.deviceModelsOverriding.Object,
|
||||
this.devices.Object,
|
||||
|
|
|
@ -24,18 +24,22 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
public class Agent : ISimulationAgent
|
||||
{
|
||||
private const int CHECK_INTERVAL_MSECS = 10000;
|
||||
private const int DIAGNOSTICS_POLLING_FREQUENCY_DAYS = 1;
|
||||
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger logDiagnostics;
|
||||
private readonly ISimulations simulations;
|
||||
private readonly ISimulationRunner runner;
|
||||
private readonly IRateLimiting rateReporter;
|
||||
private readonly IDeviceModels deviceModels;
|
||||
private Simulation simulation;
|
||||
private readonly IDevices devices;
|
||||
private DateTimeOffset lastPolledTime;
|
||||
private Simulation simulation;
|
||||
private bool running;
|
||||
|
||||
public Agent(
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger,
|
||||
ISimulations simulations,
|
||||
ISimulationRunner runner,
|
||||
IRateLimiting rateReporter,
|
||||
|
@ -43,12 +47,14 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
IDevices devices)
|
||||
{
|
||||
this.log = logger;
|
||||
this.logDiagnostics = diagnosticsLogger;
|
||||
this.simulations = simulations;
|
||||
this.runner = runner;
|
||||
this.rateReporter = rateReporter;
|
||||
this.deviceModels = deviceModels;
|
||||
this.devices = devices;
|
||||
this.running = true;
|
||||
this.lastPolledTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
public async Task RunAsync()
|
||||
|
@ -59,6 +65,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
while (this.running)
|
||||
{
|
||||
var oldSimulation = this.simulation;
|
||||
|
||||
this.SendSolutionHeartbeatAsync();
|
||||
|
||||
try
|
||||
{
|
||||
this.log.Debug("------ Checking for simulation changes ------");
|
||||
|
@ -198,6 +207,19 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
}
|
||||
|
||||
private void SendSolutionHeartbeatAsync()
|
||||
{
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
TimeSpan duration = now - this.lastPolledTime;
|
||||
|
||||
// Send heartbeat every 24 hours
|
||||
if (duration.Days >= DIAGNOSTICS_POLLING_FREQUENCY_DAYS)
|
||||
{
|
||||
this.lastPolledTime = now;
|
||||
this.logDiagnostics.LogServiceHeartbeatAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForNewSimulation(Simulation newSimulation)
|
||||
{
|
||||
if (newSimulation != null && this.simulation == null)
|
||||
|
@ -206,8 +228,10 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
if (this.simulation.ShouldBeRunning())
|
||||
{
|
||||
this.log.Debug("------ Starting new simulation ------", () => new { this.simulation });
|
||||
this.logDiagnostics.LogServiceStartAsync("Starting new simulation");
|
||||
this.runner.Start(this.simulation);
|
||||
this.log.Debug("------ New simulation started ------", () => new { this.simulation });
|
||||
this.logDiagnostics.LogServiceStartAsync("New simulation started");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Concurrency;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Exceptions;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Http;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Models;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent.DeviceConnection;
|
||||
|
@ -41,6 +43,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
// Application logger
|
||||
private readonly ILogger log;
|
||||
|
||||
// Diagnostics logger
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
|
||||
// Settings to optimize scheduling
|
||||
private readonly ConnectionLoopSettings connectionLoopSettings;
|
||||
|
||||
|
@ -109,6 +114,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
IRateLimiting rateLimiting,
|
||||
IConcurrencyConfig concurrencyConfig,
|
||||
ILogger logger,
|
||||
IDiagnosticsLogger diagnosticsLogger,
|
||||
IDeviceModels deviceModels,
|
||||
IDeviceModelsGeneration deviceModelsOverriding,
|
||||
IDevices devices,
|
||||
|
@ -120,6 +126,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
|
||||
this.concurrencyConfig = concurrencyConfig;
|
||||
this.log = logger;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
this.deviceModels = deviceModels;
|
||||
this.deviceModelsOverriding = deviceModelsOverriding;
|
||||
this.devices = devices;
|
||||
|
@ -176,9 +183,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Failed to create devices";
|
||||
this.running = false;
|
||||
this.starting = false;
|
||||
this.log.Error("Failed to create devices", e);
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
this.IncrementSimulationErrorsCount();
|
||||
|
||||
// Return and retry
|
||||
|
@ -205,13 +214,17 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (ResourceNotFoundException)
|
||||
{
|
||||
var msg = "The device model doesn't exist";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("The device model doesn't exist", () => new { model.Id });
|
||||
this.log.Error(msg, () => new { model.Id });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { model.Id });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Unexpected error preparing the device model";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("Unexpected error preparing the device model", () => new { model.Id, e });
|
||||
this.log.Error(msg, () => new { model.Id, e });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { model.Id, e.Message });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,9 +640,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Unable to start the telemetry threads";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("Unable to start the telemetry threads", e);
|
||||
throw new Exception("Unable to start the telemetry threads", e);
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new Exception(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,9 +657,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Unable to start the device connection thread";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("Unable to start the device connection thread", e);
|
||||
throw new Exception("Unable to start the device connection thread", e);
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new Exception(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,9 +674,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Unable to start the device state thread";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("Unable to start the device state thread", e);
|
||||
throw new Exception("Unable to start the device state thread", e);
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new Exception(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,9 +691,11 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.SimulationAgent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = "Unable to start the device properties thread";
|
||||
this.IncrementSimulationErrorsCount();
|
||||
this.log.Error("Unable to start the device properties thread", e);
|
||||
throw new Exception("Unable to start the device properties thread", e);
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
throw new Exception(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ namespace WebService.Test.v1.Controllers
|
|||
this.simulations = new Mock<ISimulations>();
|
||||
this.logger = new Mock<ILogger>();
|
||||
this.servicesConfig = new Mock<IServicesConfig>();
|
||||
this.deploymentConfig = new Mock<IDeploymentConfig>();
|
||||
this.connectionStringManager = new Mock<IIotHubConnectionStringManager>();
|
||||
this.simulationRunner = new Mock<ISimulationRunner>();
|
||||
this.rateReporter = new Mock<IRateLimiting>();
|
||||
|
|
|
@ -50,6 +50,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
private readonly IConfigurationManager<OpenIdConnectConfiguration> openIdCfgMan;
|
||||
private readonly IClientAuthConfig config;
|
||||
private readonly ILogger log;
|
||||
private readonly IDiagnosticsLogger diagnosticsLogger;
|
||||
private TokenValidationParameters tokenValidationParams;
|
||||
private readonly bool authRequired;
|
||||
private bool tokenValidationInitialized;
|
||||
|
@ -59,12 +60,14 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
RequestDelegate requestDelegate, // Required by ASP.NET
|
||||
IConfigurationManager<OpenIdConnectConfiguration> openIdCfgMan,
|
||||
IClientAuthConfig config,
|
||||
ILogger log)
|
||||
ILogger log,
|
||||
IDiagnosticsLogger diagnosticsLogger)
|
||||
{
|
||||
this.requestDelegate = requestDelegate;
|
||||
this.openIdCfgMan = openIdCfgMan;
|
||||
this.config = config;
|
||||
this.log = log;
|
||||
this.diagnosticsLogger = diagnosticsLogger;
|
||||
this.authRequired = config.AuthRequired;
|
||||
this.tokenValidationInitialized = false;
|
||||
|
||||
|
@ -137,7 +140,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
}
|
||||
else
|
||||
{
|
||||
this.log.Error("Authorization header not found");
|
||||
var msg = "Authorization header not found";
|
||||
this.log.Error(msg);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg);
|
||||
}
|
||||
|
||||
if (header != null && header.StartsWith(AUTH_HEADER_PREFIX))
|
||||
|
@ -146,7 +151,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
}
|
||||
else
|
||||
{
|
||||
this.log.Error("Authorization header prefix not found");
|
||||
var msg = "Authorization header prefix not found";
|
||||
this.log.Error(msg);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg);
|
||||
}
|
||||
|
||||
if (this.ValidateToken(token, context) || !this.authRequired)
|
||||
|
@ -183,11 +190,15 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
return true;
|
||||
}
|
||||
|
||||
this.log.Error("JWT token signature algorithm is not allowed.", () => new { jwtToken.SignatureAlgorithm });
|
||||
var msg = "JWT token signature algorithm is not allowed.";
|
||||
this.log.Error(msg, () => new { jwtToken.SignatureAlgorithm });
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, new { jwtToken.SignatureAlgorithm });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Failed to validate JWT token", e);
|
||||
var msg = "Failed to validate JWT token";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -226,7 +237,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.log.Error("Failed to setup OpenId Connect", e);
|
||||
var msg = "Failed to setup OpenId Connect";
|
||||
this.log.Error(msg, e);
|
||||
this.diagnosticsLogger.LogServiceErrorAsync(msg, e.Message);
|
||||
}
|
||||
|
||||
return this.tokenValidationInitialized;
|
||||
|
|
|
@ -115,6 +115,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService
|
|||
builder.RegisterType<DeviceModels>().As<IDeviceModels>().SingleInstance();
|
||||
builder.RegisterType<Services.Devices>().As<IDevices>().SingleInstance();
|
||||
builder.RegisterType<RateLimiting>().As<IRateLimiting>().SingleInstance();
|
||||
builder.RegisterType<DiagnosticsLogger>().As<IDiagnosticsLogger>().SingleInstance();
|
||||
|
||||
// The simulation runner contains the service counters, which are read and
|
||||
// written by multiple parts of the application, so we need to make sure
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Concurrency;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Diagnostics;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Http;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Runtime;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.Services.Storage;
|
||||
using Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Auth;
|
||||
|
@ -91,6 +92,7 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Runtime
|
|||
private const string LOGGING_WHITELIST_SOURCES_KEY = LOGGING_KEY + "WhiteListSources";
|
||||
private const string LOGGING_EXTRADIAGNOSTICS_KEY = LOGGING_KEY + "ExtraDiagnostics";
|
||||
private const string LOGGING_EXTRADIAGNOSTICSPATH_KEY = LOGGING_KEY + "ExtraDiagnosticsPath";
|
||||
private const string LOGGING_DIAGNOSTICS_URL_KEY = LOGGING_KEY + "diagnostics_endpoint_url";
|
||||
|
||||
private const string CLIENT_AUTH_KEY = APPLICATION_KEY + "ClientAuth:";
|
||||
private const string CORS_WHITELIST_KEY = CLIENT_AUTH_KEY + "cors_whitelist";
|
||||
|
@ -211,7 +213,8 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.Runtime
|
|||
NodesStorage = GetStorageConfig(configData, NODES_STORAGE_KEY),
|
||||
SimulationsStorage = GetStorageConfig(configData, SIMULATIONS_STORAGE_KEY),
|
||||
DevicesStorage = GetStorageConfig(configData, DEVICES_STORAGE_KEY),
|
||||
PartitionsStorage = GetStorageConfig(configData, PARTITIONS_STORAGE_KEY)
|
||||
PartitionsStorage = GetStorageConfig(configData, PARTITIONS_STORAGE_KEY),
|
||||
DiagnosticsEndpointUrl = configData.GetString(LOGGING_DIAGNOSTICS_URL_KEY) + "/diagnosticsevents"
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,9 @@ ExtraDiagnostics = false
|
|||
# be created or is not writable.
|
||||
ExtraDiagnosticsPath = "/tmp/sim/"
|
||||
|
||||
# Path to the diagnostics back end where all telemetry data is to be directed
|
||||
diagnostics_endpoint_url = "${?PCS_DIAGNOSTICS_WEBSERVICE_URL}"
|
||||
|
||||
|
||||
[DeviceSimulationService:RateLimits]
|
||||
# S3: 5000/min/unit (= 83.3/sec/unit)
|
||||
|
@ -252,4 +255,4 @@ clock_skew_seconds = 300
|
|||
IncludeScopes = true
|
||||
LogLevel:Default = "Warning"
|
||||
LogLevel:System = "Warning"
|
||||
LogLevel:Microsoft = "Warning"
|
||||
LogLevel:Microsoft = "Warning"
|
||||
|
|
|
@ -109,8 +109,9 @@ namespace Microsoft.Azure.IoTSolutions.DeviceSimulation.WebService.v1.Controller
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
errors.Add("Unable to fetch simulation status");
|
||||
this.log.Error("Unable to fetch simulation status", e);
|
||||
var msg = "Unable to fetch simulation status";
|
||||
errors.Add(msg);
|
||||
this.log.Error(msg, e);
|
||||
}
|
||||
|
||||
return simulationRunning;
|
||||
|
|
Загрузка…
Ссылка в новой задаче