Logs that matter
You cannot detect what you do not log, but logging everything drowns you in noise and cost. This lesson shows you how to choose log sources by the questions you need to answer, pull the high-value events from identity, endpoint, network, cloud and application logs, put time and fields into one shape, and query them fast enough to use in a real investigation.
By the end you can
- Pick log sources from the detection questions you need to answer, and spot coverage gaps
- Name the high-value events in identity, endpoint, network, cloud and application logs
- Keep time trustworthy: UTC everywhere, event time versus ingest time, and late events
- Normalise fields to one common schema so a query works across every source
- Query logs fast by filtering on time and source first, then pivoting across events
The everyday picture
Think of the cameras in a Mangrove Market store. If no camera points at the stockroom door, you will never see who took the missing crates, no matter how hard you look afterwards. So coverage comes first: you point cameras at the doors, the tills and the stockroom, the places where something worth watching happens.
But you cannot record every corner of every store at full quality and keep it forever. The storage fills up, and when something goes wrong you have hours of footage and no way to find the clip. So the manager does three more things. Every camera stamps its footage with the same clock, so clips from different cameras line up. The till-area footage is kept longer than the quiet-aisle footage. And to find a theft, you jump straight to the time and the camera first, then watch, instead of scrubbing through everything. Security logging is the same job: cover the places that matter, put every source on one clock, keep the important streams longer, and search by time and source before anything else.
Log what answers a question
The instinct when you first run a SIEM is to send it everything. It feels safe, but it is a trap. Every gigabyte costs money to store and index, every noisy source buries the events you need, and a query over a year of debug logs times out when you are mid-incident. The useful question is not "what can we collect?" but "what do we need to answer?"
Start from the questions an investigation asks. Who signed in, from where, and did they pass a second factor? What program started this process, and what was its parent? Did this host talk to an unfamiliar server? Who changed a setting in the cloud account at 2 a.m.? Each question points at one source and a few high-value events inside it. Everything else in that source is background you can sample or drop.
| Source | High-value events | Question it answers |
|---|---|---|
| Identity (the login system) | Sign-ins and failures, MFA prompts, password resets, privilege and group changes | Who is this account, from where, and did it really prove itself? |
| Endpoint / EDR | Process creation with command line and parent, new services, autostart and persistence | What ran on this machine, and what started it? |
| Network | Firewall allow and deny, DNS queries, web proxy requests | Did this host talk to something it should not? |
| Cloud | Control-plane audit events (a new user, a changed policy), storage access | Who changed the environment, and who read the data? |
| Application (the Partner app) | Sign-in, admin actions, bank-detail and credit-limit changes | What happened inside the app that network logs cannot see? |
From this you write a logging baseline: the short list of events every Mangrove host and app must send, no matter what. Every server and store PC sends process creation and sign-in events; every app sends auth and admin actions; the cloud account sends its full audit trail. Then you look for coverage gaps, the places with no camera. The classic Mangrove gap is the back-office PCs in stores: nobody was shipping their process and sign-in logs, so an attacker on one of them was invisible. A gap is worse than a noisy source, because a noisy source you can tune, but a gap you cannot query at all. Inside a SOC set out who reads these; here we decide what reaches them.
Quick check
Mangrove's SIEM bill is too high, so the team wants to cut volume. Which cut is safest?
High-volume, low-value streams such as full web request logs are where you cut, keeping errors and a sample. Identity, endpoint and cloud audit events are low-volume and answer the questions investigations actually ask, so dropping them creates coverage gaps. Seven-day retention everywhere would lose the slow-burn attacks that only show up weeks later.
One schema so a query works everywhere
Raw logs are a tower of Babel. The login system calls the account UserPrincipalName, the firewall calls it src_user, the Partner app calls it actor_email, and the cloud audit trail calls it userIdentity.arn. If you leave them that way, every search and every rule has to know all the dialects, and a pivot from a sign-in to a process is hand-written each time.
The fix is a common schema: one agreed set of field names that every source is mapped into as it arrives. Two public schemas are worth knowing. The Elastic Common Schema (ECS) uses dotted names such as user.name, source.ip, event.action and @timestamp. The Open Cybersecurity Schema Framework (OCSF) is a vendor-neutral effort doing the same job. You do not have to invent your own; pick one and map every source to it.
| Source and its field | Common schema field |
|---|---|
Login system UserPrincipalName | user.name |
Firewall src_ip | source.ip |
Partner app actor_email | user.name |
EDR CommandLine | process.command_line |
Cloud audit eventName | event.action |
Now one query can ask "show every event where user.name is r.khan, newest first" and get sign-ins, processes, firewall hits and app actions in a single list. That is what makes pivoting fast: you follow one account or one host across sources by the same field name, instead of translating in your head. Normalise at collection time, not at search time, so the slow work happens once and every later query is cheap. You will lean on this schema again when you write portable rules in Writing detections.
Quick check
A new store device sends its logs, but its user field arrives as login_id and is never mapped into the schema. What breaks?
Unmapped fields do not show up under the schema name, so a search on user.name skips them without any error. That silent gap is the dangerous part: the analyst trusts an incomplete answer. The events are still stored and timestamped; they are just invisible to the pivot that matters.
Time is the backbone
Every investigation is a timeline, so time has to be trustworthy before anything else is. Three rules keep it that way. Store and display everything in UTC, so a sign-in in one city and a process on a server in another line up without mental arithmetic. Keep every clock in sync with a time service (NTP); a store PC whose clock drifts ten minutes puts its events in the wrong place and hides the link to everything else. And record who observed the event, not just when.
The subtle trap is that an event has two times. Event time is when it happened on the device. Ingest time is when it reached the SIEM. Most of the time they are seconds apart, so people forget the difference. But a POS till that lost its network in a monsoon storm buffers its logs and sends them hours later, when it reconnects. The sign-in happened at 02:00; the SIEM only saw it at 09:00. This is the same split a product tracking plan draws between when something happened and when it arrived.
Late events change how you search. If you ran a rule at 03:00 that read ingest time, the 02:00 sign-in was not there yet and the rule missed it. So detection and hunting search by event time, and you re-run searches over a window that has already passed to catch events that arrived late. A row that lands in the store seven hours after it happened is not a bug; it is normal, and your queries have to expect it.
Retention, cost and logs as evidence
Logs are expensive, so you split storage into tiers. Hot storage is fast to search and holds the recent window you query every day, often the last 30 to 90 days. Cold storage is cheap and slow and holds older logs you rarely touch but must keep, often for a year or more. The split follows the question: a triage analyst works the last few days, while a breach is often found months after it started, so identity, EDR and cloud audit logs earn a long retention, and chatty debug logs do not.
Logs are also evidence. If an incident ends in a dispute, an insurance claim or a court, the logs are what prove the story, so their integrity matters. Keep them on storage the people being investigated cannot edit (write-once or append-only), limit who can delete them, and record when each log was collected. An attacker who can quietly clear logs erases the very record you would use to catch them, which is why log access is itself a high-value event worth watching.
Quick check
Finance asks Mangrove to keep only 14 days of logs to save money. The SOC pushes back. What is the strongest reason?
The dwell time between a break-in and its discovery is often weeks or months, so a 14-day window throws away the only record of how the attack started. The schema and timestamps do not depend on retention, and less data would query faster, not slower, so those are not the reason.
Querying fast
When an alert fires you have minutes, not hours, so the order of a query matters as much as its logic. Filter on time and source first: a narrow event-time window and the one or two sources that could answer the question shrink billions of rows to thousands before any other work. Then pivot on the fields you indexed, the ones you follow from event to event: user.name, host.name, source.ip. Searching an indexed field is a jump; searching unindexed free text is a scrub through everything.
Then aggregate, then drill in. Count sign-ins per account per hour, or processes per host, to find the handful that stand out, and only then read the raw events for those few. Pulling raw rows first and reading them by eye is how an investigation runs out of time. The practice exercise below is a real pivot: finding successful sign-ins that no MFA challenge protected, the exact gap that let an attacker into a store PC in Thinking like an attacker.
Build it
Build the three things a SOC needs before it can query: a logging baseline, a unified event table in the common schema, and a fast query over it.
Write the logging baseline
List the events every host and app must send, with a retention tier for each. This is the contract; a source that cannot meet it is a coverage gap to raise.
Markdown# Mangrove logging baseline (every host and app) | Source | Must send | Tier (hot days) | Keep | |-----------|--------------------------------------------|-----------------|------| | Identity | sign-in, MFA, reset, privilege change | 90 | 1 yr | | Endpoint | process creation (+ parent, command line) | 90 | 1 yr | | Network | DNS query, firewall allow/deny, proxy | 30 | 90 d | | Cloud | full control-plane audit, storage access | 90 | 1 yr | | App | sign-in, admin action, bank/credit change | 90 | 1 yr | All events in UTC, clock synced to NTP, log store append-only.Create the unified event table in the common schema
Every source is mapped into these columns as it arrives, so one table answers questions across all of them. Index the fields you pivot on and the event time.
SQLCREATE TABLE event ( id bigint PRIMARY KEY, event_time timestamptz NOT NULL, -- when it happened, UTC ingest_time timestamptz NOT NULL, -- when the SIEM saw it source text NOT NULL, -- identity | endpoint | network | cloud | app host_name text, user_name text, -- mapped from every source's own field source_ip inet, event_action text, -- signin | process_start | dns_query | ... outcome text, -- success | failure detail jsonb -- the fields unique to this source ); -- Pivot fast on who, where and when CREATE INDEX event_user_time ON event (user_name, event_time); CREATE INDEX event_host_time ON event (host_name, event_time);Query it the fast way
Filter on event time and source first, then aggregate to find the accounts that stand out, then drill into their raw events. Note the window is on
event_time, so late arrivals still match.SQL-- Accounts with the most failed sign-ins overnight, worst first SELECT user_name, count(*) AS failures FROM event WHERE source = 'identity' AND event_action = 'signin' AND outcome = 'failure' AND event_time >= '2026-10-18 00:00+00' AND event_time < '2026-10-18 06:00+00' GROUP BY user_name ORDER BY failures DESC LIMIT 20;
Bring it to your team
Ask these the next time someone adds a system or reviews the SIEM bill.
- What question would this log answer in an investigation? If there is no answer, why are we paying to store it?
- Which places could an attack happen that we have no log for at all?
- Is every source in UTC and on a synced clock, and do our rules search by event time?
- Is each source mapped into the common schema, or does a pivot silently miss it?
- How long do we keep identity, EDR and cloud audit logs, and would that survive a breach found in six months?
- Can the people we might investigate delete or edit these logs?
Red flags
- "Just send everything to the SIEM" with no baseline and no retention plan.
- Timestamps in local time, or clocks that are never checked.
- A source whose fields were never mapped, so it is invisible to every pivot.
- Rules that search by ingest time and quietly miss late events.
- Logs stored where an admin, or an attacker with admin, can delete them.
Practice
Find sign-ins that no MFA protected
The auth_event table holds identity events in UTC. A sign-in counts as protected only if there is a successful MFA event for the same account in the five minutes up to and including the sign-in. Return every successful sign-in (kind = 'login', outcome = 'success') that was not protected, as account and signed_in_at (formatted YYYY-MM-DD HH24:MI), earliest first. This is the exact gap from the capstone: a working password with no second factor.
CREATE TABLE auth_event (id int PRIMARY KEY, account text, kind text, outcome text, at_utc timestamp); INSERT INTO auth_event VALUES (1, 'ama', 'mfa', 'success', '2026-10-18 08:59:30'), (2, 'ama', 'login', 'success', '2026-10-18 09:00:10'), (3, 'r.khan', 'login', 'success', '2026-10-18 02:14:00'), (4, 'backoffice_svc', 'login', 'success', '2026-10-18 02:20:00'), (5, 'leila', 'mfa', 'success', '2026-10-18 09:50:00'), (6, 'leila', 'login', 'success', '2026-10-18 10:00:00'), (7, 'tomas', 'login', 'failure', '2026-10-18 11:00:00'), (8, 'imran', 'mfa', 'failure', '2026-10-18 12:00:00'), (9, 'imran', 'login', 'success', '2026-10-18 12:01:00');
-- Successful sign-ins not protected by a recent MFA success
SELECT account, to_char(at_utc, 'YYYY-MM-DD HH24:MI') AS signed_in_at
FROM auth_event
WHERE kind = 'login' AND outcome = 'success'
ORDER BY at_utc;
[{"account": "r.khan", "signed_in_at": "2026-10-18 02:14"},
{"account": "backoffice_svc", "signed_in_at": "2026-10-18 02:20"},
{"account": "leila", "signed_in_at": "2026-10-18 10:00"},
{"account": "imran", "signed_in_at": "2026-10-18 12:01"}]
Show a solution
SELECT l.account, to_char(l.at_utc, 'YYYY-MM-DD HH24:MI') AS signed_in_at
FROM auth_event l
WHERE l.kind = 'login' AND l.outcome = 'success'
AND NOT EXISTS (
SELECT 1 FROM auth_event m
WHERE m.account = l.account
AND m.kind = 'mfa' AND m.outcome = 'success'
AND m.at_utc > l.at_utc - interval '5 minutes'
AND m.at_utc <= l.at_utc
)
ORDER BY l.at_utc;The starter returns every successful login, including Ama's, which an MFA success 40 seconds earlier did protect. The NOT EXISTS subquery is the pivot: for each login it looks back five minutes on the same account for a successful MFA. Leila's MFA was ten minutes before, outside the window, and Imran's MFA failed, so both count as unprotected. This is the pattern behind many identity detections.
Design the coverage for one question
Mangrove wants to answer: "Did an attacker who took over a store PC copy customer data out to the internet?" Name the log sources and the one or two high-value events in each that you would need, the common-schema field you would pivot on to tie them to one host, and one coverage gap that would make the question unanswerable.
- You name endpoint logs (process creation) and network logs (DNS and firewall or proxy) as the core sources, and identity for the sign-in
- You pick a pivot field that ties events to one machine, such as
host.nameorsource.ip - You name a real high-value event in each source, not just "the logs"
- Your coverage gap is concrete, such as the store PCs not sending process logs, or no egress or proxy logging, and you say why it makes the question unanswerable
Show a model answer
Identity: the sign-in to the store PC, to know which account and whether MFA was passed. Endpoint: process creation on that host, to see an archiving or upload tool run and its parent. Network: DNS queries and firewall allow or proxy logs from that host, to see an unfamiliar destination and a large outbound transfer. Pivot on host.name to tie the sign-in, the process and the network events to the one PC, and on user.name to follow the account. The killer coverage gap is the store back-office PCs not shipping process and network logs at all: with no camera on that host, you can see the sign-in but never what ran or what left, so the question cannot be answered whatever rules you write.
Check yourself
A detection rule that looks for sign-ins outside working hours runs every hour on the last hour of data and reads ingest time. A store PC that lost its network sends a 02:00 sign-in only at 09:00. What happens, and what is the fix?
Reading ingest time means the 02:00 event is invisible until 09:00, long after the 02:00 run finished, so the rule never sees it. Searching by event time and re-running windows that have already passed catches events that arrive late, which is normal for devices with patchy networks.
You must answer "which program started this hidden process on the store PC, and with what command line?" Which source answers it?
Process creation with the parent and the command line is an endpoint event; only the EDR on that machine sees it. Firewall logs show connections not processes, cloud audit logs show changes to the cloud environment, and identity logs show who signed in, not what then ran.
Two sources name the account differently: the firewall says src_user and the app says actor_email. Neither is mapped to the schema. An analyst pivots on user.name for a suspect account and sees nothing from these two. What is the right read?
Unmapped fields do not appear under the schema name, so the pivot skips them with no error and the analyst sees a falsely quiet account. The cure is to map both sources into user.name at collection, not to trust the empty result or delete useful logs.
Flashcards
Key takeaways
- Choose log sources from the questions an investigation asks; a coverage gap you cannot query is worse than a noisy source you can tune.
- The high-value events live in identity, endpoint/EDR, network, cloud and application logs; write a baseline every host and app must meet.
- Keep time trustworthy: UTC, synced clocks, and search by event time so late events are not lost.
- Map every source into one common schema so a single query can pivot across all of them.
- Query fast by filtering on time and source first, pivoting on indexed fields, then aggregating and drilling in; keep logs as protected evidence.