Skip to content

Commit a2ee112

Browse files
committed
add console log from otel
1 parent 77b3fc9 commit a2ee112

9 files changed

Lines changed: 315 additions & 17 deletions

File tree

bash/bash.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ func (sh *Bash) BindStderr(v string, r *bufio.Reader, conn net.Conn) (err error)
108108
defer err2.Handle(&err)
109109
defer conn.Close()
110110
var filters = []string{"lo: ", "1>"}
111-
defer fmt.Println("err ch closed")
112111
switch v {
113112
case "2":
114113
fallthrough

cmd/lcode-hub/main.go

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package main
22

33
import (
4-
"bufio"
54
"bytes"
5+
"context"
66
"flag"
77
"fmt"
8+
"io"
89
"net"
910
"net/http"
1011
"os"
12+
"os/signal"
1113
"path/filepath"
1214
"regexp"
15+
"strconv"
1316
"strings"
1417
"text/template"
1518
"time"
@@ -21,27 +24,41 @@ import (
2124
"github.com/vscode-lcode/lcode/v2/bash"
2225
"github.com/vscode-lcode/lcode/v2/bash/webdav"
2326
"github.com/vscode-lcode/lcode/v2/hub"
27+
"github.com/vscode-lcode/lcode/v2/util/err0"
28+
"go.opentelemetry.io/otel"
29+
"go.opentelemetry.io/otel/attribute"
30+
"go.opentelemetry.io/otel/codes"
31+
"go.opentelemetry.io/otel/trace"
2432
"xorm.io/xorm"
2533
)
2634

2735
var args struct {
2836
hello string
2937
addr string
3038
localdomain string
39+
logLv string
3140
}
3241

3342
var VERSION = "dev"
3443
var f = flag.NewFlagSet("lcode-hub@"+VERSION, flag.ExitOnError)
44+
var defaultLogLv = "0"
3545

3646
func init() {
3747
f.StringVar(&args.addr, "addr", "127.0.0.1:4349", "local-hub listen addr")
3848
f.StringVar(&args.hello, "hello", "webdav://{{.host}}.lo.shynome.com:4349{{.path}}", "")
3949
f.StringVar(&args.localdomain, "localdomain", ".lo.shynome.com", "")
50+
f.StringVar(&args.logLv, "log", defaultLogLv, "日志输出等级: 0 - 不输出; 1 - Error; 11 - Info; 111 - Debug")
4051
}
4152

53+
var tracer = otel.Tracer("lcode-hub")
54+
4255
func main() {
4356
f.Parse(os.Args[1:])
4457

58+
loglv := To1(strconv.ParseInt(args.logLv, 2, 64))
59+
tp := newTracerProvider(LogLevel(loglv))
60+
defer tp.Shutdown(context.Background())
61+
4562
if err := hasRunning(args.addr); err == nil {
4663
return
4764
}
@@ -52,7 +69,14 @@ func main() {
5269
To(hub.Sync(db))
5370

5471
l := To1(net.Listen("tcp", args.addr))
55-
fmt.Printf("lcode-hub is running on %s\n", args.addr)
72+
defer l.Close()
73+
74+
_, span := tracer.Start(
75+
err0.WithStatus(nil, codes.Ok, ""),
76+
"lcode-hub is running",
77+
trace.WithAttributes(attribute.String("addr", args.addr)),
78+
)
79+
defer span.End()
5680

5781
bash := bash.NewBash()
5882
bash.VERSION = VERSION
@@ -61,8 +85,13 @@ func main() {
6185
helloTpl := To1(template.New("hello").Parse(args.hello + "\n"))
6286
for client := range bash.Connected() {
6387
go func(c *webdav.Client) {
64-
fmt.Println("client connected", c.ID)
65-
defer fmt.Println("client disconnected", c.ID)
88+
_, span := tracer.Start(
89+
err0.WithStatus(err0.KeepEndOutput(nil), codes.Ok, ""),
90+
"client connected",
91+
trace.WithAttributes(attribute.String("id", c.ID)),
92+
)
93+
defer span.End()
94+
6695
f := format.FindStringSubmatch(c.ID)
6796
if len(f) != 3 {
6897
return
@@ -87,6 +116,7 @@ func main() {
87116
}
88117
}
89118
hello := string(output.Bytes())
119+
hello = strings.TrimSuffix(hello, "\n")
90120
hello = strings.ReplaceAll(hello, "\n", "\nlo: ")
91121
c.Exec(fmt.Sprintf(">&2 echo lo: %s", shellescape.Quote(hello)))
92122
if noEditTargets {
@@ -102,21 +132,34 @@ func main() {
102132
hub.LocalDomain = args.localdomain
103133

104134
go http.Serve(net.Listener(bash), hub)
105-
To(bash.Serve(l))
135+
go bash.Serve(l)
136+
137+
c := make(chan os.Signal)
138+
signal.Notify(c, os.Interrupt)
139+
<-c
140+
106141
}
107142

108143
func hasRunning(addr string) (err error) {
144+
_, span := tracer.Start(context.Background(), "lcode-hub check")
145+
defer span.End()
109146
defer err2.Handle(&err)
110-
client := http.Client{Timeout: 2 * time.Second}
111-
resp := To1(client.Get(fmt.Sprintf("http://%s/version", addr)))
112-
defer resp.Body.Close()
113-
r := bufio.NewReader(resp.Body)
114-
line, _ := To2(r.ReadLine())
115-
if v := string(line); v != "lcode-hub" {
116-
err = fmt.Errorf("expect lcode-hub, but got %s", v)
147+
148+
conn := To1(net.Dial("tcp", addr))
149+
defer conn.Close()
150+
151+
conn.SetDeadline(time.Now().Add(2 * time.Second))
152+
153+
To1(io.WriteString(conn, "-1\n"))
154+
_sid := To1(io.ReadAll(conn))
155+
sid := string(_sid)
156+
157+
if !strings.HasPrefix(sid, "lcode-hub") {
158+
err = fmt.Errorf("expect lcode-hub, but got %s", sid)
117159
return
118160
}
119-
version, _ := To2(r.ReadLine())
120-
fmt.Printf("lcode-hub already has running, version: %s. exit.\n", string(version))
161+
162+
span.SetStatus(codes.Error, "lcode-hub already has running")
163+
span.SetAttributes(attribute.String("version", sid))
121164
return
122165
}

cmd/lcode-hub/main_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ import (
55
"testing"
66
)
77

8+
var changeDefaultArgs int = func() int {
9+
// VERSION = "test"
10+
defaultLogLv = "11"
11+
return 1
12+
}()
13+
814
func TestMain(m *testing.T) {
915
var running string
1016
f.StringVar(&running, "test.run", "", "golang test")
1117
f.Parse(os.Args[1:])
1218
if running != "^TestMain$" {
1319
return
1420
}
15-
// VERSION = "test"
1621
main()
1722
}

cmd/lcode-hub/otel-exporter.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io"
7+
"os"
8+
"strings"
9+
"sync"
10+
"text/template"
11+
12+
"github.com/lainio/err2"
13+
. "github.com/lainio/err2/try"
14+
"github.com/vscode-lcode/lcode/v2/util/err0"
15+
"go.opentelemetry.io/otel"
16+
"go.opentelemetry.io/otel/codes"
17+
"go.opentelemetry.io/otel/sdk/resource"
18+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
19+
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
20+
)
21+
22+
func newTracerProvider(logLevel LogLevel) (tp *sdktrace.TracerProvider) {
23+
24+
resource := To1(resource.Merge(
25+
resource.Default(),
26+
resource.NewWithAttributes(
27+
semconv.SchemaURL,
28+
semconv.ServiceNameKey.String("lcode-hub"),
29+
semconv.ServiceVersionKey.String(VERSION),
30+
),
31+
))
32+
33+
exporter := NewConsoleLogExporter(logLevel)
34+
35+
tp = sdktrace.NewTracerProvider(
36+
sdktrace.WithResource(resource),
37+
sdktrace.WithSpanProcessor(exporter),
38+
)
39+
40+
otel.SetTracerProvider(tp)
41+
42+
return
43+
}
44+
45+
type ConsoleLogExporter struct {
46+
Level LogLevel
47+
wg *sync.WaitGroup
48+
}
49+
50+
var _ sdktrace.SpanProcessor = (*ConsoleLogExporter)(nil)
51+
52+
func NewConsoleLogExporter(level LogLevel) *ConsoleLogExporter {
53+
return &ConsoleLogExporter{
54+
Level: level,
55+
wg: &sync.WaitGroup{},
56+
}
57+
}
58+
func (log *ConsoleLogExporter) Shutdown(ctx context.Context) error {
59+
log.wg.Wait()
60+
return nil
61+
}
62+
63+
func (log *ConsoleLogExporter) OnStart(ctx context.Context, span sdktrace.ReadWriteSpan) {
64+
if log.Level == NoneLogLevel {
65+
return
66+
}
67+
err0.ApplyStatusWithCtx(ctx, span)
68+
if code := span.Status().Code; code != codes.Unset {
69+
log.wg.Add(1)
70+
go func() {
71+
defer log.wg.Done()
72+
log.PrintlnSpan(span)
73+
}()
74+
} else {
75+
return
76+
}
77+
}
78+
79+
func (log *ConsoleLogExporter) OnEnd(span sdktrace.ReadOnlySpan) {
80+
log.wg.Add(1)
81+
go func() {
82+
defer log.wg.Done()
83+
log.PrintlnSpan(span)
84+
}()
85+
}
86+
87+
func (log *ConsoleLogExporter) ForceFlush(ctx context.Context) error { return nil }
88+
89+
var tpl = To1(template.New("").Parse("[{{.scope}}] {{.name}} {{.attrs}} {{.status}}"))
90+
91+
type LogLevel int
92+
93+
const (
94+
NoneLogLevel LogLevel = 0
95+
ErrorLogLevel LogLevel = 1 << (iota - 1)
96+
InfoLogLevel
97+
DebugLogLevel
98+
)
99+
100+
func (log ConsoleLogExporter) shouldLog(span sdktrace.ReadOnlySpan) bool {
101+
if _, onStart := span.(sdktrace.ReadWriteSpan); !onStart { // onEnd
102+
for _, ev := range span.Events() {
103+
if ev.Name == err0.TheLogHasBeenOutput {
104+
return false
105+
}
106+
}
107+
}
108+
switch s := span.Status(); s.Code {
109+
case codes.Unset:
110+
return log.Level&DebugLogLevel != 0
111+
case codes.Ok:
112+
return log.Level&InfoLogLevel != 0
113+
case codes.Error:
114+
return log.Level&ErrorLogLevel != 0
115+
}
116+
return false
117+
}
118+
119+
func (log ConsoleLogExporter) PrintlnSpan(span sdktrace.ReadOnlySpan) (err error) {
120+
defer err2.Handle(&err)
121+
if !log.shouldLog(span) {
122+
return
123+
}
124+
125+
var assigns = map[string]string{}
126+
output := os.Stderr
127+
switch s := span.Status(); s.Code {
128+
case codes.Unset: //debug
129+
assigns["status"] = "desc: " + s.Description
130+
case codes.Ok: //info
131+
output = os.Stdout
132+
assigns["status"] = "msg: " + s.Description
133+
case codes.Error: //error
134+
assigns["status"] = "err: " + s.Description
135+
}
136+
137+
assigns["name"] = "span: " + span.Name()
138+
139+
scope := span.InstrumentationScope()
140+
scope.Name = strings.TrimPrefix(scope.Name, "github.com/vscode-lcode/lcode/v2")
141+
assigns["scope"] = scope.Name
142+
143+
var attrs = map[string]any{}
144+
for _, kv := range span.Attributes() {
145+
k := string(kv.Key)
146+
attrs[k] = kv.Value.AsInterface()
147+
}
148+
b := To1(json.Marshal(attrs))
149+
assigns["attrs"] = "attrs: " + string(b)
150+
151+
_, onStart := span.(sdktrace.ReadWriteSpan)
152+
var endStr = "\n"
153+
if onEnd := !onStart; onEnd {
154+
endStr = " end\n"
155+
}
156+
157+
To(tpl.Execute(output, assigns))
158+
io.WriteString(output, endStr)
159+
return
160+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"go.opentelemetry.io/otel"
9+
"go.opentelemetry.io/otel/attribute"
10+
"go.opentelemetry.io/otel/codes"
11+
)
12+
13+
func TestExporter(t *testing.T) {
14+
tp := newTracerProvider(0b111)
15+
defer tp.Shutdown(context.Background())
16+
17+
tracer := otel.Tracer("test")
18+
19+
_, span := tracer.Start(context.Background(), "err")
20+
span.SetStatus(codes.Error, "some err string")
21+
span.End()
22+
23+
_, span = tracer.Start(context.Background(), "debug")
24+
span.SetAttributes(
25+
attribute.Bool("debug", true),
26+
attribute.String("some debug", "some value"),
27+
)
28+
span.End()
29+
30+
_, span = tracer.Start(context.Background(), "info")
31+
span.SetStatus(codes.Ok, "some err string")
32+
span.End()
33+
34+
time.Sleep(1 * time.Second)
35+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/lainio/err2 v0.8.13
1111
github.com/mattn/go-sqlite3 v1.14.16
1212
go.opentelemetry.io/otel v1.11.2
13+
go.opentelemetry.io/otel/sdk v1.11.2
1314
go.opentelemetry.io/otel/trace v1.11.2
1415
golang.org/x/net v0.5.0
1516
xorm.io/builder v0.3.12
@@ -26,6 +27,7 @@ require (
2627
github.com/modern-go/reflect2 v1.0.2 // indirect
2728
github.com/syndtr/goleveldb v1.0.0 // indirect
2829
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
30+
golang.org/x/sys v0.4.0 // indirect
2931
)
3032

3133
// replace golang.org/x/net/webdav => ../net-webdav

0 commit comments

Comments
 (0)