Skip to content

Configuration and Reusability

Learn how to create reusable workflows with aliases, configuration files, and external scripts. This tutorial shows you how to avoid repeating yourself and share common patterns with your team.

What You'll Learn

  • Create and use aliases with --save-alias and -a
  • Understand configuration file locations and precedence
  • View and edit configuration with --show-config and --edit-config
  • Write reusable functions with -I/--include
  • Organize complex scripts with -E/--exec-file
  • Decide when to use aliases vs includes vs exec-files
  • Share workflows with your team

Prerequisites

Sample Data

This tutorial uses:

  • examples/simple_json.jsonl - Application logs
  • Example configuration files we'll create

Part 1: Understanding Configuration Files

Configuration File Locations

Kelora searches for configuration in two places (in order):

  1. Project config - .kelora.ini in current directory (or parent directories)
  2. User config - ~/.config/kelora/kelora.ini (Unix) or %APPDATA%\kelora\kelora.ini (Windows)

Precedence: Project config overrides user config. CLI flags override both.

View Current Configuration

kelora --show-config
kelora --show-config
No config found (searched: .kelora.ini from cwd upward, /home/runner/.config/kelora/kelora.ini)

Example .kelora.ini:
defaults = -f auto --stats

[aliases]
errors = -l error --stats
json-errors = -f json -l error -F json
slow-requests = --filter 'e.response_time.to_int() > 1000' --keys timestamp,method,path,response_time

If no config exists, you'll see example templates and search paths.


Part 2: Creating Your First Alias

Aliases let you save commonly-used command patterns.

Save an Alias

Let's create an alias for finding errors:

kelora -j --levels error --keys timestamp,service,message --save-alias errors
kelora -j --levels error --keys timestamp,service,message --save-alias errors
kelora: Alias 'errors' saved to /home/runner/.config/kelora/kelora.ini

This saves the alias to your user config file.

Use the Alias

Now you can use it with -a:

kelora -a errors examples/simple_json.jsonl
kelora -a errors examples/simple_json.jsonl
timestamp='2024-01-15T10:01:30Z' service='database' message='Query timeout'
timestamp='2024-01-15T10:03:30Z' service='auth' message='Account locked'
timestamp='2024-01-15T10:16:00Z' service='api' message='Service unavailable'

What happened: kelora -a errors expanded to kelora -j --levels error --keys timestamp,service,message.


Part 3: Configuration File Structure

Configuration files use INI format with two sections:

# Root-level defaults applied to every command
defaults = -f auto --stats

# Named aliases invoked with -a/--alias
[aliases]
errors = -j --levels error --keys timestamp,service,message
slow = -j --filter 'e.duration_ms > 1000' --keys service,duration_ms,message
problems = -j --levels error,warn,critical

Edit Your Configuration

kelora --edit-config

This opens your active config file in $EDITOR (vi/vim/nano on Unix, notepad on Windows).


Part 4: Project vs User Configuration

User Configuration (Personal Preferences)

Location: ~/.config/kelora/kelora.ini

Good for:

  • Personal formatting preferences
  • Local development shortcuts
  • Your own workflow patterns

Example:

defaults = --stats --no-emoji

[aliases]
myerrors = -j --levels error --exclude-keys password,token
quickcheck = -j --stats

Project Configuration (Team Shared)

Location: .kelora.ini in project root

Good for:

  • Team-wide conventions
  • Project-specific formats
  • Shared analysis patterns

Example:

# Commit this to version control
defaults = -f json

[aliases]
api-errors = --levels error --filter 'e.service == "api"'
slow-db = --filter 'e.service == "database" && e.duration_ms > 100'
alerts = --levels critical,error --exec 'track_count(e.service)'

Pro tip: Commit .kelora.ini to your repository so the whole team uses the same patterns!


Part 5: Advanced Aliases

Chaining Multiple Aliases

Aliases can reference other aliases:

[aliases]
json = -f json
errors = --levels error
json-errors = -a json -a errors
# Create test config
echo "[aliases]
json = -f json
errors = --levels error
json-errors = -a json -a errors" > /tmp/test.ini

kelora --config-file /tmp/test.ini -a json-errors examples/simple_json.jsonl --take 2
rm /tmp/test.ini
echo "[aliases]
json = -f json
errors = --levels error
json-errors = -a json -a errors" > /tmp/test.ini

