I’ve previously written about message tracking in Exchange Server as well as some tips on how to search message tracking logs using PowerShell.

In this article I will demonstrate a few different ways that you can specify time and date ranges for message tracking log searches. This is a useful technique because it can speed up your searches by minimizing the amount of log data that the search inspects.

As an example of this speed difference, here is the result of Get-MessageTrackingLog for all logs on a single server.

[PS] C:\>Measure-Command {Get-MessageTrackingLog -ResultSize Unlimited}

TotalSeconds      : 161.8412674

In contrast, here is a search of only the last 24 hours of message tracking logs.

[PS] C:\>Measure-Command {Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -ResultSize Unlimited}

TotalSeconds      : 8.235824

If you’re wondering about that “AddHours(-24)” bit don’t worry, we’ll get to that shortly.

You can see that in a very large environment you will save yourself a lot of time by knowing how to narrow your message tracking log searches with time and date ranges.

Get-MessageTrackingLog Time/Date Range Parameters

There are two parameters for specifying time and date ranges when running the Get-MessageTrackingLog cmdlet.

  • -Start – the point in time to start returning log entries. If omitted the search will begin at the first entry of the oldest message tracking log file on the server.
  • -End – the point in time to stop returning log entries, up to but not including the time/date specified. If omitted the search will end at the last entry of the latest message tracking log file on the server.

Both parameters accept values in the System.DateTime format, meaning mm/dd/yyyy hh:mm:ss. An example of a valid date would be “9/19/2012”.

Get-MessageTrackingLog -Start 9/19/2012

Another example would be “9/19/2012 13:00:00”. Quotes need to be used when specifying both a date and time.

Get-MessageTrackingLog -Start "9/19/2012 13:00:00"

Specifying Relative Time/Date Ranges using Get-Date

Often you will find yourself in situations where you want to search the logs for a period of time without having to work out the exact start or end time for the search.

For example, you may wish to search only the last hour of logs because somebody has asked you to help troubleshoot a lost email that they only sent within the last hour.

In these situations the Get-Date cmdlet can be used to provide relative time/date ranges. This is actually how I perform most of my own searches.

On its own Get-Date returns a DateTime object (and outputs a human-friendly version of it to your shell).

[PS] C:\>Get-Date

Thursday, 20 September 2012 10:35:05 PM

We also get several methods that are of use to us in this situation.

[PS] C:\>get-date | Get-Member

   TypeName: System.DateTime

Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         System.DateTime Add(System.TimeSpan value)
AddDays              Method         System.DateTime AddDays(double value)
AddHours             Method         System.DateTime AddHours(double value)
AddMilliseconds      Method         System.DateTime AddMilliseconds(double value)
AddMinutes           Method         System.DateTime AddMinutes(double value)
AddMonths            Method         System.DateTime AddMonths(int months)
AddSeconds           Method         System.DateTime AddSeconds(double value)
AddTicks             Method         System.DateTime AddTicks(long value)
AddYears             Method         System.DateTime AddYears(int value)

I tend to use the AddHours and AddDays methods the most. Here is an example of Get-Date on its own, and then Get-Date using the AddHours method to subtract one hour.

[PS] C:\>Get-Date

Thursday, 20 September 2012 10:35:05 PM

[PS] C:\>(Get-Date).AddHours(-1)

Thursday, 20 September 2012 9:35:18 PM

So now let’s combine that with the Get-MessageTrackingLog -Start parameter to search the message tracking logs only for the last hour.

Get-MessageTrackingLog -Start (Get-Date).AddHours(-1)

Pretty simple stuff right? So let’s look at some more examples.

Remember as we go through each of these examples that you may need to use “-ResultSize Unlimited” for searches that are expected to return more than 1000 results. As a general rule I use it on all searches just to save time.

Searching Message Tracking Logs for a Single Day

In this scenario you might be tempted to specify the same value as both the start and end date, but this will return an error.

[PS] C:\>Get-MessageTrackingLog -Start 9/17/2012 -End 9/17/2012 -ResultSize unlimited
The search time range was incorrectly specified. The End and Start parameter values can't be the same.
    + CategoryInfo          : InvalidArgument: (:) [], LocalizedException
    + FullyQualifiedErrorId : 7A353D3

The correct method is to specify the next day as the end date, remembering that the time/date value used for -End is excluded from the results.

[PS] C:\>Get-MessageTrackingLog -Start 9/17/2012 -End 9/18/2012 -ResultSize unlimited

In the above example only log entries occurring between 9/17/2012 00:00:00 and 9/17/2012 23:59:59 will be returned. Obviously another way of achieving that is to specify those exact hours/minutes/seconds in your search.

