Skip to content

Commit

Permalink
feat: add toolkit logging impl (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShyunnY authored Sep 10, 2024
1 parent 3ac7e9a commit 4f94a25
Show file tree
Hide file tree
Showing 47 changed files with 566 additions and 114 deletions.
1 change: 1 addition & 0 deletions .github/workflows/plugin-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
- grpc
- irisv12
- trace-activation
- logging-activation
- fasthttp
- discard-reporter
- fiber
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Release Notes.
#### Features

* support attaching events to span in the toolkit.
* support record log in the toolkit.

#### Plugins

Expand Down
3 changes: 2 additions & 1 deletion go.work
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use (
./plugins/mux
./plugins/grpc
./plugins/irisv12
./plugins/trace-activation
./plugins/toolkit-activation
./plugins/fasthttp
./plugins/fiber
./plugins/echov4
Expand Down Expand Up @@ -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

Expand Down
91 changes: 89 additions & 2 deletions plugins/core/logreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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 {
Expand All @@ -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{
Expand All @@ -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(),
Expand All @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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()
}
1 change: 1 addition & 0 deletions plugins/core/operator/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewInstrument() *Instrument {
}

func (i *Instrument) Name() string {
return "trace-activation"
return "toolkit-activation"
}

func (i *Instrument) BasePackage() string {
Expand All @@ -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"),
Expand Down Expand Up @@ -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
}
33 changes: 33 additions & 0 deletions plugins/toolkit-activation/logging/debug_entry_intercepter.go
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions plugins/toolkit-activation/logging/error_entry_intercepter.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 4f94a25

Please sign in to comment.