javaagent-lineage-flink is a Java Agent based lineage collector for Apache Flink.
It intercepts Flink job graph generation, extracts job-level source and sink datasets, and reports a compact LineageEvent. The project is designed for production Flink clusters where lineage must be resolved before a job is submitted.
Currently supported:
| Flink version | Connector / parser | Supported lineage |
|---|---|---|
1.19.3 |
Kafka connector 3.3.0-1.19 |
Kafka source and sink for DataStream and Flink SQL |
1.20.0 |
Kafka connector 3.4.0-1.20 |
Kafka source and sink for DataStream and Flink SQL |
1.20.0 |
Paimon 1.4 |
Paimon source, exact-table sink, and CDC combined dynamic sink metadata |
Supported reporters:
| Reporter | Output |
|---|---|
| Logging reporter | Single-line JSON lineage event |
| HTTP reporter | Synchronous HTTP POST lineage event |
Flink jobs can be created through different APIs, including DataStream and Flink SQL. Instead of integrating with every API entry point, this agent hooks into the common JobGraph generation path:
PipelineExecutorUtils#getJobGraph(...)
When the input pipeline is a StreamGraph, the agent reads the generated JobGraph for JobID, reads the StreamGraph for stream nodes, parses external endpoints from those nodes, and reports:
- job id
- job name
- source datasets
- sink datasets
The project intentionally focuses on actual external read/write datasets. A lineage source or sink is not required to be a Flink API Source or Sink operator. For example, a connector-specific writer operator can still be parsed as a lineage sink.
- No runtime Flink or connector version detection.
- Users install the jars matching their Flink and connector versions.
- Incompatible versions fail normally and fail fast.
- Core, Flink instrumentation, connector lineage parsers, and reporters are packaged separately.
- Phase 1 uses the system classloader and
ServiceLoader; no custom classloader is introduced. - No external config file is required in phase 1.
- Lineage parsing and reporting errors are propagated to block job submission.
lineage-coreis the only jar passed to-javaagent.- Other lineage jars are placed on the Flink runtime classpath, usually
$FLINK_HOME/lib.
javaagent-lineage-flink/
├── lineage-core/
├── lineage-flink/
│ ├── lineage-flink-1.19/
│ ├── lineage-flink-instrumentation/
│ └── lineage-flink-parser-kafka-3.3.0/
│ └── lineage-flink-1.20/
│ ├── lineage-flink-instrumentation/
│ ├── lineage-flink-parser-kafka-3.4.0/
│ └── lineage-flink-parser-paimon-1.4/
├── lineage-reporter/
│ ├── lineage-reporter-logging/
│ └── lineage-reporter-http/
└── lineage-dist/
Runtime artifacts in the current distribution:
lineage-core
lineage-flink-1.19-instrumentation
lineage-flink-parser-kafka-3.3.0-1.19
lineage-flink-1.20-instrumentation
lineage-flink-parser-kafka-3.4.0-1.20
lineage-flink-parser-paimon-1.4-1.20
lineage-reporter-logging
lineage-reporter-http
Version ownership:
lineage-flink/lineage-flink-1.19ownsflink.version=1.19.3.lineage-flink-parser-kafka-3.3.0ownskafka.connector.version=3.3.0-1.19.lineage-flink/lineage-flink-1.20ownsflink.version=1.20.0.lineage-flink-parser-kafka-3.4.0ownskafka.connector.version=3.4.0-1.20.lineage-flink-parser-paimon-1.4ownspaimon.version=1.4.1and depends onpaimon-flink-1.20withprovidedscope.- The root project does not define Flink or connector versions.
Flink JVM
├─ -javaagent: lineage-core
│ ├─ discovers factories by ServiceLoader
│ ├─ initializes runtime
│ └─ installs instrumentation
│
├─ lineage-flink-{flink.version}-instrumentation
│ └─ intercepts PipelineExecutorUtils#getJobGraph for the selected Flink version
│
├─ lineage-flink-parser-kafka-{connector.version}
│ └─ parses Kafka source/sink endpoints from StreamNode
├─ lineage-flink-parser-paimon-1.4
│ └─ parses Paimon source/sink endpoints from StreamNode
│
└─ lineage-reporter-*
└─ reports LineageEvent
Processing flow:
LineageAgent.premain()
-> discover LineageFactory implementations
-> install Flink instrumentation
PipelineExecutorUtils#getJobGraph(...) returns
-> verify Pipeline is StreamGraph
-> read JobID from JobGraph
-> read job name and StreamNodes from StreamGraph
-> convert StreamNodes to LineageNode
-> LineageRuntime.processLineage(...)
-> parser registry parses source/sink datasets
-> coverage validator verifies both source and sink exist
-> reporter registry emits LineageEvent
Requirements:
- JDK 8+
- Maven 3.6+
Build all modules:
mvn test packageBuild the distribution package:
mvn -pl lineage-dist -am packageGenerated distribution files:
lineage-dist/target/lineage-dist-1.0.0-SNAPSHOT.tar.gz
lineage-dist/target/lineage-dist-1.0.0-SNAPSHOT.zip
The distribution contains:
javaagent-lineage-flink-1.0.0-SNAPSHOT/
└── lib/
├── common/
│ └── lineage-core-1.0.0-SNAPSHOT.jar
├── flink-1.19/
│ ├── lineage-flink-1.19-instrumentation-1.0.0-SNAPSHOT.jar
│ └── lineage-flink-parser-kafka-3.3.0-1.19-1.0.0-SNAPSHOT.jar
├── flink-1.20/
│ ├── lineage-flink-1.20-instrumentation-1.0.0-SNAPSHOT.jar
│ ├── lineage-flink-parser-kafka-3.4.0-1.20-1.0.0-SNAPSHOT.jar
│ └── lineage-flink-parser-paimon-1.4-1.20-1.0.0-SNAPSHOT.jar
└── reporter/
├── lineage-reporter-logging-1.0.0-SNAPSHOT.jar
└── lineage-reporter-http-1.0.0-SNAPSHOT.jar
See docs/quickstart.md for practical deployment examples, including local flink run, Web UI submission, YARN application mode, HTTP reporter configuration, and local debugging.
The logging reporter emits a single-line event similar to:
{"engineType":"flink","jobId":"...","jobName":"lineage-agent-kafka-debug","timestamp":1784357204468,"sources":[{"connector":"kafka","namespace":"broker-a:9092,broker-b:9092","name":"orders-input","properties":{"topic":"orders-input","bootstrap.servers":"broker-a:9092,broker-b:9092"}}],"sinks":[{"connector":"kafka","namespace":"broker-a:9092,broker-b:9092","name":"orders-output","properties":{"topic":"orders-output","bootstrap.servers":"broker-a:9092,broker-b:9092"}}]}For Kafka datasets:
connector:kafkanamespace: Kafkabootstrap.serversname: topicdirection: represented by placement insourcesorsinks
For Paimon datasets:
connector:paimonnamespace: catalog-level warehouse when availablename:database.tablefor exact-table source/sink;target_database.*for CDC combined dynamic sink when target database is available, otherwise*direction: represented by placement insourcesorsinksproperties.sourceMode/properties.sinkMode:EXACT_TABLEorCDC_COMBINEDproperties.fullName:database.tablefor exact-table datasetsproperties.tablePath: optional physical table path metadata; it is not used as warehouseproperties.targetDatabase,targetTable: CDC combined target range;targetTableis*properties.sourceDatabaseIncludePattern,sourceDatabaseExcludePattern,sourceTableIncludePattern,sourceTableExcludePattern: CDC source-side matching rules used by combined routingproperties.tableNameCaseSensitive,tableNameMergeShards,tableNamePrefix,tableNameSuffix,databasePrefixMapping,databaseSuffixMapping,tableNameMapping: stable target table naming rules for inferring which dynamic target tables a combined job may write
The core discovers components through:
META-INF/services/io.github.tkilome.flink.lineage.api.factory.LineageFactory
Supported factory types:
LineageInstrumentationFactoryLineageParserFactoryLineageReporterFactory
To add a new connector parser:
- Create a parser module under the matching Flink version module.
- Depend on the exact connector version with
providedscope. - Implement
LineageParserFactory. - Return
LineageParseResult.notMatched()if the node is unrelated. - Return
LineageParseResult.matched(...)if the node contains a relevant endpoint. - Register the factory in
META-INF/services/io.github.tkilome.flink.lineage.api.factory.LineageFactory. - Put the parser jar into
$FLINK_HOME/lib.
To add a reporter:
- Create a module under
lineage-reporter. - Implement
LineageReporterFactory. - Implement
LineageReporter. - Register the factory through the same
LineageFactoryservice file. - Put the reporter jar into
$FLINK_HOME/lib.
The agent is intentionally strict:
- If initialization fails, job submission fails.
- If instrumentation fails, job submission fails.
- If parser code is incompatible with the runtime Flink or connector version, job submission fails.
- If no source or no sink is parsed from the job, job submission fails.
- If reporting fails, job submission fails.
This behavior is deliberate. A user enabling this agent expects lineage to be available before the job starts.
- Implemented Flink versions:
1.19.3,1.20.0. - Implemented Kafka connector versions:
3.3.0-1.19,3.4.0-1.20. - Implemented Paimon parser line: Paimon
1.4on Flink1.20. - Kafka dynamic topic selectors are not statically inferred in phase 1.
- Hive and other connectors are planned but not implemented yet.
- Spark is not implemented; the module boundaries allow a future runtime-specific extension.
- No custom classloader is used in phase 1.
This project is licensed under the Apache License 2.0.