Skip to main content

Line Filters

Introduction

Line filters are a fundamental concept in LogQL, the query language used by Grafana Loki. They allow you to select logs based on their content, acting as the first step in any LogQL query. If you're working with logs in Loki, understanding line filters is essential for effectively querying and analyzing your log data.

In this guide, we'll explore what line filters are, how they work, and how to use them effectively. Whether you're troubleshooting an application issue or analyzing system performance, mastering line filters will help you quickly find the exact log entries you need.

What Are Line Filters?

Line filters determine which log lines should be included in your query results. They operate on the raw log content, before any parsing or extraction occurs. Line filters use pattern matching to identify logs that contain (or don't contain) specific text or patterns.

A basic LogQL query structure with a line filter looks like this:

logql
{stream_selector} |= "pattern"

Where:

  • {stream_selector} is a set of labels to select log streams
  • |= is a line filter operator (in this case, "contains")
  • "pattern" is the text pattern to match against

Line Filter Operators

LogQL provides several operators for line filtering:

OperatorDescriptionExample
`=`Contains | {app="frontend"} |= "error"
!=Does not contain{app="frontend"} != "debug"
`~`Contains regex | {app="frontend"} |~ "error.*timeout"
!~Does not contain regex{app="frontend"} !~ "debug|info"

Let's look at each of these operators in more detail.

Contains (|=)

The contains operator (|=) selects log lines that include the specified string. It's case-sensitive by default.

For example, to find all logs from the "api" application that contain the word "error":

logql
{app="api"} |= "error"

This will match logs like:

  • "An error occurred while processing the request"
  • "Request completed with error code 500"
  • "ERROR: Database connection failed"

Does Not Contain (!=)

The does-not-contain operator (!=) selects log lines that do not include the specified string.

For example, to find all logs from the "api" application that don't contain the word "success":

logql
{app="api"} != "success"

This will match any logs that don't include the word "success".

Contains Regex (|~)

The contains-regex operator (|~) selects log lines that match the specified regular expression pattern.

For example, to find all logs from the "api" application that contain either "error" or "warning" followed by any characters:

logql
{app="api"} |~ "(?i)(error|warning).*"

The (?i) makes the match case-insensitive, so this will match logs like:

  • "An ERROR occurred during authentication"
  • "Warning: High CPU usage detected"
  • "error code: 404, resource not found"

Does Not Contain Regex (!~)

The does-not-contain-regex operator (!~) selects log lines that don't match the specified regular expression pattern.

For example, to find all logs from the "api" application that don't contain logs at the "debug" or "info" level:

logql
{app="api"} !~ "(?i)(debug|info):"

This will exclude logs that have "debug:" or "info:" (case-insensitive) in them.

Combining Multiple Line Filters

You can chain multiple line filters together to create more specific queries. Filters are evaluated from left to right.

For example, to find error logs that are related to authentication but not to timeouts:

logql
{app="api"} |= "error" |= "auth" != "timeout"

This query will:

  1. Start with all logs from the "api" application
  2. Keep only those containing "error"
  3. Further filter to keep only those containing "auth"
  4. Finally, exclude any that contain "timeout"

Real-World Examples

Let's explore some practical examples of using line filters in different scenarios.

Example 1: Troubleshooting Application Errors

If you're investigating a specific error in your application, you might use a query like this:

logql
{app="payment-service", environment="production"} |= "transaction failed" |~ "user_id:[0-9]+"

This query:

  • Selects logs from the payment-service application in the production environment
  • Filters for logs containing the text "transaction failed"
  • Further filters for logs that include a user ID in the format "user_id:" followed by numbers

Example 2: Security Monitoring

If you're monitoring for security issues, you might look for failed login attempts:

logql
{app="auth-service"} |= "login" |= "failed" != "timeout"

This query finds all failed login attempts that weren't caused by timeouts.

Example 3: Filtering Out Noise

Sometimes you want to exclude certain noisy log entries:

logql
{app="backend", component="api"} != "health check" != "heartbeat"

This query excludes routine health checks and heartbeat messages that might otherwise flood your results.

Example 4: Complex Pattern Matching

For more advanced needs, you can use regular expressions:

logql
{app="web-server"} |~ "(?i)GET /api/v[0-9]+/users/[0-9]+" |= "status=5[0-9]{2}"

This query finds:

  • Logs from the web-server application
  • Containing GET requests to any version of the users API with a user ID
  • Where the status code is in the 500 range (server errors)

Performance Considerations

Line filters are evaluated early in the query pipeline, which makes them very efficient for reducing the amount of data that needs to be processed. Here are some tips for optimizing line filter performance:

  1. Be Specific: The more specific your line filters, the fewer logs need to be processed.

  2. Start with Equality Matchers: Begin your query with label matchers like {app="api"} to narrow down the log streams before applying line filters.

  3. Order Matters: Put the most restrictive line filters first. For example:

logql
{app="api"} |= "error" |= "database" // Better

is typically more efficient than:

logql
{app="api"} |= "database" |= "error" // Less efficient if "error" is less common
  1. Use Regex Carefully: Regular expressions (|~ and !~) are powerful but can be slower than simple string matching (|= and !=). Use them only when necessary.

Common Patterns and Best Practices

Here are some patterns and best practices for working with line filters:

  1. Finding Errors: A common pattern is to filter for error-related terms:
logql
{app="api"} |~ "(?i)(error|exception|fail|fatal)"
  1. Isolating Request Flows: To trace a specific request through your system:
logql
{environment="production"} |= "request_id=abc123"
  1. Time-Based Context: Combine line filters with time ranges to narrow down issues:
logql
{app="payment-service"} |= "transaction failed" | range 1h
  1. Quoting Special Characters: Use quotes when your filter includes special characters:
logql
{app="api"} |= "error: {\"code\":500}"

Summary

Line filters are the foundation of effective log querying in LogQL. They allow you to quickly narrow down log entries to just those relevant to your investigation. By understanding the different operators (|=, !=, |~, !~) and how to combine them, you can create precise queries that help you find exactly what you're looking for in your log data.

As you continue to work with Loki, you'll develop an intuition for which line filters to use in different situations. Remember that good line filtering not only helps you find what you need but also improves query performance by reducing the amount of data that needs to be processed.

Exercises

To reinforce your understanding of line filters, try these exercises:

  1. Write a query to find all logs containing the word "error" but not "timeout" from an application called "checkout-service".

  2. Create a query that finds logs containing HTTP status codes in the 400 range (client errors).

  3. Write a query to find logs that contain an email address pattern.

  4. Combine multiple line filters to find logs that mention both "payment" and "failed" but not "retry".

Additional Resources

In the next section, we'll explore how to extract and transform your log data using LogQL's built-in parsers and label filters.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)