Use Azure Data Explorer to query LCS Raw information logs

Note: This is now also part of the official documentation on docs.microsoft.com: Use Azure Data Explorer to query raw information logs – Finance & Operations | Dynamics 365 | Microsoft Docs. I will continue to maintain the samples at the end of this blog.

There are occasions when a customer, partner, consultant, or support engineer needs to look at the low-level Dynamics 365 Finance & Operations telemetry data. These use cases include troubleshooting of errors, performance-related investigations or just to gain some additional understanding of how the platform work. Telemetry data can be accessed by authorized users via the Environment monitoring part of the LCS portal, can be filtered in a few different ways and displayed inside the LCS portal’s raw logs section. A data grid can be used to inspect the log entries. LCS does not allow for more sophisticated pivoting and users can use Excel for that purpose. For that purpose, the telemetry data can also be downloaded formatted in CSV format.

However, Excel is not the optimal tool for advanced querying of this data. The perfect tool, designed for this purpose is the Azure Data Explorer. It provides an innovative query language Kusto that is optimized for high-performance data analytics. Answering questions like “how often has a certain process taken place, how long has it taken in 90% of the times, how often per hour has a certain action taken place over the course of a day” becomes a lot easier and can be backed up with powerful graphics as well.

Here are examples how the graphics could look like: 

A less known feature of the Azure Data Explorer is that it supports ingestion of CSV files. We can use it to get our CSV data files uploaded and staged so it can be queried with the Kusto language. If you have not setup Azure Data Explorer Cluster, follow these steps.

Steps to upload to Azure Data Explorer

  • Run your query on LCS raw logs page
  • Important: adjust the time interval or – filter to get to the right data (row limit is 5000 for export in next step)
  • export the grid to Excel
  • Open the file in Excel and save it without making any changes (this seems to fix a formatting issue)
  • In your Azure Data Explorer, right click on the cluster in the tree view and select “ingest new data” and then on the next page “ingest data from a local file”
  • Pick your cluster, name a new table for the data to be imported into, select up to 10 CSV files to import, select CSV format. Hit next a few times till your data is getting imported.
  • Use the Query tile to write a Kusto query against your data.

To learn more about the Kusto query language, go here.

Sample queries

Modern POS

all errors

Sometimes its nice to get an idea of what all the errors are. The use of the text fields to describe errors is not consistently used, so its better to use the EventIds and map them to the correct errors. They can be looked up in these two Commerce SDK files (but using the code snippet below may give you all of them already):

  • Pos.RetailLogger.js
  • Microsoft.Dynamics.Retail.Diagnostics.Sinks.man
// Note: Filter EventSeverity == Error before exporting/ingesting 
MPosErrors24h
| extend EventName = case(
EventId == 53231, "runtimeInterceptorFailed",
EventId == 48434, "posPaymentTerminalAuthorizeRefundActivityFailed",
EventId == 48347, "posAuthorizeOrRefundPaymentFailed",
EventId == 48104, "posTenderPaymentOperationFailed",
EventId == 46802, "extensibilityFrameworkExecuteRuntimeRequestFailed",
EventId == 44322, "viewModelCartProcessTextFailed",
EventId == 42112, "viewModelCartProcessTextFailed",
EventId == 42106, "RetailProxyExtensionAdapterManagerMethodNotFound",
EventId == 40450, "posInitiatedHardwareStationRequestFailed",
EventId == 40409, "peripheralsBarcodeScannerEnableFailed",
EventId == 40325, "operationSetQuantityOptionsValidationFailed",
EventId == 40255, "RetailOperationFailed",
EventId == 40209, "coreRetailOperationOnExecutingFailed",
EventId == 40191, "modelManagersChainedRequestExecutionFailed",
EventId == 40170, "modelManagersErrorParserHardwareStationError",
EventId == 40150, "modelManagersChainedRequestFactoryExecuteOfflineLogonRequestUnAvailable",
EventId == 40104, "ModelManagersCommerceRuntimeRequestError",
EventId == 40101, "ModelManagersRetailServerRequestError",
EventId == 7505 , "PaymentConnectorLogErrors",
EventId == 7503 , "PaymentConnectorLogException",
EventId == 7014 , "HardwareStationActionFailure",
EventId == 1001 , "CrtExecuteRequestErrorFailure",
strcat("*************** Unknown EventId: ", EventId))
| where EventSeverity == "Error"
| summarize count() by EventId, EventName
| order by count_ desc

Now, its easier to decide where to look next, i.e. what more detailed queries to issue for specific errors…

Errors returned by RetailServer

