From 4f94a255c2d31543c669a6b872472ac4dd6373c2 Mon Sep 17 00:00:00 2001 From: Shyunn Date: Tue, 10 Sep 2024 12:46:46 +0800 Subject: [PATCH] feat: add toolkit logging impl (#202) --- .github/workflows/plugin-tests.yaml | 1 + CHANGES.md | 1 + go.work | 3 +- plugins/core/logreport.go | 91 +++++++++++++++- .../core/logreport_test.go | 47 ++++---- plugins/core/operator/logger.go | 1 + .../go.mod | 0 .../go.sum | 0 .../instrument.go | 34 +++++- .../logging/debug_entry_intercepter.go | 33 ++++++ .../logging/error_entry_intercepter.go | 31 ++++++ .../logging/info_entry_intercepter.go | 31 ++++++ .../toolkit-activation/logging/send_entry.go | 60 +++++++++++ .../logging/warn_entry_intercepter.go | 31 ++++++ .../trace/add_event_intercepter.go | 0 .../trace/add_log_intercepter.go | 0 .../trace/async_add_event_intercepter.go | 0 .../trace/async_finish_intercepter.go | 0 .../trace/async_log_intercepter.go | 0 .../trace/async_prepare_intercepter.go | 0 .../trace/async_tag_intercepter.go | 0 .../trace/capture_intercepter.go | 0 .../trace/consts.go | 0 .../trace/continue_intercepter.go | 0 .../trace/correlation_get_intercepter.go | 0 .../trace/correlation_set_intercepter.go | 0 .../trace/create_entryspan_intercepter.go | 0 .../trace/create_exitspan_intercepter.go | 0 .../trace/create_localspan_intercepter.go | 0 .../trace/get_segmentid_intercepter.go | 0 .../trace/get_spanid_intercepter.go | 0 .../trace/get_traceid_intercepter.go | 0 .../trace/set_component_interceptor.go | 0 .../trace/set_name_intercepter.go | 0 .../trace/set_tag_intercepter.go | 0 .../trace/stopspan_intercepter.go | 0 .../logging-activation/bin/startup.sh | 20 ++++ .../logging-activation/config/excepted.yml | 88 +++++++++++++++ .../scenarios/logging-activation/go.mod | 3 + .../scenarios/logging-activation/main.go | 60 +++++++++++ .../scenarios/logging-activation/plugin.yml | 28 +++++ toolkit/{log => logging}/api.go | 2 +- tools/go-agent/instrument/logger/context.go | 100 ++++-------------- .../instrument/logger/frameworks/api.go | 3 +- .../logger/frameworks/logrus_format.go | 4 +- .../logger/frameworks/templates/init.tmpl | 2 + .../instrument/logger/frameworks/zap_root.go | 6 +- 47 files changed, 566 insertions(+), 114 deletions(-) rename tools/go-agent/instrument/logger/context_test.go => plugins/core/logreport_test.go (56%) rename plugins/{trace-activation => toolkit-activation}/go.mod (100%) rename plugins/{trace-activation => toolkit-activation}/go.sum (100%) rename plugins/{trace-activation => toolkit-activation}/instrument.go (82%) create mode 100644 plugins/toolkit-activation/logging/debug_entry_intercepter.go create mode 100644 plugins/toolkit-activation/logging/error_entry_intercepter.go create mode 100644 plugins/toolkit-activation/logging/info_entry_intercepter.go create mode 100644 plugins/toolkit-activation/logging/send_entry.go create mode 100644 plugins/toolkit-activation/logging/warn_entry_intercepter.go rename plugins/{trace-activation => toolkit-activation}/trace/add_event_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/add_log_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/async_add_event_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/async_finish_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/async_log_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/async_prepare_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/async_tag_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/capture_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/consts.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/continue_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/correlation_get_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/correlation_set_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/create_entryspan_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/create_exitspan_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/create_localspan_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/get_segmentid_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/get_spanid_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/get_traceid_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/set_component_interceptor.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/set_name_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/set_tag_intercepter.go (100%) rename plugins/{trace-activation => toolkit-activation}/trace/stopspan_intercepter.go (100%) create mode 100644 test/plugins/scenarios/logging-activation/bin/startup.sh create mode 100644 test/plugins/scenarios/logging-activation/config/excepted.yml create mode 100644 test/plugins/scenarios/logging-activation/go.mod create mode 100644 test/plugins/scenarios/logging-activation/main.go create mode 100644 test/plugins/scenarios/logging-activation/plugin.yml rename toolkit/{log => logging}/api.go (98%) diff --git a/.github/workflows/plugin-tests.yaml b/.github/workflows/plugin-tests.yaml index 46c29dc6..73293829 100644 --- a/.github/workflows/plugin-tests.yaml +++ b/.github/workflows/plugin-tests.yaml @@ -92,6 +92,7 @@ jobs: - grpc - irisv12 - trace-activation + - logging-activation - fasthttp - discard-reporter - fiber diff --git a/CHANGES.md b/CHANGES.md index 88de9245..3b552a6d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Release Notes. #### Features * support attaching events to span in the toolkit. +* support record log in the toolkit. #### Plugins diff --git a/go.work b/go.work index 87ebfb82..3d617e98 100644 --- a/go.work +++ b/go.work @@ -19,7 +19,7 @@ use ( ./plugins/mux ./plugins/grpc ./plugins/irisv12 - ./plugins/trace-activation + ./plugins/toolkit-activation ./plugins/fasthttp ./plugins/fiber ./plugins/echov4 @@ -63,6 +63,7 @@ use ( ./test/plugins/scenarios/pulsar ./test/plugins/scenarios/segmentio-kafka ./test/plugins/scenarios/go-elasticsearchv8 + ./test/plugins/scenarios/logging-activation ./tools/go-agent diff --git a/plugins/core/logreport.go b/plugins/core/logreport.go index 361b4629..cb09112b 100644 --- a/plugins/core/logreport.go +++ b/plugins/core/logreport.go @@ -18,8 +18,11 @@ package core import ( + "fmt" "time" + "github.com/apache/skywalking-go/plugins/core/operator" + commonv3 "skywalking.apache.org/repo/goapi/collect/common/v3" logv3 "skywalking.apache.org/repo/goapi/collect/logging/v3" ) @@ -37,6 +40,8 @@ type logTracingContext interface { GetEndPointName() string } +var noopContext = &NoopSpan{} + func (t *Tracer) ReportLog(ctx, timeObj interface{}, level, msg string, labels map[string]string) { tracingContext, ok := ctx.(logTracingContext) if !ok || tracingContext == nil { @@ -46,7 +51,12 @@ func (t *Tracer) ReportLog(ctx, timeObj interface{}, level, msg string, labels m if entity == nil { return } - timeData := timeObj.(time.Time) + timeData, ok := timeObj.(time.Time) + if !ok { + // as a fallback strategy to solve some plugins that + // cannot be introduced into the standard library + timeData = time.Now() + } tags := &logv3.LogTags{ Data: []*commonv3.KeyStringValuePair{ @@ -62,7 +72,6 @@ func (t *Tracer) ReportLog(ctx, timeObj interface{}, level, msg string, labels m Value: v, }) } - logData := &logv3.LogData{ Timestamp: Millisecond(timeData), Service: tracingContext.GetServiceName(), @@ -85,3 +94,81 @@ func (t *Tracer) ReportLog(ctx, timeObj interface{}, level, msg string, labels m t.Reporter.SendLog(logData) } + +func (t *Tracer) GetLogContext(withEndpoint bool) interface{} { + var ( + serviceName string + instanceName string + endpoint string + + activeSpan TracingSpan = noopContext + ) + + if s, ok := t.ActiveSpan().(TracingSpan); ok && s != nil { + activeSpan = s + if withEndpoint { + endpoint = findEndpointNameBySpan(s) + } + } + entity := t.Entity() + if e, ok := entity.(operator.Entity); ok && e != nil { + serviceName, instanceName = e.GetServiceName(), e.GetInstanceName() + } + return &SkyWalkingLogContext{ + ServiceName: serviceName, + InstanceName: instanceName, + TraceID: activeSpan.GetTraceID(), + TraceSegmentID: activeSpan.GetSegmentID(), + SpanID: activeSpan.GetSpanID(), + EndPoint: endpoint, + } +} + +func findEndpointNameBySpan(s TracingSpan) string { + tmp := s + for tmp != nil { + if name := tmp.GetOperationName(); name != "" { + return name + } + tmp = tmp.ParentSpan() + } + return "" +} + +type SkyWalkingLogContext struct { + ServiceName string + InstanceName string + TraceID string + EndPoint string + TraceSegmentID string + SpanID int32 +} + +func (s *SkyWalkingLogContext) GetServiceName() string { + return s.ServiceName +} + +func (s *SkyWalkingLogContext) GetInstanceName() string { + return s.InstanceName +} + +func (s *SkyWalkingLogContext) GetTraceID() string { + return s.TraceID +} + +func (s *SkyWalkingLogContext) GetTraceSegmentID() string { + return s.TraceSegmentID +} + +func (s *SkyWalkingLogContext) GetSpanID() int32 { + return s.SpanID +} + +func (s *SkyWalkingLogContext) GetEndPointName() string { + return s.EndPoint +} + +func (s *SkyWalkingLogContext) String() string { + return fmt.Sprintf("[%s,%s,%s,%s,%d]", s.ServiceName, s.InstanceName, + s.TraceID, s.TraceSegmentID, s.SpanID) +} diff --git a/tools/go-agent/instrument/logger/context_test.go b/plugins/core/logreport_test.go similarity index 56% rename from tools/go-agent/instrument/logger/context_test.go rename to plugins/core/logreport_test.go index 6469cd4b..2a0df3cc 100644 --- a/tools/go-agent/instrument/logger/context_test.go +++ b/plugins/core/logreport_test.go @@ -15,23 +15,17 @@ // specific language governing permissions and limitations // under the License. -package logger +package core import ( + "fmt" "testing" - "github.com/apache/skywalking-go/plugins/core" "github.com/apache/skywalking-go/plugins/core/reporter" "github.com/stretchr/testify/assert" ) -func init() { - GetOperator = func() Operator { - return core.Tracing - } -} - type ExtractorWrapper struct { F func(headerKey string) (string, error) } @@ -41,37 +35,46 @@ func (e *ExtractorWrapper) Fun() func(headerKey string) (string, error) { } func TestGetLogContext(t *testing.T) { + defer ResetTracingContext() serviceName := "test-service" serviceInstanceName := "test-instance" - core.Tracing.ServiceEntity = &reporter.Entity{ServiceName: serviceName, ServiceInstanceName: serviceInstanceName} - s, err := core.Tracing.CreateEntrySpan("/test", &ExtractorWrapper{ + Tracing.ServiceEntity = &reporter.Entity{ServiceName: serviceName, ServiceInstanceName: serviceInstanceName} + s, err := Tracing.CreateEntrySpan("/test", &ExtractorWrapper{ F: func(headerKey string) (string, error) { return "", nil }, }) assert.Nil(t, err, "err should be nil") assert.NotNil(t, s, "span cannot be nil") - context := GetLogContext(true) + context := Tracing.GetLogContext(true) assert.NotNil(t, context, "context cannot be nil") - rootSpan, ok := s.(*core.RootSegmentSpan) + rootSpan, ok := s.(*RootSegmentSpan) assert.True(t, ok, "span should be root span") - assert.Equal(t, serviceName, context.ServiceName, "service name should be equal") + swCtx, ok := context.(*SkyWalkingLogContext) + assert.True(t, ok) + assert.NotNil(t, swCtx, "skywalkingContext cannot be nil") + assert.Equal(t, serviceName, swCtx.ServiceName, "service name should be equal") assert.Equal(t, serviceInstanceName, serviceInstanceName, "service instance name should be equal") - assert.Equal(t, "/test", context.GetEndPointName(), "endpoint name should be equal") - assert.Equal(t, rootSpan.Context().GetTraceID(), context.TraceID, "trace id should be equal") - assert.Equal(t, rootSpan.Context().GetSegmentID(), context.TraceSegmentID, "trace segment id should be equal") - assert.Equal(t, rootSpan.Context().GetSpanID(), context.SpanID, "span id should be equal") - assert.NotEqualf(t, "", context.String(), "context string should not be empty") + assert.Equal(t, "/test", swCtx.GetEndPointName(), "endpoint name should be equal") + assert.Equal(t, rootSpan.Context().GetTraceID(), swCtx.TraceID, "trace id should be equal") + assert.Equal(t, rootSpan.Context().GetSegmentID(), swCtx.TraceSegmentID, "trace segment id should be equal") + assert.Equal(t, rootSpan.Context().GetSpanID(), swCtx.SpanID, "span id should be equal") + assert.NotEqualf(t, "", swCtx.String(), "context string should not be empty") rootSpan.End() } func TestGetLogContextString(t *testing.T) { - s, err := core.Tracing.CreateLocalSpan("/test") + defer ResetTracingContext() + s, err := Tracing.CreateLocalSpan("/test") assert.Nil(t, err, "err should be nil") assert.NotNil(t, s, "span cannot be nil") - contextString := GetLogContextString() - assert.NotEqualf(t, "", contextString, "context string should not be empty") - rootSpan, ok := s.(*core.RootSegmentSpan) + context := Tracing.GetLogContext(false) + assert.NotNil(t, context, "context cannot be nil") + stringCtx, ok := context.(fmt.Stringer) + assert.True(t, ok) + assert.NotNil(t, stringCtx, "stringCtx cannot be nil") + assert.NotEqualf(t, "", stringCtx.String(), "context string should not be empty") + rootSpan, ok := s.(*RootSegmentSpan) assert.True(t, ok, "span should be root span") rootSpan.End() } diff --git a/plugins/core/operator/logger.go b/plugins/core/operator/logger.go index b7b0aef7..b61c0595 100644 --- a/plugins/core/operator/logger.go +++ b/plugins/core/operator/logger.go @@ -30,4 +30,5 @@ type LogOperator interface { type LogReporter interface { ReportLog(ctx, time interface{}, level, msg string, labels map[string]string) + GetLogContext(withEndpoint bool) interface{} } diff --git a/plugins/trace-activation/go.mod b/plugins/toolkit-activation/go.mod similarity index 100% rename from plugins/trace-activation/go.mod rename to plugins/toolkit-activation/go.mod diff --git a/plugins/trace-activation/go.sum b/plugins/toolkit-activation/go.sum similarity index 100% rename from plugins/trace-activation/go.sum rename to plugins/toolkit-activation/go.sum diff --git a/plugins/trace-activation/instrument.go b/plugins/toolkit-activation/instrument.go similarity index 82% rename from plugins/trace-activation/instrument.go rename to plugins/toolkit-activation/instrument.go index 42d7c405..095e523c 100644 --- a/plugins/trace-activation/instrument.go +++ b/plugins/toolkit-activation/instrument.go @@ -35,7 +35,7 @@ func NewInstrument() *Instrument { } func (i *Instrument) Name() string { - return "trace-activation" + return "toolkit-activation" } func (i *Instrument) BasePackage() string { @@ -47,6 +47,17 @@ func (i *Instrument) VersionChecker(version string) bool { } func (i *Instrument) Points() []*instrument.Point { + var instPoints []*instrument.Point + // append toolkit/trace related enhancements Point + instPoints = append(instPoints, tracePoint()...) + + // append toolkit/logging related enhancements Point + instPoints = append(instPoints, loggingPoint()...) + + return instPoints +} + +func tracePoint() []*instrument.Point { return []*instrument.Point{ { PackagePath: "trace", At: instrument.NewStructEnhance("SpanRef"), @@ -138,6 +149,27 @@ func (i *Instrument) Points() []*instrument.Point { } } +func loggingPoint() []*instrument.Point { + return []*instrument.Point{ + { + PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Debug"), + Interceptor: "DebugEntryInterceptor", + }, + { + PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Info"), + Interceptor: "InfoEntryInterceptor", + }, + { + PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Warn"), + Interceptor: "WarnEntryInterceptor", + }, + { + PackagePath: "logging", At: instrument.NewStaticMethodEnhance("Error"), + Interceptor: "ErrorEntryInterceptor", + }, + } +} + func (i *Instrument) FS() *embed.FS { return &fs } diff --git a/plugins/toolkit-activation/logging/debug_entry_intercepter.go b/plugins/toolkit-activation/logging/debug_entry_intercepter.go new file mode 100644 index 00000000..42c015b1 --- /dev/null +++ b/plugins/toolkit-activation/logging/debug_entry_intercepter.go @@ -0,0 +1,33 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logging + +import ( + "github.com/apache/skywalking-go/plugins/core/operator" +) + +type DebugEntryInterceptor struct{} + +func (h *DebugEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error { + sendLogEntry(debugLevel, invocation.Args()...) + return nil +} + +func (h *DebugEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error { + return nil +} diff --git a/plugins/toolkit-activation/logging/error_entry_intercepter.go b/plugins/toolkit-activation/logging/error_entry_intercepter.go new file mode 100644 index 00000000..84cad3e3 --- /dev/null +++ b/plugins/toolkit-activation/logging/error_entry_intercepter.go @@ -0,0 +1,31 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logging + +import "github.com/apache/skywalking-go/plugins/core/operator" + +type ErrorEntryInterceptor struct{} + +func (h *ErrorEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error { + sendLogEntry(errorLevel, invocation.Args()...) + return nil +} + +func (h *ErrorEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error { + return nil +} diff --git a/plugins/toolkit-activation/logging/info_entry_intercepter.go b/plugins/toolkit-activation/logging/info_entry_intercepter.go new file mode 100644 index 00000000..2ef60569 --- /dev/null +++ b/plugins/toolkit-activation/logging/info_entry_intercepter.go @@ -0,0 +1,31 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logging + +import "github.com/apache/skywalking-go/plugins/core/operator" + +type InfoEntryInterceptor struct{} + +func (h *InfoEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error { + sendLogEntry(infoLevel, invocation.Args()...) + return nil +} + +func (h *InfoEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error { + return nil +} diff --git a/plugins/toolkit-activation/logging/send_entry.go b/plugins/toolkit-activation/logging/send_entry.go new file mode 100644 index 00000000..ed089366 --- /dev/null +++ b/plugins/toolkit-activation/logging/send_entry.go @@ -0,0 +1,60 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logging + +import ( + "github.com/apache/skywalking-go/plugins/core/operator" +) + +const ( + debugLevel = "debug" + infoLevel = "info" + warnLevel = "warn" + errorLevel = "error" +) + +func sendLogEntry(level string, args ...interface{}) { + if len(args) == 0 { + return + } + logReporter, ok := operator.GetOperator().LogReporter().(operator.LogReporter) + if !ok || logReporter == nil { + return + } + + msg := args[0].(string) + labels := parseLabels(args[1]) + logReporter.ReportLog(logReporter.GetLogContext(true), args[1], level, msg, labels) +} + +// parseLabels parses multiple args into a map of labels +func parseLabels(args interface{}) map[string]string { + keyValues, ok := args.([]string) + if !ok || len(keyValues) < 2 { + return nil + } + + ret := make(map[string]string) + for i := 0; i < len(keyValues); i += 2 { + v1 := keyValues[i] + v2 := keyValues[i+1] + ret[v1] = v2 + } + + return ret +} diff --git a/plugins/toolkit-activation/logging/warn_entry_intercepter.go b/plugins/toolkit-activation/logging/warn_entry_intercepter.go new file mode 100644 index 00000000..28f736fc --- /dev/null +++ b/plugins/toolkit-activation/logging/warn_entry_intercepter.go @@ -0,0 +1,31 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package logging + +import "github.com/apache/skywalking-go/plugins/core/operator" + +type WarnEntryInterceptor struct{} + +func (h *WarnEntryInterceptor) BeforeInvoke(invocation operator.Invocation) error { + sendLogEntry(warnLevel, invocation.Args()...) + return nil +} + +func (h *WarnEntryInterceptor) AfterInvoke(_ operator.Invocation, _ ...interface{}) error { + return nil +} diff --git a/plugins/trace-activation/trace/add_event_intercepter.go b/plugins/toolkit-activation/trace/add_event_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/add_event_intercepter.go rename to plugins/toolkit-activation/trace/add_event_intercepter.go diff --git a/plugins/trace-activation/trace/add_log_intercepter.go b/plugins/toolkit-activation/trace/add_log_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/add_log_intercepter.go rename to plugins/toolkit-activation/trace/add_log_intercepter.go diff --git a/plugins/trace-activation/trace/async_add_event_intercepter.go b/plugins/toolkit-activation/trace/async_add_event_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/async_add_event_intercepter.go rename to plugins/toolkit-activation/trace/async_add_event_intercepter.go diff --git a/plugins/trace-activation/trace/async_finish_intercepter.go b/plugins/toolkit-activation/trace/async_finish_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/async_finish_intercepter.go rename to plugins/toolkit-activation/trace/async_finish_intercepter.go diff --git a/plugins/trace-activation/trace/async_log_intercepter.go b/plugins/toolkit-activation/trace/async_log_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/async_log_intercepter.go rename to plugins/toolkit-activation/trace/async_log_intercepter.go diff --git a/plugins/trace-activation/trace/async_prepare_intercepter.go b/plugins/toolkit-activation/trace/async_prepare_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/async_prepare_intercepter.go rename to plugins/toolkit-activation/trace/async_prepare_intercepter.go diff --git a/plugins/trace-activation/trace/async_tag_intercepter.go b/plugins/toolkit-activation/trace/async_tag_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/async_tag_intercepter.go rename to plugins/toolkit-activation/trace/async_tag_intercepter.go diff --git a/plugins/trace-activation/trace/capture_intercepter.go b/plugins/toolkit-activation/trace/capture_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/capture_intercepter.go rename to plugins/toolkit-activation/trace/capture_intercepter.go diff --git a/plugins/trace-activation/trace/consts.go b/plugins/toolkit-activation/trace/consts.go similarity index 100% rename from plugins/trace-activation/trace/consts.go rename to plugins/toolkit-activation/trace/consts.go diff --git a/plugins/trace-activation/trace/continue_intercepter.go b/plugins/toolkit-activation/trace/continue_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/continue_intercepter.go rename to plugins/toolkit-activation/trace/continue_intercepter.go diff --git a/plugins/trace-activation/trace/correlation_get_intercepter.go b/plugins/toolkit-activation/trace/correlation_get_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/correlation_get_intercepter.go rename to plugins/toolkit-activation/trace/correlation_get_intercepter.go diff --git a/plugins/trace-activation/trace/correlation_set_intercepter.go b/plugins/toolkit-activation/trace/correlation_set_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/correlation_set_intercepter.go rename to plugins/toolkit-activation/trace/correlation_set_intercepter.go diff --git a/plugins/trace-activation/trace/create_entryspan_intercepter.go b/plugins/toolkit-activation/trace/create_entryspan_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/create_entryspan_intercepter.go rename to plugins/toolkit-activation/trace/create_entryspan_intercepter.go diff --git a/plugins/trace-activation/trace/create_exitspan_intercepter.go b/plugins/toolkit-activation/trace/create_exitspan_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/create_exitspan_intercepter.go rename to plugins/toolkit-activation/trace/create_exitspan_intercepter.go diff --git a/plugins/trace-activation/trace/create_localspan_intercepter.go b/plugins/toolkit-activation/trace/create_localspan_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/create_localspan_intercepter.go rename to plugins/toolkit-activation/trace/create_localspan_intercepter.go diff --git a/plugins/trace-activation/trace/get_segmentid_intercepter.go b/plugins/toolkit-activation/trace/get_segmentid_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/get_segmentid_intercepter.go rename to plugins/toolkit-activation/trace/get_segmentid_intercepter.go diff --git a/plugins/trace-activation/trace/get_spanid_intercepter.go b/plugins/toolkit-activation/trace/get_spanid_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/get_spanid_intercepter.go rename to plugins/toolkit-activation/trace/get_spanid_intercepter.go diff --git a/plugins/trace-activation/trace/get_traceid_intercepter.go b/plugins/toolkit-activation/trace/get_traceid_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/get_traceid_intercepter.go rename to plugins/toolkit-activation/trace/get_traceid_intercepter.go diff --git a/plugins/trace-activation/trace/set_component_interceptor.go b/plugins/toolkit-activation/trace/set_component_interceptor.go similarity index 100% rename from plugins/trace-activation/trace/set_component_interceptor.go rename to plugins/toolkit-activation/trace/set_component_interceptor.go diff --git a/plugins/trace-activation/trace/set_name_intercepter.go b/plugins/toolkit-activation/trace/set_name_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/set_name_intercepter.go rename to plugins/toolkit-activation/trace/set_name_intercepter.go diff --git a/plugins/trace-activation/trace/set_tag_intercepter.go b/plugins/toolkit-activation/trace/set_tag_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/set_tag_intercepter.go rename to plugins/toolkit-activation/trace/set_tag_intercepter.go diff --git a/plugins/trace-activation/trace/stopspan_intercepter.go b/plugins/toolkit-activation/trace/stopspan_intercepter.go similarity index 100% rename from plugins/trace-activation/trace/stopspan_intercepter.go rename to plugins/toolkit-activation/trace/stopspan_intercepter.go diff --git a/test/plugins/scenarios/logging-activation/bin/startup.sh b/test/plugins/scenarios/logging-activation/bin/startup.sh new file mode 100644 index 00000000..f1e0727d --- /dev/null +++ b/test/plugins/scenarios/logging-activation/bin/startup.sh @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" +go build ${GO_BUILD_OPTS} -o logging-activation + +./logging-activation \ No newline at end of file diff --git a/test/plugins/scenarios/logging-activation/config/excepted.yml b/test/plugins/scenarios/logging-activation/config/excepted.yml new file mode 100644 index 00000000..ee673f88 --- /dev/null +++ b/test/plugins/scenarios/logging-activation/config/excepted.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +segmentItems: [] +meterItems: [] +logItems: + - serviceName: logging-activation + logSize: ge 4 + logs: + - timestamp: nq 0 + endpoint: GET:/provider + body: + type: TEXT + content: + text: this is debug msg + traceContext: + traceId: not null + traceSegmentId: not null + spanId: 0 + tags: + data: + - key: LEVEL + value: debug + - key: foo1 + value: bar1 + layer: GENERAL + - timestamp: nq 0 + endpoint: GET:/consumer + body: + type: TEXT + content: + text: this is info msg + traceContext: + traceId: not null + traceSegmentId: not null + spanId: 0 + tags: + data: + - key: LEVEL + value: info + - key: foo2 + value: bar2 + - timestamp: nq 0 + endpoint: GET:/consumer + body: + type: TEXT + content: + text: this is warn msg + traceContext: + traceId: not null + traceSegmentId: not null + spanId: 0 + tags: + data: + - key: LEVEL + value: warn + - key: foo3 + value: bar3 + - timestamp: nq 0 + endpoint: GET:/consumer + body: + type: TEXT + content: + text: this is error msg + traceContext: + traceId: not null + traceSegmentId: not null + spanId: 0 + tags: + data: + - key: LEVEL + value: error + - key: foo4 + value: bar4 + layer: GENERAL diff --git a/test/plugins/scenarios/logging-activation/go.mod b/test/plugins/scenarios/logging-activation/go.mod new file mode 100644 index 00000000..48ce6abb --- /dev/null +++ b/test/plugins/scenarios/logging-activation/go.mod @@ -0,0 +1,3 @@ +module test/plugins/scenarios/logging-activation + +go 1.19 diff --git a/test/plugins/scenarios/logging-activation/main.go b/test/plugins/scenarios/logging-activation/main.go new file mode 100644 index 00000000..39ca1df1 --- /dev/null +++ b/test/plugins/scenarios/logging-activation/main.go @@ -0,0 +1,60 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package main + +import ( + "io" + "net/http" + + _ "github.com/apache/skywalking-go" + "github.com/apache/skywalking-go/toolkit/logging" +) + +func providerHandler(w http.ResponseWriter, r *http.Request) { + logging.Debug("this is debug msg", "foo1", "bar1") + _, _ = w.Write([]byte("success")) +} + +func consumerHandler(w http.ResponseWriter, r *http.Request) { + resp, err := http.Get("http://localhost:8080/provider?test=1") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + logging.Info("this is info msg", "foo2", "bar2") + logging.Warn("this is warn msg", "foo3", "bar3") + logging.Error("this is error msg", "foo4", "bar4") + _, _ = w.Write(body) +} + +func main() { + http.HandleFunc("/provider", providerHandler) + http.HandleFunc("/consumer", consumerHandler) + + http.HandleFunc("/health", func(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) + }) + + _ = http.ListenAndServe(":8080", nil) +} diff --git a/test/plugins/scenarios/logging-activation/plugin.yml b/test/plugins/scenarios/logging-activation/plugin.yml new file mode 100644 index 00000000..f8316f34 --- /dev/null +++ b/test/plugins/scenarios/logging-activation/plugin.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +entry-service: http://${HTTP_HOST}:${HTTP_PORT}/consumer +health-checker: http://${HTTP_HOST}:${HTTP_PORT}/health +start-script: ./bin/startup.sh +framework: go +export-port: 8080 +support-version: + - go: 1.19 + - go: 1.20 + - go: 1.21 + - go: 1.22 + - go: 1.23 +toolkit: true diff --git a/toolkit/log/api.go b/toolkit/logging/api.go similarity index 98% rename from toolkit/log/api.go rename to toolkit/logging/api.go index 909c5ebf..366c9cc2 100644 --- a/toolkit/log/api.go +++ b/toolkit/logging/api.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package log +package logging // Debug logs a message at DebugLevel func Debug(msg string, keyValues ...string) { diff --git a/tools/go-agent/instrument/logger/context.go b/tools/go-agent/instrument/logger/context.go index 25defd33..10072acc 100644 --- a/tools/go-agent/instrument/logger/context.go +++ b/tools/go-agent/instrument/logger/context.go @@ -17,7 +17,9 @@ package logger -import "fmt" +import ( + "fmt" +) var GetOperator = func() Operator { return nil } var ChangeLogger = func(v interface{}) {} @@ -43,6 +45,11 @@ type TracingSpan interface { GetParentSpan() interface{} } +type LogReporter interface { + ReportLog(ctx, time interface{}, level, msg string, labels map[string]string) + GetLogContext(withEndpoint bool) interface{} +} + type Entity interface { GetServiceName() string GetInstanceName() string @@ -71,91 +78,20 @@ func (span *NoopSpan) GetEndPointName() string { return "" } -type SkyWalkingLogContext struct { - ServiceName string - InstanceName string - TraceID string - EndPoint string - TraceSegmentID string - SpanID int32 -} - -func (s *SkyWalkingLogContext) GetServiceName() string { - return s.ServiceName -} - -func (s *SkyWalkingLogContext) GetInstanceName() string { - return s.InstanceName -} - -func (s *SkyWalkingLogContext) GetTraceID() string { - return s.TraceID -} - -func (s *SkyWalkingLogContext) GetTraceSegmentID() string { - return s.TraceSegmentID -} - -func (s *SkyWalkingLogContext) GetSpanID() int32 { - return s.SpanID -} - -func (s *SkyWalkingLogContext) GetEndPointName() string { - return s.EndPoint -} - -var noopContext = &NoopSpan{} - -func GetLogContext(withEndpoint bool) *SkyWalkingLogContext { - operator := GetOperator() - var activeSpan TracingSpan = noopContext - var serviceName, instanceName, endpoint string - if operator != nil { - tracingOperator := operator.Tracing().(TracingOperator) - if s, ok := tracingOperator.ActiveSpan().(TracingSpan); ok && s != nil { - activeSpan = s - if withEndpoint { - endpoint = findEndpointNameBySpan(s) - } - } - entity := operator.Entity() - if entity != nil { - if e, ok := entity.(Entity); ok && e != nil { - serviceName, instanceName = e.GetServiceName(), e.GetInstanceName() - } - } - } - return &SkyWalkingLogContext{ - ServiceName: serviceName, - InstanceName: instanceName, - TraceID: activeSpan.GetTraceID(), - TraceSegmentID: activeSpan.GetSegmentID(), - SpanID: activeSpan.GetSpanID(), - EndPoint: endpoint, +func GetLogContext(withEndpoint bool) interface{} { + report, ok := GetOperator().LogReporter().(LogReporter) + if !ok || report == nil { + return nil } -} -func findEndpointNameBySpan(s TracingSpan) string { - tmp := s - for tmp != nil { - if name := tmp.GetEndPointName(); name != "" { - return name - } - parent := tmp.GetParentSpan() - if parentTmp, ok := parent.(TracingSpan); ok && parentTmp != nil { - tmp = parentTmp - } else { - tmp = nil - } - } - return "" + return report.GetLogContext(withEndpoint) } func GetLogContextString() string { - return GetLogContext(false).String() -} + stringer, ok := GetLogContext(false).(fmt.Stringer) + if !ok { + return "" + } -func (s *SkyWalkingLogContext) String() string { - return fmt.Sprintf("[%s,%s,%s,%s,%d]", s.ServiceName, s.InstanceName, - s.TraceID, s.TraceSegmentID, s.SpanID) + return stringer.String() } diff --git a/tools/go-agent/instrument/logger/frameworks/api.go b/tools/go-agent/instrument/logger/frameworks/api.go index c6c9a653..9b6be07a 100644 --- a/tools/go-agent/instrument/logger/frameworks/api.go +++ b/tools/go-agent/instrument/logger/frameworks/api.go @@ -19,7 +19,6 @@ package frameworks import ( "embed" - "fmt" "time" "github.com/apache/skywalking-go/plugins/core/operator" @@ -52,7 +51,7 @@ var LogReporterLabelKeys []string var LogTracingContextKey = "SW_CTX" // GetLogContext get the tracing context -var GetLogContext = func(withEndpoint bool) fmt.Stringer { +var GetLogContext = func(withEndpoint bool) interface{} { return nil } diff --git a/tools/go-agent/instrument/logger/frameworks/logrus_format.go b/tools/go-agent/instrument/logger/frameworks/logrus_format.go index dc6294f7..653919c5 100644 --- a/tools/go-agent/instrument/logger/frameworks/logrus_format.go +++ b/tools/go-agent/instrument/logger/frameworks/logrus_format.go @@ -48,7 +48,9 @@ func (format *WrapFormat) Format(entry *logrus.Entry) ([]byte, error) { if ctx == nil { return format.Base.Format(entry) } - logContext = ctx + if stringer, ok := ctx.(fmt.Stringer); ok { + logContext = stringer + } labels := make(map[string]string, len(keys)) for _, key := range keys { for k, v := range entry.Data { diff --git a/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl b/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl index ccacac30..9b17f80c 100644 --- a/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl +++ b/tools/go-agent/instrument/logger/frameworks/templates/init.tmpl @@ -27,7 +27,9 @@ var {{.LogReporterLabelsVarName}} = {{.LogTypeInConfig.Reporter.LabelKeys.ToGoSt type logReporter interface { ReportLog(ctx, time interface{}, level, msg string, labels map[string]string) + GetLogContext(withEndpoint bool) interface{} } + var {{.LogReportFuncName}} = func(ctx interface{}, time time.Time, level, msg string, labels map[string]string) { op := {{.GetOperatorMethodName}}() if op == nil { diff --git a/tools/go-agent/instrument/logger/frameworks/zap_root.go b/tools/go-agent/instrument/logger/frameworks/zap_root.go index aca45835..30e23578 100644 --- a/tools/go-agent/instrument/logger/frameworks/zap_root.go +++ b/tools/go-agent/instrument/logger/frameworks/zap_root.go @@ -39,8 +39,10 @@ func TracingContextEnhance(entry *zapcore.CheckedEntry) (interface{}, *zapcore.F var getEndpoint = LogReporterEnable if LogReporterEnable || LogTracingContextEnable { ctx := GetLogContext(getEndpoint) - f := zap.String(LogTracingContextKey, ctx.String()) - return ctx, &f + if c, ok := ctx.(fmt.Stringer); ok { + f := zap.String(LogTracingContextKey, c.String()) + return ctx, &f + } } return nil, nil }