Troubleshooting Exchange with LogParser:MAPI Client Access logs

A while ago I wrote the post Troubleshooting Exchange with LogParser:RCA logs, which describes how you can parse RCA logs using PowerShell and LogParser.

With the new protocol MAPI over HTTP also new kinds of logs were introduced. When it comes to connectivity or performance issues, those logs might help you to find the root cause.

Update 14.01.2017

Exchange 2016 CU4

In Exchange 2016 CU4 the location and the layout of these logs were changed. Before this build the logs could be found in $exinstall\Logging\MAPI Client Access and the total number of folders in $exinstall\Logging was 64


Note: The number is 66 as there are 64 folders and 2 items.

Once you’ve upgrade to Exchange 2016 CU4 the number increased to 70


and the location is moved to $exinstall\Logging\MapiHttp\Mailbox


As the layout changed, not all the queries could be run against these logs. ConcurrentConnections is not available anymore. In order to query MapiHttp logs of Exchange 2016 CU4 or later versions, just use the new switch E16CU4orLater.

How it works?

As the other scripts you need to fulfill the following prerequisites:

  • LogParser
  • a server from where you will run the script. This server needs SMB access to all Exchange servers as we will reach the MAPI Client Access logs via UNC path
  • adjust the execution policy. Here is an example, which bypass the policy only for the running process:
    Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
  • the script itself, which you can download here

The script will search for Exchange server within a given AD site, determines the installation path and search then in those paths for log files. There are several possibilities you can filter for (e.g.: servers, AD sites, dates, users).

The full list of parameters:



UserID a given mailbox, which you want to query the logs for. The last part from the LegacyExchangeDN is extracted for this e.g.:”/o=contoso/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=Administrator37d” will be “Administrator37d”

Note: It doesn’t has to be the exact one!

UserIDs same as UserID, but it allows you to define multiple users comma separated
StartDate this is used for filtering the logfiles to be parsed. The default format is yyMMdd, but you can also use yyMMddHH.

Default: Current day

EndDate this is used for filtering the logfiles to be parsed. The default format is yyMMdd, but you can also use yyMMddHH.

Default: Current day

LogParser Define path to LogParser.exe.

Default: “${env:ProgramFiles(x86)}\Log Parser 2.2\LogParser.exe”

ADsite Search for Exchange servers in one or multiple sites. The default is the current site from the script is running. You can define multiple sites comma separated.

Default: The site of the machine you are running the script

 Outpath Define where the CSV files will be stored. Default:”$env:temp”
ConcurrentConnections Switch to query number of concurrent connections within the given ConcurentIntervall
ConcurrentIntervall Define the interval of the query ConcurrentConnections in seconds.

Default: 900 seconds=15 minutes

ClientReport Creates a report of all unique user-agents and the number of hits for each.
Note: This is NOT the unique number of users
ErrorReport Creates a report of all errors
SpecifiedServers Comma separated filter for only specified servers in an AD site
E16CU4orLater With Exchange 2016 CU4 the path and design of these log files have been changed. With this switch you can query these.
Localpath if you have log files in a local folder. There is no filtering by date! All files will be analyzed

How does it looks like?

In this example I queried for my user. In this AD site only one DAG with 4 nodes exists:

 .\Get-MAPIStats.ps1 -UserID Ingo -Outpath $env:USERPROFILE\Documents


The output is a CSV file, which you can import in your favorite editor or with PowerShell. As you can see there are a lot of details:



In the following example I analyzed the concurrent connections within 15 minutes for all users on those servers:

.\Get-MAPIStats.ps1 -ConcurrentConnections -Outpath $env:USERPROFILE\Documents


And here the output:


There was nothing bad, but as you might realized there is one mailbox with 196 concurrent connections. In general not a problem, but I’ve seen client with several thousands connections. There could be several reasons (e.g.: broken profile, Outlook Add-Ins), which cause this misbehaviour. When it comes to high number of misbehaving clients, it could be a severe performance issue for your servers.

To get a report about all logged errors run the following:

.\Get-MAPIStats.ps1 -ErrorReport -Outpath $env:USERPROFILE\Documents


