EN 
06.12.2025 Mikuláš WELCOME IN MY WORLD

This website is originally written in the Czech language. Most content is machine (AI) translated into English. The translation may not be exact and may contain errors.

Tento článek si můžete zobrazit v originální české verzi. You can view this article in the original Czech version.
Exchange server a statistiky posílání zpráv

Exchange server and messaging statistics

Edited 25.04.2019 10:00 | created | Petr Bouška - Samuraj |
This article has been updated for Exchange 2016! I think Exchange Server is missing one crucial thing and that is various traffic statistics. It is common for competing products to display a range of information that Exchange does not directly include. For example, hourly, daily, weekly and monthly statistics on emails sent and received, both internally and from the Internet. Statistics for different connectors. Undeliverability data, most frequent recipients and senders, popular domains. And speaking of miscellaneous information, the Exchange could also clearly report on its health. This article is a small guide on how to get some information.
displayed: 23 555x (21 806 CZ, 1 749 EN) | Comments [5]

Note: The described information was tested on Exchange Server 2010 SP3.

I wrote this article for Exchange 2010, a lot of things were based on practical tests. With the advent of Exchange 2016, the outlined script stopped working because some events and even the way of logging addresses changed. I studied the information for the new version and especially performed an analysis of many different data samples for the main situations (I couldn't find documentation on what events are logged for certain situations). I didn't want to rewrite the entire article, so in the second half of the article I added my notes related to Exchange 2016, but also a new perspective on the logs (so some things are different than described in the old article).

The current description of using Get-MessageTrackingLog is in the article Exchange Server 2016 DSN, Message Tracking and Messaging Analytics.

What are Message Tracking Logs

The only thing we have available on the Exchange server are Message Tracking Logs. The Hub Transport, Edge Transport and Mailbox server roles log all message transfer activities into these logs. The logs are text files stored on the server. By default, they are kept for 30 days and are located in the path C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\MessageTracking. We can view the current settings using the Exchange Management Shell (EMS, i.e. PowerShell).

Get-MailboxServer SERVER | FL Message*
Get-TransportServer SERVER | FL Message*

If we want to change any value, we can again use PowerShell. Detailed description is available from Microsoft at Configure Message Tracking.

How to work with the logs

We can work with the logs in several ways. In the Exchange Management Console (EMC) under the Toolbox there is the Tracking Log Explorer. We can also use another Microsoft tool, which is the Log Parser Studio. Or we can use EMS, where we have the cmdlet Get-MessageTrackingLog available.

Examples of tracking log browsing

The following examples serve only to outline the syntax of the Get-MessageTrackingLog cmdlet. At the beginning, we define a one-day time interval.

$Start = (Get-Date).AddDays(-1)
$End = (Get-Date)

Find all messages (and events) sent from the address jmeno@firma.cz and display the listed information for them.

Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.Sender -eq "jmeno@firma.cz" } | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,TotalBytes,RecipientCount,MessageId -AutoSize 

Find all events for a message with a specific MessageId.

Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.MessageId -eq "<928D4983D1BD6A4790DB8323A23E8A8B4AB386AF@server.firma.local>" }| FT EventId,Sender,Recipients,MessageSubject -AutoSize 

Display all events of the types RECEIVE, SEND, DELIVER.

Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.EventId -eq "RECEIVE" -or $_.EventId -eq "SEND" -or $_.EventId -eq "DELIVER" } | FT EventId,Sender,Recipients -AutoSize 

Another way to search for events of one type RECEIVE (we can do the same for searching by Sender, etc.).

Get-MessageTrackingLog -Start $Start -End $End -EventID "RECEIVE" -ResultSize Unlimited | FT Sender, Recipients, TotalBytes -AutoSize 

Information in the log

A large number of events of various types (EventID) are stored in the logs. The main events that we will probably be interested in are RECEIVE, SUBMIT, SEND and DELIVER. The related information is the source of the event (Source), where the main ones are SMTP and STOREDRIVER.

A certain description of the events and other values can be found in the article Understanding Message Tracking, Message Tracking (Exchange Server 2013) and Search Message Tracking Logs. Unfortunately, there is nowhere a graph or description of the sequence of individual events for a certain action (e.g. sending an email outside the company). The article Exchange 2013 Mail Flow (Part 3) describes something, but it is not everything that needs to be known, and I couldn't find more information.

Another important value in the log is MessageId, which is a unique identifier that identifies a single message circulating in our organization. We can then filter out all unique messages (always just one event) for a certain time period (here we save them to a variable).