// Note: Filter EventSeverity == Error before exporting/ingesting 
MPosErrors24h
| extend EventName = case(
EventId == 53231, "runtimeInterceptorFailed",
EventId == 48434, "posPaymentTerminalAuthorizeRefundActivityFailed",
EventId == 48347, "posAuthorizeOrRefundPaymentFailed",
EventId == 48104, "posTenderPaymentOperationFailed",
EventId == 46802, "extensibilityFrameworkExecuteRuntimeRequestFailed",
EventId == 44322, "viewModelCartProcessTextFailed",
EventId == 42112, "viewModelCartProcessTextFailed",
EventId == 42106, "RetailProxyExtensionAdapterManagerMethodNotFound",
EventId == 40450, "posInitiatedHardwareStationRequestFailed",
EventId == 40409, "peripheralsBarcodeScannerEnableFailed",
EventId == 40325, "operationSetQuantityOptionsValidationFailed",
EventId == 40255, "RetailOperationFailed",
EventId == 40209, "coreRetailOperationOnExecutingFailed",
EventId == 40191, "modelManagersChainedRequestExecutionFailed",
EventId == 40170, "modelManagersErrorParserHardwareStationError",
EventId == 40150, "modelManagersChainedRequestFactoryExecuteOfflineLogonRequestUnAvailable",
EventId == 40104, "ModelManagersCommerceRuntimeRequestError",
EventId == 40101, "ModelManagersRetailServerRequestError",
EventId == 7505 , "PaymentConnectorLogErrors",
EventId == 7503 , "PaymentConnectorLogException",
EventId == 7014 , "HardwareStationActionFailure",
EventId == 1001 , "CrtExecuteRequestErrorFailure",
strcat("*************** Unknown EventId: ", EventId))
| where EventSeverity == "Error"
| where EventId == 40101
| summarize count() by requestAction, error
| order by count_ desc

CSU

All errors

Similarly to the MPOS errors, we can get them from the CommerceSDK (Microsoft.Dynamics.Retail.Diagnostics.Sinks.man)

// Note: Filter EventSeverity == Error before exporting/ingesting 
CSUErrors24h
| extend EventName = case(
EventId == 1017, "CrtSlowHandlerExecution",
EventId == 1005, "CrtExecuteRequestWarningFailure",
EventId == 5055, "RetailServerRequestWarningFailure",
EventId == 60208, "SyncLibraryMergeDataIntoTableWarning",
EventId == 60125, "ProcessDeleteRequestStart",
EventId == 2617, "CrtWorkflowUserAuthenticationRequestHandlerFailure",
EventId == 2486, "CrtServicesEmployeePasswordDoesNotMatch",
EventId == 5106, "RetailServerSecretRetrievalWarning",
EventId == 2503, "CurrencyServiceGetSupportedChannelCurrenciesNotFound",
EventId == 6943, "RtsClientLibraryApiExecutionWarning",
EventId == 3007, "CrtTransactionServiceClientRtsExecutionWarning",
EventId == 2413, "CrtServicesSalesOrderTransactionServiceMarkReturnedItemsFailure",
EventId == 2483, "CrtServicesLocalLogonFailedDueToIncorrectStaffId",
strcat("*************** Unknown EventId: ", EventId))
| summarize count() by EventId, EventName
| order by count_ desc

A next step would be to go through each of these error types, and look at them closer. A few of those could indicate quality issues that could effect the user experience.

In many cases, these errors can be fixed by cleaning up the extended code, adding proper SQL indexes or investigate better problem approaches.

In some cases, these errors could indicate problems with out-of-box experience (OOBE) or deployment. Please open a support request to get these fixed by Microsoft.

Slow CRT handlers by requesttype

// Note: Filter EventSeverity == Error before exporting/ingesting 
CSUErrors24h
| extend EventName = case(
EventId == 1017, "CrtSlowHandlerExecution",
EventId == 1005, "CrtExecuteRequestWarningFailure",
EventId == 5055, "RetailServerRequestWarningFailure",
EventId == 60208, "SyncLibraryMergeDataIntoTableWarning",
EventId == 60125, "ProcessDeleteRequestStart",
EventId == 2617, "CrtWorkflowUserAuthenticationRequestHandlerFailure",
EventId == 2486, "CrtServicesEmployeePasswordDoesNotMatch",
EventId == 5106, "RetailServerSecretRetrievalWarning",
EventId == 2503, "CurrencyServiceGetSupportedChannelCurrenciesNotFound",
EventId == 6943, "RtsClientLibraryApiExecutionWarning",
EventId == 3007, "CrtTransactionServiceClientRtsExecutionWarning",
EventId == 2413, "CrtServicesSalesOrderTransactionServiceMarkReturnedItemsFailure",
EventId == 2483, "CrtServicesLocalLogonFailedDueToIncorrectStaffId",
strcat("*************** Unknown EventId: ", EventId))
| where EventId == 1017
| summarize count(), sum(inclusiveExecutionTime), percentiles(inclusiveExecutionTime, 75, 90) by handlerAssemblyName, requestType
| order by sum_inclusiveExecutionTime desc

