diff --git a/CRUSH.md b/CRUSH.md
new file mode 100644
index 0000000..b3cee5a
--- /dev/null
+++ b/CRUSH.md
@@ -0,0 +1,501 @@
+# CRUSH.md - Log Socket v2 Development Guide
+
+This document provides context and conventions for working on **log-socket v2** - a real-time log viewer with WebSocket support and namespace filtering, written in Go.
+
+## Project Overview
+
+Log Socket v2 is a Go library and standalone application that provides:
+- Real-time log streaming via WebSocket
+- **Namespace-based log organization** (NEW in v2)
+- Web-based log viewer with namespace filtering
+- Support for multiple log levels (TRACE, DEBUG, INFO, NOTICE, WARN, ERROR, PANIC, FATAL)
+- Client architecture allowing multiple subscribers to filtered log streams
+
+**Key insight**: This is both a library (importable Go package) and a standalone application. The main.go serves as an example implementation.
+
+## Essential Commands
+
+### Build
+```bash
+go build -v ./...
+```
+
+### Run Server
+```bash
+# Default (runs on 0.0.0.0:8080)
+go run main.go
+
+# Custom address
+go run main.go -addr localhost:8080
+```
+
+Once running, open browser to `http://localhost:8080` to view logs.
+
+### Test
+```bash
+go test -v ./...
+```
+
+### Install
+```bash
+go install github.com/taigrr/log-socket/v2@latest
+```
+
+### Dependencies
+```bash
+go get .
+```
+
+## Project Structure
+
+```
+.
+├── main.go # Example server with multiple namespaces
+├── log/ # Core logging package
+│ ├── log.go # Package-level logging functions + namespace tracking
+│ ├── logger.go # Logger type with namespace support
+│ ├── types.go # Type definitions (includes Namespace fields)
+│ └── log_test.go # Tests
+├── ws/ # WebSocket server
+│ ├── server.go # LogSocketHandler with namespace filtering
+│ └── namespaces.go # HTTP handler for namespace list API
+└── browser/ # Web UI
+ ├── browser.go # HTTP handler serving embedded HTML
+ └── viewer.html # Embedded web interface with namespace filter
+```
+
+## Major Changes in v2
+
+### Module Path
+- **v1**: `github.com/taigrr/log-socket`
+- **v2**: `github.com/taigrr/log-socket/v2`
+
+### Namespace Support
+
+**Core concept**: Namespaces allow organizing logs by component, service, or domain (e.g., "api", "database", "auth").
+
+#### Types Changes
+
+**Entry** now includes:
+```go
+type Entry struct {
+ Timestamp time.Time `json:"timestamp"`
+ Output string `json:"output"`
+ File string `json:"file"`
+ Level string `json:"level"`
+ Namespace string `json:"namespace"` // NEW
+ level Level
+}
+```
+
+**Client** now uses a slice for filtering:
+```go
+type Client struct {
+ LogLevel Level `json:"level"`
+ Namespaces []string `json:"namespaces"` // Empty = all namespaces
+ writer LogWriter
+ initialized bool
+}
+```
+
+**Logger** has namespace field:
+```go
+type Logger struct {
+ FileInfoDepth int
+ Namespace string // NEW
+}
+```
+
+#### API Changes
+
+**CreateClient** now variadic:
+```go
+// v1
+func CreateClient() *Client
+
+// v2
+func CreateClient(namespaces ...string) *Client
+
+// Examples:
+client := log.CreateClient() // All namespaces
+client := log.CreateClient("api") // Single namespace
+client := log.CreateClient("api", "database") // Multiple namespaces
+```
+
+**NewLogger** constructor added:
+```go
+func NewLogger(namespace string) *Logger
+
+// Example:
+apiLogger := log.NewLogger("api")
+apiLogger.Info("API request received")
+```
+
+### Namespace Tracking
+
+Global namespace registry tracks all used namespaces:
+```go
+var (
+ namespaces map[string]bool
+ namespacesMux sync.RWMutex
+)
+
+func GetNamespaces() []string
+```
+
+Namespaces are automatically registered when logs are created.
+
+### WebSocket Changes
+
+**Query parameter for filtering**:
+```
+ws://localhost:8080/ws?namespaces=api,database
+```
+
+The handler parses comma-separated namespace list and creates a filtered client.
+
+### Web UI Changes
+
+- Namespace filter input field added to controls
+- Namespace column added to log table
+- Reconnect button to apply namespace filter
+- WebSocket URL includes namespace query parameter
+
+## Code Organization & Architecture
+
+### Log Package (`log/`)
+
+Dual API remains, but with namespace support:
+
+1. **Package-level functions**: Use "default" namespace
+ - `log.Info()`, `log.Debug()`, etc.
+ - All entry creations include `Namespace: DefaultNamespace`
+
+2. **Logger instances**: Use custom namespace
+ - Create with `log.NewLogger(namespace)`
+ - All entry creations include `Namespace: l.Namespace`
+
+### Client Architecture (Updated)
+
+**Client filtering by namespace**:
+
+1. **Empty Namespaces slice**: Receives all logs regardless of namespace
+2. **Non-empty Namespaces**: Only receives logs matching one of the specified namespaces
+
+**matchesNamespace helper**:
+```go
+func (c *Client) matchesNamespace(namespace string) bool {
+ // Empty Namespaces slice means match all
+ if len(c.Namespaces) == 0 {
+ return true
+ }
+ for _, ns := range c.Namespaces {
+ if ns == namespace {
+ return true
+ }
+ }
+ return false
+}
+```
+
+**Entry flow with namespace filtering**:
+1. Log function called with namespace
+2. `Entry` created with namespace field
+3. Namespace registered in global map
+4. `createLog()` sends to all clients
+5. Each client checks `matchesNamespace()`
+6. Only matching clients receive the entry
+
+### WebSocket Handler (`ws/`)
+
+**Namespace parameter parsing**:
+```go
+namespacesParam := r.URL.Query().Get("namespaces")
+var namespaces []string
+if namespacesParam != "" {
+ namespaces = strings.Split(namespacesParam, ",")
+}
+lc := logger.CreateClient(namespaces...)
+```
+
+**Namespaces API handler** (`ws/namespaces.go`):
+```go
+func NamespacesHandler(w http.ResponseWriter, r *http.Request) {
+ namespaces := logger.GetNamespaces()
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "namespaces": namespaces,
+ })
+}
+```
+
+### Browser Package (`browser/`)
+
+Still embeds viewer.html, but HTML now includes:
+- Namespace filter input
+- Namespace column in grid (5 columns instead of 4)
+- `reconnectWithNamespace()` method
+- WebSocket URL construction with query parameter
+
+## Go Version & Dependencies
+
+- **Go version**: 1.24.4 (specified in go.mod)
+- **Only external dependency**: `github.com/gorilla/websocket v1.5.3`
+
+## Naming Conventions & Style
+
+### Namespaces
+- Use lowercase strings: `"api"`, `"database"`, `"auth"`, `"cache"`
+- Default constant: `DefaultNamespace = "default"`
+- Comma-separated in query params: `?namespaces=api,database`
+
+### Log Levels
+Unchanged from v1 - still use uppercase strings and iota constants.
+
+### Variable Names
+- Use descriptive names (`apiLogger`, `dbLogger`, `namespaces`)
+- Exception: Loop variables, short-lived scopes
+
+## Important Patterns & Gotchas
+
+### 1. Namespace Tracking is Automatic
+When any log is created, its namespace is automatically added to the global registry:
+```go
+func createLog(e Entry) {
+ // Track namespace
+ namespacesMux.Lock()
+ namespaces[e.Namespace] = true
+ namespacesMux.Unlock()
+ // ... rest of function
+}
+```
+
+**No manual registration needed** - just log and the namespace appears.
+
+### 2. Empty Namespace List = All Logs
+Both for clients and WebSocket connections:
+```go
+client := log.CreateClient() // Gets ALL logs
+ws://localhost:8080/ws // Gets ALL logs
+```
+
+This is the default behavior to maintain backward compatibility.
+
+### 3. Client Namespace Filtering is Inclusive (OR)
+If a client has multiple namespaces, it receives logs matching ANY of them:
+```go
+client := log.CreateClient("api", "database")
+// Receives logs from "api" OR "database", not "auth"
+```
+
+### 4. Namespace Field Always Set
+All logging functions set namespace:
+- Package functions: `DefaultNamespace`
+- Logger methods: `l.Namespace`
+
+**Never nil or empty** - there's always a namespace.
+
+### 5. WebSocket Namespace Reconnection
+Changing namespace filter requires reconnecting WebSocket:
+```javascript
+reconnectWithNamespace() {
+ if (this.ws) {
+ this.ws.onclose = null; // Prevent auto-reconnect
+ this.ws.close();
+ this.ws = null;
+ }
+ this.reconnectAttempts = 0;
+ this.connectWebSocket(); // Creates new connection with new filter
+}
+```
+
+UI provides "Reconnect" button for this purpose.
+
+### 6. Stderr Client Uses All Namespaces
+The built-in stderr client (created in `init()`) listens to all namespaces:
+```go
+stderrClient = CreateClient(DefaultNamespace)
+```
+
+But only prints logs matching its own namespace in `logStdErr()`:
+```go
+if e.level >= c.LogLevel && c.matchesNamespace(e.Namespace) {
+ fmt.Fprintf(os.Stderr, "%s\t%s\t[%s]\t%s\t%s\n", ...)
+}
+```
+
+**Wait, that's a bug!** The stderr client is created with `DefaultNamespace` but should be created with no namespaces to see all logs. Let me check this.
+
+Actually looking at the code:
+```go
+stderrClient = CreateClient(DefaultNamespace)
+```
+
+This means stderr client only sees "default" namespace logs. This might be intentional, but seems like a bug. Should probably be:
+```go
+stderrClient = CreateClient() // No args = all namespaces
+```
+
+### 7. Grid Layout Updated
+The log viewer grid changed from 4 to 5 columns:
+```css
+/* v1 */
+grid-template-columns: 180px 80px 1fr 120px;
+
+/* v2 */
+grid-template-columns: 180px 80px 100px 1fr 120px;
+```
+
+Order: Timestamp, Level, Namespace, Message, Source
+
+## Testing
+
+### Test Updates for v2
+
+All `CreateClient()` calls in tests now pass namespace:
+```go
+// v1
+c := CreateClient()
+
+// v2
+c := CreateClient("test")
+c := CreateClient(DefaultNamespace)
+```
+
+Tests verify namespace appears in output (see stderr format).
+
+### Running Tests
+```bash
+go test -v ./...
+```
+
+All existing tests pass with namespace support added.
+
+## CI/CD
+
+GitHub Actions workflow (`.github/workflows/ci.yaml`):
+- Still uses Go 1.21 (should update to 1.24.4 to match go.mod)
+- No changes needed for v2 functionality
+
+## Common Tasks
+
+### Adding a New Namespace
+
+No code changes needed! Just create a logger:
+```go
+cacheLogger := log.NewLogger("cache")
+cacheLogger.Info("Cache initialized")
+```
+
+Namespace automatically tracked and available via API.
+
+### Creating Namespace-Specific Client
+
+```go
+// Subscribe only to API logs
+apiClient := log.CreateClient("api")
+defer apiClient.Destroy()
+
+for {
+ entry := apiClient.Get()
+ // Only receives logs from "api" namespace
+ processAPILog(entry)
+}
+```
+
+### Filtering WebSocket by Namespace
+
+Frontend:
+```javascript
+// Set namespace filter
+document.getElementById('namespaceFilter').value = 'api,database';
+// Click reconnect button or call:
+logViewer.reconnectWithNamespace();
+```
+
+Backend automatically creates filtered client based on query param.
+
+### Getting All Active Namespaces
+
+```go
+namespaces := log.GetNamespaces()
+// Returns: ["default", "api", "database", "auth", ...]
+```
+
+Or via HTTP:
+```bash
+GET /api/namespaces
+```
+
+Returns:
+```json
+{
+ "namespaces": ["default", "api", "database", "auth"]
+}
+```
+
+## Migration from v1 to v2
+
+### Import Paths
+```go
+// v1
+import "github.com/taigrr/log-socket/log"
+import "github.com/taigrr/log-socket/ws"
+import "github.com/taigrr/log-socket/browser"
+
+// v2
+import "github.com/taigrr/log-socket/v2/log"
+import "github.com/taigrr/log-socket/v2/ws"
+import "github.com/taigrr/log-socket/v2/browser"
+```
+
+### CreateClient Calls
+```go
+// v1
+client := log.CreateClient()
+
+// v2 - same behavior
+client := log.CreateClient() // Empty = all namespaces
+
+// v2 - new filtering capability
+client := log.CreateClient("api")
+client := log.CreateClient("api", "database")
+```
+
+### Default() Logger
+```go
+// v1
+logger := log.Default()
+
+// v2 - uses default namespace
+logger := log.Default()
+
+// v2 - new namespaced option
+logger := log.NewLogger("api")
+```
+
+### WebSocket URL
+```
+v1: ws://host/ws
+v2: ws://host/ws # All namespaces (backward compatible)
+v2: ws://host/ws?namespaces=api # Filtered
+v2: ws://host/ws?namespaces=api,database # Multiple
+```
+
+## Repository Context
+
+- **License**: Check LICENSE file
+- **Funding**: GitHub sponsors (.github/FUNDING.yml)
+- **Open Source**: github.com/taigrr/log-socket
+- **Version**: v2.x.x (major version bump for breaking changes)
+
+## Future Agent Notes
+
+- **v2 is a breaking change**: Major version bump follows Go modules convention
+- Namespace support is the primary new feature
+- Backward compatible behavior: empty namespace list = all logs
+- Namespace tracking is automatic via global registry
+- Web UI has been significantly updated for namespace support
+- Possible bug: stderr client might need to use `CreateClient()` instead of `CreateClient(DefaultNamespace)` to see all logs
+- All tests updated and passing
+- Example in main.go demonstrates multiple namespaces
diff --git a/README.md b/README.md
index ba3ef48..7b3b6e9 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,26 @@
-# Log Socket
+# Log Socket v2
-A real-time log viewer with WebSocket support, written in Go. This tool provides a web-based interface for viewing and filtering logs in real-time.
+A real-time log viewer with WebSocket support and namespace filtering, written in Go.
+
+## What's New in v2
+
+**Breaking Changes:**
+- Module path changed to `github.com/taigrr/log-socket/v2`
+- `CreateClient()` now accepts variadic `namespaces ...string` parameter
+- `Logger` type now includes `Namespace` field
+- New `NewLogger(namespace string)` constructor for namespaced loggers
+
+**New Features:**
+- **Namespace support**: Organize logs by namespace (api, database, auth, etc.)
+- **Namespace filtering**: Subscribe to specific namespaces via WebSocket
+- **Frontend namespace selector**: Filter logs by namespace in the web UI
+- **Namespace API**: GET `/api/namespaces` to list all active namespaces
## Features
- Real-time log streaming via WebSocket
- Web-based log viewer with filtering capabilities
+- **Namespace-based log organization**
- Support for multiple log levels (TRACE, DEBUG, INFO, WARN, ERROR, PANIC, FATAL)
- Color-coded log levels for better visibility
- Auto-scrolling with toggle option
@@ -16,47 +31,194 @@ A real-time log viewer with WebSocket support, written in Go. This tool provides
## Installation
```bash
-go install github.com/taigrr/log-socket@latest
+go install github.com/taigrr/log-socket/v2@latest
```
-## Example Preview
+## Quick Start
-1. Start the server:
+```go
+package main
- ```bash
- log-socket
- ```
+import (
+ "net/http"
+ logger "github.com/taigrr/log-socket/v2/log"
+ "github.com/taigrr/log-socket/v2/ws"
+ "github.com/taigrr/log-socket/v2/browser"
+)
- By default, the server runs on `0.0.0.0:8080`. You can specify a different address using the `-addr` flag:
+func main() {
+ defer logger.Flush()
+
+ // Set up HTTP handlers
+ http.HandleFunc("/ws", ws.LogSocketHandler)
+ http.HandleFunc("/api/namespaces", ws.NamespacesHandler)
+ http.HandleFunc("/", browser.LogSocketViewHandler)
+
+ // Use default namespace
+ logger.Info("Application started")
+
+ // Create namespaced loggers
+ apiLogger := logger.NewLogger("api")
+ dbLogger := logger.NewLogger("database")
+
+ apiLogger.Info("API server ready")
+ dbLogger.Debug("Database connected")
+
+ logger.Fatal(http.ListenAndServe(":8080", nil))
+}
+```
- ```bash
- log-socket -addr localhost:8080
- ```
+## Usage
-2. Open your browser and navigate to `http://localhost:8080`
+### Starting the Server
- 
+```bash
+log-socket
+```
-## Logging Interface
+By default, the server runs on `0.0.0.0:8080`. Specify a different address:
-The package provides a comprehensive logging interface with the following methods:
+```bash
+log-socket -addr localhost:8080
+```
-- `Trace/Tracef/Traceln`: For trace-level logging
-- `Debug/Debugf/Debugln`: For debug-level logging
-- `Info/Infof/Infoln`: For info-level logging
-- `Notice/Noticef/Noticeln`: For notice-level logging
-- `Warn/Warnf/Warnln`: For warning-level logging
-- `Error/Errorf/Errorln`: For error-level logging
-- `Panic/Panicf/Panicln`: For panic-level logging
-- `Fatal/Fatalf/Fatalln`: For fatal-level logging
+### Web Interface
+
+Open your browser and navigate to `http://localhost:8080`
+
+**Namespace Filtering:**
+- The namespace dropdown is automatically populated from `/api/namespaces`
+- Select one or more namespaces to filter (hold Ctrl/Cmd to multi-select)
+- Default is "All Namespaces" (shows everything)
+- Click "Reconnect" to apply the filter
+
+## API
+
+### Logging Interface
+
+The package provides two ways to log:
+
+#### 1. Package-level functions (default namespace)
+
+```go
+logger.Trace("trace message")
+logger.Debug("debug message")
+logger.Info("info message")
+logger.Notice("notice message")
+logger.Warn("warning message")
+logger.Error("error message")
+logger.Panic("panic message") // Logs and panics
+logger.Fatal("fatal message") // Logs and exits
+```
+
+Each has formatted (`f`) and line (`ln`) variants:
+```go
+logger.Infof("User %s logged in", username)
+logger.Infoln("This adds a newline")
+```
+
+#### 2. Namespaced loggers
+
+```go
+apiLogger := logger.NewLogger("api")
+apiLogger.Info("Request received")
+
+dbLogger := logger.NewLogger("database")
+dbLogger.Warn("Slow query detected")
+```
+
+### Creating Clients with Namespace Filters
+
+```go
+// Listen to all namespaces
+client := logger.CreateClient()
+
+// Listen to specific namespace
+client := logger.CreateClient("api")
+
+// Listen to multiple namespaces
+client := logger.CreateClient("api", "database", "auth")
+```
+
+### WebSocket API
+
+#### Log Stream Endpoint
+
+**URL:** `ws://localhost:8080/ws`
+
+**Query Parameters:**
+- `namespaces` (optional): Comma-separated list of namespaces to filter
+
+**Examples:**
+```
+ws://localhost:8080/ws # All namespaces
+ws://localhost:8080/ws?namespaces=api # Only "api" namespace
+ws://localhost:8080/ws?namespaces=api,database # Multiple namespaces
+```
+
+**Message Format:**
+```json
+{
+ "timestamp": "2024-11-10T15:42:49.777298-05:00",
+ "output": "API request received",
+ "file": "main.go:42",
+ "level": "INFO",
+ "namespace": "api"
+}
+```
+
+#### Namespaces List Endpoint
+
+**URL:** `GET http://localhost:8080/api/namespaces`
+
+**Response:**
+```json
+{
+ "namespaces": ["default", "api", "database", "auth"]
+}
+```
## Web Interface Features
-- **Filtering**: Type in the search box to filter logs
-- **Auto-scroll**: Toggle auto-scrolling with the checkbox
+- **Namespace Dropdown**: Dynamically populated from `/api/namespaces`, multi-select support
+- **Text Search**: Filter logs by content, level, namespace, or source file
+- **Auto-scroll**: Toggle auto-scrolling with checkbox
- **Download**: Save all logs as a JSON file
- **Clear**: Remove all logs from the viewer
-- **Color Coding**: Different log levels are color-coded for easy identification
+- **Color Coding**: Different log levels are color-coded
+- **Reconnect**: Reconnect WebSocket with new namespace filter
+
+## Migration from v1
+
+### Import Path
+
+```go
+// v1
+import "github.com/taigrr/log-socket/log"
+
+// v2
+import "github.com/taigrr/log-socket/v2/log"
+```
+
+### CreateClient Changes
+
+```go
+// v1
+client := log.CreateClient()
+
+// v2 - specify namespace(s) or leave empty for all
+client := log.CreateClient() // All namespaces
+client := log.CreateClient("api") // Single namespace
+client := log.CreateClient("api", "db") // Multiple namespaces
+```
+
+### New Logger Constructor
+
+```go
+// v2 only - create namespaced logger
+apiLogger := log.NewLogger("api")
+apiLogger.Info("Message in api namespace")
+```
## Dependencies
@@ -64,6 +226,8 @@ The package provides a comprehensive logging interface with the following method
## Notes
-The web interface is not meant to be used as-is.
-It functions perfectly well for some scenarios, but it is broken out into a different package intentionally, such that users can add their own as they see fit.
-It's mostly here to provide an example of how to consume the websocket data and display it.
+The web interface is provided as an example implementation. Users are encouraged to customize it for their specific needs. The WebSocket endpoint (`/ws`) can be consumed by any WebSocket client.
+
+## License
+
+See LICENSE file for details.
diff --git a/browser/viewer.html b/browser/viewer.html
index 5d00c05..c8b98fc 100644
--- a/browser/viewer.html
+++ b/browser/viewer.html
@@ -126,7 +126,7 @@
.log-row {
display: grid;
- grid-template-columns: 180px 80px 1fr 120px;
+ grid-template-columns: 180px 80px 100px 1fr 120px;
gap: 15px;
padding: 10px 15px;
border-bottom: 1px solid var(--border-color);
@@ -321,6 +321,17 @@
📊 Log Viewer