mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-03-06 08:17:42 -05:00
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 0.0.5 to 1.0.6. - [Commits](https://github.com/olekukonko/tablewriter/compare/v0.0.5...v1.0.6) --- updated-dependencies: - dependency-name: github.com/olekukonko/tablewriter dependency-version: 1.0.6 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
171 lines
6.6 KiB
Go
171 lines
6.6 KiB
Go
package lh
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/olekukonko/ll/lx"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// JSONHandler is a handler that outputs log entries as JSON objects.
|
|
// It formats log entries with timestamp, level, message, namespace, fields, and optional
|
|
// stack traces or dump segments, writing the result to the provided writer.
|
|
// Thread-safe with a mutex to protect concurrent writes.
|
|
type JSONHandler struct {
|
|
writer io.Writer // Destination for JSON output
|
|
timeFmt string // Format for timestamp (default: RFC3339Nano)
|
|
pretty bool // Enable pretty printing with indentation if true
|
|
fieldMap map[string]string // Optional mapping for field names (not used in provided code)
|
|
mu sync.Mutex // Protects concurrent access to writer
|
|
}
|
|
|
|
// JsonOutput represents the JSON structure for a log entry.
|
|
// It includes all relevant log data, such as timestamp, level, message, and optional
|
|
// stack trace or dump segments, serialized as a JSON object.
|
|
type JsonOutput struct {
|
|
Time string `json:"ts"` // Timestamp in specified format
|
|
Level string `json:"lvl"` // Log level (e.g., "INFO")
|
|
Class string `json:"class"` // Entry class (e.g., "Text", "Dump")
|
|
Msg string `json:"msg"` // Log message
|
|
Namespace string `json:"ns"` // Namespace path
|
|
Stack []byte `json:"stack"` // Stack trace (if present)
|
|
Dump []dumpSegment `json:"dump"` // Hex/ASCII dump segments (for ClassDump)
|
|
Fields map[string]interface{} `json:"fields"` // Custom fields
|
|
}
|
|
|
|
// dumpSegment represents a single segment of a hex/ASCII dump.
|
|
// Used for ClassDump entries to structure position, hex values, and ASCII representation.
|
|
type dumpSegment struct {
|
|
Offset int `json:"offset"` // Starting byte offset of the segment
|
|
Hex []string `json:"hex"` // Hexadecimal values of bytes
|
|
ASCII string `json:"ascii"` // ASCII representation of bytes
|
|
}
|
|
|
|
// NewJSONHandler creates a new JSONHandler writing to the specified writer.
|
|
// It initializes the handler with a default timestamp format (RFC3339Nano) and optional
|
|
// configuration functions to customize settings like pretty printing.
|
|
// Example:
|
|
//
|
|
// handler := NewJSONHandler(os.Stdout)
|
|
// logger := ll.New("app").Enable().Handler(handler)
|
|
// logger.Info("Test") // Output: {"ts":"...","lvl":"INFO","class":"Text","msg":"Test","ns":"app","stack":null,"dump":null,"fields":null}
|
|
func NewJSONHandler(w io.Writer, opts ...func(*JSONHandler)) *JSONHandler {
|
|
h := &JSONHandler{
|
|
writer: w, // Set output writer
|
|
timeFmt: time.RFC3339Nano, // Default timestamp format
|
|
}
|
|
// Apply configuration options
|
|
for _, opt := range opts {
|
|
opt(h)
|
|
}
|
|
return h
|
|
}
|
|
|
|
// Handle processes a log entry and writes it as JSON.
|
|
// It delegates to specialized methods based on the entry's class (Dump or regular),
|
|
// ensuring thread-safety with a mutex.
|
|
// Returns an error if JSON encoding or writing fails.
|
|
// Example:
|
|
//
|
|
// handler.Handle(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes JSON object
|
|
func (h *JSONHandler) Handle(e *lx.Entry) error {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
|
|
// Handle dump entries separately
|
|
if e.Class == lx.ClassDump {
|
|
return h.handleDump(e)
|
|
}
|
|
// Handle standard log entries
|
|
return h.handleRegular(e)
|
|
}
|
|
|
|
// handleRegular handles standard log entries (non-dump).
|
|
// It converts the entry to a JsonOutput struct and encodes it as JSON,
|
|
// applying pretty printing if enabled. Logs encoding errors to stderr for debugging.
|
|
// Returns an error if encoding or writing fails.
|
|
// Example (internal usage):
|
|
//
|
|
// h.handleRegular(&lx.Entry{Message: "test", Level: lx.LevelInfo}) // Writes JSON object
|
|
func (h *JSONHandler) handleRegular(e *lx.Entry) error {
|
|
// Create JSON output structure
|
|
entry := JsonOutput{
|
|
Time: e.Timestamp.Format(h.timeFmt), // Format timestamp
|
|
Level: e.Level.String(), // Convert level to string
|
|
Class: e.Class.String(), // Convert class to string
|
|
Msg: e.Message, // Set message
|
|
Namespace: e.Namespace, // Set namespace
|
|
Dump: nil, // No dump for regular entries
|
|
Fields: e.Fields, // Copy fields
|
|
Stack: e.Stack, // Include stack trace if present
|
|
}
|
|
// Create JSON encoder
|
|
enc := json.NewEncoder(h.writer)
|
|
if h.pretty {
|
|
// Enable indentation for pretty printing
|
|
enc.SetIndent("", " ")
|
|
}
|
|
// Log encoding attempt for debugging
|
|
fmt.Fprintf(os.Stderr, "Encoding JSON entry: %v\n", e.Message)
|
|
// Encode and write JSON
|
|
err := enc.Encode(entry)
|
|
if err != nil {
|
|
// Log encoding error for debugging
|
|
fmt.Fprintf(os.Stderr, "JSON encode error: %v\n", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// handleDump processes ClassDump entries, converting hex dump output to JSON segments.
|
|
// It parses the dump message into structured segments with offset, hex, and ASCII data,
|
|
// encoding them as a JsonOutput struct.
|
|
// Returns an error if parsing or encoding fails.
|
|
// Example (internal usage):
|
|
//
|
|
// h.handleDump(&lx.Entry{Class: lx.ClassDump, Message: "pos 00 hex: 61 62 'ab'"}) // Writes JSON with dump segments
|
|
func (h *JSONHandler) handleDump(e *lx.Entry) error {
|
|
var segments []dumpSegment
|
|
lines := strings.Split(e.Message, "\n")
|
|
|
|
// Parse each line of the dump message
|
|
for _, line := range lines {
|
|
if !strings.HasPrefix(line, "pos") {
|
|
continue // Skip non-dump lines
|
|
}
|
|
parts := strings.SplitN(line, "hex:", 2)
|
|
if len(parts) != 2 {
|
|
continue // Skip invalid lines
|
|
}
|
|
// Parse position
|
|
var offset int
|
|
fmt.Sscanf(parts[0], "pos %d", &offset)
|
|
|
|
// Parse hex and ASCII
|
|
hexAscii := strings.SplitN(parts[1], "'", 2)
|
|
hexStr := strings.Fields(strings.TrimSpace(hexAscii[0]))
|
|
|
|
// Create dump segment
|
|
segments = append(segments, dumpSegment{
|
|
Offset: offset, // Set byte offset
|
|
Hex: hexStr, // Set hex values
|
|
ASCII: strings.Trim(hexAscii[1], "'"), // Set ASCII representation
|
|
})
|
|
}
|
|
|
|
// Encode JSON output with dump segments
|
|
return json.NewEncoder(h.writer).Encode(JsonOutput{
|
|
Time: e.Timestamp.Format(h.timeFmt), // Format timestamp
|
|
Level: e.Level.String(), // Convert level to string
|
|
Class: e.Class.String(), // Convert class to string
|
|
Msg: "dumping segments", // Fixed message for dumps
|
|
Namespace: e.Namespace, // Set namespace
|
|
Dump: segments, // Include parsed segments
|
|
Fields: e.Fields, // Copy fields
|
|
Stack: e.Stack, // Include stack trace if present
|
|
})
|
|
}
|