$log = Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.EventId -eq "RECEIVE" -or $_.EventId -eq "SEND" -or $_.EventId -eq "DELIVER" } | Sort-Object MessageID -Unique | Select-Object Sender,Recipients 

Then we can print them out (here we return only the sender and recipients) or count their number.

$log | FT * -AutoSize 
$log | Measure-Object 

All properties that the cmdlet returns are Timestamp, ClientIp, ClientHostname, ServerIp, ServerHostname, SourceContext, ConnectorId, Source, EventId, InternalMessageId, MessageId, Recipients, RecipientStatus, TotalBytes, RecipientCount, RelatedRecipientAddress, Reference, MessageSubject, Sender, ReturnPath, MessageInfo, MessageLatency, MessageLatencyType, EventData.

Variations of message sending

In practice, there are many situations that we want to record and that generate various messages in the log. We have Inbound (incoming), Outbound (outgoing), Local (local), Remote (remote) messages. In addition, multiple Exchange server roles are involved in the process, passing the message on and also generating events. The roles can be installed on a single server, but also each separately, which will cause different behavior. We can also have multiple servers with the same role (for redundancy and load balancing, using DAG, CAS Array, etc.), perhaps even in different Sites.

It then depends on which Client Access Server (CAS) the user is connected to and where their mailbox database is located. In common cases in practice, the situation is that even if we have multiple Exchange servers in the network, almost every message is passed between two of them. In the image below, we can find all possible variations when two users send a message to each other.

Varianty posílání zpráv

Many tutorials on the Internet use the Get-MessageTrackingLog command and simply filter the SEND events for sent messages and RECEIVE for received messages. But in my opinion, this is wrong. When we list the events for a single message, we find that the RECEIVE event is logged several times (because the servers pass the message between themselves).

Counting messages

Another important thing to consider is which messages we want to count as outgoing and which as incoming. I count a message as sent if the sender is in my Exchange organization. Similarly, I count it as received if the recipient is in my organization. If two users in the organization send a message to each other, it will be counted as both sent and received. The question is how to count a message that is sent to multiple recipients (I'm not dealing with this right now).

To summarize the information above, a single message (no matter how it is sent) always generates more than one event in the log. In many cases, the RECEIVE event is generated multiple times (because the message is sent between internal servers) and sometimes even SEND. On the other hand, there are also situations where the SEND event is not generated at all when sending a message.

We can list a portion of the log sorted by time, including milliseconds for accuracy, and analyze which events are used in which circumstances.

Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Sort-Object Timestamp | FT @{label='Timestamp [ms]';Expression={"{0:dd.MM.yyyy HH:mm:ss.fff}" -f $_.Timestamp}}, EventId, Source, ClientHostname, ServerHostname, Sender, Recipients, MessageId

I wanted to put together a table of all situations of sending and receiving a message, what events are generated in them and in what order. After analyzing a larger number of practical situations, I found that the order in the log probably does not correspond to reality. Several times I have the receipt (receive) of a message from the internet logged earlier on the second mail server than on the one that receives messages from the internet. Because we are working with milliseconds, the slight difference in time between the servers probably plays a role.

So at least the events that are most commonly logged for the main situations. Abbreviations: I - internet, E1 - first Exchange server receiving from the internet, E2 - second Exchange server.

communication counted situation events in the log
I - E1 - E2 1x receive RECEIVE - RECEIVE - DELIVER - SEND
E2 - E1 - I 1x send SUBMIT - RECEIVE - SEND
E1 - E2 1x receive, 1x send RECEIVE - SUBMIT - SEND (not always) - RECEIVE - DELIVER

As you can see, we can't simply count the RECEIVE and SEND events. In the first case, where only the receipt of the message should be counted once, it would be counted twice for receipt and once for sending. So to determine the sent and received messages, we need to use something else.

Listing events by operation

One thing that can help us is that we can filter messages sent or received from company addresses (domains), or sent between company addresses. Here we first prepare the variables we will use also later. We define a 10-minute time interval. We create a list of servers that generate logs, and store all events from these servers for the time interval in the $log variable.

$Start = (Get-Date).AddMinutes(-10)
$End = (Get-Date)
$hubs = Get-ExchangeServer | where {$_.isHubTransportServer -eq $true -or $_.isMailboxServer -eq $true} 
$log = $hubs | Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Select-Object Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,TotalBytes,RecipientCount,MessageId

We will further filter the data in the $log variable. We will display the records where the sender's or recipient's address ends with the name of our domain.

$log | Where-Object { $_.Sender -like "*firma.cz" } | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId
$log | Where-Object { $_.Recipients -like "*firma.cz*" } | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId
$log | Where-Object { $_.Sender -like "*firma.cz" -and $_.Recipients -like "*firma.cz*" } | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId

Another option that is more favorable for our situation is to use communicating servers. Incoming messages to the company should have only one RECEIVE event, where the ClientHostname address is different from the company's mail servers. They can also include company application servers (which we can also filter out, usually by domain). If we have a pre-positioned server (such as an Antispam GW) through which all incoming mail passes, we can use its address.

$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.ClientHostname -notlike "mailfirma*" } | Sort-Object Timestamp | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId 

