Lineage, audit logs and system tables
In this lesson you will
- Use lineage for impact analysis, root cause and privacy questions
- Name the key columns of the audit table and its regional scope
- State the documented lineage limitations before making claims
- Turn governance rules into scheduled checks with owners
Governance without proof is a policy document
A grant model says who should have access. Auditors, security teams and your own incident reviews ask a different question: who actually used it, what changed, and where did this number come from? Unity Catalog answers with two kinds of evidence: lineage, which records how data flows, and audit logs, which record who did what. Both are queryable as system tables.
In this lesson we use lineage to assess impact, audit logs to answer access questions, and system tables to build a small set of governance checks that run on a schedule. We do not cover shipping logs to an external SIEM.
Lineage
The documentation says Unity Catalog captures lineage automatically for queries run on Databricks, and aggregates it across all workspaces attached to the metastore. It tracks tables, views, columns, ML model versions and file paths. Notebooks, jobs and dashboards appear as consumers of a table. In Catalog Explorer, a table's Lineage tab lists downstream jobs and dashboards, and the lineage graph shows column-level links.
You do not instrument anything. If your pipeline reads bronze.orders_raw and writes silver.orders, the link exists.
Three uses pay for the effort:
- Impact analysis before a change. "If I rename this column, which tables, jobs and dashboards break?" Look at downstream lineage before you merge.
- Root cause during an incident. "This gold number is wrong." Walk upstream to the first table where the value diverges.
- Evidence for privacy questions. "Where does this personal data field end up?" Column lineage answers it, and it is the base of a record of processing.
Query it
Lineage is also available in the system.access.table_lineage and system.access.column_lineage system tables, so you can analyse it in SQL. The documentation says they keep a rolling window of 395 days by default, while the lineage shown in Catalog Explorer is kept indefinitely, starting from 1 September 2024.
Illustration
-- Schematic: which targets are built from a given source table in the last 90 days?
-- Confirm the exact column names in your workspace with DESCRIBE system.access.table_lineage.
SELECT DISTINCT target_table_full_name
FROM system.access.table_lineage
WHERE source_table_full_name = 'prd_finance.silver.invoices'
AND event_date >= current_date() - INTERVAL 90 DAYS
AND target_table_full_name IS NOT NULL;Know the limits
The documentation lists limitations that change what you can claim:
- Lineage captured before 1 September 2024 is not available.
- Jobs submitted through the Jobs API
runs submitrequest or thespark submittask type show no link to the job run. - Lineage is not preserved when catalogs, schemas, tables, views or columns are renamed.
- RDDs, global temporary views and
information_schematables are not captured. - Lineage through temporary tables ignores
UPDATE,DELETEandMERGE INTOchanges. - Column lineage is not shown for path-based references, although the system table records them.
- User-defined functions are captured only at table level.
Use lineage as strong evidence, not as a guarantee. A missing edge means "not captured", and it does not prove that no flow exists. When you build a record of processing for a regulator, say how it was produced.
Audit logs
Audit events land in system.access.audit. The documentation describes the columns you use most:
| Column | Meaning |
|---|---|
event_time and event_date | When the event happened. Filter on event_date for performance |
user_identity | A struct with the identity that made the request, including email |
service_name | The service that initiated the request, for example unityCatalog |
action_name | The kind of event, for example getTable |
request_params | Parameters of the request. Keys with SQL definitions are omitted unless you are an account admin or in the databricks_pii_access group |
response | A struct with statusCode and errorMessage |
Two facts matter when you rely on it. Most audit logs are only available in the workspace's region, and account-level events record workspace_id as 0. And when a workspace is deleted, its audit events older than 14 days are removed from the table, so export what your retention policy requires before you decommission one.
A starting query, built from the documented columns:
Illustration
-- Schematic: who read a table in the last 7 days, and did the request succeed?
-- Action names and request_params keys vary by event type: inspect a few rows first.
SELECT event_time,
user_identity.email,
action_name,
response.statusCode
FROM system.access.audit
WHERE event_date >= current_date() - INTERVAL 7 DAYS
AND service_name = 'unityCatalog'
AND action_name = 'getTable'
ORDER BY event_time DESC;Read a few raw rows before you write a filter. The values of action_name and the keys inside request_params depend on the event, and your query should be based on what your workspace actually emits.
Questions the evidence answers
| Question | Evidence |
|---|---|
Who changed the grants on prd_finance.gold last month? | Audit events for permission changes |
| Which dashboards break if I drop this table? | Table lineage, consumers |
Where does the phone column flow? | Column lineage |
| Has anyone queried the payroll table who is not in HR? | Audit events, cross-checked with group membership |
| Which groups have a grant they never use? | Audit access events compared with SHOW GRANTS |
The last row feeds the quarterly access review from lesson 3: remove what is never used.
Governance checks as code
Turn the questions into scheduled queries, owned like any pipeline, with alerts for hits. A short list to start with:
- Direct grants to users. List grants whose principal is a person and not a group. The target is zero.
- Owners that are people. Same check on ownership.
- Access to sensitive tables by non-members. Compare audit reads on sensitive tables with the allowed groups.
- Changes to the security schema. Alert on any change to filter and mask functions.
- Unusual volume. A principal that reads far more than usual.
Run these in the platform repository, deploy them through the bundle, and route alerts to the platform owner group. This also demonstrates governance to a client: you can show the checks, the last run and the alerts. Our case on self-service data onboarding at RIVM describes how governed onboarding scales when the controls are part of the platform.
Retention and privacy of the evidence
Audit logs and lineage hold personal data, such as e-mail addresses of users. Apply our privacy rules to your own evidence: restrict who can read the tables, set a retention period, and export only what you need. The audit table is among the most sensitive data in the platform.
Common mistakes
- Reading lineage as complete. Check the limitations.
- Filtering on
event_timeinstead ofevent_date. Slow queries. - Writing a query before looking at the data. Event shapes vary.
- Running checks by hand. Schedule them and alert.
- Forgetting regional scope. Audit logs are mostly per region.
Summary
Use lineage for impact, root cause and privacy questions, and audit logs for access questions. Query the system tables, respect the documented limits, and turn your governance rules into scheduled checks with owners.
Next lesson: sharing data with other organisations without losing control.
The idea in one picture
Evidence that governance works
- Rule in code
- Lineage and audit tables
- Scheduled check
- Alert to owner
- Quarterly access review
Knowledge check
Answer each question. You see the reason behind every answer at once.
0 of 3 answered
Hands-on lab
Goal. Write four governance checks as queries and a runner that evaluates them over sample data.
Where to run it
Local Python Run this lab on your own machine with Python 3.10 or newer in a virtual environment.
Steps
- Create grants.csv and audit.csv with 30 sample rows each, including a direct user grant, a person owner and a read of a sensitive table by a non-member.
- Load both into SQLite or pandas. Write four checks: direct user grants, person owners, sensitive reads by non-members, unusual read volume per principal.
- Write a runner that prints each check with its owner, its hit count and a pass or fail result.
- Remove the planted problems and confirm that all checks pass.
Expected result
Four checks fail on the planted data with named owners and pass on the clean data.
Go deeper
Official documentation and recommended courses from the providers.
- Databricks
- Paid exam
- Advanced
Databricks Certified Data Engineer Professional
The advanced credential for production pipelines, streaming, security and performance.
- docs.databricks.com
Data lineage
- docs.databricks.com
Audit log system table
Open documentation : Audit log system table. Opens at the provider
Read at NexOps
Articles from our own projects on the same topic.
Sources
Last verified 2026-11-11
We check every source against the official documentation. Products change, so confirm details there before you build.