Pozn.: Popisované informace byly otestovány na Exchange Serveru 2010 SP3.
Tento článek jsem psal pro Exchange 2010, řada věcí vycházela z praktických testů. S příchodem Exchange 2016 přestal nastíněný skript fungovat, protože došlo ke změně některých událostí a třeba také způsobu logování adres. Prostudoval jsem informace k nové verzi a hlavně provedl analýzu mnoha různých vzorků dat pro hlavní situace (dokumentaci, jaké události se logují pro určité situace, jsem nenalezl). Nechtěl jsem přepisovat celý článek, takže v druhé polovině článku jsem doplnil svoje poznámky, které se týkají Exchange 2016, ale také nového pohledu na logy (takže jsou některé věci jinak, než popisuje starý článek).
Aktuální popis použití Get-MessageTrackingLog
je v článku Exchange Server 2016 DSN, Message Tracking a analýza posílání zpráv.
Co jsou to Message Tracking Logs
Jediné, co na Exchange serveru máme k dispozici, jsou Message Tracking Logs. Do těch zapisuje role Hub Transport, Edge Transport a Mailbox server všechny aktivity o přenosu zpráv. Logy jsou textové soubory, které se ukládají na serveru. Defaultně se uchovávají po dobu 30 dní a nachází se v cestě C:\Program Files\Microsoft\Exchange Server\V14\TransportRoles\Logs\MessageTracking
. Na aktuální nastavení se můžeme podívat pomocí Exchange Management Shell (EMS, tedy PowerShellu).
Get-MailboxServer SERVER | FL Message* Get-TransportServer SERVER | FL Message*
Pokud bychom chtěli nějakou hodnotu změnit, tak opět využijeme PowerShell. Detailní popis je u Microsoftu v Configure Message Tracking.
Jak s logy pracovat
S logy můžeme pracovat několika způsoby. V Exchange Management Console (EMC) se pod Toolbox nachází Tracking Log Explorer. Můžeme využít další nástroj Microsoftu, což je Log Parser Studio. Nebo můžeme použít EMS, kde máme k dispozici cmdlet Get-MessageTrackingLog.
Příklady procházení tracking logu
Následující příklady slouží pouze k nastínění syntaxe cmdletu Get-MessageTrackingLog
. Na začátku si definujeme časový interval jednoho dne.
$Start = (Get-Date).AddDays(-1) $End = (Get-Date)
Najdeme všechny zprávy (a události) odeslané z adresy jmeno@firma.cz
a k nim zobrazíme vyjmenované informace.
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
Najdeme všechny události k jedné zprávě s daným MessageId
.
Get-MessageTrackingLog -Start $Start -End $End -ResultSize Unlimited | Where-Object { $_.MessageId -eq "<928D4983D1BD6A4790DB8323A23E8A8B4AB386AF@server.firma.local>" }| FT EventId,Sender,Recipients,MessageSubject -AutoSize
Zobrazíme všechny události daných typů 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
Jiný způsob zápisů pro vyhledání událostí jednoho typu RECEIVE (stejně můžeme provést i hledání odesílatele pomocí parametru Sender, atd).
Get-MessageTrackingLog -Start $Start -End $End -EventID "RECEIVE" -ResultSize Unlimited | FT Sender, Recipients, TotalBytes -AutoSize
Informace v logu
Do logů se ukládá velké množství událostí různých typů (EventID
). Hlavní události, které nás asi budou zajímat, jsou RECEIVE, SUBMIT, SEND a DELIVER. Související údaj je zdroj události (Source
), kde hlavní je SMTP a STOREDRIVER.
Určitý popis událostí a dalších hodnot nalezneme v článku Understanding Message Tracking, Message Tracking (Exchange Server 2013) a Search Message Tracking Logs. Bohužel nikde není graf nebo popis posloupnosti jednotlivých událostí při nějaké akci (například odeslání emailu mimo firmu). Něco popisuje článek Exchange 2013 Mail Flow (Part 3), ale není to vše, co by bylo třeba znát a více informací jsem nenalezl.
Další důležitou hodnotou v logu je MessageId
, což je jednoznačný identifikátor, který určuje jednu zprávu kolující v naší organizaci. Můžeme pak vyfiltrovat unikátně všechny zprávy (vždy pouze jednu událost) za určité časové období (zde si je ukládáme do proměnné).
$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
Následně si je vypsat (zde vracíme pouze odesílatele a příjemce) nebo spočítat jejich počet.
$log | FT * -AutoSize $log | Measure-Object
Všechny vlastnosti, které cmdlet vrací, jsou Timestamp, ClientIp, ClientHostname, ServerIp, ServerHostname, SourceContext, ConnectorId, Source, EventId, InternalMessageId, MessageId, Recipients, RecipientStatus, TotalBytes, RecipientCount, RelatedRecipientAddress, Reference, MessageSubject, Sender, ReturnPath, MessageInfo, MessageLatency, MessageLatencyType, EventData.
Varianty posílání zpráv
V praxi nastává mnoho situací, které chceme zaznamenat a které generují různé zprávy do logu. Zprávy máme Inbound (příchozí), Outbound (odchozí), Local (lokální), Remote (vzdálené). Navíc se do procesu zapojuje více rolí Exchange serveru, které si zprávu předávají a také generují události. Role mohou být nainstalovány na jednom serveru, ale také každá samostatně, což způsobí jiné chování. Také můžeme mít více serverů se stejnou rolí (kvůli redundanci a rozložení zátěže, využívá se DAG, CAS Array, apod.), třeba i v různých Site.
Záleží pak na tom, ke kterému Client Access Server (CAS) je uživatel připojen a na kterém se nachází jeho mailbox databáze. V běžných případech v praxi nastává situace, že i když máme v síti více Exchange serverů, tak skoro každou zprávu si předávají mezi sebou dva z nich. Na obrázku níže můžeme nalézt všechny možné varianty, kdy si dva uživatelé posílají zprávu.
Mnoho návodů na internetu využívá příkaz Get-MessageTrackingLog
a jednoduše filtruje události SEND
pro odeslané zprávy a RECEIVE
pro přijaté. To je, ale dle mého názoru, špatně. Když si vypíšeme události pro jednu zprávu, tak zjistíme, že třeba událost RECEIVE
se zaloguje několikrát (protože si zprávu předávají ještě servery mezi sebou).
Počítání zpráv
Další důležitá věc na zamyšlení je, jaké zprávy chceme počítat jako odchozí a jaké jako příchozí. Já počítám zprávu mezi odeslané, pokud je odesílatel v mé Exchange organizaci. Stejně tak mezi přijaté, pokud je příjemce v mé organizaci. Pokud si posílají zprávu dva uživatelé organizace, tak se započítá mezi odeslané i přijaté. Otázka je, jak počítat zprávu, která je odeslána více příjemcům (to v tuto chvíli neřeším).
Abych shrnul výše uvedené informace, jedna zpráva (jedno jak poslána) vždy vygeneruje více než jednu událost v logu. V hodně případech se vygeneruje vícekrát událost RECEIVE (protože se zpráva posílá mezi interními servery) a někdy i SEND. Naopak jsou i situace, kdy se při odeslání zprávy vůbec událost SEND negeneruje.
Můžeme si vypsat nějakou část logu seřazenou dle času, pro přesnost včetně milisekund, a analyzovat jaké události se při jaké příležitosti používají.
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
Chtěl jsem dát dohromady tabulku všech situací odeslání a příjmu zprávy, jaké se při ní generují události a v jakém pořadí. Po analýze většího množství praktických situací, jsem zjistil, že pořadí v logu patrně neodpovídá skutečnosti. Několikrát mám u zprávy z internetu dříve zalogované přijetí (receive) na druhém poštovním serveru, než na tom, který přijímá zprávy z internetu. Protože pracujeme s milisekundami, tak patrně hraje roli i nepatrný rozdíl v čase mezi servery.
Takže alespoň události, které se nejčastěji logují pro hlavní situace. Zkratky: I - internet, E1 - první Exchange server přijímá z internetu, E2 - druhý Exchange server.
komunikace | počítaná situace | události v logu |
---|---|---|
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 (ne vždy) - RECEIVE - DELIVER |
Jak je vidět, tak nemůžeme jednoduše počítat události RECEIVE a SEND. V prvním případě, kdy se má započítat pouze jednou příjem zprávy, by se započítal dvakrát příjem a jednou odeslání. Takže pro určení odeslaných a přijatých zpráv musíme použít něco jiného.
Výpis událostí dle operace
Jedna věc, která nám může pomoci je, že si můžeme vyfiltrovat zprávy odeslané nebo přijaté z firemních adres (domén), případně poslané mezi firemními adresami. Zde si nejprve připravíme proměnné, které budeme používat i dále. Definujeme časový interval 10 minut. Vytvoříme seznam serverů, které generují logy, a seznam všech událostí z těchto serverů za časový interval uložíme do proměnné $log
.
$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
Dále již budeme pouze filtrovat údaje v proměnné $log
. Zobrazíme záznamy, kde je adresa odesílatele nebo příjemce ukončena názvem naší domény.
$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
Pro naši situaci výhodnější je jiná možnost, kdy využijeme komunikující servery. Příchozí zprávy do firmy by měli mít právě jednu událost RECEIVE, kde je jako ClientHostname
adresa jiná než firemní poštovní servery. Mohou zde být firemní aplikační servery (které můžeme také odfiltrovat, většinou dle domény). Pokud máme nějaký předřazený server (třeba jako Antispam GW), přes který chodí veškerá příchozí pošta, tak můžeme využít jeho adresu.
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.ClientHostname -notlike "mailfirma*" } | Sort-Object Timestamp | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId
Stejně tak odchozí zprávy do internetu by měli mít právě jednu událost SEND, kde je ServerHostname
nějaká adresa serveru v internetu (ne náš poštovní server).
$log | Where-Object { $_.EventId -eq "SEND" -and $_.ServerHostname -notlike "mailfirma*" } | Sort-Object Timestamp | FT Timestamp,EventId,Source,ClientHostname,ServerHostname,Sender,Recipients,MessageId
Pomocí výše popsaných úvah můžeme spočítat různé statistické údaje. Ze všech zpráv můžeme získat informaci o velikosti největšího emailu, průměrné velikosti a celkovém objemu zpráv.
$mail_all_stats = $log | Sort-Object MessageID -Unique | Measure-Object -Property TotalBytes -Sum -Maximum -Average
Spočítáme počet přijatých zpráv z internetu, z aplikačních serverů a odeslaných zpráv do internetu. Když tyto hodnoty odečteme od celkového počtu zpráv, tak by nám měl vyjít počet zpráv zaslaných lokálně (to znamená, že je počítáme jako odeslané i jako přijaté). Chybu nám tu budou zavádět zprávy odeslané více příjemcům a zvlášť pokud část příjemců je interní a část mimo firmu. Tyto zprávy se většinou započítají jako jedna (buď interní, nebo externí).
Nejčastější příjemci a odesilatelé
Další zajímavý údaj může být, jaký odesílatel odeslal nejvíce zpráv a jaký příjemce jich nejvíce obdržel. Někoho to může zajímat pouze z pohledu odeslaných zpráv z firmy, mne ale zajímá údaj ze všech zpráv. Pro seznam dvaceti nejčastějších odesílatelů vyfiltrujeme události, kde je vyplněn odesílatel, dle MessageID
získáme unikátní události (tedy jednotlivé zprávy), ty seskupíme dle odesílatele, tím získáme i počty (Count). Pak již pouze seřadíme od největšího a vypíšeme požadovaný počet hodnot.
$log | Where-Object { $_.Sender } | Sort-Object MessageID -Unique | Group-Object Sender | Sort-Object Count -Descending | Select -First 20 | FT Count, Name -AutoSize
Seznam nejčastějších příjemců získáme obdobně.
$log | Where-Object { $_.Recipients } | Sort-Object MessageID -Unique | Group-Object Recipients | Sort-Object Count -Descending | Select -First 20 | FT Count, Values -AutoSize
Dlouhodobé údaje
To, co jsme si popsali v předchozích kapitolách, můžeme využít třeba ve skriptu, který spustíme každý den v noci a výsledek si necháme zaslat na email. Skript můžeme spustit i za delší období (maximálně takové, jak dlouho se nám ukládají logy), ale to nemusí být praktické. Jiná možnost je, při pravidelném denním spouštění, ukládat denní statistiky do CSV textového souboru. Poté můžeme, třeba v Excelu, získávat dlouhodobější statistiky a grafy.
Komplexní skript
Výše jsem popsal svoje úvahy, které vedly k sestavení následujícího skriptu, který při denním spouštění přináší určité statistické údaje o mailové komunikaci. Hodnoty nejsou stoprocentně přesné a část skriptu vychází z praktického pozorování chování určitého prostředí, takže v jiné konfiguraci může být trochu jiné. Rozhodně je potřeba upravit podmínky v řádcích, kde se počítají hodnoty $mail_receive_inet
, $mail_receive_app
, $mail_send_inet
, tak jak bylo popsáno výše.
Když se skript spustí na poštovním serveru, tak vytvoří textový soubor c:\Skripty\Mail-statistics.txt
, který následně odešle jako email (zde jsou další hodnoty, které je třeba upravit dle vašeho prostředí). Potom je dobré připravit soubor c:\Skripty\mail-statistics.csv
, do kterého se vloží jeden řádek hlavičky CSV souboru:
date;total;processed;size[MB];receive;receive-inet;receive-app;receive-local;send;send-inet;send-local
Do tohoto souboru se budou doplňovat denní statistické údaje.
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 "prijemce@firma.cz" -Subject "Statistiky z poštovních serverů" -SmtpServer "mailserver1.firma.local" -Attachments "c:\Skripty\Mail-statistics.txt" -Encoding ([System.Text.Encoding]::Unicode) -Body "Ahojda,`n`nStatistiky z posílání pošty za včerejší den.`n`nVáš administrátor ;-)`n`nPS: Vše naleznete v příloze`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 a statistiky odeslaných zpráv
Popis níže jsou moje úvahy, které vychází z pozorování a testů. Nemusí tedy být správné nebo přesné. Stejně tak výpočty různých typů zpráv jsou přibližné hodnoty, protože některé speciální situace vyžadují další podmínky a jiné započítaní do celku. Ale řekl bych, že výsledná nepřesnost je pár procent.
Připojení na server
Pokud pracujeme na Exchange serveru (v PowerShellu a ne v Exchange Management Shellu), tak musíme přidat Exchange SnapIn.
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
Druhá možnost je připojit se vzdáleně.
$ExchSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://SERVER/PowerShell/?SerializationLevel=Full -Authentication Kerberos Import-PSSession -Session $ExchSession
Pro zrychlení můžeme načítat pouze vybrané příkazy.
Import-PSSession -Session $ExchSession -CommandName Get-Mailbox, New-Mailbox, Enable-Mailbox, Set-Mailbox, Add-MailboxFolderPermission, Set-CASMailbox -FormatTypeName *
Na závěr je třeba se odpojit.
Remove-PsSession $ExchSession
Výběr dat z logů
Nejprve si z Exchange serverů uložíme data, za určité období, do proměnné. Při analýze událostí (EventId) zjistíme, že nás zajímají pouze některé. Stejně tak nepotřebujeme ve výsledku všechny atributy (položky, protože počítáme statistické údaje).
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
Zpracování zpráv
Email (zpráva) je odeslán jedním odesílatelem na jednu nebo více adres, tedy přijat jedním nebo více příjemci (může být také poslán na DG, pak dojde k expand Distribution Group a doručení jednotlivým členům). Události k jedné zprávě jsou identifikovány stejným MessageId. Takže můžeme vyfiltrovat pro každé MessageId jednu událost (dostaneme unikátní zpracované zprávy).
$log | Sort-Object MessageID -Unique
Tímto způsobem se vybere nějaká událost, kterou nemůžeme určit, a určité atributy se pro různé události mění (třeba velikost zprávy zpracováním narůstá, seznam příjemců může být různý, protože k jedné zprávě může být více událostí doručení), takže nedostaneme úplně přesné hodnoty. Ale můžeme získat počet zpracovaných zpráv a třeba zhruba celkovou velikost (ve skriptu na konci článku je tento příkaz upraven, aby vracel událost s největší velikosti pro zprávu).
$log | Sort-Object MessageID -Unique | Measure-Object -Property TotalBytes -Sum -Maximum -Average
Pokud bychom chtěli zprávu více příjemcům počítat vícekrát (jako samostatné zprávy odesílatel a každý příjemce), tak bychom museli využít atribut RecipientCount.
Některé zprávy automaticky generují další zprávy, například DSN, Inbox Rules, Out Of Office. Ty mají nové MessageId a v atributu Reference bývá odkaz na zprávu, z které vznikli.
Standardně je každá zpráva odeslaná (jedenkrát) a přijatá (i vícekrát - více příjemců). Když se díváme z pohledu poštovního serveru, tak při odesílání zprávy server nejprve zprávu přijme a pak ji odesílá nebo lokálně doručuje. Interně probíhá často více SMTP odesílání a doručování, pokud máme více serverů a zpráva se posílá mezi nimi.
Ve skriptu (na konci článku) počítáme:
- $mail_all_stats.Count - počet unikátních MessageId, tedy zpracovaných zpráv, každá by měla mít příjem a odeslání
- $mail_receive_inet - počet událostí přijetí zprávy z internetu
- $mail_receive_local - počet událostí přijetí zprávy z lokální schránky
- $mail_receive_app_anon - počet událostí přijetí zprávy z aplikačního serveru anonymně
- $mail_receive_app_auth - počet událostí přijetí zprávy z aplikačního serveru autentizovaně
- $mail_send_inet - počet událostí odeslání zprávy do internetu (jedna zpráva může mít více událostí)
- $mail_send_local - počet událostí doručení do lokální schránky (jedna zpráva může mít více událostí)
- $mail_fail - všechny chybové události (nepodařilo se doručit)
- $mail_send - celkový počet odeslání
- $mail_receive - celkový počet přijetí
- $mail_receive_app - celkový počet přijetí z aplikačních serverů
Přijaté zprávy - Receive
Jedná se o zprávy přijaté
- od lokální schránky (k doručení lokálně nebo mimo) - vyzvednuté ze schránky
- od odesílatelů mimo organizaci (z internetu, k doručení lokálně) - přes SMTP Receive konektor
- od firemních aplikačních serverů (k doručení lokálně nebo mimo) - přes SMTP Receive konektor
Některé události nemají standardní příjem, třeba automatická odpověď má Source MAILBOXRULE
. Logy zpracováváme za určitou dobu, tak se může stát, že pro nějakou zprávu chybí události ze začátku nebo konce. Proto je v praxi celkový počet zpráv větší, než počet událostí příjmu.
$mail_all_stats.Count >= $mail_receive_inet + $mail_receive_local + $mail_receive_app_anon + $mail_receive_app_auth
Příjem z lokální schránky
- každá zpráva má jednu
STOREDRIVER
událostRECEIVE
, v OriginalClientIp je většinou adresa odesílatele
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "STOREDRIVER" }
Identifikace dalších přijatých zpráv je složitější, protože SMTP
události RECEIVE
existují i při předávání zprávy mezi interními servery.
Příjem mimo organizaci
- jde o anonymní (neautentizovaný) příjem přes SMTP Receive konektor
SMTP
událostRECEIVE
- OriginalClientIp je adresa odesílatele
- pro anonymní příjem nastavuje Exchange atribut Directionality na
Incoming
- takto můžeme posílat i emaily z aplikačních serverů, které nechceme započítat mezi příjem z internetu, takže ještě vynecháme interní adresy odesílatelů (uvedené adresy jsou pouze příklad)
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" ` -and $_.OriginalClientIp -notlike "10.0.*" -and $_.OriginalClientIp -notlike "192.168.*" }
Příjem od aplikačních serverů
- anonymní - podobné jako příjem mimo organizaci, ale naopak specifikujeme adresy aplikačních serverů, z kterých se zprávy přijímají
$log | Where-Object { $_.EventId -eq "RECEIVE" -and $_.Source -eq "SMTP" -and $_.Directionality -eq "Incoming" ` -and ($_.OriginalClientIp -like "10.0.*" -or $_.OriginalClientIp -like "192.168.*") }
- autentizované - nejsložitější situace
-
SMTP
událostiRECEIVE
- Directionality je
Originating
- takto se identifikují i zprávy mezi poštovními servery
- v OriginalClientIp může být IP našich serverů nebo klienta (Outlook, může se připojovat i z internetu), potřebujeme tedy určit možné adresy aplikačních serverů mimo poštovních serverů
$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.*") }
Odeslané (doručené) zprávy - Send
Jedná se o zprávy odeslané
- k doručení do lokální schránky - vložení do schránky
- příjemcům mimo organizaci (internet) - pomocí SMTP Send konektoru
Pokud se posílá zpráva na více příjemců, tak pro jedno MessageId existuje jedna událost příjmu (odesílatel je vždy jeden), ale může existovat více událostí odeslání. Zpráva se doručuje do každé databáze schránek, kde se nachází některý příjemce (vždy je jedna událost pro všechny příjemce v jedné DB), případně se odesílá mimo organizaci (jedna událost pro každý cílový server). Většinou je tedy odeslaných zpráv více než celkový počet zpráv.
$mail_all_stats.Count <= $mail_send_inet + $mail_send_local
Odeslání mimo organizaci
- každá zpráva má jednu
SMTP
událostSENDEXTERNAL
- v ServerIp je adresa serveru příjemce
$log | Where-Object { $_.EventId -eq "SENDEXTERNAL" -and $_.Source -eq "SMTP" }
Doručení do lokální schránky
- pro každou skupinu příjemců, kteří jsou ve stejné databázi, existuje jedna
STOREDRIVER
událostDELIVER
- v OriginalClientIp je většinou adresa odesílatele
$log | Where-Object { $_.EventId -eq "DELIVER" -and $_.Source -eq "STOREDRIVER" }
Nejčastější odesílatelé
Odesílatel zprávy je vždy jeden, takže můžeme vzít unikátní zprávy a seskupit podle odesílatele. Tím dostaneme počty zpráv od jednotlivých odesílatelů.
$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
Nejčastější příjemci
Složitější je situace u příjemců, kterých může být více (a zda u distribučních skupin chceme sledovat email skupiny nebo jednotlivé členy). Ukládají se jako pole a podle něj nejde groupovat. Ale můžeme využít parametr ExpandProperty, pak se každá položka pole vloží na samostatný řádek. Přijdeme o ostatní atributy, ale ty v tomto případě nepotřebujeme. Seskupením tedy dostaneme počty mailů pro jednotlivé příjemce (celkový součet pak může být větší než počet unikátních zpráv).
$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
Výsledek, ale nebude přesný. Musíme si uvědomit, že zpráva pro více příjemců má v některých zalogovaných událostech všechny příjemce, ale ve většině jich je méně. Třeba událost o doručení je pro každou mailbox DB jedna a obsahuje příjemce v dané DB. Potřebujeme tedy pro každé MessageId vybrat určitou událost, kde jsou všichni příjemci. Napadl mne způsob níže, kde seskupíme podle MessageId a zpracujeme jednotlivé položky (pro každé MessageId), vybereme vždy tu s největším počtem příjemců. Z ní vezmeme příjemce, které expandujeme a seskupíme.
$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
Nástin komplexního skriptu pro 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 "prijemce@firma.cz" -Subject "Statistiky z poštovních serverů" -SmtpServer "10.0.0.100" -Attachments "c:\Scripts\Mail-statistics.txt" -Encoding ([System.Text.Encoding]::Unicode) -Body "Ahojda,`n`nStatistiky z posílání pošty za včerejší den.`n`nVáš administrátor ;-)`n`nPS: Vše naleznete v příloze`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
Příklad výsledku skriptu
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}
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?
odpověď na [1]Jan: Neřešil, časy mám OK. Čekal bych, že záleží na nastavení serveru.
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
odpověď na [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í).
odpověď na [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.