Similarly, outgoing messages to the internet should have only one SEND event, where the ServerHostname is the address of a server on the internet (not our mail server).

$log | Where-Object { $_.EventId -eq "SEND" -and $_.ServerHostname -notlike "mailfirma*" } | Sort-Object Timestamp | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId 

Using the considerations described above, we can calculate various statistical data. From all the messages, we can get information about the size of the largest email, the average size, and the total volume of messages.

$mail_all_stats = $log | Sort-Object MessageID -Unique | Measure-Object -Property TotalBytes -Sum -Maximum -Average 

We will calculate the number of received messages from the internet, from application servers, and sent messages to the internet. When we subtract these values from the total number of messages, the result should be the number of messages sent locally (which means that we count them as both sent and received). The error will be introduced to us by messages sent to multiple recipients, and especially if some of the recipients are internal and some are outside the company. These messages are usually counted as one (either internal or external).

Most frequent recipients and senders

Another interesting piece of information may be which sender sent the most messages and which recipient received the most. Someone may be interested in this only from the point of view of messages sent from the company, but I am interested in the data from all messages. For a list of the twenty most frequent senders, we will filter the events where the sender is filled in, get the unique events (i.e. individual messages) according to MessageID, group them by sender, which will also give us the counts (Count). Then we just sort by descending order and print the required number of values.

$log | Where-Object { $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending | Select -First 20 | FT Count, Name -AutoSize 

We will get the list of most frequent recipients similarly.

$log | Where-Object { $_.Recipients } | Sort-Object MessageID -Unique | Group-Object Recipients | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize 

Long-term data

What we have described in the previous chapters can be used, for example, in a script that we run every night and have the result emailed to us. We can run the script for a longer period (maximum as long as the logs are stored), but this may not be practical. Another option is, when regularly running the daily script, to save the daily statistics to a CSV text file. Then we can, for example, in Excel, obtain long-term statistics and graphs.

Complex script

I have described my considerations that led to the following script, which, when run daily, provides certain statistical data on email communication. The values are not 100% accurate, and part of the script is based on practical observation of the behavior of a particular environment, so in a different configuration it may be slightly different. Definitely, it is necessary to adjust the conditions in the lines where the values $mail_receive_inet, $mail_receive_app, $mail_send_inet are calculated, as described above.

When the script runs on the mail server, it creates a text file c:\Skripty\Mail-statistics.txt, which it then sends as an email (here are other values that need to be adjusted to your environment). Then it's a good idea to prepare the file c:\Skripty\mail-statistics.csv, into which a single header row will be inserted:

date;total;processed;size[MB];receive;receive-inet;receive-app;receive-local;send;send-inet;send-local

Daily statistical data will be added to this file.

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010

$Start = (Get-Date -Hour 00 -Minute 00 -Second 00).AddDays(-1)
$End = (Get-Date -Hour 23 -Minute 9 -Second 59).AddDays(-1)

$hubs = Get-ExchangeServer | where {$_.isHubTransportServer -eq $true -or $_.isMailboxServer -eq $true}

$log = $hubs | Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.EventId -ne "NOTIFYMAPI" -and $_.EventId -ne "HAREDIRECT" } | Select-Object EventId,ClientHostname,ServerHostname,Sender,Recipients,TotalBytes,MessageId