F&O

All errors

// Note: use selection "All error events" to only get the errors
FNOErrors24h
| summarize count() by formName, targetName, errorLabel
| order by count_ desc

FormName, targetName, errLabel are all good candidates to look up in Azure DevOps to find out the code location. That gives more insight in what areas these error occur.

Example: The @SYS18885 is thrown from ReqPlanData class. Now I can focus on figuring out why that is the case (is Master planning not configured right?)

Slow SELECT queries

// use selection "slow queries" in LCS diagnostics
SlowQueries
| extend MainTableName = extract("FROM\\s([A-Z0-9]+)\\s", 1, statement)
| where statement contains "SELECT"

slow inserts

// use selection "slow queries" in LCS diagnostics
SlowQueries
| extend MainTableName = extract("INSERT INTO ([A-Z0-9]+)\\s", 1, statement)
| where statement contains "INSERT"

Slow deletes

// use selection "slow queries" in LCS diagnostics
SlowQueries
| extend MainTableName = extract("DELETE FROM ([A-Z0-9]+)\\s", 1, statement)
| where statement contains "DELETE FROM"

Batch jobs performance

// use selection "All logs" and add query "where TaskName Equals BatchFinishedOneTask" in LCS diagnostics
BatchFinishedOneTask
| summarize count(), sum(durationInMilliSeconds), percentiles(durationInMilliSeconds, 75, 90) by className
| order by sum_durationInMilliSeconds desc

Getting started with HardwareStation IPaymentDevice development

The following article outlines the steps to carry out in order to be able to use, develop and debug a simple IPaymentDevice-derived peripheral that can be used by the Dynamics 365 for Retail and Operations HardwareStation.

The following steps should be carried out on a development box.

Configure Hardware profile and settings

I used HW0002 profile. We need to use the PinPad device type and set some new parameters. Lets use “SAMPLEDEVICE2”. Notice that the originally shipped Microsoft code already uses “SAMPLEDEVICE”, so I deliberately choose a different string.

Edit as seen in the screenshot and save.
Next, pick the store you want to use for development and testing. I picked Houston. Go into the store settings and update the HardwareStation settings, make sure you match the same hardware profile from above.

Note, that I am running all components (HQ and store) on the same cloud-hosted box. There is a certificate for all sites already installed, so I decided to use the same url, in order to re-use the SSL certificate (pick it during the installation). I choose hardwarestation.cloud.dynamics.com. It could be anything, but you will have to get through some additional steps to get the SSL cert or self-signed cert to work properly.
Once these things are all setup and saved, we need to push these changes to the channel, via the 1090 (Registers) job. Run it and make sure the changes have been applied (via “Download sessions” form)

Development setup

Go back to the same screen for the Houston store, and download the hardware station installer. Once downloaded, install it on the development box. You also need to add a hosts file entry for the development box:

127.0.0.1 retailhardwarestation.cloud.dynamics.com

and a host header of

retailhardwarestation.cloud.dynamics.com

