use zerolog

Kim committed Oct 21, 2025 at 11:44 UTC 615ce777b41078b67932801a3483006183689e00
7 files changed +112 -93
cmd/example_client/main.go
+7 -7
@@ -3,7 +3,6 @@ package main
3 import (
4 "context"
5 "fmt"
6 - "log"
6 "net/http"
7 "os"
8 "os/signal"
@@ -12,6 +11,7 @@ import (
11 "time"
12
13 "github.com/gosuda/relaydns/relaydns"
14 + "github.com/rs/zerolog/log"
15 "github.com/spf13/cobra"
16 )
17
@@ -48,7 +48,7 @@ func init() {
48
49 func main() {
50 if err := rootCmd.Execute(); err != nil {
51 - log.Fatal(err)
51 + log.Fatal().Err(err).Msg("execute root command")
52 }
53 }
54
@@ -76,9 +76,9 @@ func runClient(cmd *cobra.Command, args []string) error {
76 _, _ = w.Write([]byte("ok"))
77 })
78
79 - log.Println("[client] local backend http", flagBackendHTTP)
79 + log.Info().Msgf("[client] local backend http %s", flagBackendHTTP)
80 if err := http.ListenAndServe(flagBackendHTTP, mux); err != nil {
81 - log.Println("[client] http backend error:", err)
81 + log.Error().Err(err).Msg("[client] http backend error")
82 cancel()
83 }
84 }()
@@ -111,17 +111,17 @@ func runClient(cmd *cobra.Command, args []string) error {
111 // 자기 주소/피어ID 로그로 찍어서 디버깅 편하게
112 if addrs := h.Addrs(); len(addrs) > 0 {
113 for _, a := range addrs {
114 - log.Printf("[client] host addr: %s/p2p/%s", a.String(), h.ID().String())
114 + log.Info().Msgf("[client] host addr: %s/p2p/%s", a.String(), h.ID().String())
115 }
116 } else {
117 - log.Printf("[client] host peer: %s (no listen addrs yet)", h.ID().String())
117 + log.Info().Msgf("[client] host peer: %s (no listen addrs yet)", h.ID().String())
118 }
119
120 // 종료 대기
121 sig := make(chan os.Signal, 1)
122 signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
123 <-sig
124 - log.Println("[client] shutting down")
124 + log.Info().Msg("[client] shutting down")
125 time.Sleep(200 * time.Millisecond)
126 return nil
127 }
cmd/server/main.go
+8 -8
@@ -2,13 +2,13 @@ package main
2
3 import (
4 "context"
5 - "log"
5 "os"
6 "os/signal"
7 "syscall"
8 "time"
9
10 "github.com/gosuda/relaydns/relaydns"
11 + "github.com/rs/zerolog/log"
12 "github.com/spf13/cobra"
13 )
14
@@ -46,7 +46,7 @@ func init() {
46
47 func main() {
48 if err := rootCmd.Execute(); err != nil {
49 - log.Fatal(err)
49 + log.Fatal().Err(err).Msg("execute root command")
50 }
51 }
52
@@ -70,9 +70,9 @@ func runServer(cmd *cobra.Command, args []string) error {
70 if adminHTTP == "" {
71 return
72 }
73 - log.Println("[server] admin http:", adminHTTP)
73 + log.Info().Msgf("[server] admin http: %s", adminHTTP)
74 if err := d.ServeHTTP(adminHTTP); err != nil {
75 - log.Println("[server] admin http error:", err)
75 + log.Error().Err(err).Msg("[server] admin http error")
76 cancel()
77 }
78 }()
@@ -82,9 +82,9 @@ func runServer(cmd *cobra.Command, args []string) error {
82 if ingressTCP == "" {
83 return
84 }
85 - log.Println("[server] tcp ingress:", ingressTCP)
85 + log.Info().Msgf("[server] tcp ingress: %s", ingressTCP)
86 if err := d.ServeTCP(ingressTCP); err != nil {
87 - log.Println("[server] tcp ingress error:", err)
87 + log.Error().Err(err).Msg("[server] tcp ingress error")
88 cancel()
89 }
90 }()
@@ -94,9 +94,9 @@ func runServer(cmd *cobra.Command, args []string) error {
94 if ingressHTTP == "" {
95 return
96 }
97 - log.Println("[server] http ingress (tcp-level):", ingressHTTP)
97 + log.Info().Msgf("[server] http ingress (tcp-level): %s", ingressHTTP)
98 if err := d.ServeTCP(ingressHTTP); err != nil {
99 - log.Println("[server] http ingress error:", err)
99 + log.Error().Err(err).Msg("[server] http ingress error")
100 cancel()
101 }
102 }()
go.mod
+3
@@ -6,6 +6,7 @@ require (
6 github.com/libp2p/go-libp2p v0.44.0
7 github.com/libp2p/go-libp2p-pubsub v0.15.0
8 github.com/multiformats/go-multiaddr v0.16.0
9 + github.com/rs/zerolog v1.34.0
10 github.com/spf13/cobra v1.10.1
11 )
12
@@ -37,6 +38,8 @@ require (
38 github.com/libp2p/go-reuseport v0.4.0 // indirect
39 github.com/libp2p/go-yamux/v5 v5.0.1 // indirect
40 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
41 + github.com/mattn/go-colorable v0.1.13 // indirect
42 + github.com/mattn/go-isatty v0.0.20 // indirect
43 github.com/miekg/dns v1.1.66 // indirect
44 github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
45 github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
go.sum
+15
@@ -20,6 +20,7 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
20 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
21 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
22 github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
23 +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
24 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
25 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -40,6 +41,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
41 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
42 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
43 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
44 +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
45 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
46 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
47 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -124,6 +126,12 @@ github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxf
126 github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs=
127 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
128 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
129 +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
130 +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
131 +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
132 +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
133 +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
134 +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
135 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
136 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
137 github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
@@ -215,6 +223,7 @@ github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7ITo
223 github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54=
224 github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U=
225 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
226 +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
227 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
228 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
229 github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -237,6 +246,9 @@ github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssk
246 github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao=
247 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
248 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
249 +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
250 +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
251 +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
252 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
253 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
254 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -381,10 +393,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
393 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
394 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
395 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
396 +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
397 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
398 +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
399 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
400 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
401 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
402 +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
403 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
404 golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
405 golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
relaydns/client.go
+46 -46
@@ -1,25 +1,25 @@
1 package relaydns
2
3 -import (
4 - "context"
5 - "encoding/json"
6 - "errors"
7 - "fmt"
8 - "io"
9 - "log"
10 - "net"
11 - "net/http"
12 - "net/url"
13 - "sort"
14 - "strings"
15 - "sync"
16 - "time"
17 -
18 - pubsub "github.com/libp2p/go-libp2p-pubsub"
19 - "github.com/libp2p/go-libp2p/core/host"
20 - "github.com/libp2p/go-libp2p/core/network"
21 - "github.com/libp2p/go-libp2p/core/protocol"
22 -)
3 +import (
4 + "context"
5 + "encoding/json"
6 + "errors"
7 + "fmt"
8 + "io"
9 + "net"
10 + "net/http"
11 + "net/url"
12 + "sort"
13 + "strings"
14 + "sync"
15 + "time"
16 +
17 + pubsub "github.com/libp2p/go-libp2p-pubsub"
18 + "github.com/libp2p/go-libp2p/core/host"
19 + "github.com/libp2p/go-libp2p/core/network"
20 + "github.com/libp2p/go-libp2p/core/protocol"
21 + "github.com/rs/zerolog/log"
22 +)
23
24 type ClientConfig struct {
25 ServerURL string
@@ -82,38 +82,38 @@ func NewClient(ctx context.Context, h host.Host, cfg ClientConfig) (*RelayClient
82 if len(cfg.Bootstraps) > 0 {
83 boot = append(boot, cfg.Bootstraps...)
84 }
85 - if cfg.ServerURL != "" {
86 - if addrs, err := fetchMultiaddrsFromHealth(cfg.ServerURL, cfg.HTTPTimeout); err != nil {
87 - log.Printf("relaydns: fetch /health from %s failed: %v", cfg.ServerURL, err)
88 - } else {
89 - sortMultiaddrs(addrs, cfg.PreferQUIC, cfg.PreferLocal)
90 - boot = append(boot, addrs...)
91 - }
92 - }
93 - boot = uniq(boot)
94 - if len(boot) > 0 {
95 - ConnectBootstraps(ctx, h, boot)
96 - } else {
97 - log.Printf("relaydns: no bootstrap sources provided (Bootstraps/ServerURL); discovery may fail")
98 - }
85 + if cfg.ServerURL != "" {
86 + if addrs, err := fetchMultiaddrsFromHealth(cfg.ServerURL, cfg.HTTPTimeout); err != nil {
87 + log.Warn().Err(err).Msgf("relaydns: fetch /health from %s failed", cfg.ServerURL)
88 + } else {
89 + sortMultiaddrs(addrs, cfg.PreferQUIC, cfg.PreferLocal)
90 + boot = append(boot, addrs...)
91 + }
92 + }
93 + boot = uniq(boot)
94 + if len(boot) > 0 {
95 + ConnectBootstraps(ctx, h, boot)
96 + } else {
97 + log.Warn().Msg("relaydns: no bootstrap sources provided (Bootstraps/ServerURL); discovery may fail")
98 + }
99
100 // 1) stream handler
101 switch {
102 case cfg.Handler != nil:
103 h.SetStreamHandler(b.protoID, cfg.Handler)
104 case cfg.TargetTCP != "":
105 - h.SetStreamHandler(b.protoID, func(s network.Stream) {
106 - defer s.Close()
107 - c, err := net.Dial("tcp", cfg.TargetTCP)
108 - if err != nil {
109 - log.Printf("relaydns: dial %s: %v", cfg.TargetTCP, err)
110 - return
111 - }
112 - defer c.Close()
113 - // raw byte pipe
114 - go io.Copy(c, s)
115 - io.Copy(s, c)
116 - })
105 + h.SetStreamHandler(b.protoID, func(s network.Stream) {
106 + defer s.Close()
107 + c, err := net.Dial("tcp", cfg.TargetTCP)
108 + if err != nil {
109 + log.Error().Err(err).Msgf("relaydns: dial %s", cfg.TargetTCP)
110 + return
111 + }
112 + defer c.Close()
113 + // raw byte pipe
114 + go io.Copy(c, s)
115 + io.Copy(s, c)
116 + })
117 default:
118 return nil, fmt.Errorf("relaydns: either Handler or TargetTCP must be set")
119 }
relaydns/director.go
+7 -6
@@ -5,7 +5,6 @@ import (
5 "encoding/json"
6 "fmt"
7 "io"
8 - "log"
8 "net"
9 "net/http"
10 "sync"
@@ -15,6 +14,7 @@ import (
14 "github.com/libp2p/go-libp2p/core/host"
15 "github.com/libp2p/go-libp2p/core/peer"
16 ma "github.com/multiformats/go-multiaddr"
17 + "github.com/rs/zerolog/log"
18 )
19
20 type Director struct {
@@ -130,7 +130,8 @@ func (d *Director) ServeTCP(addr string) error {
130 if err != nil {
131 return err
132 }
133 - log.Printf("director TCP listening on %s (protocol %s)", addr, d.protocol)
133 +
134 + log.Info().Msgf("director TCP listening on %s (protocol %s)", addr, d.protocol)
135 for {
136 c, err := ln.Accept()
137 if err != nil {
@@ -144,17 +145,17 @@ func (d *Director) handleConn(c net.Conn) {
145 defer c.Close()
146 entry, ok := d.pick.choose()
147 if !ok {
147 - log.Println("no backend peers available")
148 + log.Warn().Msg("no backend peers available")
149 return
150 }
151 // ensure we're connected (AddrInfo contains addrs)
152 if err := d.h.Connect(d.ctx, *entry.AddrInfo); err != nil {
152 - log.Printf("connect %s failed: %v", entry.AddrInfo.ID, err)
153 + log.Error().Err(err).Msgf("connect %s failed", entry.AddrInfo.ID)
154 return
155 }
156 s, err := d.h.NewStream(d.ctx, entry.AddrInfo.ID, protocolID(d.protocol))
157 if err != nil {
157 - log.Printf("new stream: %v", err)
158 + log.Error().Err(err).Msg("new stream")
159 return
160 }
161 defer s.Close()
@@ -209,6 +210,6 @@ func (d *Director) ServeHTTP(addr string) error {
210 w.Header().Set("Content-Type", "application/json")
211 _ = json.NewEncoder(w).Encode(resp)
212 })
212 - log.Printf("director HTTP API on %s", addr)
213 + log.Info().Msgf("director HTTP API on %s", addr)
214 return http.ListenAndServe(addr, mux)
215 }
relaydns/host.go
+26 -26
@@ -1,15 +1,15 @@
1 package relaydns
2
3 -import (
4 - "context"
5 - "fmt"
6 - "log"
7 -
8 - "github.com/libp2p/go-libp2p"
9 - "github.com/libp2p/go-libp2p/core/host"
10 - "github.com/libp2p/go-libp2p/core/peer"
11 - ma "github.com/multiformats/go-multiaddr"
12 -)
3 +import (
4 + "context"
5 + "fmt"
6 +
7 + "github.com/libp2p/go-libp2p"
8 + "github.com/libp2p/go-libp2p/core/host"
9 + "github.com/libp2p/go-libp2p/core/peer"
10 + ma "github.com/multiformats/go-multiaddr"
11 + "github.com/rs/zerolog/log"
12 +)
13
14 func MakeHost(ctx context.Context, port int, enableRelay bool) (host.Host, error) {
15 addrs := []string{
@@ -41,19 +41,19 @@ func MakeHost(ctx context.Context, port int, enableRelay bool) (host.Host, error
41 func ConnectBootstraps(ctx context.Context, h host.Host, addrs []string) {
42 for _, s := range addrs {
43 m, err := ma.NewMultiaddr(s)
44 - if err != nil {
45 - log.Printf("bootstrap bad multiaddr %q: %v", s, err)
46 - continue
47 - }
48 - ai, err := peer.AddrInfoFromP2pAddr(m)
49 - if err != nil {
50 - log.Printf("bootstrap missing /p2p/ in %q: %v", s, err)
51 - continue
52 - }
53 - if err := h.Connect(ctx, *ai); err != nil {
54 - log.Printf("bootstrap connect %s: %v", ai.ID, err)
55 - } else {
56 - log.Printf("connected bootstrap %s", ai.ID)
57 - }
58 - }
59 -}
44 + if err != nil {
45 + log.Warn().Err(err).Msgf("bootstrap bad multiaddr %q", s)
46 + continue
47 + }
48 + ai, err := peer.AddrInfoFromP2pAddr(m)
49 + if err != nil {
50 + log.Warn().Err(err).Msgf("bootstrap missing /p2p/ in %q", s)
51 + continue
52 + }
53 + if err := h.Connect(ctx, *ai); err != nil {
54 + log.Warn().Err(err).Msgf("bootstrap connect %s", ai.ID)
55 + } else {
56 + log.Info().Msgf("connected bootstrap %s", ai.ID)
57 + }
58 + }
59 +}