The output is the same as in the first query, but the script search only for entries, where the failures field is not empty:MAPIStats_07.PNG

Filtering tips

For faster parsing I recommend to define a Start and EndDate including the hours you want to parse.


.\Get-MAPIStats.ps1 -UserID Ingo -StartDate 16111506 -EndDate 16111508

Also keep in mind that those logs are only written on the server, where the mailbox’ database is currently mounted.


.\Get-MAPIStats.ps1 -UserID Ingo -SpecifiedServers server1,server2

If you have a DAG with 16 servers and the users mailboxdatabase has 4 copies, you need to parse only the servers, which have a copy of this database. Why those 4 and not only the one where it’s currently active? You don’t know whether the database did a failover just before you checked where it’s mounted.

Happy parsing! I hope this helps and feedback is always more than welcome!


19 thoughts on “Troubleshooting Exchange with LogParser:MAPI Client Access logs

  1. Hi Ingo,
    There is a small error in your script regarding the selection of Exchange servers.
    You have defined a filter on CAS and combined CAS/MBX, but it should be MBX and combined CAS/MBX.
    There are no MAPI Client Access Logs on the Exchange 2013 CAS servers.
    [array]$Servers += GetExchServer -Role 16385,16439 -ADSites $ADSite
    should be:
    [array]$Servers += GetExchServer -Role 54,16439 -ADSites $ADSite

    Liked by 1 person

    • Hi Michael,
      actually I wrote the script when we migrated to Exchange 2013 and no Exchange 2016 was in production. Short answer: Yes. The game changed with Exchange 2016 CU4 as mentioned in my update, which created the need for an additional switch.


  2. Hey Ingo!
    Great script, thanks!
    I was trying to run it against a couple of Exchange 2016 servers (with CU4), but I keep getting this error:

    PS C:\temp\logs> .\Get-MAPIStats.ps1 -E16CU4orLater
    C:\temp\logs\Get-MAPIStats.ps1 : Parameter set cannot be resolved using the specified named parameters.

    Any clue on what might be the problem? If I run the script without parameters, it gives me the “no server found!” error.





      • Yes, it’s a small lab environment, with 2 Exchange 2016 boxes. It’s odd because I ran it on another lab with 2010, 2013 and 2016 machines, and it picked up the 2013 and 2016 without a problem.
        I’ve also double checked the AD objects for the versions, role numbers, etc, and everything seems to be in order.





      • Hi Ingo!

        So I finally had some time to do some more testing 🙂

        It seems to be related to the position of the parameters.

        I’ve tried a few combinations and here are the results:

        get-mapistats.ps1 -E16CU4orLater -errorreport
        get-mapistats.ps1 -E16CU4orLater -errorreport -outpath .\
        get-mapistats.ps1 -E16CU4orLater -clientreport -outpath .\
        get-mapistats.ps1 -E16CU4orLater -clientreport

        Doesn’t Work:
        get-mapistats.ps1 -E16CU4orLater
        get-mapistats.ps1 -E16CU4orLater -outpath .\
        get-mapistats.ps1 -E16CU4orLater -specifiedservers hggn-ex1
        get-mapistats.ps1 -specifiedservers hggn-ex1 -e16cu4orlater

        I hope this can help 🙂





      • Hi HN, thanks for testing! The point is that this is on purpose. The switch -E16CU4orLater is intended to work only with User/Users or ErrorReport. The information to get the report for ConcurrentConnections is not available anymore in the logs. And I made a user or users mandatory to search for. Otherwise you would get all log entries. I enforced this with the ParameterSetName in the script.
        Makes sense?


  3. I think if have Exchange manager Tools installed. It is better to use Get-ExchangeServer to get all exchange servers.


      • Why not leverage Remote Powershell and connect to an Exchange box to pull that data? 🙂


      • Have you every tried to parse several GB of log files with PowerShell? It takes ages and consumes a lot of memory. LogParser just runs through within a fractional amount of time. 🙂


      • Ah I see, but I meant just for the Get-ExchangeServer part, after that, you could drop the remote Powershell session and let LogParser do its magic 😀


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s