I had to check many log files of an Exchange 2016 server to see which clients or applications were on which Exchange Send Connector and what emails were being received on which Receive Connector. A nice thing to do using PowerShell 🙂 This blog post will show you how to get a simple overview of the logs.
How the script works
It uses the ExchangeInstallPath to set the path for scanning SMTP logs, and it reads all the logs from there for both SmtpSend and SmtpReceive. It outputs the log file’s name, the date/time, which Connector was used, and the Local and Remote endpoint with reverse DNS lookup, if possible, so you get the hostnames instead of just an IP-Address… (Without the port numbers for easier filtering), the event (Send or Receive), and the date (It filters only to report the ones with a @-sign, making the CSV export much smaller). It also shows the total amount of log files so that you know it’s processing and many are still left.
Usually, I throw all the results in a $total variable, but… I had to export it to a temporary CSV file because the memory usage exceeded 3Gb on larger servers…
Running the script
You must run the script on an Exchange Server (Because it uses the ExchangeInstallPath variable, but you can adjust it to a UNC-path if needed). When started, it checks the access to the logs and if the report can be saved in the specified location:

When done, it will report that it’s done exporting the events and that the temporary CSV is removed.

The smtplogging.csv looks like this (I removed some details by replacing it with XXXXX)

The script
Below are the contents of the script. Save it on your Exchange Server and run it from there (Or change all the $env:ExchangeInstallPath occurrences to a UNC path)
#Read SMTP files from $ExchangeInstallPath variable try { $files = Get-ChildItem -Path "$($env:ExchangeInstallPath)\TransportRoles\Logs\FrontEnd\ProtocolLog" -Recurse -Filter *log -ErrorAction Stop | Sort-Object LastWriteTime Write-Host ("Reading SMTP Logfiles from {0}" -f "$($env:ExchangeInstallPath)TransportRoles\Logs\FrontEnd\ProtocolLog") -ForegroundColor Green } catch { Write-Warning ("Couldn't access {0}, check permissions and exiting now..." -f "$($env:ExchangeInstallPath)\TransportRoles\Logs\FrontEnd\ProtocolLog") return } #Set variables $report = 'C:\temp\smtplogging.csv' $filenumber = 0 try { New-Item -Path $report -ItemType File -ErrorAction Stop -Force:$true -Confirm:$false | Out-Null Write-Host ("{0} is valid for saving results, continuing..." -f $report) -ForegroundColor Green Remove-Item -Path $report -Force:$true -Confirm:$false | Out-Null } catch { Write-Warning ("Coudn't write to {0}, check permissions and exiting now..." -f $report) return } foreach ($logfile in $files) { $filenumber++ write-host ("[{0}/{1}] Processing file {2}" -f $filenumber, $files.count, $logfile.FullName) -ForegroundColor Green try { $csv = Import-Csv -Path $logfile.Fullname -Header 'date-time', 'connector-id', 'session-id', 'sequence-number', 'local-endpoint', 'remote-endpoint', 'event', 'data', 'context' -Delimiter ',' -Encoding UTF8 } catch { Write-Warning ("Could not process {0}, file in use or cleaned during processing?") } foreach ($connection in $csv) { if (-not ($connection.'date-time'.StartsWith('#')) -and ($connection.data -match '@')) { #Set $EventField variable (https://learn.microsoft.com/en-us/exchange/mail-flow/connectors/protocol-logging?view=exchserver-2019#fields-in-the-protocol-log) Switch ($connection.event) { '+' { $EventField = "Connect" } '-' { $EventField = "Disconnect" } '>' { $EventField = "Send" } '<' { $EventField = "Receive" } '*' { $EventField = "Information" } } $total = [pscustomobject]@{ FileName = $logfile.FullName DateTime = $connection.'date-time' Connector = $connection.'connector-id' LocalEndpoint = if ((Resolve-DnsName -ErrorAction SilentlyContinue $connection.'local-endpoint'.Split(':')[0]).NameHost) { "$((Resolve-DnsName $connection.'local-endpoint'.Split(':')[0]).NameHost)" } else { "$($connection.'local-endpoint'.Split(':')[0])" } RemoteEndpoint = if ((Resolve-DnsName -ErrorAction SilentlyContinue $connection.'remote-endpoint'.Split(':')[0]).NameHost) { "$((Resolve-DnsName $connection.'remote-endpoint'.Split(':')[0]).NameHost)" } else { "$($connection.'remote-endpoint'.Split(':')[0])" } Event = $EventField Data = $connection.data } #Export results to temporary CSV file $total | Export-Csv -Path "$($report).tmp" -NoTypeInformation -Delimiter ';' -Encoding UTF8 -Append } } } #Read temporary CSV, sort on date and save results to CSV location specified in the $report variable Write-Host ("Exporting results to {0}" -f $report) -ForegroundColor Green try { Import-Csv -Path "$($report).tmp" -Delimiter ';' -Encoding UTF8 -ErrorAction Stop | Sort-Object DateTime | Export-Csv -Path $report -NoTypeInformation -Delimiter ';' -Encoding UTF8 -ErrorAction Stop } catch { Write-Warning ("Couldn't sort and export {0} to {1}, check permissions... " -f "$($report).tmp", $report) } #Cleanup temporary file try { Remove-Item -Path "$($report).tmp" -ErrorAction Stop | Out-Null Write-Host ("Cleaned {0}, done!" -f "$($report).tmp") -ForegroundColor Green } catch { Write-Warning ("Couldn't remove {0}, check permissions or if file is in use..." -f "$($report).tmp") }
Download the script(s) from GitHub here