kelora --config-file /tmp/test.ini -a json-errors examples/simple_json.jsonl --take 2
rm /tmp/test.ini
timestamp='2024-01-15T10:01:30Z' level='ERROR' message='Query timeout' service='database'
  query='SELECT * FROM users' duration_ms=5000
timestamp='2024-01-15T10:03:30Z' level='ERROR' message='Account locked' service='auth'
  username='admin' attempts=5

Limit: Aliases can reference up to 10 levels deep (prevents infinite loops).

Complex Aliases with Scripting

You can put full pipelines in aliases:

[aliases]
analyze-api = -j --filter 'e.service == "api"' \
              --exec 'track_count(e.level)' \
              --exec 'track_sum("total_duration", e.duration_ms)' \
              --metrics

Tip: Use backslash \ for line continuation in INI files.


Part 6: Reusable Functions with --include

For complex logic, create reusable Rhai function libraries.

Create a Helper Library

We've created examples/helpers.rhai with useful functions:

cat examples/helpers.rhai
cat examples/helpers.rhai
// Reusable helper functions for log analysis

// Check if event is a problem (error or slow)
fn is_problem(event) {
    let is_error = event.level == "ERROR" || event.level == "CRITICAL";
    // Check for slow requests: response_time in seconds or duration_ms in milliseconds
    let is_slow = (event.has("response_time") && event.response_time > 1.0) ||
                  (event.has("duration_ms") && event.duration_ms > 1000) ||
                  (event.has("latency_ms") && event.latency_ms > 1000);
    is_error || is_slow
}

// Classify severity based on level and metrics
fn classify_severity(level, value) {
    if level == "CRITICAL" || level == "ERROR" {
        "high"
    } else if level == "WARN" || value > 1000 {
        "medium"
    } else {
        "low"
    }
}

// Extract domain from URL or email
fn extract_domain(text) {
    if text.contains("@") {
        // Email format
        let parts = text.split("@");
        if parts.len() > 1 {
            parts[1]
        } else {
            "unknown"
        }
    } else if text.contains("://") {
        // URL format
        let after_protocol = text.split("://")[1];
        let domain_part = after_protocol.split("/")[0];
        let without_port = domain_part.split(":")[0];
        without_port
    } else {
        text
    }
}

// Mask sensitive data
fn mask_sensitive(value) {
    if value.len() <= 4 {
        "***"
    } else {
        value.sub_string(0, 2) + "***" + value.sub_string(value.len() - 2, 2)
    }
}

Use the Library

kelora -j examples/simple_json.jsonl \
    -I examples/helpers.rhai \
    --exec 'e.is_problem = is_problem(e)' \
    --filter 'e.is_problem' \
    -k service,level,is_problem,message
kelora -j examples/simple_json.jsonl \
    -I examples/helpers.rhai \
    --exec 'e.is_problem = is_problem(e)' \
    --filter 'e.is_problem' \
    -k service,level,is_problem,message
service='database' level='ERROR' is_problem=true message='Query timeout'
service='auth' level='ERROR' is_problem=true message='Account locked'
service='scheduler' level='INFO' is_problem=true message='Backup completed'
service='disk' level='CRITICAL' is_problem=true message='Disk space critical'
service='api' level='ERROR' is_problem=true message='Service unavailable'

How it works:

  1. -I examples/helpers.rhai loads the function definitions
  2. Functions become available in --exec, --exec-file, --begin, and --end stages
  3. Call them like any built-in function

Filter Limitations

--include does not work with --filter because filters must be pure expressions. Use --exec instead for filtering with custom functions.

Multiple Include Files

You can load multiple libraries:

kelora -j app.log \
    -I lib/validators.rhai \
    -I lib/transforms.rhai \
    -I lib/metrics.rhai \
    --exec 'e.valid = validate_event(e)' \
    --filter 'e.valid'

Part 7: Complex Scripts with --exec-file

For longer transformations, put the entire script in a file.

Create a Transformation Script

We've created examples/enrich_events.rhai:

cat examples/enrich_events.rhai
cat examples/enrich_events.rhai
// This script enriches log events with additional computed fields

// Add timestamp metadata
if e.has("timestamp") {
    let ts = e.timestamp;
    if ts.contains("T") {
        let parts = ts.split("T");
        e.date = parts[0];
        if parts.len() > 1 {
            e.time = parts[1].split("Z")[0];
        }
    }
}

// Classify by severity
if e.has("level") {
    e.severity = if e.level == "ERROR" || e.level == "CRITICAL" {
        "high"
    } else if e.level == "WARN" {
        "medium"
    } else {
        "low"
    };
}