[PS] C:\>Get-MessageTrackingLog -Start "9/17/2012 00:00:00" -End "9/17/2012 23:59:59" -ResultSize unlimited

That is more typing for the exact same result, so you may as well stick to the more efficient method of simply specifying the date on its own.

Searching Between Two Specific Times/Dates

As a follow on from the previous example it should by now be clear that you can search between any precise start and end times.

Just remember that the hh:mm:ss can be either 24-hour time or AM/PM. If you do not specify AM/PM and enter an ambiguous time such as “11:59:59” then the time will be interpreted as AM.

For example, this will search to 11:59:59 PM.

[PS] C:\>Get-MessageTrackingLog -Start "9/17/2012 00:00:00" -End "9/17/2012 11:59:59 PM" -ResultSize unlimited

And this will search to 11:59:59 PM, because the time is specified using 24-hour format.

[PS] C:\>Get-MessageTrackingLog -Start "9/17/2012 00:00:00" -End "9/17/2012 23:59:59" -ResultSize unlimited

But this will search to 11:59:59 AM, because ambiguous times will default to AM.

[PS] C:\>Get-MessageTrackingLog -Start "9/17/2012 00:00:00" -End "9/17/2012 11:59:59" -ResultSize unlimited

Searching Between a Fixed and a Relative Time/Date Range

You may also wish to combine fixed and relative time/date values in your search.

For example, a search of all messages starting from noon on 17th September and ending 8 hours later.

[PS] C:\>Get-MessageTrackingLog -Start "9/17/2012 12:00:00" -End (Get-Date "17/9/2012 12:00:00").AddHours(8) -ResultSize unlimited

This may seem a bit of a strange example, but the reason I’m using it is to demonstrate one little gotcha with DateTime values.

On my server the regional settings as set to “English (Australia)” for time, meaning the format is dd/mm/yyyy instead of the US mm/dd/yyy.

This means that I need to use US format for Get-MessageTrackingLog, but Australian format for Get-Date.

But I can avoid this confusion and also save a little typing by capturing the start date in a variable first, that I can then re-use in my Get-MessageTrackingLog command, because once the DateTime object has been capture in a variable the regional settings become irrelevant and each cmdlet is able to interpret it correctly regardless.

[PS] C:\>$date = Get-Date "17/9/2012 12:00:00"

[PS] C:\>Get-MessageTrackingLog -Start $date -End $date.AddHours(8) -ResultSize unlimited

This habit also gives you the advantage of a fixed point in time if you were running multiple searches moving through the logs hour by hour as I sometimes do.

[PS] C:\>Get-MessageTrackingLog -Start $date.AddHours(-2) -End $date.AddHours(-1) -ResultSize unlimited

[PS] C:\>Get-MessageTrackingLog -Start $date.AddHours(-3) -End $date.AddHours(-2) -ResultSize unlimited

[PS] C:\>Get-MessageTrackingLog -Start $date.AddHours(-4) -End $date.AddHours(-3) -ResultSize unlimited

Filtering Search Results using Where-Object

As a final example remember that a good practice is to capture your message tracking log search results into a variable so that you can quickly and easily filter those results down further without needing to re-run your search.

[PS] C:\>$msgs = Get-MessageTrackingLog -Start 9/17/2012 -End 9/18/2012 -ResultSize unlimited

You can then simply use Where-Object to return more specific time/date ranges from the data already captured in that variable, using the TimeStamp value.

For example, where in the previous command I collected all message tracking log entries for the 17th September, I can now filter that down to only those entries that were written between 14:00 and 14:05.

[PS] C:\>$msgs | Where-Object {$_.TimeStamp -gt "9/17/2012 14:00:00" -and $_.TimeStamp -lt "9/17/2012 14:05:00"}

When using comparison operators against DateTime values remember that -gt (greater than) means “after/later than” and -lt (less than) means “before/earlier than”.


