Skip to content

Latest commit

 

History

History
210 lines (156 loc) · 4.59 KB

File metadata and controls

210 lines (156 loc) · 4.59 KB

Contributing to loq

Thank you for your interest in contributing to loq! This document provides guidelines and instructions for contributing.

Code of Conduct

Be kind, respectful, and constructive. We're all here to build something useful together.

How to Contribute

Reporting Bugs

  1. Check existing issues to avoid duplicates
  2. Use the bug report template
  3. Include:
    • loq version (loq --version)
    • OS and version
    • Steps to reproduce
    • Expected vs actual behavior
    • Sample log line(s) if relevant

Suggesting Features

  1. Check existing issues/discussions
  2. Describe the use case
  3. Propose a solution if you have one

Pull Requests

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Make your changes
  4. Add/update tests
  5. Ensure all tests pass: bun test
  6. Ensure coverage stays above 95%: bun test --coverage
  7. Submit a PR

Development Setup

# Clone your fork
git clone https://github.com/YOUR_USERNAME/loq.git
cd loq

# Install dependencies
bun install

# Run tests
bun test

# Run in dev mode
bun run dev

Project Structure

src/
├── cli/           # Command-line interface
├── config/        # Configuration loading
├── parser/        # Log parsing
│   └── formats/   # Individual format parsers
├── query/         # Query language
├── output/        # Output formatting
└── utils/         # Utilities

tests/             # Mirror of src/ structure

Adding a New Log Format

1. Create the parser

// src/parser/formats/myformat.ts
import type { LogEntry, LogParser } from '../types';

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

  detect(line: string): boolean {
    // Fast check - is this line in my format?
    return /^MYFORMAT:/.test(line);
  },

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

    return {
      raw: line,
      timestamp: match[1],
      level: match[2].toLowerCase(),
      message: match[3],
      fields: {
        // Include all parsed fields for querying
        timestamp: match[1],
        level: match[2],
        message: match[3],
      },
    };
  },
};

2. Register the parser

// src/parser/auto-detect.ts
import { myFormatParser } from './formats/myformat';

const builtinParsers: LogParser[] = [
  jsonParser,
  myFormatParser,  // Add here - order matters for detection
  apacheParser,
  // ...
];

3. Add tests

// tests/parsers/myformat.test.ts
import { describe, expect, test } from 'bun:test';
import { myFormatParser } from '../../src/parser/formats/myformat';

describe('MyFormat Parser', () => {
  describe('detect', () => {
    test('detects valid format', () => {
      expect(myFormatParser.detect('MYFORMAT: [2024-01-01] INFO - test')).toBe(true);
    });

    test('rejects other formats', () => {
      expect(myFormatParser.detect('{"level":"info"}')).toBe(false);
    });
  });

  describe('parse', () => {
    test('parses correctly', () => {
      const result = myFormatParser.parse('MYFORMAT: [2024-01-01] ERROR - Something failed');
      expect(result).not.toBeNull();
      expect(result!.timestamp).toBe('2024-01-01');
      expect(result!.level).toBe('error');
      expect(result!.message).toBe('Something failed');
    });

    test('returns null for invalid line', () => {
      expect(myFormatParser.parse('invalid')).toBeNull();
    });
  });
});

Coding Guidelines

TypeScript

  • Use strict mode
  • Prefer explicit types for public APIs
  • Use type imports where possible

Testing

  • Aim for 95%+ coverage
  • Test both success and failure cases
  • Use descriptive test names
  • Group related tests with describe()

Performance

  • Parser detect() should be fast (regex test, not full parse)
  • Stream large files, don't load into memory
  • Avoid unnecessary allocations in hot paths

Error Handling

  • Don't crash on invalid input
  • Return null from parsers for non-matching lines
  • Log warnings for config issues, don't fail

Commit Messages

Use conventional commits:

feat: add support for Docker log format
fix: handle missing timestamp in syslog
docs: update README with new examples
test: add coverage for edge cases
refactor: simplify query executor

Release Process

Releases are automated via GitHub Actions when a tag is pushed:

git tag v0.2.0
git push origin v0.2.0

Contributors

Thanks to everyone who has contributed to loq!


Questions? Open an issue or discussion!