for the newly installed website.
At the end of the installation, make sure the final ping works to the newly installed station succeeds (url is https://retailhardwarestation.cloud.dynamics.com/HardwareStation/ping).

Your first payment connector

Re-use the HardwareStation.Peripherals.Desktop project from the Retail Sdk but strip out everything we do not need. At the end, only leave the SampleManagerDevice.cs, make sure to rename it to “SAMPLEDEVICE2” in the class export attribute and remove any binary references to HardwareStation.Peripherals.*. We are doing that so we won’t need to drop all these other binaries. Its unfortunate that the simple Retail Sdk sample has that many dependencies. Here we are going for the simplest way to get started, so we remove all of these references.

Compile the code.

Drop the dll and pdb into the HardwareStation’s binary folder. For me, that was at “C:\Program Files (x86)\Microsoft Dynamics 365\70\Retail Hardware Station\Package\bin”.

Last but not least, add the assembly to the HWST’s web.config file at the //hardwarestation/composition node, at the top. I added this entry:

<add source=”assembly” value=”SampleExtensionLibrary” />
(I renamed it, but any assembly name will work for you here.)

Test and step through the debugger

We need POS (Modern POS or Cloud POS) and pair the hardware station. The quickest way would be to use Cloud POS as no installation is needed. Once you paired the station with POS (via the “Select HardwareStation” button) you should log off once.

A good way to verify that the HardwareStation is being used is to use F12 Developer tools or Fiddler. Whenever an item gets added to the cart, we should see certain calls to the HWST:

At this point, we are ready to debug. Attach to the w3wp.exe project for the HardwareStation (i.e use ProcessExplorer to find out the process ID) and set breakpoints in your SAMPLEDEVICE2 payment device (i.e. OpenAsync method).

Hook all this into the Retail Sdk

In order to make sure that our officially built HardwareStation has everything we need, we need to carry out these steps on files that are under source control:

  • Edit Customization.settings to include our new dll into $(ISV_HardwareStation_CustomizableFile)
  • Edit RetailSDK\Packages\HardwareStation\Web.Config with the registration of the new dll
  • add your new project with the IPaymentDevice implementation into the RetailSdk, so it is part of the build (with dirs.proj or a *.sln file)
  • submit the changes to VSTS, do a build and deploy the Retail combined package to your D365 environment. (at that point the download from the store page should give you a HardwareStation package with the right files)

Now we are in a position to try, debug, evaluate the calls and can implement the “real” code. Happy coding.

Update your Retail Sdk to add payment packaging and deployment support (AX 7 RTW and Update 1)

Update 8/8/2016: Hotfix to Update 1 is now available: “KB 3183058: Retail Sdk update for packaging and deployment of payment files.”. If you can update, that is recommended, if you cannot, this article still applies.

The steps below allow you to update some files in your Retail Sdk so that payment packages can be built as well.  The same code changes as in this zip package will be released as part of Update 2 and possibly even as a hotfix to Update 1.

Benefits

The update allows you to generate all packages with all payment files (payment device assemblies, payment connector assemblies, and payment web files) embedded. All you would need to do is to get the payment files from the 3rd party, place them into the 3 locations dedicated for them and build. Whether built manually on command line or via VSTS automated build, the packages would include the files correctly.
Furthermore, the deployment of these packages will place the file in the correct locations. The packages that include these files are RetailServer, ModermPOS, ModernPOSOffline, CloudPOS, HardwareStation and AOS. The developer is not needed to know where the files need to be deployed too.

One-time setup steps

  1. Make sure you do not have any open files in your current VSTS client. If you do, look at RetailSdk-PaymentIncremental.zip and make sure at least these files are not in edit mode.
  2. Take the RetailSdk-PaymentIncremental.zip (link is below) and unpack it into the Retail Sdk customization branch (Note: At a future date, these changes will come through an update, at that point a simple code merge will have to be done). Make sure that the top folders of the zip file are being copied into the root of the Retail Sdk ($RetailSdk).
    SdkPaymentPackagingZipContents
  3. In Visual Studio, look at the changes and make sure no customizations (to the same files) were overwritten. If they were, merge the files rather than just copy them in. However, the chances are high none of these files were customized.
  4. Add the payment files from the 3rd party vendor to the Sdk, under the PaymentExternals folder. You should have gotten 3 different folders, and these should be matching the new Sdk folders.
  5. Do a local verification build. Open a Visual Studio 2015 Developer command window and issue a “msbuild /v:Rebuild” from the top of the Retail Sdk.
  6. If it built successfully, this part is done.

Verification

  1. After doing the build, verify that $RetailSdk\Packages\AosPaymentsPackage\content.folder\AOSService\Code\WebArtifacts has the files from the $RetailSdk\PaymentExternals\PaymentWebFiles folder.
  2. Verify, that $RetailSdk\Packages\AosPaymentsPackage\content.folder\AOSService\Code\Assemblies has the files from $RetailSdk\PaymentExternals\IPaymentProcessorAssemblies
  3. Verify that $RetailSdk\Packages\ModernPOS\bin\Debug\content.folder\CustomizedFiles\ClientBroker has the files from $RetailSdk\PaymentExternals\IPaymentDeviceAssemblies.
  4. Deploy the packages via runbook and verify functionality (on development or sandbox environment).
  5. If there are any issues with the payment assemblies themselves (not with packaging or deployment), contact the party that released the files
  6. Once everything worked (one-time steps and verification with payment files and deployment), submit the changes from the zip file plus the payment files to your VSTS.

Deployment

There is no difference for deployment of the existing packages. One thing to note is that the new package AOSPaymentPackage must be installed by itself and last.  The deployment for production environments is exactly the same (via DSE if Microsoft-hosted). The AosPaymentPackage must be installed last.

 

RetailSdk-PaymentIncremental-RTWandUpdate1