From b374888542a529f02812b14cac29dc862f24eff5 Mon Sep 17 00:00:00 2001 From: fujiwara Date: Fri, 16 Aug 2024 17:23:52 +0900 Subject: [PATCH 1/5] add tests before refactoring --- export_test.go | 6 ++++ go.mod | 5 +--- go.sum | 1 - lambda_test.go | 16 +++++++---- tracer.go | 20 +++++++------ tracer_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 export_test.go create mode 100644 tracer_test.go diff --git a/export_test.go b/export_test.go new file mode 100644 index 0000000..930c706 --- /dev/null +++ b/export_test.go @@ -0,0 +1,6 @@ +package tracer + +var ( + ExtractTaskID = extractTaskID + ExtractClusterName = extractClusterName +) diff --git a/go.mod b/go.mod index 5fa9161..aaf7347 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/fujiwara/tracer -go 1.17 +go 1.21 require ( github.com/aws/aws-lambda-go v1.28.0 @@ -9,7 +9,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.15.11 github.com/aws/aws-sdk-go-v2/service/ecs v1.18.12 github.com/aws/aws-sdk-go-v2/service/sns v1.17.10 - github.com/stretchr/testify v1.6.1 ) require ( @@ -22,8 +21,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.11.13 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.16.10 // indirect github.com/aws/smithy-go v1.12.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index fee26c3..5e95637 100644 --- a/go.sum +++ b/go.sum @@ -48,7 +48,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/lambda_test.go b/lambda_test.go index 64a78f3..5598529 100644 --- a/lambda_test.go +++ b/lambda_test.go @@ -1,9 +1,9 @@ -package tracer +package tracer_test import ( "testing" - "github.com/stretchr/testify/require" + "github.com/fujiwara/tracer" ) func TestExtractClusterName(t *testing.T) { @@ -23,8 +23,10 @@ func TestExtractClusterName(t *testing.T) { for _, c := range cases { t.Run(c.input, func(t *testing.T) { - actual := extractClusterName(c.input) - require.Equal(t, c.expected, actual) + actual := tracer.ExtractClusterName(c.input) + if c.expected != actual { + t.Errorf("expected: %s, actual: %s", c.expected, actual) + } }) } } @@ -49,8 +51,10 @@ func TestExtractTaskID(t *testing.T) { for _, c := range cases { t.Run(c.input, func(t *testing.T) { - actual := extractTaskID(c.cluster, c.input) - require.Equal(t, c.expected, actual) + actual := tracer.ExtractTaskID(c.cluster, c.input) + if c.expected != actual { + t.Errorf("expected: %s, actual: %s", c.expected, actual) + } }) } } diff --git a/tracer.go b/tracer.go index 8e8f3d7..ae4639a 100644 --- a/tracer.go +++ b/tracer.go @@ -50,6 +50,12 @@ func (t *Tracer) AddEvent(ts *time.Time, source, message string) { t.timeline.Add(newEvent(ts, source, message)) } +func NewTimeline() *Timeline { + return &Timeline{ + seen: make(map[string]bool), + } +} + type Timeline struct { events []*TimeLineEvent seen map[string]bool @@ -107,14 +113,12 @@ func New(ctx context.Context) (*Tracer, error) { func NewWithConfig(config aws.Config) (*Tracer, error) { return &Tracer{ - ecs: ecs.NewFromConfig(config), - logs: cloudwatchlogs.NewFromConfig(config), - sns: sns.NewFromConfig(config), - timeline: &Timeline{ - seen: make(map[string]bool), - }, - buf: new(bytes.Buffer), - w: os.Stdout, + ecs: ecs.NewFromConfig(config), + logs: cloudwatchlogs.NewFromConfig(config), + sns: sns.NewFromConfig(config), + timeline: NewTimeline(), + buf: new(bytes.Buffer), + w: os.Stdout, }, nil } diff --git a/tracer_test.go b/tracer_test.go new file mode 100644 index 0000000..bfbaf7b --- /dev/null +++ b/tracer_test.go @@ -0,0 +1,77 @@ +package tracer_test + +import ( + "strings" + "testing" + "time" + + "github.com/fujiwara/tracer" +) + +func ptr[T any](v T) *T { + return &v +} + +var ( + testEvents = []tracer.TimeLineEvent{ + { + Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Message: "test message 1", + Source: "test_source 1", + }, + { + Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_999, time.UTC)), + Message: "test message 5", + Source: "test_source 5", + }, + { + Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 6, 123_999_000, time.UTC)), + Message: "test message 2", + Source: "test_source 2", + }, + { + // same timestamp to test sort stable + Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Message: "test message 3", + Source: "test_source 3", + }, + { + // duplicate event + Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Message: "test message 3", + Source: "test_source 3", + }, + } + expectedOutput = `2021-01-02T03:04:05.123Z test_source 1 test message 1 +2021-01-02T03:04:05.123Z test_source 3 test message 3 +2021-01-02T03:04:05.123Z test_source 5 test message 5 +2021-01-02T03:04:06.123Z test_source 2 test message 2 +` +) + +func TestTimeLineEvent(t *testing.T) { + t.Setenv("TZ", "UTC") + now := time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC) + ev := tracer.TimeLineEvent{ + Timestamp: &now, + Message: "test message", + Source: "test_source", + } + if ev.String() != "2021-01-02T03:04:05.123Z\ttest_source\ttest message\n" { + t.Errorf("unexpected string: %s", ev.String()) + } +} + +func TestTimeLine(t *testing.T) { + t.Setenv("TZ", "UTC") + tl := tracer.NewTimeline() + for _, ev := range testEvents { + ev := ev + tl.Add(&ev) + } + b := new(strings.Builder) + tl.Print(b) + if b.String() != expectedOutput { + t.Errorf("unexpected output: %s", b.String()) + } +} From a8ac224393ff5f8e40eaafed8d1083fb805fb9e2 Mon Sep 17 00:00:00 2001 From: fujiwara Date: Fri, 16 Aug 2024 17:37:31 +0900 Subject: [PATCH 2/5] refactoring --- tracer.go | 58 +++++++++++++++++++++++++++----------------------- tracer_test.go | 30 ++++++++++++++++---------- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/tracer.go b/tracer.go index ae4639a..1208198 100644 --- a/tracer.go +++ b/tracer.go @@ -46,7 +46,7 @@ type Tracer struct { option *RunOption } -func (t *Tracer) AddEvent(ts *time.Time, source, message string) { +func (t *Tracer) AddEvent(ts time.Time, source, message string) { t.timeline.Add(newEvent(ts, source, message)) } @@ -63,36 +63,40 @@ type Timeline struct { } func (tl *Timeline) Add(event *TimeLineEvent) { + if event.Timestamp.IsZero() { // ignore zero time event + return + } tl.mu.Lock() defer tl.mu.Unlock() tl.events = append(tl.events, event) } -func (tl *Timeline) Print(w io.Writer) { +func (tl *Timeline) Print(w io.Writer) (int, error) { tl.mu.Lock() defer tl.mu.Unlock() tls := make([]*TimeLineEvent, 0, len(tl.events)) - for _, e := range tl.events { - if e.Timestamp == nil { - continue - } - tls = append(tls, e) - } + tls = append(tls, tl.events...) sort.SliceStable(tls, func(i, j int) bool { - return (*tls[i].Timestamp).Before(*tls[j].Timestamp) + return tls[i].Timestamp.Before(tls[j].Timestamp) }) + n := 0 for _, e := range tls { s := e.String() if !tl.seen[s] { - fmt.Fprint(w, e.String()) + l, err := fmt.Fprint(w, e.String()) + if err != nil { + return n, err + } + n += l tl.seen[s] = true } } + return n, nil } type TimeLineEvent struct { - Timestamp *time.Time + Timestamp time.Time Source string Message string } @@ -122,7 +126,7 @@ func NewWithConfig(config aws.Config) (*Tracer, error) { }, nil } -func newEvent(ts *time.Time, src, msg string) *TimeLineEvent { +func newEvent(ts time.Time, src, msg string) *TimeLineEvent { return &TimeLineEvent{ Timestamp: ts, Source: src, @@ -243,18 +247,18 @@ func (t *Tracer) traceTask(ctx context.Context, cluster string, taskID string) ( t.fetchServiceEvents(ctx, cluster, taskGroup[1]) } - t.AddEvent(task.CreatedAt, "TASK", "Created") - t.AddEvent(task.ConnectivityAt, "TASK", "Connected") - t.AddEvent(task.StartedAt, "TASK", "Started") - t.AddEvent(task.PullStartedAt, "TASK", "Pull started") - t.AddEvent(task.PullStoppedAt, "TASK", "Pull stopped") - t.AddEvent(task.StoppedAt, "TASK", "Stopped") - t.AddEvent(task.StoppingAt, "TASK", "Stopping") + t.AddEvent(aws.ToTime(task.CreatedAt), "TASK", "Created") + t.AddEvent(aws.ToTime(task.ConnectivityAt), "TASK", "Connected") + t.AddEvent(aws.ToTime(task.StartedAt), "TASK", "Started") + t.AddEvent(aws.ToTime(task.PullStartedAt), "TASK", "Pull started") + t.AddEvent(aws.ToTime(task.PullStoppedAt), "TASK", "Pull stopped") + t.AddEvent(aws.ToTime(task.StoppedAt), "TASK", "Stopped") + t.AddEvent(aws.ToTime(task.StoppingAt), "TASK", "Stopping") if task.StoppedReason != nil { - t.AddEvent(task.StoppingAt, "TASK", "StoppedReason:"+*task.StoppedReason) + t.AddEvent(aws.ToTime(task.StoppingAt), "TASK", "StoppedReason:"+aws.ToString(task.StoppedReason)) } - t.AddEvent(task.StoppingAt, "TASK", "StoppedCode:"+string(task.StopCode)) - t.AddEvent(task.ExecutionStoppedAt, "TASK", "Execution stopped") + t.AddEvent(aws.ToTime(task.StoppingAt), "TASK", "StoppedCode:"+string(task.StopCode)) + t.AddEvent(aws.ToTime(task.ExecutionStoppedAt), "TASK", "Execution stopped") for _, c := range task.Containers { containerName := *c.Name @@ -265,10 +269,10 @@ func (t *Tracer) traceTask(ctx context.Context, cluster string, taskID string) ( if c.Reason != nil { msg += fmt.Sprintf(" (reason: %s)", *c.Reason) } - t.AddEvent(&t.now, "CONTAINER:"+containerName, msg) + t.AddEvent(t.now, "CONTAINER:"+containerName, msg) } - t.AddEvent(&t.now, "TASK", "LastStatus:"+aws.ToString(task.LastStatus)) + t.AddEvent(t.now, "TASK", "LastStatus:"+aws.ToString(task.LastStatus)) return &task, nil } @@ -325,9 +329,9 @@ func (t *Tracer) fetchServiceEvents(ctx context.Context, cluster, service string return fmt.Errorf("no services found: %w", err) } for _, e := range res.Services[0].Events { - ts := *e.CreatedAt + ts := aws.ToTime(e.CreatedAt) if ts.After(t.headBegin) && ts.Before(t.headEnd) || ts.After(t.tailBegin) && ts.Before(t.tailEnd) { - t.AddEvent(e.CreatedAt, "SERVICE", *e.Message) + t.AddEvent(ts, "SERVICE", aws.ToString(e.Message)) } } return nil @@ -362,7 +366,7 @@ func (t *Tracer) fetchLogs(ctx context.Context, containerName, group, stream str fetched++ for _, e := range res.Events { ts := msecToTime(aws.ToInt64(e.Timestamp)) - t.AddEvent(&ts, "CONTAINER:"+containerName, aws.ToString(e.Message)) + t.AddEvent(ts, "CONTAINER:"+containerName, aws.ToString(e.Message)) } if aws.ToString(nextToken) == aws.ToString(res.NextForwardToken) { break diff --git a/tracer_test.go b/tracer_test.go index bfbaf7b..ab85a26 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -5,42 +5,44 @@ import ( "testing" "time" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/fujiwara/tracer" ) -func ptr[T any](v T) *T { - return &v -} - var ( testEvents = []tracer.TimeLineEvent{ { - Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Timestamp: time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC), Message: "test message 1", Source: "test_source 1", }, { - Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_999, time.UTC)), + Timestamp: time.Date(2021, 1, 2, 3, 4, 5, 123_999_999, time.UTC), Message: "test message 5", Source: "test_source 5", }, { - Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 6, 123_999_000, time.UTC)), + Timestamp: time.Date(2021, 1, 2, 3, 4, 6, 123_999_000, time.UTC), Message: "test message 2", Source: "test_source 2", }, { // same timestamp to test sort stable - Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Timestamp: time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC), Message: "test message 3", Source: "test_source 3", }, { // duplicate event - Timestamp: ptr(time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC)), + Timestamp: time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC), Message: "test message 3", Source: "test_source 3", }, + { + Timestamp: aws.ToTime(nil), + Message: "test message ignored", + Source: "test_source ignored", + }, } expectedOutput = `2021-01-02T03:04:05.123Z test_source 1 test message 1 2021-01-02T03:04:05.123Z test_source 3 test message 3 @@ -53,7 +55,7 @@ func TestTimeLineEvent(t *testing.T) { t.Setenv("TZ", "UTC") now := time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC) ev := tracer.TimeLineEvent{ - Timestamp: &now, + Timestamp: now, Message: "test message", Source: "test_source", } @@ -70,7 +72,13 @@ func TestTimeLine(t *testing.T) { tl.Add(&ev) } b := new(strings.Builder) - tl.Print(b) + n, err := tl.Print(b) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if n != len(expectedOutput) { + t.Errorf("unexpected length: %d", n) + } if b.String() != expectedOutput { t.Errorf("unexpected output: %s", b.String()) } From 3640a5db8bad703df3e6aed1063ec914530d9f16 Mon Sep 17 00:00:00 2001 From: fujiwara Date: Fri, 16 Aug 2024 17:38:57 +0900 Subject: [PATCH 3/5] rename TimeLine to Timeline --- tracer.go | 14 +++++++------- tracer_test.go | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tracer.go b/tracer.go index 1208198..26b0520 100644 --- a/tracer.go +++ b/tracer.go @@ -57,12 +57,12 @@ func NewTimeline() *Timeline { } type Timeline struct { - events []*TimeLineEvent + events []*TimelineEvent seen map[string]bool mu sync.Mutex } -func (tl *Timeline) Add(event *TimeLineEvent) { +func (tl *Timeline) Add(event *TimelineEvent) { if event.Timestamp.IsZero() { // ignore zero time event return } @@ -75,7 +75,7 @@ func (tl *Timeline) Print(w io.Writer) (int, error) { tl.mu.Lock() defer tl.mu.Unlock() - tls := make([]*TimeLineEvent, 0, len(tl.events)) + tls := make([]*TimelineEvent, 0, len(tl.events)) tls = append(tls, tl.events...) sort.SliceStable(tls, func(i, j int) bool { return tls[i].Timestamp.Before(tls[j].Timestamp) @@ -95,13 +95,13 @@ func (tl *Timeline) Print(w io.Writer) (int, error) { return n, nil } -type TimeLineEvent struct { +type TimelineEvent struct { Timestamp time.Time Source string Message string } -func (e *TimeLineEvent) String() string { +func (e *TimelineEvent) String() string { ts := e.Timestamp.In(time.Local) return fmt.Sprintf("%s\t%s\t%s\n", ts.Format(TimeFormat), e.Source, e.Message) } @@ -126,8 +126,8 @@ func NewWithConfig(config aws.Config) (*Tracer, error) { }, nil } -func newEvent(ts time.Time, src, msg string) *TimeLineEvent { - return &TimeLineEvent{ +func newEvent(ts time.Time, src, msg string) *TimelineEvent { + return &TimelineEvent{ Timestamp: ts, Source: src, Message: msg, diff --git a/tracer_test.go b/tracer_test.go index ab85a26..08bf4ab 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -10,7 +10,7 @@ import ( ) var ( - testEvents = []tracer.TimeLineEvent{ + testEvents = []tracer.TimelineEvent{ { Timestamp: time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC), Message: "test message 1", @@ -51,10 +51,10 @@ var ( ` ) -func TestTimeLineEvent(t *testing.T) { +func TestTimelineEvent(t *testing.T) { t.Setenv("TZ", "UTC") now := time.Date(2021, 1, 2, 3, 4, 5, 123_999_000, time.UTC) - ev := tracer.TimeLineEvent{ + ev := tracer.TimelineEvent{ Timestamp: now, Message: "test message", Source: "test_source", @@ -64,7 +64,7 @@ func TestTimeLineEvent(t *testing.T) { } } -func TestTimeLine(t *testing.T) { +func TestTimeline(t *testing.T) { t.Setenv("TZ", "UTC") tl := tracer.NewTimeline() for _, ev := range testEvents { From c49da5a9e5bd88a5119853094fb3d3f128c5eecc Mon Sep 17 00:00:00 2001 From: fujiwara Date: Fri, 16 Aug 2024 17:42:15 +0900 Subject: [PATCH 4/5] make test --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 842ae7c..4d53724 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,6 @@ tracer: *.go *.go cmd/tracer/* install: go install github.com/fujiwara/tracer/cmd/tracer + +test: + go test -v ./... From 27068fbd4ace158a53c422a810ce1dc0dee8f37d Mon Sep 17 00:00:00 2001 From: fujiwara Date: Fri, 16 Aug 2024 17:43:07 +0900 Subject: [PATCH 5/5] go test 1.23 --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7991c5d..5473c37 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,8 +5,9 @@ jobs: strategy: matrix: go: - - "1.19" - - "1.20" + - "1.21" + - "1.22" + - "1.23" name: Build runs-on: ubuntu-latest steps: @@ -17,7 +18,7 @@ jobs: id: go - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build & Test run: |