Franz: macOS Client for Apache Kafka
Franz is a native macOS client for Apache Kafka. It helps you manage your Kafka clusters, topics and consumer groups and it provides convenient functionality for monitoring the data being published to topics.
1 Connections
When you start Franz, you are presented with the Welcome Window. From the Welcome Window you can connect to servers you’ve previously used or create new connections.
You can access the Welcome Window using the “Window” -> “Welcome to Franz” menu item or by pressing ⇧ ⌘ 1.
1.1 Security
Connection metadata is stored inside Franz’ internal metadata database, but passwords are stored in the macOS Keychain.
1.2 Workspaces
When you connect to a Kafka cluster, a Workspace Window is opened for that cluster. All operations within the workspace operate on the same connection. When you close a Workspace Window, all of its associated connections and interface objects are closed.
1.3 Topics
From the Workspace Window sidebar, you can select topics to view general information about them, to browse through their record data and publish new data, or to view their configuration.
You can publish new data on a topic by pressing the plus icon on the top right corner of the Workspace Window toolbar.
1.3.1 Information Tab
The Information tab (⌘ 1) displays general information about the selected topic.
1.3.2 Records Table Tab
The Records Table tab (⌘ 2) on a topic lets you stream live data being published on a topic or jump to any offset you like and paginate through the data manually.
When you open the Records Table tab on a topic, it immediately starts streaming recent data into the table. You can stop this by pressing the “Toggle Live Mode” button on the bottom left corner of the table. You can configure how much data is requested from the topic on each fetch by click the “Options...” button in the bottom right, and you can manually load more data by pressing the “Load More Records...” button.
From the “Options...” popover, you can also jump to any offset you like. See Jump Popover for details.
You can right-click on any record with a non-null key to publish a tombstone for it. Additionally, you can drag and drop any non-null key or value from the table to any application that accepts files to export the dragged value. You can use the “Key Format” and “Value Format” options from the “Options...” popover to control what format the columns are exported as.
Double-clicking any record will bring up its Record Detail Window.
1.3.3 Records Table Scripting
You can control the values displayed in the Records Table by writing Lua scripts. With the Records Table for a topic selected, press the scripting button – located in the center bottom of the table – to bring up the scripting window. Using the scripting window, you can edit the transform function to control how data is presented in the Records Table.
To activate and deactivate a script, press the bolt icon in the scripting window toolbar or use the ⌘ ↩ keyboard shortcut. After a script is activated, any changes made to the text of the script will cause it to be deactivated.
The record argument to the transform function is a Lua table with the following fields:
field | description |
partition_id | the partition the record was published to |
offset | the record’s offset as a non-negative integer |
timestamp | the record’s timestamp in milliseconds since the UNIX epoch |
key | the record’s key as a string or nil |
value | the record’s value as a string or nil |
You may modify any of these fields to control how the record is displayed in the Records Table. Changing the data types of these fields is prohibited and will lead to an error when data gets loaded.
Returning nil from the transform function will cause the record to be skipped in the Records Table. You can leverage this to, for example, filter records by partition:
function script.transform(record) if record.partition_id ~= 2 then return nil end return record end
Within the scripting environment, a json table is provided with functions for encoding and decoding JSON data. For example, the following script can be used to read the example property of the record’s JSON value:
local script = {} function script.transform(record) record.value = json.decode(record.value).example return record end return script
See the Scripting Reference for a list of all the functionality available within the scripting environment.
1.3.4 Jumping to Offsets
From the “Options...” popover of a Records Table, push the “Jump...” button to get to the Jump Popover (⇧ ⌘ J). From there, you can reset the record iterator to various offsets, as described below.
Earliest
Queries each partition for its earliest offset and moves the iterator back. This is slightly different than explicitly resetting all partitions to offset 0 as the first offset on a partition might not necessarily be 0 (as in the case of compacted records). Functionally, however, it has the same effect: the iterator will start iterating through records from the very beginning of the topic’s history.
Timestamp
Queries each partition for the first offset on or after the given date and time and moves the iterator there. For partitions where the timestamp represents a time after the latest offset, it makes an additional query to find the latest offset.
Recent
Queries each partition for its latest offset and moves the iterator to that position, minus the requested delta.
Latest
Queries each partition for its latest offset and moves the iterator forward.
Offset
Moves the iterator to the given offset for every partition. For partitions that are behind the selected offset, no new data will be received until they reach it.
1.3.5 Consumer Groups Tab
The Consumer Groups Tab (⌘ 3) displays the active consumer groups for the selected topic. This is an easy way to discover what groups are actively reading from individual topics.
1.3.6 Configuration Table Tab
The Configuration Table (⌘ 4) tab displays the selected topic’s configuration. Non-default values are presented in bold and sensitive values are hidden by default. You may reveal sensitive values by right clicking on them and pressing the “Reveal” context menu item.
1.4 Record Detail Window
The Record Detail Window displays the contents of individual records. You can configure the default format for the key and the value on a per-topic basis by customizing the “Key Format” and the “Value Format” from the Records Table “Options...” popover.
1.5 Consumer Groups
When you select a consumer group from the Workspace Window sidebar, you are presented with the Consumer Offsets Table. There, you can see member assignments, offsets and lag as well as reset individual offsets by right-clicking any of the entries.
You may only reset offsets if the consumer group is in the empty state.
1.6 Schema Registry
With a Workspace Window in the foreground, you can configure a Schema Registry from the main menu by selecting “Schema Registry” -> “Configure...”. Once a registry is configured, records are automatically converted to JSON according to the schemas found in the registry before being displayed in the Records Table and before being passed to any Lua scripts. To remove a registry, open the configuration window and remove its URL then press “Save”.
See this YouTube video for a live demo.
2 Keyboard Shortcuts
⇧ ⌘ 1 —
⇧ ⌘ J —
⌘ 1 —
⌘ 2 —
⌘ 3 —
⌘ 4 —
⌘ R —
⌘ T —
⇧ ⌘ T —
⌘ ↩ —
⌘ , —
3 Known Issues and Limitations
If any of these limitations are showstoppers for you, please e-mail me at bogdan@defn.io and let me know.
3.1 Schema Registry
The only type of schema registry currently supported is the Confluent Schema Registry.
4 Scripting Reference
avro.Codec | |
|
any | |
|
table | |
|
string | |
|
tuple | |||||||||||||
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
number | |
|
any | |
|
table | |
|
string | |
|
string | |
|
number | |
|
string | |
|
string | |
|
string | |
|
string | |
|
string | |
|
4.1 Examples
4.1.1 Decoding JSON Data
Use json.decode to decode your data.
local script = {} function script.transform(record) local object = json.decode(record.value) record.value = tostring(object.field) return record end return script
4.1.2 Decoding Avro Data
Use avro.parse to convert an Avro Schema into a codec. Then, use that codec to decode your record data.
local script = {} local schema = [[ { "type": "record", "name": "Person", "fields": [ { "name": "Name", "type": "string" }, { "name": "Age", "type": "int" } ] } ]] local person_codec = avro.parse(schema) function script.transform(record) local person = person_codec:read(record.value) record.value = person.Name return record end return script
You can write your schema as a Lua table and convert it to JSON using json.encode. For example, you could rewrite the above example to:
local script = {} local schema = json.encode( { type = "record", name = "Person", fields = { { name = "Name", type = "string" }, { name = "Age", type = "int" } } } ) local person_codec = avro.parse(schema) function script.transform(record) local person = person_codec:read(record.value) record.value = person.Name return record end return script
See this YouTube video for a live demo.
4.1.3 Decoding MessagePack Data
Use msgpack.unpack to decode your data.
local script = {} function script.transform(record) local object = msgpack.unpack(record.value) record.value = tostring(object.field) return record end return script
5 Guides
5.1 Mutual TLS
Franz supports connecting to Kafka servers with mTLS (also known as “two-way SSL”) enabled. To connect to a server with mTLS, merely provide an SSL Key and an SSL Certificate during connection setup.
5.1.1 How to extract Java Keystore keys & certificates
If your client key and certificates are stored in a Java Keystore file (typically, a file with the .jks extension), then you must first extract and convert them to PEM format. You can do this using the keytool utility provided by your Java Runtime Environment. For example, assuming you have a keystore file named "client.jks", you can run the following command:
keytool \ |
-importkeystore \ |
-srckeystore client.jks \ |
-destkeystore client.p12 \ |
-srcstoretype jks \ |
-deststoretype pkcs12 |
The result is a PKCS12 store named "client.p12". Next, convert this store to PEM format by running:
openssl pkcs12 \ |
-nodes \ |
-in client.p12 \ |
-out client.pem |
Finally, select the "client.pem" file as both the SSL Key and the SSL Cert from the Franz Connection Dialog and connect to your broker.
6 Privacy
Apart from when checking for updates, Franz never phones home for any reason. Automatic Updates can be turned off from the Preferences Window (⌘ ,).
7 Credits
Franz is built using the Racket programming language and distributes its runtime alongside the application. Racket is licensed under the MIT License.
The source code for Franz is available for all to read on GitHub.