npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

loq-cli

v0.1.0

Published

Fast, friendly CLI log analyzer - jq for logs

Readme

loq

Fast, friendly CLI log analyzer built with Bun. Think "jq for logs."

CI Documentation License: MIT Bun

Features

  • Auto-detects log formats - JSON, Apache, Nginx, Syslog, CLF
  • Simple query syntax - loq app.log where level=error
  • SQL-like queries - loq "SELECT * FROM app.log WHERE status >= 400"
  • Powerful filtering - equality, comparison, contains, regex
  • Aggregations - count, group by, sum, avg, min, max
  • Multiple outputs - colorized, table, JSON, CSV
  • Streaming - handles large files efficiently
  • Pipe support - cat *.log | loq where level=error
  • Tail mode - loq -f app.log where level=error
  • Extensible - add custom log formats via config

Installation

Using Bun

# Install Bun if you haven't
curl -fsSL https://bun.sh/install | bash

# Clone and install
git clone https://github.com/code-with-auto/loq.git
cd loq
bun install
bun link

Global binary

# After bun link, loq is available at ~/.bun/bin/loq
# Add to your PATH or use directly:
~/.bun/bin/loq --help

Quick Start

# View all logs with colorized output
loq app.log

# Filter by level
loq app.log where level=error

# Filter HTTP logs by status
loq access.log where "status >= 400"

# Search in messages
loq app.log where message contains "timeout"

# Regex matching
loq app.log where path matches "^/api/v[0-9]+"

# Boolean logic
loq app.log where level=error and message contains "database"
loq app.log where level=error or level=warn

# Aggregations
loq app.log count by level
loq access.log count by status

# Limit results
loq app.log where level=error limit 10

# Tail mode (like tail -f)
loq -f app.log where level=error

# Pipe support
cat logs/*.log | loq where level=error
kubectl logs pod/my-app | loq where level=error

# Output formats
loq app.log -o json where level=error
loq app.log -o csv where level=error
loq app.log -o table

Query Syntax

DSL Syntax

loq <file> [where <conditions>] [count [by <field>]] [limit <n>]

Operators:

  • =, != - equality
  • >, <, >=, <= - comparison
  • contains - substring match
  • matches - regex match

Boolean:

  • and, or, not
  • Parentheses for grouping: (level=error or level=warn) and status>=500

Time filters:

  • after yesterday
  • before today
  • between "10:00" and "11:00" today

SQL Syntax

loq "SELECT * FROM app.log WHERE level='error' LIMIT 10"
loq "SELECT level, COUNT(*) FROM app.log GROUP BY level"
loq "SELECT path, AVG(response_time) FROM access.log GROUP BY path"

Supported Log Formats

Auto-detected formats

| Format | Example | |--------|---------| | JSON | {"level":"info","message":"Server started"} | | Apache/Nginx | 192.168.1.1 - - [20/Dec/2024:10:00:00 +0000] "GET /api HTTP/1.1" 200 1234 | | Syslog | Dec 20 12:34:56 myhost myapp[1234]: Message | | CLF | 127.0.0.1 - - [10/Oct/2000:13:55:36 -0700] "GET / HTTP/1.0" 200 2326 |

Custom formats

Create ~/.loqrc or ./loq.config.ts:

// loq.config.ts
export default {
  formats: [
    {
      name: 'my-app',
      detect: /^\[\d{4}-\d{2}-\d{2}/,
      parse: {
        pattern: /^\[(?<timestamp>[^\]]+)\] (?<level>\w+): (?<message>.+)$/,
        fields: {
          timestamp: 'timestamp',
          level: 'level',
          message: 'message',
        },
      },
    },
    {
      name: 'nginx-json',
      detect: (line) => {
        try {
          const obj = JSON.parse(line);
          return 'request_uri' in obj;
        } catch {
          return false;
        }
      },
      parse: (line) => {
        const obj = JSON.parse(line);
        return {
          timestamp: obj.time_iso8601,
          level: obj.status >= 400 ? 'error' : 'info',
          message: `${obj.request_method} ${obj.request_uri}`,
          fields: obj,
        };
      },
    },
  ],
  aliases: {
    errors: 'where level=error',
    slow: 'where response_time>1000',
  },
};

Or use JSON (~/.loqrc):

{
  "formats": [
    {
      "name": "bracketed",
      "detect": "^\\[\\d{4}",
      "parse": {
        "pattern": "^\\[([^\\]]+)\\] (\\w+): (.+)$",
        "fields": {
          "timestamp": 1,
          "level": 2,
          "message": 3
        }
      }
    }
  ]
}

CLI Options

Options:
  -f, --follow       Tail mode (like tail -f)
  -o, --output       Output format: color, table, json, csv
  --format           Force log format: json, apache, syslog, clf
  -n, --limit        Limit number of results
  -h, --help         Show help
  -v, --version      Show version

Development

# Install dependencies
bun install

# Run in development mode
bun run dev

# Run tests
bun test

# Run tests with coverage
bun test --coverage

# Type check
bun run typecheck

# Build standalone binary
bun build src/index.ts --compile --outfile loq

# Generate documentation
bun run docs

Documentation

API documentation is automatically generated using TypeDoc and hosted on GitHub Pages.

# Generate docs locally
bun run docs

# Serve docs locally
bun run docs:serve
# Then open http://localhost:8080

Project Structure

loq/
├── src/
│   ├── index.ts              # CLI entry point
│   ├── cli/
│   │   └── args.ts           # Argument parsing
│   ├── config/
│   │   └── loader.ts         # Config file loading
│   ├── parser/
│   │   ├── types.ts          # Types & plugin system
│   │   ├── auto-detect.ts    # Format detection
│   │   └── formats/          # Built-in parsers
│   ├── query/
│   │   ├── lexer.ts          # Tokenizer
│   │   ├── parser.ts         # Query parser
│   │   ├── ast.ts            # AST types
│   │   └── executor.ts       # Query execution
│   ├── output/
│   │   ├── formatter.ts      # Output formatting
│   │   └── colors.ts         # Terminal colors
│   └── utils/
│       └── time.ts           # Time utilities
├── tests/                    # Test files
├── .github/workflows/        # CI/CD
└── package.json

Adding Custom Formats

Option 1: Config file

Create ~/.loqrc with your format definitions (see above).

Option 2: Contribute a parser

  1. Create a parser in src/parser/formats/:
// src/parser/formats/myformat.ts
import type { LogEntry, LogParser } from '../types';

export const myFormatParser: LogParser = {
  name: 'myformat',

  detect(line: string): boolean {
    // Return true if this line matches your format
    return line.startsWith('MYFORMAT:');
  },

  parse(line: string): LogEntry | null {
    // Parse the line and return a LogEntry
    const match = line.match(/^MYFORMAT: \[(.+?)\] (\w+) - (.+)$/);
    if (!match) return null;

    return {
      raw: line,
      timestamp: match[1],
      level: match[2],
      message: match[3],
      fields: { /* any additional fields */ },
    };
  },
};
  1. Register in src/parser/auto-detect.ts:
import { myFormatParser } from './formats/myformat';

const builtinParsers: LogParser[] = [
  myFormatParser,  // Add here
  jsonParser,
  // ...
];
  1. Add tests in tests/parsers/myformat.test.ts

  2. Submit a PR!

License

MIT

Contributing

Contributions welcome! See CONTRIBUTORS.md for guidelines.