// Add duration classification if present
// Handle response_time (seconds), duration_ms (milliseconds), or latency_ms (milliseconds)
let duration_in_ms = if e.has("response_time") {
    e.response_time * 1000.0
} else if e.has("duration_ms") {
    e.duration_ms
} else if e.has("latency_ms") {
    e.latency_ms
} else {
    -1.0
};

if duration_in_ms >= 0.0 {
    e.speed = if duration_in_ms < 100.0 {
        "fast"
    } else if duration_in_ms < 1000.0 {
        "normal"
    } else {
        "slow"
    };
}

// Track by service and level
if e.has("service") && e.has("level") {
    track_count(e.service + ":" + e.level);
}

// Return enriched event
e

Use the Script

kelora -j examples/simple_json.jsonl \
    -E examples/enrich_events.rhai \
    -k date,time,severity,speed,service,message \
    --take 5
kelora -j examples/simple_json.jsonl \
    -E examples/enrich_events.rhai \
    -k date,time,severity,speed,service,message \
    --take 5
date='2024-01-15' time='10:00:00' severity='low' service='api' message='Application started'
date='2024-01-15' time='10:00:05' severity='low' service='api' message='Loading configuration'
date='2024-01-15' time='10:00:10' severity='low' service='database'
  message='Connection pool initialized'
date='2024-01-15' time='10:01:00' severity='medium' service='api'
  message='High memory usage detected'
date='2024-01-15' time='10:01:30' severity='high' speed='slow' service='database'
  message='Query timeout'
kelora hint: Metrics recorded; rerun with -m or --metrics=json to view them.

Note: -E runs the script contents in the exec stage (once per event).


Part 8: When to Use What

Decision Matrix

Need Use Why
Common flag combinations Alias Quick, sharable, version-controlled
Reusable functions --include Define once, use in --exec/--begin/--end
Multi-step transformations --exec-file Keep complex logic organized in files
Personal shortcuts User config alias Quick access to your patterns
Team conventions Project config alias Everyone uses same workflows
One-off commands Plain CLI flags No need to save

Pattern Examples

Pattern 1: Personal Shortcut (User Alias)

# Save to user config
kelora -j --keys timestamp,message -b --save-alias quick

# Use anywhere
kelora -a quick any-file.log

Pattern 2: Team Convention (Project Alias)

# In .kelora.ini (commit to repo)
[aliases]
production-errors = -j --levels error,critical \
                    --filter 'e.environment == "production"' \
                    --exec 'track_count(e.service)'