$mail_all_stats = $log | Sort-Object MessageID -Unique | Measure-Object -Property TotalBytes -Sum -Maximum -Average
$mail_all_unique = $mail_all_stats.Count
$mail_receive_inet = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.ClientHostname -notlike "mailserver1" -and $_.ClientHostname -notlike "mailserver2" -and $_.ClientHostname -notlike "*firma.local" } | Measure-Object).Count
$mail_receive_app = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.ClientHostname -notlike " mailserver1" -and $_.ClientHostname -notlike " mailserver2" -and $_.ClientHostname -like "*firma.local" } | Measure-Object).Count
$mail_send_inet = ($log | Where-Object { $_.EventId -eq "SEND" -and $_.ServerHostname -notlike " mailserver1" -and $_.ServerHostname -notlike " mailserver2" } | Measure-Object).Count
$mail_local = $mail_all_unique - $mail_receive_inet - $mail_receive_app - $mail_send_inet
$mail_send = $mail_send_inet + $mail_local
$mail_receive = $mail_receive_inet + $mail_receive_app + $mail_local
$mail_all = $mail_send + $mail_receive

$str = "Mail servers statistics for date " + $Start.ToShortDateString() +"`r`n`r`n"
$str += "Servers:`r`n" + $hubs
$str += "`r`n`r`nTotal emails:`r`n"
$str += " count: " + "{0:N0}" -f $mail_all + "`r`n"
$str += " processed messages: " + "{0:N0}" -f $mail_all_unique + "`r`n"
$str += " size: " + "{0:N2}" -f ($mail_all_stats.Sum / (1024 * 1024)) + " MB `r`n"
$str += " biggest mail: " + "{0:N2}" -f ($mail_all_stats.Maximum / (1024 * 1024)) + " MB `r`n"
$str += " average size: " + "{0:N2}" -f ($mail_all_stats.Average / 1024) + " kB `r`n"
$str += "`r`n`r`nReceive emails:`r`n"
$str += " count: " + "{0:N0}" -f $mail_receive + "`r`n"
$str += " from internet: " + "{0:N0}" -f $mail_receive_inet + "`r`n"
$str += " from application servers: " + "{0:N0}" -f $mail_receive_app + "`r`n"
$str += " local: " + "{0:N0}" -f $mail_local + "`r`n"
$str += "`r`n`r`nSend emails:`r`n"
$str += " count: " + "{0:N0}" -f $mail_send + "`r`n"
$str += " to internet: " + "{0:N0}" -f $mail_send_inet + "`r`n"
$str += " local: " + "{0:N0}" -f $mail_local + "`r`n"
$str += "`r`n`r`nTop senders:"
$str += $log | Where-Object { $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending | Select -First 20 | FT Count, Name -AutoSize | Out-String
$str += "Top recipients:"
$str += $log | Where-Object { $_.Recipients } | Sort-Object MessageID -Unique | Group-Object Recipients | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize | Out-String
$str | Out-File -FilePath "c:\Skripty\Mail-statistics.txt" -Width 300

Send-MailMessage -From "admin@firma.cz" -To "recipient@firma.cz" -Subject "Mail server statistics" -SmtpServer "mailserver1.firma.local" -Attachments "c:\Skripty\Mail-statistics.txt" -Encoding ([System.Text.Encoding]::Unicode) -Body "Hello,`n`nStatistics on mail sent yesterday.`n`nYour administrator ;-)`n`nPS: Everything is in the attachment`n"

#date;total;processed;size[MB];receive;receive-inet;receive-app;receive-local;send;send-inet;send-local 
$out = $Start.ToShortDateString() + ";" + $mail_all + ";" + $mail_all_unique + ";" + ($mail_all_stats.Sum / (1024 * 1024)) + ";" + $mail_receive + ";" + $mail_receive_inet + ";" + $mail_receive_app + ";" + $mail_local + ";" + $mail_send + ";" + $mail_send_inet + ";" + $mail_local
$out | Out-File c:\Skripty\mail-statistics.csv -Append -Encoding default

Exchange 2016 and sent message statistics

The description below are my thoughts, which are based on observation and testing. So they may not be correct or precise. Likewise, the calculations of different types of messages are approximate values, because some special situations require additional conditions and different counting towards the total. But I would say that the resulting inaccuracy is a few percent.

Connecting to the server

If we are working on an Exchange server (in PowerShell and not in Exchange Management Shell), we need to add the Exchange SnapIn.

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn 

Another option is to connect remotely.

$ExchSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI https://SERVER/PowerShell/?SerializationLevel=Full -Authentication Kerberos
Import-PSSession -Session $ExchSession

To speed things up, we can load only selected commands.

Import-PSSession -Session $ExchSession -CommandName Get-Mailbox, New-Mailbox, Enable-Mailbox, Set-Mailbox,
 Add-MailboxFolderPermission, Set-CASMailbox -FormatTypeName *

At the end, it is necessary to disconnect.

Remove-PsSession $ExchSession

Selecting data from logs

First, we will save the data from the Exchange servers for a certain period into a variable. When analyzing the events (EventId), we will find that we are interested in only some of them. We also do not need all the attributes (items, because we are calculating statistical data) in the result.

Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object {
 $_.EventId -eq "RECEIVE" -or $_.EventId -eq "SEND" -or $_.EventId -eq "DELIVER" -or $_.EventId -eq "SENDEXTERNAL" `
 -or $_.EventId -eq "FAIL" } | Select-Object Timestamp, EventId, Source, Sender, Recipients, ClientIp, OriginalClientIp,
 ServerIp, TotalBytes, MessageId, Directionality, RecipientCount 

Message processing

Email (message) is sent by one sender to one or more addresses, i.e. received by one or more recipients (it can also be sent to a DG, then the Distribution Group will be expanded and delivered to individual members). Events for one message are identified by the same MessageId. So we can filter for one event per MessageId (we get unique processed messages).

$log | Sort-Object MessageID -Unique 

In this way, some event is selected that we cannot determine, and certain attributes change for different events (for example, the message size increases during processing, the list of recipients may be different, because there can be multiple delivery events for one message), so we do not get completely accurate values. But we can get the number of processed messages and maybe the approximate total size (the command at the end of the script is modified to return the event with the largest size for the message).

$log | Sort-Object MessageID -Unique | Measure-Object -Property TotalBytes -Sum -Maximum -Average 

If we wanted to count the message sent to multiple recipients multiple times (as separate sender and each recipient messages), we would have to use the RecipientCount attribute.

Some messages automatically generate additional messages, for example DSN, Inbox Rules, Out Of Office. They have a new MessageId and the Reference attribute usually contains a reference to the message from which they originated.

Normally, each message is sent (once) and received (even multiple times - multiple recipients). When we look at it from the mail server perspective, when sending a message, the server first receives the message and then sends it or delivers it locally. Internally, there are often multiple SMTP sends and deliveries if we have multiple servers and the message is sent between them.

In the script (at the end of the article) we count:

  • $mail_all_stats.Count - the number of unique MessageId, i.e. processed messages, each should have a receive and a send
  • $mail_receive_inet - the number of events for receiving messages from the internet
  • $mail_receive_local - the number of events for receiving messages from the local mailbox
  • $mail_receive_app_anon - the number of events for receiving messages from the application server anonymously
  • $mail_receive_app_auth - the number of events for receiving messages from the application server authenticated
  • $mail_send_inet - the number of events for sending messages to the internet (one message can have multiple events)
  • $mail_send_local - the number of events for delivering to the local mailbox (one message can have multiple events)
  • $mail_fail - all error events (failed to deliver)
  • $mail_send - the total number of sends
  • $mail_receive - the total number of receives
  • $mail_receive_app - the total number of receives from application servers

Received messages - Receive

These are the messages received

  • from the local mailbox (for local or external delivery) - picked up from the mailbox
  • from senders outside the organization (from the internet, for local delivery) - through the SMTP Receive connector
  • from the company's application servers (for local or external delivery) - through the SMTP Receive connector

Some events do not have a standard receive, for example an automatic reply has the Source MAILBOXRULE. We process the logs for a certain period of time, so it may happen that some messages are missing events from the beginning or end. Therefore, in practice the total number of messages is greater than the number of receive events.

$mail_all_stats.Count >= $mail_receive_inet + $mail_receive_local + $mail_receive_app_anon + $mail_receive_app_auth

Receive from local mailbox

  • each message has one STOREDRIVER event RECEIVE, in OriginalClientIp is usually the sender's address
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "STOREDRIVER" } 

Identification of other received messages is more complicated, because SMTP events RECEIVE also exist when forwarding messages between internal servers.

Receive from outside the organization

  • this is an anonymous (unauthenticated) receive through the SMTP Receive connector
  • SMTP event RECEIVE
  • OriginalClientIp is the sender's address
  • for anonymous receive, Exchange sets the Directionality attribute to Incoming
  • we can also send emails from application servers this way, which we don't want to count as receive from the internet, so we'll also exclude internal sender addresses (the addresses shown are just examples)
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" `
 -and $_.OriginalClientIp -notlike "10.0.*" -and $_.OriginalClientIp -notlike "192.168.*" } 

Receive from application servers

  • anonymous - similar to receiving from outside the organization, but instead we specify the addresses of the application servers from which the messages are received
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" `
 -and ($_.OriginalClientIp -like "10.0.*" -or $_.OriginalClientIp -like "192.168.*") } 
  • authenticated - the most complex situation
  • SMTP events RECEIVE
  • Directionality is Originating
  • this is also how messages between mail servers are identified
  • in OriginalClientIp it can be the IP of our servers or the client (Outlook, can also connect from the internet), so we need to determine the possible addresses of application servers outside the mail servers
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Originating" `
 -and $_.OriginalClientIp -notlike "10.0.0.100" -and $_.OriginalClientIp -notlike "10.0.0.110" `
 -and ($_.OriginalClientIp -like "10.0.0.*" -or $_.OriginalClientIp -like "192.168.*") } 

Sent (delivered) messages - Send

These are the messages sent

  • for delivery to the local mailbox - placed in the mailbox
  • to recipients outside the organization (internet) - using the SMTP Send connector

If a message is sent to multiple recipients, then for one MessageId there is one receive event (the sender is always one), but there may be multiple send events. The message is delivered to each mailbox database where one of the recipients is located (there is always one event for all recipients in one DB), or it is sent outside the organization (one event for each target server). So there are usually more sent messages than the total number of messages.

$mail_all_stats.Count <= $mail_send_inet + $mail_send_local

Send to outside the organization

  • each message has one SMTP event SENDEXTERNAL
  • in ServerIp is the address of the recipient's server
$log | Where-Object { $_.EventId -eq "SENDEXTERNAL" -and $_.Source -eq "SMTP" } 

Delivery to local mailbox

  • for each group of recipients who are in the same database, there is one STOREDRIVER event DELIVER
  • in OriginalClientIp is usually the sender's address
$log | Where-Object { $_.EventId -eq "DELIVER" -and $_.Source -eq "STOREDRIVER" } 

Most frequent senders

Sender of the message is always one, so we can take the unique messages and group them by sender. This will give us the number of messages from each sender.

$log | Where-Object { $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending |
 Select-Object -First 20 | FT Count, Name -AutoSize | Out-String

Most frequent recipients

The situation with recipients is more complicated, as there can be more of them (and whether we want to track the email of the distribution group or its individual members). They are stored as an array and cannot be grouped by it. But we can use the ExpandProperty parameter, then each array item is placed on a separate line. We lose the other attributes, but we don't need them in this case. Grouping will give us the number of emails for individual recipients (the total sum may then be greater than the number of unique messages).

$log | Where-Object { $_.Recipients } | Sort-Object MessageID -Unique | Select-Object -ExpandProperty Recipients |
 Group-Object | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize | Out-String

The result may not be accurate. We must be aware that a message for multiple recipients has all recipients in some of the logged events, but in most of them, there are fewer. For example, the delivery event is one for each mailbox DB and contains the recipients in that DB. Therefore, for each MessageId, we need to select a specific event where all recipients are present. I came up with the method below, where we group by MessageId and process the individual items (for each MessageId), always selecting the one with the largest number of recipients. From it, we take the recipients, expand them, and group them.

$log | Where-Object { $_.Recipients } | Group-Object MessageID | ForEach-Object { $_.Group |
 Sort-Object RecipientCount -Descending | Select-Object -First 1 | Select-Object -ExpandProperty Recipients } |
 Group-Object | Sort-Object Count -Descending | Select-Object -First 20 | FT Count, Values -AutoSize | Out-String

Outline of a complex script for Exchange 2016

Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn

$Start = (Get-Date -Hour 00 -Minute 00 -Second 00).AddDays(-1)
$End = (Get-Date -Hour 23 -Minute 59 -Second 59).AddDays(-1)

$servers = Get-TransportService

$log = $servers | Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.EventId -eq "RECEIVE" -or $_.EventId -eq "SEND" -or $_.EventId -eq "DELIVER" -or $_.EventId -eq "SENDEXTERNAL" -or $_.EventId -eq "FAIL" } | Select-Object Timestamp, EventId, Source, Sender, Recipients, ClientIp, OriginalClientIp, ServerIp, TotalBytes, MessageId, Directionality, RecipientCount

$mail_all_stats = $log | Group-Object MessageID | ForEach-Object { $_.Group | Sort-Object TotalBytes -Descending | Select-Object -First 1 } | Measure-Object -Property TotalBytes -Sum -Maximum -Average

$mail_send_inet = ($log | Where-Object { $_.EventId -eq "SENDEXTERNAL" -and $_.Source -eq "SMTP" }).Count
$mail_send_local = ($log | Where-Object { $_.EventId -eq "DELIVER" -and $_.Source -eq "STOREDRIVER" }).Count
$mail_receive_local = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "STOREDRIVER" }).Count
$mail_receive_inet = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" -and $_.OriginalClientIp -notlike "10.0.*" -and $_.OriginalClientIp -notlike "192.168.*" }).Count
$mail_receive_app_anon = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" -and ($_.OriginalClientIp -like "10.0.*" -or $_.OriginalClientIp -like "192.168.*") }).Count
$mail_receive_app_auth = ($log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Originating" -and $_.OriginalClientIp -notlike "10.0.0.100" -and $_.OriginalClientIp -notlike "10.0.0.110" -and ($_.OriginalClientIp -like "10.0.0.*" -or $_.OriginalClientIp -like "192.168.*") }).Count
$mail_fail = ($log | Where-Object { $_.EventId -eq "FAIL" }).Count

$mail_send = $mail_send_inet + $mail_send_local
$mail_receive = $mail_receive_inet + $mail_receive_local + $mail_receive_app_anon + $mail_receive_app_auth
$mail_receive_app = $mail_receive_app_anon + $mail_receive_app_auth

$top_senders = $log | Where-Object { $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending | Select-Object -First 20 | FT Count, Name -AutoSize | Out-String
$all_recipients = $log | Where-Object { $_.Recipients } | Group-Object MessageID | ForEach-Object { $_.Group | Sort-Object RecipientCount -Descending | Select-Object -First 1 | Select-Object -ExpandProperty Recipients }
$top_recipients = $all_recipients | Group-Object | Sort-Object Count -Descending | Select-Object -First 20 | FT Count, Values -AutoSize | Out-String
$top_FAIL_recipients = $log | Where-Object { $_.EventId -eq "FAIL" -and $_.Recipients } | Sort-Object MessageID -Unique | Select-Object -ExpandProperty Recipients | Group-Object | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize | Out-String
$top_FAIL_senders = $log | Where-Object { $_.EventId -eq "FAIL" -and $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize | Out-String

$str = "Mail servers statistics for date " + $Start.ToShortDateString() +"`r`n`r`n"
$str += "Servers:`r`n" + $servers
$str += "`r`n`r`nTotal emails:`r`n"
$str += "  count: " + "{0:N0}" -f $mail_all_stats.Count + "`r`n"
$str += "  size: " + "{0:N2}" -f ($mail_all_stats.Sum / (1024 * 1024)) + " MB `r`n"
$str += "  biggest mail: " + "{0:N2}" -f ($mail_all_stats.Maximum / (1024 * 1024)) + " MB `r`n"
$str += "  average size: " + "{0:N2}" -f ($mail_all_stats.Average / 1024) + " kB `r`n"
$str += "`r`nReceive emails:`r`n"
$str += "  count: " + "{0:N0}" -f $mail_receive + "`r`n"
$str += "  from local mailbox: " + "{0:N0}" -f $mail_receive_local + "`r`n"
$str += "  from internet sender: " + "{0:N0}" -f $mail_receive_inet + "`r`n"
$str += "  from application servers: " + "{0:N0}" -f $mail_receive_app + "`r`n"
$str += "`r`nSent emails:`r`n"
$str += "  count: " + "{0:N0}" -f $mail_send + "`r`n"
$str += "  to local mailbox: " + "{0:N0}" -f $mail_send_local + "`r`n"
$str += "  to internet recipient: " + "{0:N0}" -f $mail_send_inet + "`r`n"
$str += "  recipients count: " + "{0:N0}" -f $all_recipients.Count + "`r`n"
$str += "`r`nFAIL emails: " + "{0:N0}" -f $mail_fail + "`r`n"
$str += "`r`nTop senders:"
$str += $top_senders
$str += "Top recipients:"
$str += $top_recipients
$str += "Top FAIL recipients:"
$str += $top_FAIL_recipients
$str += "Top FAIL senders:"
$str += $top_FAIL_senders
$str | Out-File -FilePath "c:\Scripts\Mail-statistics.txt" -Width 400

Send-MailMessage -From "admin@firma.cz" -To "recipient@firma.cz" -Subject "Mail server statistics" -SmtpServer "10.0.0.100" -Attachments "c:\Scripts\Mail-statistics.txt" -Encoding ([System.Text.Encoding]::Unicode) -Body "Hello,`n`nMail statistics for yesterday.`n`nYour administrator ;-)`n`nPS: All details are in the attachment`n"

