Skip to content
Open
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ go.work
turncat
stunnerctl
stunnerd
icetester
icetestercmd/wrapper/stunnerd-wrapper
48 changes: 48 additions & 0 deletions Dockerfile.wrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Build stage
FROM golang:1.23-alpine AS builder

# Set working directory
WORKDIR /app

# Copy go mod files
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy source code
COPY . .

# Build the wrapper binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o stunnerd-wrapper ./cmd/wrapper

# Final stage
FROM alpine:latest

# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates

# Create non-root user
RUN addgroup -g 1001 -S stunner && \
adduser -u 1001 -S stunner -G stunner

# Set working directory
WORKDIR /app

# Copy binary from builder stage
COPY --from=builder /app/stunnerd-wrapper .

# Change ownership to non-root user
RUN chown stunner:stunner /app/stunnerd-wrapper

# Switch to non-root user
USER stunner

# Expose default TURN port
EXPOSE 3478

# Set entrypoint
ENTRYPOINT ["./stunnerd-wrapper"]

# Default command (can be overridden)
CMD ["--help"]
36 changes: 36 additions & 0 deletions cmd/stunnerd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package main
import (
"context"
"fmt"
"log"
"log/slog"
"os"
"os/signal"
"strings"
"syscall"
"time"

Expand All @@ -17,6 +20,17 @@ import (
cdsclient "github.com/l7mp/stunner/pkg/config/client"
)

// slogWriter converts log output to slog
type slogWriter struct {
logger *slog.Logger
}

func (w *slogWriter) Write(p []byte) (n int, err error) {
msg := strings.TrimSpace(string(p))
w.logger.Info(msg)
return len(p), nil
}

var (
version = "dev"
commitHash = "n/a"
Expand All @@ -33,6 +47,7 @@ func main() {
"Number of readloop threads (CPU cores) per UDP listener. Zero disables UDP multithreading (default: 0)")
var dryRun = flag.BoolP("dry-run", "d", false, "Suppress side-effects, intended for testing (default: false)")
var verbose = flag.BoolP("verbose", "v", false, "Verbose logging, identical to <-l all:DEBUG>")
var jsonLog = flag.BoolP("json-log", "j", false, "Enable JSON formatted logging (default: false)")

// Kubernetes config flags
k8sConfigFlags := cliopt.NewConfigFlags(true)
Expand All @@ -44,6 +59,27 @@ func main() {

flag.Parse()

// Check for JSON logging environment variable
if jsonLogEnv := os.Getenv("STUNNER_JSON_LOG"); jsonLogEnv == "true" || jsonLogEnv == "1" {
*jsonLog = true
}

// Setup JSON logging if requested
if *jsonLog {
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
})

// Create a slog logger
slogger := slog.New(handler)

// Redirect standard log to slog using our custom writer
log.SetFlags(0)
log.SetOutput(&slogWriter{logger: slogger})

slogger.Info("JSON logging enabled")
}

logLevel := stnrv1.DefaultLogLevel
if *verbose {
logLevel = "all:DEBUG"
Expand Down
45 changes: 45 additions & 0 deletions cmd/wrapper/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Git files
.git
.gitignore

# Documentation
*.md
docs/

# Test files
*_test.go
test/

# Build artifacts
bin/
build/
dist/

# IDE files
.vscode/
.idea/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
*.log

# Temporary files
tmp/
temp/

# Docker files
Dockerfile*
.dockerignore

# Kubernetes manifests
*.yaml
*.yml
deploy/

# Examples
examples/
48 changes: 48 additions & 0 deletions cmd/wrapper/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Build stage
FROM golang:1.21-alpine AS builder

# Set working directory
WORKDIR /app

# Copy go mod files from root directory
COPY ../../go.mod ../../go.sum ./

# Download dependencies
RUN go mod download

# Copy source code from root directory
COPY ../../ .

# Build the wrapper binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o stunnerd-wrapper ./cmd/wrapper

# Final stage
FROM alpine:latest

# Install ca-certificates for HTTPS requests
RUN apk --no-cache add ca-certificates

# Create non-root user
RUN addgroup -g 1001 -S stunner && \
adduser -u 1001 -S stunner -G stunner

# Set working directory
WORKDIR /app

# Copy binary from builder stage
COPY --from=builder /app/stunnerd-wrapper .

# Change ownership to non-root user
RUN chown stunner:stunner /app/stunnerd-wrapper

# Switch to non-root user
USER stunner

# Expose default TURN port
EXPOSE 3478

# Set entrypoint
ENTRYPOINT ["./stunnerd-wrapper"]

# Default command (can be overridden)
CMD ["--help"]
99 changes: 99 additions & 0 deletions cmd/wrapper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Stunner JSON Logging Wrapper

This directory contains a wrapper implementation that converts all Stunner logs to JSON format.

## Overview

The `StunnerWrapper` class encapsulates the original `stunnerd` main functionality and provides:

- **Automatic JSON Logging**: All logs are automatically converted to JSON format
- **Structured Logging**: Log entries include structured fields for better parsing
- **Backward Compatibility**: Maintains the same command-line interface as the original `stunnerd`
- **Enhanced Observability**: Better integration with log aggregation systems

## Key Features

### JSON Logging Conversion
- All standard log output is converted to JSON format
- Structured fields for better log parsing and analysis
- Consistent log format across all Stunner components

### Wrapper Architecture
- `StunnerWrapper` class encapsulates the main functionality
- Clean separation between logging concerns and business logic
- Easy to extend with additional logging features

### Enhanced Logging
- Structured JSON output with consistent field names
- Better error context and debugging information
- Improved log levels and categorization

## Usage

The wrapper can be used as a drop-in replacement for the original `stunnerd`:

```bash
# Build the wrapper
go build -o stunnerd-wrapper ./cmd/wrapper

# Run with JSON logging (always enabled)
./stunnerd-wrapper --config k8s --verbose

# Run with custom configuration
./stunnerd-wrapper --config file://config.yaml --log all:DEBUG
```

## Log Output Format

All logs are output in JSON format with structured fields:

```json
{
"time": "2024-01-15T10:30:45.123Z",
"level": "INFO",
"msg": "Starting stunnerd with JSON logging wrapper",
"id": "default/stunnerd-hostname",
"buildInfo": "dev (n/a) <unknown>"
}
```

## Implementation Details

### StunnerWrapper Class
- `SetupJSONLogging()`: Configures JSON logging for all output
- `InitializeStunner()`: Sets up the Stunner instance with logging
- `LoadConfiguration()`: Handles configuration loading with JSON logging
- `StartMainLoop()`: Runs the main event loop with enhanced logging
- `Close()`: Cleanup and resource management

### Log Conversion
- `slogWriter`: Custom writer that converts standard log output to JSON
- Automatic redirection of all log output to JSON format
- Structured field extraction from log messages

## Benefits

1. **Better Observability**: JSON logs are easier to parse and analyze
2. **Log Aggregation**: Better integration with ELK stack, Splunk, etc.
3. **Debugging**: Structured fields make debugging easier
4. **Monitoring**: Better integration with monitoring systems
5. **Compliance**: Structured logging helps with audit requirements

## Migration

To migrate from the original `stunnerd` to the wrapper:

1. Replace the binary with the wrapper version
2. No configuration changes required
3. All existing command-line options work the same
4. JSON logging is automatically enabled

## Development

The wrapper approach allows for easy extension:

- Add custom log fields
- Implement log filtering
- Add log rotation
- Integrate with external logging services
- Add metrics and monitoring hooks
Loading
Loading