Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Support for Loggy::ClassLogger and Loggy::InstanceLogger modules
- Detection of `log(...)` calls in classes that include/extend Loggy modules
- Support for all log levels (debug, info, warn, error, fatal) in Loggy calls
- Default info level for Loggy log calls without explicit level
- Comprehensive test coverage for Loggy integration
- Documentation for Loggy support in README.md
- LOGGY_SUPPORT.md with detailed usage examples

## [0.1.4] - Previous Release

(Add previous release notes here if available)
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
diffdash (0.1.3)
diffdash (0.1.4)
ast (~> 2.4)
dotenv (>= 2.8)
faraday (>= 2.0)
Expand Down
157 changes: 157 additions & 0 deletions LOGGY_SUPPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Loggy Module Support

## Overview

Diffdash now supports the [Loggy](https://github.com/ddollar/loggy) gem's logging modules:
- `Loggy::ClassLogger`
- `Loggy::InstanceLogger`

When a class includes, prepends, or extends these modules, Diffdash will detect `log(...)` method calls and include them in generated Grafana dashboards.

## Supported Patterns

### Include Loggy::ClassLogger

```ruby
class PaymentProcessor
include Loggy::ClassLogger

def process_payment
log(:info, "payment_started")
log(:error, "payment_failed")
end
end
```

### Include Loggy::InstanceLogger

```ruby
class OrderService
include Loggy::InstanceLogger

def create_order
log(:info, "order_created")
log(:warn, "order_validation_warning")
end
end
```

### Extend for Class Methods

```ruby
class BatchProcessor
extend Loggy::ClassLogger

def self.process_batch
log(:info, "batch_started")
log(:info, "batch_completed")
end
end
```

## Log Levels

All standard log levels are supported:

```ruby
log(:debug, "debug_message")
log(:info, "info_message")
log(:warn, "warning_message")
log(:error, "error_message")
log(:fatal, "fatal_message")
```

### Default Level

If no level is specified, `info` is used:

```ruby
log("task_completed") # Equivalent to log(:info, "task_completed")
```

## Mixed Logging

Loggy and standard logger calls can coexist in the same class:

```ruby
class TransactionProcessor
include Loggy::InstanceLogger

def process_transaction
log(:info, "transaction_started") # Loggy style
logger.info "Using standard logger" # Standard style
log(:info, "transaction_completed") # Loggy style
end
end
```

Both styles will be detected and included in the dashboard.

## Inheritance Support

Loggy modules work with Diffdash's inheritance tracking:

```ruby
# app/services/base_processor.rb
class BaseProcessor
include Loggy::ClassLogger

def log_start
log(:info, "processing_started")
end
end

# app/services/payment_processor.rb
class PaymentProcessor < BaseProcessor
def charge
log_start # Inherited method with Loggy log call
log(:info, "payment_charged")
end
end
```

When `PaymentProcessor` is changed, signals from both the class and its parent `BaseProcessor` are extracted.

## How It Works

1. **Module Detection**: The AST visitor tracks `include`, `prepend`, and `extend` statements
2. **Context Awareness**: When processing a `log(...)` call, the visitor checks if the current class has included/prepended/extended a Loggy module
3. **Level Extraction**: The first argument is checked to see if it's a log level symbol; otherwise, `info` is used
4. **Event Name Extraction**: The message argument is processed the same way as standard logger calls

## Testing

Comprehensive test coverage includes:
- Detection of `log(...)` calls with Loggy modules
- All log levels (debug, info, warn, error, fatal)
- Default info level behavior
- Mixed Loggy and standard logger usage
- Include, prepend, and extend patterns
- Classes without Loggy modules (negative tests)
- Nested classes with Loggy modules

Run tests:

```bash
bundle exec rspec spec/diffdash/ast/visitor_spec.rb -fd | grep -A 10 "with Loggy"
bundle exec rspec spec/diffdash/integration/loggy_integration_spec.rb
```

## Grafana Compatibility

Loggy log calls are treated identically to standard logger calls in Grafana dashboards:
- Log panels use Loki as the data source
- Event names are extracted from log messages
- Log levels are preserved in metadata
- All standard Grafana log visualization features apply

## Example Output

When Diffdash processes a file with Loggy logs:

```
[diffdash] Dashboard created with 3 panels: 3 logs
[diffdash] Uploaded to: https://myorg.grafana.net/d/abc123/feature-branch
```

The dashboard will include log panels for each detected `log(...)` call, just like standard logger calls.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ In dry-run mode:
- `logger.info`, `logger.debug`, `logger.warn`, `logger.error`, `logger.fatal`
- `Rails.logger.*`
- `@logger.*`
- `log(...)` in classes that include/extend `Loggy::ClassLogger` or `Loggy::InstanceLogger`

### Metrics

Expand Down Expand Up @@ -207,6 +208,41 @@ end

When `PaymentProcessor` is changed, signals from `BaseProcessor` and `Loggable` are also extracted.

### Loggy Module Support

Diffdash supports the [Loggy](https://github.com/ddollar/loggy) gem's `ClassLogger` and `InstanceLogger` modules:

```ruby
class PaymentProcessor
include Loggy::ClassLogger

def process_payment
log(:info, "payment_started")
log(:error, "payment_failed") if error
# Or with default info level:
log("payment_completed")
end
end

class OrderService
include Loggy::InstanceLogger

def create_order
log(:info, "order_created")
end
end

class BatchProcessor
extend Loggy::ClassLogger

def self.process_batch
log(:warn, "batch_processing_started")
end
end
```

The `log(...)` method calls are detected and included in dashboards when the class includes, prepends, or extends `Loggy::ClassLogger` or `Loggy::InstanceLogger`.

## Dashboard Behavior

- **Deterministic UID:** Dashboard UID is derived from the branch name, ensuring the same PR always updates the same dashboard
Expand Down
75 changes: 75 additions & 0 deletions examples/loggy_example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Example demonstrating Loggy::ClassLogger and Loggy::InstanceLogger support

# Example 1: Using Loggy::ClassLogger
class PaymentProcessor
include Loggy::ClassLogger

def process_payment(amount)
log(:info, "payment_started")

# Process payment logic here

if amount > 0
log(:info, "payment_processed_successfully")
else
log(:error, "invalid_payment_amount")
end
end
end

# Example 2: Using Loggy::InstanceLogger
class OrderProcessor
include Loggy::InstanceLogger

def process_order(order_id)
log(:info, "order_processing_started")

# Order processing logic

log(:info, "order_completed")
rescue StandardError => e
log(:error, "order_processing_failed")
raise
end
end

# Example 3: Using extend with Loggy::ClassLogger for class methods
class BatchProcessor
extend Loggy::ClassLogger

def self.process_batch(batch_size)
log(:info, "batch_processing_started")

# Batch processing logic

log(:info, "batch_completed")
end
end

# Example 4: Mixed logging approaches (Loggy + standard logger)
class TransactionProcessor
include Loggy::InstanceLogger

def process_transaction(txn_id)
# Using Loggy's log method
log(:info, "transaction_started")

# Can also use standard logger.info if needed
logger.info "Standard logger message"

log(:info, "transaction_completed")
end
end

# Example 5: Different log levels
class ErrorHandler
include Loggy::ClassLogger

def handle_request
log(:debug, "request_received")
log(:info, "request_processing")
log(:warn, "potential_issue_detected")
log(:error, "request_failed")
log(:fatal, "critical_system_error")
end
end
Loading
Loading