2.0.0 4803e67

setup.go RawHistoryFolder

1package logging
2
3import (
4	"fmt"
5	"io"
6	"os"
7	"path/filepath"
8
9	"github.com/rs/zerolog"
10	"github.com/rs/zerolog/log"
11	"gopkg.in/natefinch/lumberjack.v2"
12	"gordon/internal/config"
13)
14
15var (
16	MainLogger  zerolog.Logger
17	ProxyLogger zerolog.Logger
18)
19
20// Setup initializes the logging system based on the configuration
21func Setup(cfg *config.Config) error {
22	if !cfg.Logging.Enabled {
23		// Keep console logging only
24		log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
25		MainLogger = log.Logger
26		ProxyLogger = log.Logger
27		return nil
28	}
29
30	// Create logs directory with secure permissions (0700 - owner only)
31	if err := os.MkdirAll(cfg.Logging.Dir, 0700); err != nil {
32		return fmt.Errorf("failed to create logs directory: %w", err)
33	}
34
35	// Create container logs directory with secure permissions
36	containerLogDir := filepath.Join(cfg.Logging.Dir, cfg.Logging.ContainerLogDir)
37	if err := os.MkdirAll(containerLogDir, 0700); err != nil {
38		return fmt.Errorf("failed to create container logs directory: %w", err)
39	}
40
41	// Set log level
42	level, err := zerolog.ParseLevel(cfg.Logging.Level)
43	if err != nil {
44		level = zerolog.InfoLevel
45		log.Warn().Str("invalid_level", cfg.Logging.Level).Msg("Invalid log level, using info")
46	}
47	zerolog.SetGlobalLevel(level)
48
49	// Configure time format
50	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
51
52	// Setup main application logger
53	mainLogFile := filepath.Join(cfg.Logging.Dir, cfg.Logging.MainLogFile)
54	mainFileWriter := &lumberjack.Logger{
55		Filename:   mainLogFile,
56		MaxSize:    cfg.Logging.MaxSize,
57		MaxBackups: cfg.Logging.MaxBackups,
58		MaxAge:     cfg.Logging.MaxAge,
59		Compress:   cfg.Logging.Compress,
60	}
61
62	// Set file permissions to be secure (readable only by owner)
63	if err := os.Chmod(mainLogFile, 0600); err != nil && !os.IsNotExist(err) {
64		log.Warn().Err(err).Str("file", mainLogFile).Msg("Failed to set secure permissions on log file")
65	}
66
67	// Setup proxy logger
68	proxyLogFile := filepath.Join(cfg.Logging.Dir, cfg.Logging.ProxyLogFile)
69	proxyFileWriter := &lumberjack.Logger{
70		Filename:   proxyLogFile,
71		MaxSize:    cfg.Logging.MaxSize,
72		MaxBackups: cfg.Logging.MaxBackups,
73		MaxAge:     cfg.Logging.MaxAge,
74		Compress:   cfg.Logging.Compress,
75	}
76
77	// Set file permissions to be secure (readable only by owner)
78	if err := os.Chmod(proxyLogFile, 0600); err != nil && !os.IsNotExist(err) {
79		log.Warn().Err(err).Str("file", proxyLogFile).Msg("Failed to set secure permissions on log file")
80	}
81
82	// Create multi-writers (console + file)
83	consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
84	
85	mainMultiWriter := io.MultiWriter(consoleWriter, mainFileWriter)
86	proxyMultiWriter := io.MultiWriter(consoleWriter, proxyFileWriter)
87
88	// Initialize loggers
89	MainLogger = zerolog.New(mainMultiWriter).With().Timestamp().Logger()
90	ProxyLogger = zerolog.New(proxyMultiWriter).With().Timestamp().Logger()
91
92	// Set the global logger to the main logger
93	log.Logger = MainLogger
94
95	log.Info().
96		Str("main_log", mainLogFile).
97		Str("proxy_log", proxyLogFile).
98		Str("container_log_dir", containerLogDir).
99		Str("level", level.String()).
100		Msg("File logging initialized")
101
102	return nil
103}
104
105// GetContainerLogWriter returns a log writer for a specific container
106func GetContainerLogWriter(cfg *config.Config, containerID string, containerName string) (*lumberjack.Logger, error) {
107	if !cfg.Logging.Enabled {
108		return nil, fmt.Errorf("logging is disabled")
109	}
110
111	containerLogDir := filepath.Join(cfg.Logging.Dir, cfg.Logging.ContainerLogDir)
112	
113	// Ensure container log directory exists with secure permissions
114	if err := os.MkdirAll(containerLogDir, 0700); err != nil {
115		return nil, fmt.Errorf("failed to create container log directory: %w", err)
116	}
117
118	// Use container ID as the main log file name
119	logFile := filepath.Join(containerLogDir, fmt.Sprintf("%s.log", containerID))
120	
121	writer := &lumberjack.Logger{
122		Filename:   logFile,
123		MaxSize:    cfg.Logging.MaxSize,
124		MaxBackups: cfg.Logging.MaxBackups,
125		MaxAge:     cfg.Logging.MaxAge,
126		Compress:   cfg.Logging.Compress,
127	}
128
129	// Set file permissions to be secure (readable only by owner)
130	if err := os.Chmod(logFile, 0600); err != nil && !os.IsNotExist(err) {
131		log.Warn().Err(err).Str("file", logFile).Msg("Failed to set secure permissions on container log file")
132	}
133
134	// Create a symlink for easier access using container name (if provided)
135	if containerName != "" && containerName != containerID {
136		symlinkPath := filepath.Join(containerLogDir, fmt.Sprintf("%s.log", containerName))
137		
138		// Remove existing symlink if it exists
139		if _, err := os.Lstat(symlinkPath); err == nil {
140			os.Remove(symlinkPath)
141		}
142		
143		// Create new symlink
144		relativeLogFile := fmt.Sprintf("%s.log", containerID)
145		if err := os.Symlink(relativeLogFile, symlinkPath); err != nil {
146			log.Warn().
147				Err(err).
148				Str("symlink", symlinkPath).
149				Str("target", relativeLogFile).
150				Msg("Failed to create container name symlink")
151		}
152	}
153
154	return writer, nil
155}
156
157// Close gracefully closes all logging writers
158func Close() {
159	// Lumberjack loggers will be closed automatically when the program exits
160	// This function is provided for future extensibility
161}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close