#date;total;size[MB];receive;receive-inet;receive-app;receive-local;sent;sent-inet;sent-local
$out = $Start.ToShortDateString() + ";" + $mail_all_stats.Count + ";" + ($mail_all_stats.Sum / (1024 * 1024)) + ";" + $mail_receive + ";" + $mail_receive_inet + ";" + $mail_receive_app + ";" + $mail_receive_local + ";" + $mail_send + ";" + $mail_send_inet + ";" + $mail_send_local
$out | Out-File c:\Scripts\mail-statistics.csv -Append -Encoding default

Example script output

Mail servers statistics for date 24.04.2019

Servers:
MAIL1 MAIL2

Total emails:
  count: 13 763
  size: 750.04 MB 
  biggest mail: 18.32 MB 
  average size: 55.80 kB 

Receive emails:
  count: 13 627
  from local mailbox: 2 586
  from internet sender: 3 343
  from application servers: 7 698

Sent emails:
  count: 16 229
  to local mailbox: 13 910
  to internet recipient: 2 319
  recipients count: 22 235

FAIL emails: 101

Top senders:
Count Name                            
----- ----                            
 3927 xxxxx@firma.cz       
 1259 xxxxx@firma.cz
  565 xxxxx@firma.cz      
  467 xxxxx@firma.cz      

