Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

766 linhas
26 KiB

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.ObjectModel;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using Windows.Security.Cryptography.Certificates;
  7. using Windows.UI.Core;
  8. using Windows.UI.Xaml;
  9. using MQTTnet.Client;
  10. using MQTTnet.Client.Connecting;
  11. using MQTTnet.Client.Disconnecting;
  12. using MQTTnet.Client.Options;
  13. using MQTTnet.Diagnostics;
  14. using MQTTnet.Exceptions;
  15. using MQTTnet.Extensions.ManagedClient;
  16. using MQTTnet.Extensions.Rpc;
  17. using MQTTnet.Formatter;
  18. using MQTTnet.Implementations;
  19. using MQTTnet.Protocol;
  20. using MQTTnet.Server;
  21. using MQTTnet.Server.Status;
  22. using MqttClientConnectedEventArgs = MQTTnet.Client.Connecting.MqttClientConnectedEventArgs;
  23. using MqttClientDisconnectedEventArgs = MQTTnet.Client.Disconnecting.MqttClientDisconnectedEventArgs;
  24. namespace MQTTnet.TestApp.UniversalWindows
  25. {
  26. public sealed partial class MainPage
  27. {
  28. private readonly ConcurrentQueue<MqttNetLogMessage> _traceMessages = new ConcurrentQueue<MqttNetLogMessage>();
  29. private readonly ObservableCollection<IMqttClientStatus> _sessions = new ObservableCollection<IMqttClientStatus>();
  30. private IMqttClient _mqttClient;
  31. private IManagedMqttClient _managedMqttClient;
  32. private IMqttServer _mqttServer;
  33. public MainPage()
  34. {
  35. InitializeComponent();
  36. ClientId.Text = Guid.NewGuid().ToString("D");
  37. MqttNetGlobalLogger.LogMessagePublished += OnTraceMessagePublished;
  38. }
  39. private async void OnTraceMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e)
  40. {
  41. _traceMessages.Enqueue(e.TraceMessage);
  42. await UpdateLogAsync();
  43. }
  44. private async Task UpdateLogAsync()
  45. {
  46. while (_traceMessages.Count > 100)
  47. {
  48. _traceMessages.TryDequeue(out _);
  49. }
  50. var logText = new StringBuilder();
  51. foreach (var traceMessage in _traceMessages)
  52. {
  53. logText.AppendFormat(
  54. "[{0:yyyy-MM-dd HH:mm:ss.fff}] [{1}] [{2}] [{3}] [{4}]{5}",
  55. traceMessage.Timestamp,
  56. traceMessage.Level,
  57. traceMessage.Source,
  58. traceMessage.ThreadId,
  59. traceMessage.Message,
  60. Environment.NewLine);
  61. if (traceMessage.Exception != null)
  62. {
  63. logText.AppendLine(traceMessage.Exception.ToString());
  64. }
  65. }
  66. await Trace.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
  67. {
  68. Trace.Text = logText.ToString();
  69. });
  70. }
  71. private async void Connect(object sender, RoutedEventArgs e)
  72. {
  73. var tlsOptions = new MqttClientTlsOptions
  74. {
  75. UseTls = UseTls.IsChecked == true,
  76. IgnoreCertificateChainErrors = true,
  77. IgnoreCertificateRevocationErrors = true,
  78. AllowUntrustedCertificates = true
  79. };
  80. var options = new MqttClientOptions
  81. {
  82. ClientId = ClientId.Text,
  83. ProtocolVersion = MqttProtocolVersion.V500
  84. };
  85. if (UseTcp.IsChecked == true)
  86. {
  87. options.ChannelOptions = new MqttClientTcpOptions
  88. {
  89. Server = Server.Text,
  90. Port = int.Parse(Port.Text),
  91. TlsOptions = tlsOptions
  92. };
  93. }
  94. if (UseWs.IsChecked == true)
  95. {
  96. options.ChannelOptions = new MqttClientWebSocketOptions
  97. {
  98. Uri = Server.Text,
  99. TlsOptions = tlsOptions
  100. };
  101. }
  102. if (options.ChannelOptions == null)
  103. {
  104. throw new InvalidOperationException();
  105. }
  106. options.Credentials = new MqttClientCredentials
  107. {
  108. Username = User.Text,
  109. Password = Password.Text
  110. };
  111. options.CleanSession = CleanSession.IsChecked == true;
  112. options.KeepAlivePeriod = TimeSpan.FromSeconds(double.Parse(KeepAliveInterval.Text));
  113. if (UseMqtt310.IsChecked == true)
  114. {
  115. options.ProtocolVersion = MqttProtocolVersion.V310;
  116. }
  117. else if (UseMqtt311.IsChecked == true)
  118. {
  119. options.ProtocolVersion = MqttProtocolVersion.V311;
  120. }
  121. else if (UseMqtt500.IsChecked == true)
  122. {
  123. options.ProtocolVersion = MqttProtocolVersion.V500;
  124. }
  125. try
  126. {
  127. if (_mqttClient != null)
  128. {
  129. await _mqttClient.DisconnectAsync();
  130. _mqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage);
  131. _mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected(x));
  132. _mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected(x));
  133. }
  134. var factory = new MqttFactory();
  135. if (UseManagedClient.IsChecked == true)
  136. {
  137. _managedMqttClient = factory.CreateManagedMqttClient();
  138. _managedMqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage);
  139. _managedMqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected(x));
  140. _managedMqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected(x));
  141. await _managedMqttClient.StartAsync(new ManagedMqttClientOptions
  142. {
  143. ClientOptions = options
  144. });
  145. }
  146. else
  147. {
  148. _mqttClient = factory.CreateMqttClient();
  149. _mqttClient.UseApplicationMessageReceivedHandler(HandleReceivedApplicationMessage);
  150. _mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(x => OnConnected(x));
  151. _mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(x => OnDisconnected(x));
  152. await _mqttClient.ConnectAsync(options);
  153. }
  154. }
  155. catch (Exception exception)
  156. {
  157. Trace.Text += exception + Environment.NewLine;
  158. }
  159. }
  160. private void OnDisconnected(MqttClientDisconnectedEventArgs e)
  161. {
  162. _traceMessages.Enqueue(new MqttNetLogMessage("", DateTime.Now, -1,
  163. "", MqttNetLogLevel.Info, "! DISCONNECTED EVENT FIRED", null));
  164. Task.Run(UpdateLogAsync);
  165. }
  166. private void OnConnected(MqttClientConnectedEventArgs e)
  167. {
  168. _traceMessages.Enqueue(new MqttNetLogMessage("", DateTime.Now, -1,
  169. "", MqttNetLogLevel.Info, "! CONNECTED EVENT FIRED", null));
  170. Task.Run(UpdateLogAsync);
  171. }
  172. private async Task HandleReceivedApplicationMessage(MqttApplicationMessageReceivedEventArgs eventArgs)
  173. {
  174. var item = $"Timestamp: {DateTime.Now:O} | Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload)} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel}";
  175. await Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
  176. {
  177. if (AddReceivedMessagesToList.IsChecked == true)
  178. {
  179. ReceivedMessages.Items.Add(item);
  180. }
  181. });
  182. }
  183. private async void Publish(object sender, RoutedEventArgs e)
  184. {
  185. try
  186. {
  187. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  188. if (QoS1.IsChecked == true)
  189. {
  190. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  191. }
  192. if (QoS2.IsChecked == true)
  193. {
  194. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  195. }
  196. var payload = new byte[0];
  197. if (PlainText.IsChecked == true)
  198. {
  199. payload = Encoding.UTF8.GetBytes(Payload.Text);
  200. }
  201. if (Base64.IsChecked == true)
  202. {
  203. payload = Convert.FromBase64String(Payload.Text);
  204. }
  205. var message = new MqttApplicationMessageBuilder()
  206. .WithContentType(ContentType.Text)
  207. .WithResponseTopic(ResponseTopic.Text)
  208. .WithTopic(Topic.Text)
  209. .WithPayload(payload)
  210. .WithQualityOfServiceLevel(qos)
  211. .WithRetainFlag(Retain.IsChecked == true)
  212. .Build();
  213. if (_mqttClient != null)
  214. {
  215. await _mqttClient.PublishAsync(message);
  216. }
  217. if (_managedMqttClient != null)
  218. {
  219. await _managedMqttClient.PublishAsync(message);
  220. }
  221. }
  222. catch (Exception exception)
  223. {
  224. Trace.Text += exception + Environment.NewLine;
  225. }
  226. }
  227. private async void Disconnect(object sender, RoutedEventArgs e)
  228. {
  229. try
  230. {
  231. if (_mqttClient != null)
  232. {
  233. await _mqttClient.DisconnectAsync();
  234. _mqttClient.Dispose();
  235. _mqttClient = null;
  236. }
  237. if (_managedMqttClient != null)
  238. {
  239. await _managedMqttClient.StopAsync();
  240. _managedMqttClient.Dispose();
  241. _managedMqttClient = null;
  242. }
  243. }
  244. catch (Exception exception)
  245. {
  246. Trace.Text += exception + Environment.NewLine;
  247. }
  248. }
  249. private void ClearLog(object sender, RoutedEventArgs e)
  250. {
  251. while (_traceMessages.Count > 0)
  252. {
  253. _traceMessages.TryDequeue(out _);
  254. }
  255. Trace.Text = string.Empty;
  256. }
  257. private async void Subscribe(object sender, RoutedEventArgs e)
  258. {
  259. try
  260. {
  261. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  262. if (SubscribeQoS1.IsChecked == true)
  263. {
  264. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  265. }
  266. if (SubscribeQoS2.IsChecked == true)
  267. {
  268. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  269. }
  270. var topicFilter = new TopicFilter { Topic = SubscribeTopic.Text, QualityOfServiceLevel = qos };
  271. if (_mqttClient != null)
  272. {
  273. await _mqttClient.SubscribeAsync(topicFilter);
  274. }
  275. if (_managedMqttClient != null)
  276. {
  277. await _managedMqttClient.SubscribeAsync(topicFilter);
  278. }
  279. }
  280. catch (Exception exception)
  281. {
  282. Trace.Text += exception + Environment.NewLine;
  283. }
  284. }
  285. private async void Unsubscribe(object sender, RoutedEventArgs e)
  286. {
  287. try
  288. {
  289. if (_mqttClient != null)
  290. {
  291. await _mqttClient.UnsubscribeAsync(SubscribeTopic.Text);
  292. }
  293. if (_managedMqttClient != null)
  294. {
  295. await _managedMqttClient.UnsubscribeAsync(SubscribeTopic.Text);
  296. }
  297. }
  298. catch (Exception exception)
  299. {
  300. Trace.Text += exception + Environment.NewLine;
  301. }
  302. }
  303. // This code is for the Wiki at GitHub!
  304. // ReSharper disable once UnusedMember.Local
  305. private async void StartServer(object sender, RoutedEventArgs e)
  306. {
  307. if (_mqttServer != null)
  308. {
  309. return;
  310. }
  311. JsonServerStorage storage = null;
  312. if (ServerPersistRetainedMessages.IsChecked == true)
  313. {
  314. storage = new JsonServerStorage();
  315. if (ServerClearRetainedMessages.IsChecked == true)
  316. {
  317. storage.Clear();
  318. }
  319. }
  320. _mqttServer = new MqttFactory().CreateMqttServer();
  321. var options = new MqttServerOptions();
  322. options.DefaultEndpointOptions.Port = int.Parse(ServerPort.Text);
  323. options.Storage = storage;
  324. options.EnablePersistentSessions = ServerAllowPersistentSessions.IsChecked == true;
  325. await _mqttServer.StartAsync(options);
  326. }
  327. private async void StopServer(object sender, RoutedEventArgs e)
  328. {
  329. if (_mqttServer == null)
  330. {
  331. return;
  332. }
  333. await _mqttServer.StopAsync();
  334. _mqttServer = null;
  335. }
  336. private void ClearReceivedMessages(object sender, RoutedEventArgs e)
  337. {
  338. ReceivedMessages.Items.Clear();
  339. }
  340. private async void ExecuteRpc(object sender, RoutedEventArgs e)
  341. {
  342. var qos = MqttQualityOfServiceLevel.AtMostOnce;
  343. if (RpcQoS1.IsChecked == true)
  344. {
  345. qos = MqttQualityOfServiceLevel.AtLeastOnce;
  346. }
  347. if (RpcQoS2.IsChecked == true)
  348. {
  349. qos = MqttQualityOfServiceLevel.ExactlyOnce;
  350. }
  351. var payload = new byte[0];
  352. if (RpcText.IsChecked == true)
  353. {
  354. payload = Encoding.UTF8.GetBytes(RpcPayload.Text);
  355. }
  356. if (RpcBase64.IsChecked == true)
  357. {
  358. payload = Convert.FromBase64String(RpcPayload.Text);
  359. }
  360. try
  361. {
  362. var rpcClient = new MqttRpcClient(_mqttClient);
  363. var response = await rpcClient.ExecuteAsync(TimeSpan.FromSeconds(5), RpcMethod.Text, payload, qos);
  364. RpcResponses.Items.Add(RpcMethod.Text + " >>> " + Encoding.UTF8.GetString(response));
  365. }
  366. catch (MqttCommunicationTimedOutException)
  367. {
  368. RpcResponses.Items.Add(RpcMethod.Text + " >>> [TIMEOUT]");
  369. }
  370. catch (Exception exception)
  371. {
  372. RpcResponses.Items.Add(RpcMethod.Text + " >>> [EXCEPTION (" + exception.Message + ")]");
  373. }
  374. }
  375. private void ClearRpcResponses(object sender, RoutedEventArgs e)
  376. {
  377. RpcResponses.Items.Clear();
  378. }
  379. private void ClearSessions(object sender, RoutedEventArgs e)
  380. {
  381. _sessions.Clear();
  382. }
  383. private void RefreshSessions(object sender, RoutedEventArgs e)
  384. {
  385. if (_mqttServer == null)
  386. {
  387. return;
  388. }
  389. var sessions = _mqttServer.GetClientStatusAsync().GetAwaiter().GetResult();
  390. _sessions.Clear();
  391. foreach (var session in sessions)
  392. {
  393. _sessions.Add(session);
  394. }
  395. ListViewSessions.DataContext = _sessions;
  396. }
  397. #region Wiki Code
  398. private async Task WikiCode()
  399. {
  400. {
  401. // Use a custom identifier for the trace messages.
  402. var clientOptions = new MqttClientOptionsBuilder()
  403. .Build();
  404. }
  405. {
  406. // Create a new MQTT client.
  407. var factory = new MqttFactory();
  408. var client = factory.CreateMqttClient();
  409. // Create TCP based options using the builder.
  410. var options = new MqttClientOptionsBuilder()
  411. .WithClientId("Client1")
  412. .WithTcpServer("broker.hivemq.com")
  413. .WithCredentials("bud", "%spencer%")
  414. .WithTls()
  415. .WithCleanSession()
  416. .Build();
  417. await client.ConnectAsync(options);
  418. // Reconnecting
  419. client.UseDisconnectedHandler(async e =>
  420. {
  421. Console.WriteLine("### DISCONNECTED FROM SERVER ###");
  422. await Task.Delay(TimeSpan.FromSeconds(5));
  423. try
  424. {
  425. await client.ConnectAsync(options);
  426. }
  427. catch
  428. {
  429. Console.WriteLine("### RECONNECTING FAILED ###");
  430. }
  431. });
  432. // Consuming messages
  433. client.UseApplicationMessageReceivedHandler(e =>
  434. {
  435. Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
  436. Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
  437. Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
  438. Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
  439. Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
  440. Console.WriteLine();
  441. });
  442. // Subscribe after connect
  443. client.UseConnectedHandler(async e =>
  444. {
  445. Console.WriteLine("### CONNECTED WITH SERVER ###");
  446. // Subscribe to a topic
  447. await client.SubscribeAsync(new TopicFilterBuilder().WithTopic("my/topic").Build());
  448. Console.WriteLine("### SUBSCRIBED ###");
  449. });
  450. // Subscribe to a topic
  451. await client.SubscribeAsync(new TopicFilterBuilder().WithTopic("my/topic").Build());
  452. // Unsubscribe from a topic
  453. await client.UnsubscribeAsync("my/topic");
  454. // Publish an application message
  455. var applicationMessage = new MqttApplicationMessageBuilder()
  456. .WithTopic("A/B/C")
  457. .WithPayload("Hello World")
  458. .WithAtLeastOnceQoS()
  459. .Build();
  460. await client.PublishAsync(applicationMessage);
  461. }
  462. {
  463. {
  464. // Use TCP connection.
  465. var options = new MqttClientOptionsBuilder()
  466. .WithTcpServer("broker.hivemq.com", 1883) // Port is optional
  467. .Build();
  468. }
  469. {
  470. // Use secure TCP connection.
  471. var options = new MqttClientOptionsBuilder()
  472. .WithTcpServer("broker.hivemq.com")
  473. .WithTls()
  474. .Build();
  475. }
  476. {
  477. // Use WebSocket connection.
  478. var options = new MqttClientOptionsBuilder()
  479. .WithWebSocketServer("broker.hivemq.com:8000/mqtt")
  480. .Build();
  481. }
  482. {
  483. // Create TCP based options manually
  484. var options = new MqttClientOptions
  485. {
  486. ClientId = "Client1",
  487. Credentials = new MqttClientCredentials
  488. {
  489. Username = "bud",
  490. Password = "%spencer%"
  491. },
  492. ChannelOptions = new MqttClientTcpOptions
  493. {
  494. Server = "broker.hivemq.org",
  495. TlsOptions = new MqttClientTlsOptions
  496. {
  497. UseTls = true
  498. }
  499. },
  500. };
  501. }
  502. }
  503. // ----------------------------------
  504. {
  505. var options = new MqttServerOptions();
  506. options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  507. {
  508. if (c.ClientId.Length < 10)
  509. {
  510. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  511. return;
  512. }
  513. if (c.Username != "mySecretUser")
  514. {
  515. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  516. return;
  517. }
  518. if (c.Password != "mySecretPassword")
  519. {
  520. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  521. return;
  522. }
  523. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  524. });
  525. var factory = new MqttFactory();
  526. var mqttServer = factory.CreateMqttServer();
  527. await mqttServer.StartAsync(options);
  528. Console.WriteLine("Press any key to exit.");
  529. Console.ReadLine();
  530. await mqttServer.StopAsync();
  531. }
  532. // ----------------------------------
  533. // For UWP apps:
  534. MqttTcpChannel.CustomIgnorableServerCertificateErrorsResolver = o =>
  535. {
  536. if (o.Server == "server_with_revoked_cert")
  537. {
  538. return new[] { ChainValidationResult.Revoked };
  539. }
  540. return new ChainValidationResult[0];
  541. };
  542. {
  543. // Start a MQTT server.
  544. var mqttServer = new MqttFactory().CreateMqttServer();
  545. await mqttServer.StartAsync(new MqttServerOptions());
  546. Console.WriteLine("Press any key to exit.");
  547. Console.ReadLine();
  548. await mqttServer.StopAsync();
  549. }
  550. {
  551. // Configure MQTT server.
  552. var optionsBuilder = new MqttServerOptionsBuilder()
  553. .WithConnectionBacklog(100)
  554. .WithDefaultEndpointPort(1884);
  555. var options = new MqttServerOptions
  556. {
  557. };
  558. options.ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  559. {
  560. if (c.ClientId != "Highlander")
  561. {
  562. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  563. return;
  564. }
  565. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  566. });
  567. var mqttServer = new MqttFactory().CreateMqttServer();
  568. await mqttServer.StartAsync(optionsBuilder.Build());
  569. }
  570. {
  571. // Setup client validator.
  572. var options = new MqttServerOptions
  573. {
  574. ConnectionValidator = new MqttServerConnectionValidatorDelegate(c =>
  575. {
  576. if (c.ClientId.Length < 10)
  577. {
  578. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
  579. return;
  580. }
  581. if (c.Username != "mySecretUser")
  582. {
  583. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  584. return;
  585. }
  586. if (c.Password != "mySecretPassword")
  587. {
  588. c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
  589. return;
  590. }
  591. c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
  592. })
  593. };
  594. }
  595. {
  596. // Create a new MQTT server.
  597. var mqttServer = new MqttFactory().CreateMqttServer();
  598. }
  599. {
  600. // Setup and start a managed MQTT client.
  601. var options = new ManagedMqttClientOptionsBuilder()
  602. .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
  603. .WithClientOptions(new MqttClientOptionsBuilder()
  604. .WithClientId("Client1")
  605. .WithTcpServer("broker.hivemq.com")
  606. .WithTls().Build())
  607. .Build();
  608. var mqttClient = new MqttFactory().CreateManagedMqttClient();
  609. await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic("my/topic").Build());
  610. await mqttClient.StartAsync(options);
  611. }
  612. {
  613. // Use a custom log ID for the logger.
  614. var factory = new MqttFactory();
  615. var client = factory.CreateMqttClient(new MqttNetLogger("MyCustomId"));
  616. }
  617. {
  618. var client = new MqttFactory().CreateMqttClient();
  619. var message = new MqttApplicationMessageBuilder()
  620. .WithTopic("MyTopic")
  621. .WithPayload("Hello World")
  622. .WithExactlyOnceQoS()
  623. .WithRetainFlag()
  624. .Build();
  625. await client.PublishAsync(message);
  626. }
  627. {
  628. // Write all trace messages to the console window.
  629. MqttNetGlobalLogger.LogMessagePublished += (s, e) =>
  630. {
  631. var trace = $">> [{e.TraceMessage.Timestamp:O}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Source}] [{e.TraceMessage.Level}]: {e.TraceMessage.Message}";
  632. if (e.TraceMessage.Exception != null)
  633. {
  634. trace += Environment.NewLine + e.TraceMessage.Exception.ToString();
  635. }
  636. Console.WriteLine(trace);
  637. };
  638. }
  639. }
  640. #endregion
  641. }
  642. }