Render Eliot logs as an ASCII tree
—
Advanced filtering capabilities for selective processing of Eliot tasks based on various criteria. Filters work on the original Eliot message dictionaries before task parsing, enabling efficient selection using JMESPath queries, UUID matching, timestamp ranges, and logical combinations.
from datetime import datetime
from iso8601 import parse_dateCreate filter functions using JMESPath query expressions to select tasks based on any field or condition in the original Eliot message structure.
def filter_by_jmespath(query):
"""
Produce a function for filtering a task by a JMESPath query expression.
Parameters:
- query: str - JMESPath query string used as a predicate
Returns:
Callable[[dict], bool] - Filter function that returns True if task matches query
"""Usage Examples:
from eliottree import filter_by_jmespath
# Filter tasks containing a 'uri' field
uri_filter = filter_by_jmespath('uri')
# Filter by specific action type
http_filter = filter_by_jmespath('action_type == `"http_client:request"`')
# Filter by HTTP status codes
error_filter = filter_by_jmespath('contains(`[401, 500]`, status)')
# Complex filtering with multiple conditions
critical_filter = filter_by_jmespath(
'http_status == `401` && uri && contains(uri, `"/criticalEndpoint"`)'
)
# Apply filter to messages
filtered_tasks = [task for task in tasks if uri_filter(task)]Filter entire task trees by their unique identifier, useful for isolating specific request flows or debugging particular operations.
def filter_by_uuid(task_uuid):
"""
Produce a function for filtering tasks by their UUID.
Implementation note: This is implemented as a JMESPath wrapper that creates
a query equivalent to: task_uuid == `<uuid_value>`
Parameters:
- task_uuid: str - UUID string to match against task_uuid field
Returns:
Callable[[dict], bool] - Filter function for UUID matching
"""Usage Example:
from eliottree import filter_by_uuid
# Filter by specific task UUID
uuid_filter = filter_by_uuid("f3a32bb3-ea6b-457c-aa99-08a3d0491ab4")
# Apply filter
specific_tasks = [task for task in tasks if uuid_filter(task)]Filter tasks based on their occurrence time using start and end date boundaries, enabling time-range analysis of log data.
def filter_by_start_date(start_date):
"""
Produce a function for filtering by task timestamps after (or on) a certain date and time.
Parameters:
- start_date: datetime - Tasks must occur at or after this datetime
Returns:
Callable[[dict], bool] - Filter function for start date filtering
"""
def filter_by_end_date(end_date):
"""
Produce a function for filtering by task timestamps before a certain date and time.
Parameters:
- end_date: datetime - Tasks must occur before this datetime
Returns:
Callable[[dict], bool] - Filter function for end date filtering
"""Usage Examples:
from datetime import datetime
from iso8601 import parse_date
from eliottree import filter_by_start_date, filter_by_end_date
# Filter tasks after a specific time
start_time = parse_date("2015-03-03T04:28:00Z")
after_filter = filter_by_start_date(start_time)
# Filter tasks before a specific time
end_time = parse_date("2015-03-03T04:30:00Z")
before_filter = filter_by_end_date(end_time)
# Apply filters
recent_tasks = [task for task in tasks if after_filter(task)]
time_range_tasks = [task for task in tasks
if after_filter(task) and before_filter(task)]Combine multiple filter functions using logical AND operations to create sophisticated filtering criteria.
def combine_filters_and(*filters):
"""
Combine several filters together in a logical-AND fashion.
Parameters:
- *filters: Variable number of filter functions
Returns:
Callable[[Any], bool] - Combined filter function that returns True only
if all input filters return True
"""Usage Examples:
from eliottree import (
filter_by_jmespath, filter_by_start_date, filter_by_end_date,
combine_filters_and
)
# Combine multiple filters
error_filter = filter_by_jmespath('http_status == `401`')
uri_filter = filter_by_jmespath('uri && contains(uri, `"/api"`)')
time_filter = filter_by_start_date(parse_date("2015-03-03T04:00:00Z"))
# Create combined filter
combined_filter = combine_filters_and(error_filter, uri_filter, time_filter)
# Apply combined filter
filtered_tasks = [task for task in tasks if combined_filter(task)]import json
from datetime import datetime
from iso8601 import parse_date
from eliottree import (
tasks_from_iterable, render_tasks, get_theme,
filter_by_jmespath, filter_by_start_date, combine_filters_and
)
def process_filtered_logs(log_file, start_time_str, action_type):
"""Process logs with multiple filtering criteria."""
# Load messages
with open(log_file, 'r') as f:
messages = [json.loads(line) for line in f]
# Create filters
time_filter = filter_by_start_date(parse_date(start_time_str))
action_filter = filter_by_jmespath(f'action_type == `"{action_type}"`')
# Combine filters
combined_filter = combine_filters_and(time_filter, action_filter)
# Apply filtering
filtered_messages = [msg for msg in messages if combined_filter(msg)]
# Process and render
tasks = tasks_from_iterable(filtered_messages)
theme = get_theme(dark_background=True)
render_tasks(sys.stdout.write, tasks, theme=theme)
# Usage
process_filtered_logs(
'eliot.log',
'2015-03-03T04:28:00Z',
'http_client:request'
)Filters can be composed and reused for different analysis scenarios:
# Define reusable filters
def create_error_filters():
return {
'http_errors': filter_by_jmespath('status >= `400`'),
'timeout_errors': filter_by_jmespath('contains(error_type || `""`, `"timeout"`)'),
'auth_errors': filter_by_jmespath('status == `401` || status == `403`'),
}
def create_time_range_filter(start_str, end_str):
start_filter = filter_by_start_date(parse_date(start_str))
end_filter = filter_by_end_date(parse_date(end_str))
return combine_filters_and(start_filter, end_filter)
# Usage
error_filters = create_error_filters()
time_range = create_time_range_filter('2015-03-03T04:00:00Z', '2015-03-03T05:00:00Z')
# Combine for specific analysis
auth_errors_in_range = combine_filters_and(
error_filters['auth_errors'],
time_range
)Common query patterns for Eliot log analysis:
# Existence checks
filter_by_jmespath('uri') # Has URI field
filter_by_jmespath('error_message') # Has error message
# Exact matches
filter_by_jmespath('action_type == `"http_request"`')
filter_by_jmespath('status == `200`')
# Numeric comparisons
filter_by_jmespath('status >= `400`') # HTTP errors
filter_by_jmespath('duration > `5.0`') # Slow operations
# String operations
filter_by_jmespath('contains(uri, `"/api/"`)')
filter_by_jmespath('starts_with(action_type, `"db:"`)')
# Array operations
filter_by_jmespath('contains(`[401, 403, 500]`, status)')
filter_by_jmespath('length(task_level) > `2`')
# Complex conditions
filter_by_jmespath('status >= `400` && contains(uri, `"/critical"`)')Install with Tessl CLI
npx tessl i tessl/pypi-eliot-tree