Stream Jira Issues to Redpanda for Real-Time Metrics
This lab demonstrates a production-ready pipeline that streams Jira issues to Redpanda topics in real-time. The pipeline transforms raw Jira API responses into a normalized, consumer-friendly schema and routes issues to different topics, enabling use cases SLA monitoring and team performance analytics.
Architecture
Redpanda Connect periodically queries the Jira REST API for recently updated issues in your project. Each issue is transformed to extract key fields, flatten nested objects, and compute flags like is_high_priority and is_completed. Based on these flags, issues are routed to different Redpanda topics:
-
jira.issues.all: All issues -
jira.issues.high-priority: Issues with Highest/High priority -
jira.issues.completed: Issues marked as Done/Closed/Resolved
Use cases
SLA monitoring and alerting
-
Automatically detect stale high-priority issues.
-
Send alerts when issues haven’t been updated in a certain number of days.
-
Track response times and prevent SLA breaches.
-
Route critical issues to on-call teams.
Prerequisites
You must have the following installed on your host machine:
-
rpk (Redpanda CLI) - required to generate the Enterprise license
This lab also requires:
-
Jira instance with API access
-
Jira API token from Atlassian
This lab is intended for Linux and macOS users. If you are using Windows, you must use the Windows Subsystem for Linux (WSL) to run the commands in this lab.
Run the lab
-
Clone the repository:
git clone https://github.com/redpanda-data/redpanda-labs.git -
Change into the
docker-compose/jira-metrics-pipeline/directory:cd redpanda-labs/docker-compose/jira-metrics-pipeline -
Copy the example environment file:
cp .env.example .env -
Edit the
.envfile and configure:# Versions (optional - defaults are provided) REDPANDA_VERSION=v26.1.9 REDPANDA_CONSOLE_VERSION=v3.7.3 REDPANDA_CONNECT_VERSION=4.70.0 # Jira credentials (required) JIRA_BASE_URL=https://<domain>.atlassian.net JIRA_USERNAME=<email-address> JIRA_API_TOKEN=<api-token> JIRA_PROJECT=<jira-project-key> -
Generate a Redpanda Enterprise trial license:
rpk generate license \ --name "<first-name>" \ --last-name "<last-name>" \ --email "<email-address>" \ --company "<company-name>"This creates a 30-day trial license in
./redpanda.license. -
Export the license as a shell environment variable:
export REDPANDA_LICENSE=$(cat ./redpanda.license)The
REDPANDA_LICENSEmust be exported in your shell. It cannot be loaded from the.envfile. The--env-fileflag loads application variables (likeJIRA_*), but the license must be exported separately. -
Start the Docker containers:
docker compose up -d -
Create the Kafka topics:
docker compose exec redpanda rpk topic create jira.issues.all -p 3 docker compose exec redpanda rpk topic create jira.issues.high-priority -p 3 docker compose exec redpanda rpk topic create jira.issues.completed -p 3 -
Open Redpanda Console at localhost:8080 to view your topics and messages.
-
Monitor the Jira issues streaming into Redpanda:
# Watch all issues docker compose exec redpanda rpk topic consume jira.issues.all --format json # Watch high-priority issues docker compose exec redpanda rpk topic consume jira.issues.high-priority --format json # Watch completed issues docker compose exec redpanda rpk topic consume jira.issues.completed --format json
How it works
The pipeline performs the following steps:
-
Query Jira: Every 30 seconds, Redpanda Connect queries Jira for recently updated issues in your project
-
Transform data: Each Jira issue is transformed into a normalized schema:
-
Flattened fields: status, priority, issue type (extracted from nested Jira objects)
-
Null-safe people fields: assignee and reporter (defaults to "Unassigned"/"Unknown")
-
Raw timestamps: created, updated, resolved (preserved in original Jira format)
-
Extracted component names from component objects
-
Computed boolean flags:
is_completed,is_high_priority -
Generated issue URL:
{JIRA_BASE_URL}/browse/{key} -
Pipeline processing timestamp
-
-
Route messages: Issues are intelligently routed to topics based on computed flags:
-
High priority (Highest/High) →
jira.issues.high-priority -
Completed (Done/Closed/Resolved) →
jira.issues.completed -
All others →
jira.issues.all
-
-
Consume and analyze: Multiple downstream applications can consume from these topics independently:
-
Metrics dashboards (Grafana, Kibana)
-
Alert systems (Slack, PagerDuty)
-
Data warehouses (Snowflake, BigQuery)
-
Workflow automation
-
Pipeline configuration
The complete Redpanda Connect pipeline configuration:
# JIRA Metrics Pipeline
#
# This pipeline queries JIRA issues and streams them to Redpanda topics
# for real-time metrics, alerting, and analytics.
#
# Environment variables required:
# - REDPANDA_LICENSE: Enterprise license (must be exported in shell)
# - JIRA_BASE_URL: https://your-domain.atlassian.net
# - JIRA_USERNAME: your-email@example.com
# - JIRA_API_TOKEN: your-api-token
# - JIRA_PROJECT: YOUR_PROJECT_KEY
# - REDPANDA_BROKERS: redpanda:9092 (set in docker-compose.yml)
input:
generate:
mapping: |
# Query JIRA for recent issues (last 7 days for testing)
root.jql = "project = ${JIRA_PROJECT} AND updated >= -7d ORDER BY updated DESC"
root.maxResults = 10
root.fields = [
"key",
"summary",
"status",
"priority",
"assignee",
"reporter",
"created",
"updated",
"resolutiondate",
"issuetype",
"labels",
"components"
]
# Query every 30 seconds
interval: 30s
pipeline:
processors:
# Execute JIRA query
- jira:
base_url: "${JIRA_BASE_URL}"
username: "${JIRA_USERNAME}"
api_token: "${JIRA_API_TOKEN}"
max_results_per_page: 100
request_timeout: 30s
max_retries: 10
# Transform and enrich each issue with metrics
- mapping: |
# Basic issue info
root.issue_key = this.key
root.summary = this.fields.summary
root.status = this.fields.status.name
root.priority = this.fields.priority.name
root.issue_type = this.fields.issuetype.name
root.url = "${JIRA_BASE_URL}/browse/" + this.key
# People
root.assignee = if this.fields.assignee != null {
this.fields.assignee.displayName
} else { "Unassigned" }
root.reporter = if this.fields.reporter != null {
this.fields.reporter.displayName
} else { "Unknown" }
# Dates (keep original format for downstream processing)
root.created = this.fields.created
root.updated = this.fields.updated
root.resolved = this.fields.resolutiondate
# Labels and components
root.labels = this.fields.labels
root.components = this.fields.components.map_each(c -> c.name)
# Flags for routing
root.is_completed = this.fields.status.name.lowercase().contains("done") ||
this.fields.status.name.lowercase().contains("closed") ||
this.fields.status.name.lowercase().contains("resolved")
root.is_high_priority = ["Highest", "High"].contains(this.fields.priority.name)
# Note: Timestamp-based metrics (age, staleness, lead time) can be calculated
# by downstream consumers using the raw `created`, `updated`, and `resolved` fields.
# Add pipeline processing timestamp
root.pipeline_timestamp = now()
# Route to primary topic based on issue properties
- mapping: |
# Route based on priority and completion status
meta kafka_topic = if this.is_high_priority {
"jira.issues.high-priority"
} else if this.is_completed {
"jira.issues.completed"
} else {
"jira.issues.all"
}
output:
kafka:
addresses: ["${REDPANDA_BROKERS}"]
topic: '${! meta("kafka_topic") }'
max_in_flight: 1
batching:
count: 100
period: 1s
compression: snappy
Customize the pipeline
Adjust polling frequency
Edit connect-configs/jira-pipeline.yaml:
input:
generate:
interval: 60s # Change from 30s to 1m, 5m, etc.
Modify JQL query
Target different issues by changing the JQL query:
root.jql = "project = YOUR_PROJECT AND status != Closed AND updated >= -1h"
Troubleshooting
License errors
If you see license-related errors in the Connect logs:
-
Verify the license is exported:
echo $REDPANDA_LICENSE -
If the environment variable is empty, regenerate and export the license:
rpk generate license --name "Your Name" \ --last-name "Last" \ --email "you@example.com" \ --company "Company" export REDPANDA_LICENSE=$(cat ./redpanda.license) -
Restart the containers:
docker compose restart connect
Jira authentication errors
If you receive 401 Unauthorized errors:
-
Verify your API token is correct.
-
Ensure you’re using your email address as the username.
-
Check that your Jira instance URL is correct (include
https://). -
Test your credentials with curl:
curl -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \ "${JIRA_BASE_URL}/rest/api/3/myself"
No issues appearing
If no issues are being published:
-
Check the JQL query returns results in Jira directly.
-
Verify the
JIRA_PROJECTenvironment variable matches your project key. -
Check Connect logs for errors:
docker compose logs connect -
Adjust the query timeframe:
root.jql = "project = YOUR_PROJECT AND updated >= -24h" # Last 24 hours
Next steps
After you have Jira issues streaming into Redpanda, you can extend this pipeline with Redpanda Connect outputs:
Send alerts and notifications
-
Slack: Post issue updates to channels using the
slack_postoutput -
Discord: Send notifications with the
discordoutput -
HTTP/Webhooks: Trigger PagerDuty, Opsgenie, or custom webhooks using the
http_clientoutput
Stream to data warehouses
-
Snowflake: Load issues into Snowflake with the
snowflake_streamingoutput -
BigQuery: Stream to Google BigQuery using the
gcp_bigqueryoutput -
PostgreSQL/MySQL: Store in relational databases with
sql_insertorsql_rawoutputs
Calculate metrics
Use the raw timestamp fields (created, updated, resolved) to calculate:
-
Lead time: Average time from creation to completion
-
Cycle time: Average time from "In Progress" to "Done"
-
Throughput: Issues completed per time period
-
Aging: Distribution of issue ages
-
SLA compliance: Percentage of issues resolved within target timeframes