Skip to content

Commit 65ffcc2

Browse files
authored
Merge pull request #163 from kube-logging/feat/file-log-writer
feat(writers): filelog writer
2 parents 51cd520 + 7845912 commit 65ffcc2

6 files changed

Lines changed: 264 additions & 34 deletions

File tree

conf/configuration.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,10 @@ func Init() {
6767
Viper.SetDefault("golang.weight.info", 1)
6868
Viper.SetDefault("golang.weight.warning", 0)
6969
Viper.SetDefault("golang.weight.debug", 0)
70+
71+
Viper.SetDefault("destination.file.create", true)
72+
Viper.SetDefault("destination.file.append", true)
73+
Viper.SetDefault("destination.file.mode", 0644)
74+
Viper.SetDefault("destination.file.dir_mode", 0755)
75+
Viper.SetDefault("destination.file.sync", false)
7076
}

loggen/loggen.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/kube-logging/log-generator/formats/golang"
3535
"github.com/kube-logging/log-generator/formats/web"
3636
"github.com/kube-logging/log-generator/log"
37+
"github.com/kube-logging/log-generator/writers"
3738
)
3839

3940
type List struct {
@@ -48,7 +49,7 @@ type LogGen struct {
4849
GolangLog golang.GolangLogIntensity `json:"golang_log"`
4950

5051
m sync.Mutex `json:"-"`
51-
writer LogWriter
52+
writer writers.LogWriter
5253
}
5354

5455
type LogGenRequest struct {
@@ -259,9 +260,18 @@ func (l *LogGen) Run() {
259260
count := conf.Viper.GetInt("message.count")
260261

261262
if len(conf.Viper.GetString("destination.network")) != 0 {
262-
l.writer = newNetworkWriter(conf.Viper.GetString("destination.network"), conf.Viper.GetString("destination.address"))
263+
l.writer = writers.NewNetworkWriter(conf.Viper.GetString("destination.network"), conf.Viper.GetString("destination.address"))
264+
} else if len(conf.Viper.GetString("destination.file.path")) != 0 {
265+
l.writer = writers.NewFileWriter(writers.FileLogWriterConfig{
266+
Path: conf.Viper.GetString("destination.file.path"),
267+
Create: conf.Viper.GetBool("destination.file.create"),
268+
Append: conf.Viper.GetBool("destination.file.append"),
269+
FileMode: os.FileMode(conf.Viper.GetUint32("destination.file.mode")),
270+
DirMode: os.FileMode(conf.Viper.GetUint32("destination.file.dir_mode")),
271+
SyncAfterWrite: conf.Viper.GetBool("destination.file.sync"),
272+
})
263273
} else {
264-
l.writer = newStdoutWriter()
274+
l.writer = writers.NewStdoutWriter()
265275
}
266276

267277
l.golangSet()

writers/filelog.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright © 2026 Kube logging authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package writers
16+
17+
import (
18+
"fmt"
19+
"os"
20+
"path/filepath"
21+
"sync"
22+
23+
logger "github.com/sirupsen/logrus"
24+
25+
"github.com/kube-logging/log-generator/log"
26+
"github.com/kube-logging/log-generator/metrics"
27+
)
28+
29+
type FileLogWriterConfig struct {
30+
Path string
31+
Create bool
32+
Append bool
33+
DirMode os.FileMode
34+
FileMode os.FileMode
35+
SyncAfterWrite bool
36+
}
37+
38+
type FileLogWriter struct {
39+
config FileLogWriterConfig
40+
file *os.File
41+
mu sync.Mutex
42+
closed bool
43+
}
44+
45+
func NewFileWriter(config FileLogWriterConfig) LogWriter {
46+
flw := &FileLogWriter{
47+
config: config,
48+
}
49+
if err := flw.openLocked(); err != nil {
50+
logger.Fatalf("failed to open log file: %v", err)
51+
}
52+
53+
return flw
54+
}
55+
56+
func (flw *FileLogWriter) Send(l log.Log) {
57+
msg, size := l.String()
58+
if l.IsFramed() {
59+
msg = fmt.Sprintf("%d %s", len(msg), msg)
60+
}
61+
msg += "\n"
62+
63+
flw.mu.Lock()
64+
defer flw.mu.Unlock()
65+
66+
if flw.closed {
67+
logger.Warn("Attempted to write to closed FileLogWriter")
68+
return
69+
}
70+
71+
if flw.wasRotated() {
72+
logger.Info("Log file was rotated, reopening...")
73+
if err := flw.openLocked(); err != nil {
74+
logger.Errorf("failed to reopen rotated log file: %v", err)
75+
return
76+
}
77+
}
78+
79+
_, err := flw.file.WriteString(msg)
80+
if err != nil {
81+
logger.Errorf("error writing to file %s: %v", flw.config.Path, err)
82+
return
83+
}
84+
85+
if flw.config.SyncAfterWrite {
86+
if err := flw.file.Sync(); err != nil {
87+
logger.Errorf("error syncing file %s: %v", flw.config.Path, err)
88+
}
89+
}
90+
91+
metrics.EventEmitted.With(l.Labels()).Inc()
92+
metrics.EventEmittedBytes.With(l.Labels()).Add(size)
93+
}
94+
95+
func (flw *FileLogWriter) Close() {
96+
flw.mu.Lock()
97+
defer flw.mu.Unlock()
98+
99+
if flw.closed {
100+
return
101+
}
102+
flw.closed = true
103+
104+
if flw.file != nil {
105+
if err := flw.file.Sync(); err != nil {
106+
logger.Errorf("error syncing file %s on close: %v", flw.config.Path, err)
107+
}
108+
if err := flw.file.Close(); err != nil {
109+
logger.Errorf("error closing file %s: %v", flw.config.Path, err)
110+
}
111+
flw.file = nil
112+
}
113+
}
114+
115+
func (flw *FileLogWriter) openLocked() error {
116+
if flw.file != nil {
117+
_ = flw.file.Sync()
118+
_ = flw.file.Close()
119+
flw.file = nil
120+
}
121+
122+
dir := filepath.Dir(flw.config.Path)
123+
if dir != "" && dir != "." {
124+
if _, err := os.Stat(dir); os.IsNotExist(err) {
125+
if !flw.config.Create {
126+
return fmt.Errorf("directory %s does not exist and file.create is false", dir)
127+
}
128+
if err := os.MkdirAll(dir, flw.config.DirMode); err != nil {
129+
return fmt.Errorf("failed to create directory %s: %w", dir, err)
130+
}
131+
logger.Infof("Created directory: %s", dir)
132+
}
133+
}
134+
135+
flags := os.O_WRONLY
136+
if flw.config.Append {
137+
flags |= os.O_APPEND
138+
} else {
139+
flags |= os.O_TRUNC
140+
}
141+
142+
if _, err := os.Stat(flw.config.Path); os.IsNotExist(err) {
143+
if !flw.config.Create {
144+
return fmt.Errorf("file %s does not exist and file.create is false", flw.config.Path)
145+
}
146+
flags |= os.O_CREATE
147+
}
148+
149+
file, err := os.OpenFile(flw.config.Path, flags, flw.config.FileMode)
150+
if err != nil {
151+
return fmt.Errorf("failed to open file %s: %w", flw.config.Path, err)
152+
}
153+
flw.file = file
154+
155+
logger.Infof("Opened log file: %s (append=%v, sync=%v)", flw.config.Path, flw.config.Append, flw.config.SyncAfterWrite)
156+
return nil
157+
}
158+
159+
func (flw *FileLogWriter) wasRotated() bool {
160+
if flw.file == nil {
161+
return true
162+
}
163+
164+
currentStat, err := flw.file.Stat()
165+
if err != nil {
166+
return true
167+
}
168+
169+
pathStat, err := os.Stat(flw.config.Path)
170+
if err != nil {
171+
return true
172+
}
173+
174+
// compare inodes
175+
return !os.SameFile(currentStat, pathStat)
176+
}
Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2023 Kube logging authors
1+
// Copyright © 2025 Kube logging authors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,54 +12,27 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License."""
1414

15-
package loggen
15+
package writers
1616

1717
import (
1818
"fmt"
1919
"net"
2020
"time"
2121

22-
logger "github.com/sirupsen/logrus"
23-
2422
"github.com/cenkalti/backoff/v4"
23+
logger "github.com/sirupsen/logrus"
2524

2625
"github.com/kube-logging/log-generator/log"
2726
"github.com/kube-logging/log-generator/metrics"
2827
)
2928

30-
type LogWriter interface {
31-
Send(log.Log)
32-
Close()
33-
}
34-
35-
type StdoutLogWriter struct{}
36-
37-
func newStdoutWriter() LogWriter {
38-
return &StdoutLogWriter{}
39-
}
40-
41-
func (*StdoutLogWriter) Send(l log.Log) {
42-
msg, size := l.String()
43-
44-
if l.IsFramed() {
45-
msg = fmt.Sprintf("%d %s", len(msg), msg)
46-
}
47-
48-
fmt.Println(msg)
49-
50-
metrics.EventEmitted.With(l.Labels()).Inc()
51-
metrics.EventEmittedBytes.With(l.Labels()).Add(size)
52-
}
53-
54-
func (*StdoutLogWriter) Close() {}
55-
5629
type NetworkLogWriter struct {
5730
network string
5831
address string
5932
conn net.Conn
6033
}
6134

62-
func newNetworkWriter(network string, address string) LogWriter {
35+
func NewNetworkWriter(network string, address string) LogWriter {
6336
nlw := &NetworkLogWriter{
6437
network: network,
6538
address: address,

writers/stdout.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright © 2023 Kube logging authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License."""
14+
15+
package writers
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/kube-logging/log-generator/log"
21+
"github.com/kube-logging/log-generator/metrics"
22+
)
23+
24+
type StdoutLogWriter struct{}
25+
26+
func NewStdoutWriter() LogWriter {
27+
return &StdoutLogWriter{}
28+
}
29+
30+
func (*StdoutLogWriter) Send(l log.Log) {
31+
msg, size := l.String()
32+
33+
if l.IsFramed() {
34+
msg = fmt.Sprintf("%d %s", len(msg), msg)
35+
}
36+
37+
fmt.Println(msg)
38+
39+
metrics.EventEmitted.With(l.Labels()).Inc()
40+
metrics.EventEmittedBytes.With(l.Labels()).Add(size)
41+
}
42+
43+
func (*StdoutLogWriter) Close() {}

writers/writers.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright © 2023 Kube logging authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License."""
14+
15+
package writers
16+
17+
import "github.com/kube-logging/log-generator/log"
18+
19+
type LogWriter interface {
20+
Send(log.Log)
21+
Close()
22+
}

0 commit comments

Comments
 (0)