As you can see when you become familiar with the use of time/date ranges you can perform very fast, very precise message tracking log searches on your Exchange Servers using PowerShell.

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.


  1. Eugene

    Hi Paul, how fined received mails last month, but in night time, for example 20:00 and 08:00 next day. Thanks

  2. Simon

    Hi Paul,

    I just wanted to say I have googled “exchange message tracking logs practical 365” many times over the years to get the date bit right, here I am again in 2022 =) …your Exchange info is invaluable, thank you for sharing!

    If it helps anyone, adding | Out-GridView is a handy option, you can then copy/paste the raw data into Excel. A great way to sort and filter after the fact.

    Get-MessageTrackingLog -Start (Get-Date).AddHours(-24) -ResultSize Unlimited | Out-GridView

    Cheers 🍺

  3. Zeeshan

    Thank you great article could you please help me changing the timestamp to yyyy/MM/DD

  4. Manfred

    Dear Paul,
    thx very much for this tutorial.
    Is there any Chance you could also post the command for my topic:
    find all mails (inbound or outbound) from date XXX to ZZZ “afterhours”. (time range 1800 ’til 0600).
    If it is possible!


  5. Kapil

    Hi Paul,

    I have changed the path of message tracking logs from one drive to another drive on the same MBX exchange 2013 server. After changing the path I restarted transport service, it generated new tracking log files but old logs where not moved to the new location. So I manually copied all the logs and indexing folder from old location to new location and again restarted the server. Now I am able to do message tracking of old mails but when I put “-End” in message tracking command there is no result and when I remove -End from command I get the tracking events for old mails. For new mails after changing the path works fine. Unable to understand the issue. Please guide on this issue.

    1. Avatar photo
      Paul Cunningham

      Without seeing the command you’re running I can’t offer any suggestions.

      1. Kapil K

        Hi Paul,

        I checked by using below command

        Get-MessageTrackingLog -Server -Start “4/5/2017” -End “4/6/2017”

        But If I use Get-MessageTrackingLog -Server -Start “4/5/2017”, then it shows the proper output

  6. Jim

    When I include the Timestamp field in the format-table output, I often find that rows are out of order even though I am sorting the Timestamp field. They are apparently out of order because it is only resolving the time to hh:mm:ss, not hh:mm:ss.000 syntax. Is there a way to get the Timestamp column to show the latter syntax to get the rows in the correct order?

  7. Brad


    I have a client that is asking me to show the number of time each of their Mail Contacts were “Sent To” in the past 6 months & 3 months. My thought was to pull all the recipient email addresses for a certain period of time and then compare this with their list of Contacts.

    Do you know of a way to pull all the recipient addresses into a .csv file for a period of time?

  8. Pravin

    I have to generate spam report with the message tracking logs, i am able to get the report with count, with per day but in the report i am not able to put the Date. can you help me in this.

    I have ran below command.

    Get-TransportServer -identity hub1 |Get-MessageTrackingLog -EventID send -Recipients spam@abc.com -Start (Get-Date).AddDays(-1) -End (Get-Date) -Resultsize unlimited | Group-Object ClientHostname

    It is giving me below output.

    Count Name Group
    —– —- —–
    125 Hub1 {Microsoft.Exchange.Management.TransportLogSearchTasks.MessageTrackingEvent,

    i want in this report just Date which will give me the date, count, server.

    Thanks in advance.

  9. dosh

    back in the days of exchange 2003, one did not have to bother with such scripting as message tracking was more user friendly – I still regret having to upgrade to 2010.
    Why did 2010 developers decided to make things more difficult?

  10. Marietta

    I agree, this is a really great article. I haven’t seen this much information in a while. I have been using time tracking software for a while. This seems like it will save a lot of time! Thanks so much for sharing.

  11. Ios.

    Great article, as always, Thanks Paul.

    Only this morning I was playing with the MTL in 2010 and came up with the following which may be of help to others. It’s very basic, no error checking, but when saved as a .ps1 script, lets you grab the last x minutes of logs across all transport servers. The key issue I was trying to solve was that when searching multiple transport servers at the same time, you’d get all of the results from the first server, then all from the 2nd, etc.., which when you’re looking for a particular time, can be a pain. It isn’t pretty, and I probably wouldn’t use it where there are more than a couple of hub servers, but it works :

    [PS] C:Scripts>.Get-MTLLastMins.ps1 15
    #copy the rest to a script
    param([int]$mins=60); #default to 60mins if no number added
    Write-Host “Last $mins minutes of MessageTrackingLog across all Transport Servers.”
    Get-TransportServer | Get-MessageTrackingLog -Start (Get-Date).AddMinutes(-$mins) -Resultsize unlimited | Select-Object TimeStamp,EventId,Source,@{Name=”MessageSubject”;Expression={($_.MessageSubject).substring(0,25)}},@{Name=”Sender”;Expression={($_.Sender).substring(0,30)}},@{Name=”ClientHost”;Expression={($_.ClientHostname).substring(0,14)}},@{Name=”ServerHost”;Expression={($_.ServerHostName)}},Recipients | Sort TimeStamp | ft
    #end of script

  12. Turbomcp

    interesting as always
    very much appreciated Paul

  13. Sherlon Martina

    Great article. this will save a lot of time.

Leave a Reply