Top recipients:
Count Values                       
----- ------                       
  804 {xxxxx@firma.cz}          
  777 {xxxxx@firma.cz}          
  758 {xxxxx@firma.cz}           
  748 {xxxxx@firma.cz}   
  
Top FAIL recipients:
Count Values                                                                                      
----- ------                                                                                      
   26 {xxxxx@firma.cz}                                                                        
   10 {xxxxx@gmail.com}                                                    
    7 {xxxxx@seznam.cz}

Top FAIL senders:
Count Values                                                         
----- ------                                                         
   16 {xxxxx@firma.cz}                             
   16 {xxxxx@firma.cz}
   12 {xxxxx@firma.cz}
Author:

Related articles:

Microsoft Exchange

Almost since the beginning of my practice, I have been involved in the administration of the Microsoft mail server, i.e. Exchange Server. I started with the 2003 version and worked my way up to Exchange Online. The articles cover many areas of management. Most since the migration to Exchange Server 2016 and its complete configuration. But also Exchange Hybrid and e-mail security.

If you want write something about this article use comments.

Comments
  1. [1] Jan

    Ahoj, velice povedený článek :)

    Nicméně měl bych dotaz, řešil jsi někdy nastavení správného času v logu? Ukazuje se mi tam o 2 hodiny méně, zřejmě je log nastaven dle GMT. Dá se to nějak změnit?

    Tuesday, 01.04.2014 10:21 | answer
  2. [2] Samuraj

    respond to [1]Jan: Neřešil, časy mám OK. Čekal bych, že záleží na nastavení serveru.

    Tuesday, 01.04.2014 10:26 | answer
  3. [3] Martin

    Zdravím, chci složit poklonu za vynikající článek. A rád bych položil jeden dotaz. Lze v rámci logování získávat informaci o IP adrese, na které se uživatel přihlásil do svého OWA účtu?

    Děkuji

    Friday, 24.04.2015 13:48 | answer
  4. [4] Samuraj

    respond to [3]Martin: Díky :-). To je trochu jiná oblast, než řešil tento článek. Nevím, jestli je na to nějaká jednoduchá možnost, ale připojení na OWA je klasicky služba IIS. To se loguje do W3SVC1 logů a z nich by to mohlo jít vytáhnout, i když je potřeba vymyslet nějaké inteligentní filtrování (je tam i ActiveSync a vůbec spousta informací).

    Friday, 24.04.2015 14:06 | answer
  5. [5] Marek

    respond to [3]Martin:

    Ahoj,

    musíš použít audit pro daný účet. Než můžeš hledat v audit logu, musíš jej povolit a potom si nastavit práva, aby jsi v něm mohl hledat.

    Monday, 21.01.2019 14:01 | answer
Add comment

Insert tag: strong em link

Help:
  • maximum length of comment is 2000 characters
  • HTML tags are not allowed (they will be removed), you can use only the special tags listed above the input field
  • new line (ENTER) ends paragraph and start new one
  • when you respond to a comment, put the original comment number in squar brackets at the beginning of the paragraph (line)