# Anyone on team can run
kelora -a production-errors logs/*.jsonl --metrics

Pattern 3: Reusable Logic (Include File)

# lib/validation.rhai
fn is_valid_email(text) {
    text.contains("@") && text.contains(".")
}

# Use in pipeline
kelora -j app.log \
    -I lib/validation.rhai \
    --filter 'is_valid_email(e.email)'

Pattern 4: Complex Pipeline (Exec File)

# transforms/normalize.rhai
// Normalize all fields
e.level = e.level.to_upper();
e.service = e.service.to_lower();

if e.has("duration_ms") {
    e.duration_s = e.duration_ms / 1000;
}

track_count(e.service + ":" + e.level);

# Use in pipeline
kelora -j app.log -E transforms/normalize.rhai --metrics

Part 9: Real-World Workflow

Let's build a complete reusable workflow.

Step 1: Create Project Structure

mkdir -p myproject/scripts
cd myproject

Step 2: Create Shared Functions

Create scripts/helpers.rhai:

fn critical_event(e) {
    e.level == "CRITICAL" ||
    (e.level == "ERROR" && e.service == "database")
}

fn add_alert_tag(e) {
    if critical_event(e) {
        e.alert = true;
        e.priority = "P1";
    } else if e.level == "ERROR" {
        e.alert = true;
        e.priority = "P2";
    }
}

Step 3: Create Transformation Script

Create scripts/enrich.rhai:

// Add computed fields
if e.has("duration_ms") {
    e.duration_s = e.duration_ms / 1000;
    e.slow = e.duration_ms > 1000;
}

// Classify severity
if e.has("level") {
    e.severity = if e.level == "CRITICAL" || e.level == "ERROR" {
        "high"
    } else if e.level == "WARN" {
        "medium"
    } else {
        "low"
    };
}

Step 3: Create Project Config

Create .kelora.ini:

defaults = -f json --stats

[aliases]
errors = --levels error,critical -I scripts/helpers.rhai
enrich = -I scripts/helpers.rhai -E scripts/enrich.rhai
alerts = -a errors --exec 'add_alert_tag(e)' --filter 'e.alert'
analyze = -a enrich --exec 'track_count(e.severity)' --metrics

Step 4: Use the Workflow

# Quick error check
kelora -a errors app.log

# Full enrichment
kelora -a enrich app.log --take 10

# Generate alerts
kelora -a alerts app.log -F json -o alerts.json

# Analyze severity distribution
kelora -a analyze app.log

Step 5: Share with Team

git add .kelora.ini scripts/
git commit -m "Add log analysis workflows"
git push

Now your team can clone and run the same patterns!


Part 10: Configuration Best Practices

✅ Do

  • Commit project .kelora.ini - Share team conventions
  • Keep aliases focused - One purpose per alias
  • Document in README - Explain what each alias does
  • Use includes for functions - Reusable logic in files
  • Version control scripts - Track changes to transforms
  • Test aliases - Verify they work before committing

❌ Don't

  • Don't put secrets in config - Use environment variables instead
  • Don't make aliases too complex - Use exec-files for long scripts
  • Don't override critical flags - Be careful with defaults
  • Don't chain too deeply - Keep alias references simple
  • Don't commit personal preferences - Use user config for those

Part 11: Troubleshooting

View Expanded Command

See what aliases expand to:

kelora -a myalias app.log --verbose

Ignore Configuration

Test without config:

kelora --ignore-config -j app.log --levels error

Use Specific Config File

Test project config without affecting user config:

kelora --config-file .kelora.ini --show-config

Check Include File Errors

kelora -I myfile.rhai app.log --verbose
# Shows any syntax errors in the include file

Part 12: Advanced Techniques

Conditional Defaults

You can't have conditionals in INI, but you can create multiple configs:

# .kelora.dev.ini (development)
defaults = -f json --stats --verbose

# .kelora.prod.ini (production)
defaults = -f json -q

# Use specific config
kelora --config-file .kelora.dev.ini app.log

Environment-Specific Aliases

[aliases]
dev = -f json --stats --verbose
prod = -f json -q --strict
staging = -f json --stats

# Use based on environment
kelora -a $ENV app.log  # where ENV=dev|prod|staging

Combining with Shell Scripts

Create analyze.sh:

#!/bin/bash
# Production log analyzer

set -e

LOG_FILE="$1"
OUTPUT_DIR="results"

mkdir -p "$OUTPUT_DIR"

# Run multiple analyses
kelora -a errors "$LOG_FILE" -o "$OUTPUT_DIR/errors.json"
kelora -a slow "$LOG_FILE" -o "$OUTPUT_DIR/slow.json"
kelora -a analyze "$LOG_FILE" > "$OUTPUT_DIR/metrics.txt"

echo "Analysis complete. Results in $OUTPUT_DIR/"


Summary

You've learned:

  • ✅ Create aliases with --save-alias for reusable commands
  • ✅ Use aliases with -a to avoid repetition
  • ✅ Understand project vs user configuration
  • ✅ View config with --show-config and edit with --edit-config
  • ✅ Write reusable functions with -I/--include
  • ✅ Organize complex scripts with -E/--exec-file
  • ✅ Choose the right tool for each use case
  • ✅ Share workflows with your team via version control
  • ✅ Best practices for configuration management

Practice Exercises

Exercise 1: Create a Personal Alias

Create an alias for your most common log viewing pattern:

Solution
# Example: compact error view
kelora -j --levels error -k timestamp,service,message -b --save-alias myerrors

# Use it
kelora -a myerrors app.log

Exercise 2: Build a Helper Library

Create myhelpers.rhai with a function to check if a status code is an error:

Solution
// myhelpers.rhai
fn is_error_status(status) {
    status >= 400 && status < 600
}

// Use it
kelora -j app.log \
    -I myhelpers.rhai \
    --filter 'e.has("status") && is_error_status(e.status)'

Exercise 3: Create a Project Workflow

Set up a .kelora.ini for a web application with aliases for:

  • API errors
  • Slow requests
  • Traffic analysis
Solution
defaults = -f combined

[aliases]
api-errors = --filter 'e.status >= 400' --keys ip,status,method,path
slow = --filter 'e.request_time.to_float() > 1.0' --keys ip,request_time,path
traffic = --exec 'track_count(e.method); track_count(e.status.to_string())' --metrics

Next Steps

Now that you can create reusable workflows, continue to:

Related guides: