- commit
- e05304b
- parent
- 5359de0
- author
- Antonio Mika
- date
- 2024-10-03 20:37:21 +0000 UTC
Setup a send log sink
4 files changed,
+202,
-1
M
go.mod
+2,
-0
1@@ -40,6 +40,7 @@ require (
2 github.com/picosh/send v0.0.0-20240820031602-5d3b1a4494cc
3 github.com/picosh/tunkit v0.0.0-20240709033345-8315d4f3cd0e
4 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
5+ github.com/samber/slog-multi v1.2.3
6 github.com/sendgrid/sendgrid-go v3.14.0+incompatible
7 github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d
8 github.com/x-way/crawlerdetect v0.2.21
9@@ -133,6 +134,7 @@ require (
10 github.com/rogpeppe/go-internal v1.11.0 // indirect
11 github.com/rs/xid v1.5.0 // indirect
12 github.com/safchain/ethtool v0.3.0 // indirect
13+ github.com/samber/lo v1.47.0 // indirect
14 github.com/secure-io/sio-go v0.3.1 // indirect
15 github.com/sendgrid/rest v2.6.9+incompatible // indirect
16 github.com/shirou/gopsutil/v3 v3.24.5 // indirect
M
go.sum
+4,
-0
1@@ -261,6 +261,10 @@ github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDj
2 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
3 github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
4 github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
5+github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
6+github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
7+github.com/samber/slog-multi v1.2.3 h1:np8YoAZbGP699xA92SYZxs7zzKpL1/yBYk6q8/caXpc=
8+github.com/samber/slog-multi v1.2.3/go.mod h1:ACuZ5B6heK57TfMVkVknN2UZHoFfjCwRxR0Q2OXKHlo=
9 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
10 github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
11 github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
1@@ -271,7 +271,13 @@ func CreateLogger(space string) *slog.Logger {
2 log := slog.New(
3 slog.NewTextHandler(os.Stdout, opts),
4 )
5- return log.With("service", space)
6+
7+ newLog, err := SendLogRegister(log, 100)
8+ if err != nil {
9+ slog.Error("unable to start send logger", "error", err)
10+ }
11+
12+ return newLog.With("service", space)
13 }
14
15 func LoggerWithUser(logger *slog.Logger, user *db.User) *slog.Logger {
1@@ -0,0 +1,189 @@
2+package shared
3+
4+import (
5+ "fmt"
6+ "io"
7+ "log/slog"
8+ "net"
9+ "os"
10+ "path/filepath"
11+ "strings"
12+ "sync"
13+ "time"
14+
15+ slogmulti "github.com/samber/slog-multi"
16+ "golang.org/x/crypto/ssh"
17+)
18+
19+type SendLogWriter struct {
20+ SSHClient *ssh.Client
21+ Session *ssh.Session
22+ StdinPipe io.Writer
23+ Done chan struct{}
24+ Messages chan []byte
25+ Timeout time.Duration
26+ closeOnce sync.Once
27+ startOnce sync.Once
28+}
29+
30+func (c *SendLogWriter) Write(data []byte) (int, error) {
31+ var (
32+ n int
33+ err error
34+ )
35+
36+ select {
37+ case c.Messages <- data:
38+ n = len(data)
39+ case <-time.After(c.Timeout):
40+ err = fmt.Errorf("unable to send data within timeout")
41+ }
42+
43+ return n, err
44+}
45+
46+func (c *SendLogWriter) Open() {
47+ go func() {
48+ for {
49+ select {
50+ case data := <-c.Messages:
51+ _, err := c.StdinPipe.Write(data)
52+ if err != nil {
53+ slog.Info("received error on write", "error", err)
54+ }
55+ case <-c.Done:
56+ return
57+ }
58+ }
59+ }()
60+}
61+
62+func createSSHClient(remoteHost string, keyLocation string, keyPassphrase string, remoteHostname string, remoteUser string) *ssh.Client {
63+ if !strings.Contains(remoteHost, ":") {
64+ remoteHost += ":22"
65+ }
66+
67+ rawConn, err := net.Dial("tcp", remoteHost)
68+ if err != nil {
69+ slog.Error(
70+ "Unable to create ssh client, tcp connection not established",
71+ slog.Any("error", err),
72+ )
73+ panic(err)
74+ }
75+
76+ keyPath, err := filepath.Abs(keyLocation)
77+ if err != nil {
78+ slog.Error(
79+ "Unable to create ssh client, cannot find key file",
80+ slog.Any("error", err),
81+ )
82+ panic(err)
83+ }
84+
85+ f, err := os.Open(keyPath)
86+ if err != nil {
87+ slog.Error(
88+ "Unable to create ssh client, unable to open key",
89+ slog.Any("error", err),
90+ )
91+ panic(err)
92+ }
93+ defer f.Close()
94+
95+ data, err := io.ReadAll(f)
96+ if err != nil {
97+ slog.Error(
98+ "Unable to create ssh client, unable to read key",
99+ slog.Any("error", err),
100+ )
101+ panic(err)
102+ }
103+
104+ var signer ssh.Signer
105+
106+ if keyPassphrase != "" {
107+ signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(keyPassphrase))
108+ } else {
109+ signer, err = ssh.ParsePrivateKey(data)
110+ }
111+
112+ if err != nil {
113+ slog.Error(
114+ "Unable to create ssh client, unable to parse key",
115+ slog.Any("error", err),
116+ )
117+ panic(err)
118+ }
119+
120+ sshConn, chans, reqs, err := ssh.NewClientConn(rawConn, remoteHostname, &ssh.ClientConfig{
121+ Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
122+ HostKeyCallback: ssh.InsecureIgnoreHostKey(),
123+ User: remoteUser,
124+ })
125+ if err != nil {
126+ slog.Error(
127+ "Unable to create ssh client, unable to create client conn",
128+ slog.Any("error", err),
129+ )
130+ panic(err)
131+ }
132+
133+ sshClient := ssh.NewClient(sshConn, chans, reqs)
134+
135+ return sshClient
136+}
137+
138+func SendLogRegister(logger *slog.Logger, buffer int) (*slog.Logger, error) {
139+ if buffer < 0 {
140+ buffer = 0
141+ }
142+
143+ currentHandler := logger.Handler()
144+
145+ sshClient := createSSHClient(
146+ "send.pico.sh:22",
147+ os.Getenv("SSH_KEY"),
148+ os.Getenv("SSH_PASSPHRASE"),
149+ "send.pico.sh",
150+ os.Getenv("SSH_USER"),
151+ )
152+
153+ sesh, err := sshClient.NewSession()
154+ if err != nil {
155+ return logger, nil
156+ }
157+
158+ stdinPipe, err := sesh.StdinPipe()
159+ if err != nil {
160+ return logger, nil
161+ }
162+
163+ err = sesh.Start("pub log-sink -b=false")
164+ if err != nil {
165+ return logger, nil
166+ }
167+
168+ logWriter := &SendLogWriter{
169+ SSHClient: sshClient,
170+ Session: sesh,
171+ StdinPipe: stdinPipe,
172+ Done: make(chan struct{}),
173+ Messages: make(chan []byte, buffer),
174+ Timeout: 10 * time.Millisecond,
175+ }
176+
177+ logWriter.Open()
178+
179+ return slog.New(
180+ slogmulti.Fanout(
181+ currentHandler,
182+ slog.NewJSONHandler(logWriter, &slog.HandlerOptions{
183+ AddSource: true,
184+ Level: slog.LevelDebug,
185+ }),
186+ ),
187+ ), nil
188+}
189+
190+var _ io.Writer = (*SendLogWriter)(nil)