Skip to content

Commit

Permalink
Add opentelemetry support
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
  • Loading branch information
glazychev-art committed Jun 18, 2021
1 parent aabb4c9 commit b09998d
Show file tree
Hide file tree
Showing 14 changed files with 594 additions and 87 deletions.
14 changes: 11 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,32 @@ require (
github.com/google/go-cmp v0.5.5
github.com/google/uuid v1.1.2
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/hashicorp/go-uuid v1.0.1 // indirect
github.com/nats-io/nats-streaming-server v0.17.0
github.com/nats-io/stan.go v0.6.0
github.com/networkservicemesh/api v0.0.0-20210617173100-f34297145219
github.com/open-policy-agent/opa v0.16.1
github.com/opentracing/opentracing-go v1.1.0
github.com/opentracing/opentracing-go v1.2.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.7.0
github.com/spiffe/go-spiffe/v2 v2.0.0-alpha.4.0.20200528145730-dc11d0c74e85
github.com/stretchr/testify v1.7.0
github.com/uber/jaeger-client-go v2.21.1+incompatible
github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/uber/jaeger-lib v2.4.0+incompatible // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0
go.opentelemetry.io/otel v0.20.0
go.opentelemetry.io/otel/exporters/otlp v0.20.0
go.opentelemetry.io/otel/metric v0.20.0
go.opentelemetry.io/otel/sdk v0.20.0
go.opentelemetry.io/otel/sdk/metric v0.20.0
go.opentelemetry.io/otel/trace v0.20.0
go.uber.org/atomic v1.7.0
go.uber.org/goleak v1.1.10
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect
golang.org/x/tools v0.1.2 // indirect
gonum.org/v1/gonum v0.6.2
google.golang.org/grpc v1.35.0
google.golang.org/grpc v1.37.0
google.golang.org/protobuf v1.26.0
)
61 changes: 53 additions & 8 deletions go.sum

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pkg/networkservice/chains/nsmgr/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"github.com/networkservicemesh/sdk/pkg/networkservice/common/interpose"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/recvfd"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/mechanisms/sendfd"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/metrics"
"github.com/networkservicemesh/sdk/pkg/networkservice/common/roundrobin"
"github.com/networkservicemesh/sdk/pkg/networkservice/core/adapters"
"github.com/networkservicemesh/sdk/pkg/registry"
Expand Down Expand Up @@ -191,6 +192,7 @@ func NewServer(ctx context.Context, tokenGenerator token.GeneratorFunc, options
roundrobin.NewServer(),
excludedprefixes.NewServer(ctx),
recvfd.NewServer(), // Receive any files passed
metrics.NewServer(),
interpose.NewServer(&interposeRegistryServer),
filtermechanisms.NewServer(&urlsRegistryServer),
heal.NewServer(ctx,
Expand Down
74 changes: 74 additions & 0 deletions pkg/networkservice/common/metrics/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed 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 metrics provides a chain element that sends metrics to collector
package metrics

import (
"context"

"github.com/golang/protobuf/ptypes/empty"
"github.com/networkservicemesh/api/pkg/api/networkservice"

"github.com/networkservicemesh/sdk/pkg/networkservice/core/next"
"github.com/networkservicemesh/sdk/pkg/tools/opentelemetry/meterhelper"
)

type metricServer struct {
helpers map[string]meterhelper.MeterHelper
}

// NewServer returns a new metric server chain element
func NewServer() networkservice.NetworkServiceServer {
return &metricServer{
helpers: make(map[string]meterhelper.MeterHelper),
}
}

func (t *metricServer) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*networkservice.Connection, error) {
conn, err := next.Server(ctx).Request(ctx, request)
if err != nil {
return nil, err
}

t.writeMetrics(ctx, conn.GetPath())
return conn, nil
}

func (t *metricServer) Close(ctx context.Context, conn *networkservice.Connection) (*empty.Empty, error) {
_, err := next.Server(ctx).Close(ctx, conn)
if err != nil {
return nil, err
}

t.writeMetrics(ctx, conn.GetPath())
return &empty.Empty{}, nil
}

func (t *metricServer) writeMetrics(ctx context.Context, path *networkservice.Path) {
if path != nil {
for _, pathSegment := range path.GetPathSegments() {
if pathSegment.Metrics == nil {
continue
}
_, ok := t.helpers[pathSegment.Id]
if !ok {
t.helpers[pathSegment.Id] = meterhelper.NewMeterHelper(pathSegment.Name, path.GetPathSegments()[0].Id)
}
t.helpers[pathSegment.Id].WriteMetrics(ctx, pathSegment.Metrics)
}
}
}
28 changes: 2 additions & 26 deletions pkg/tools/jaeger/jaeger.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,14 @@ import (
"fmt"
"io"
"os"
"strconv"

"github.com/networkservicemesh/sdk/pkg/tools/log"

"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)

const (
opentracingEnv = "TRACER_ENABLED"
opentracingDefault = true
"github.com/networkservicemesh/sdk/pkg/tools/log"
)

// IsOpentracingEnabled returns true if opentracing enabled
func IsOpentracingEnabled() bool {
val, err := readEnvBool(opentracingEnv, opentracingDefault)
if err == nil {
return val
}
return opentracingDefault
}

func readEnvBool(env string, value bool) (bool, error) {
str := os.Getenv(env)
if str == "" {
return value, nil
}

return strconv.ParseBool(str)
}

type emptyCloser struct {
}

Expand All @@ -66,7 +42,7 @@ func (*emptyCloser) Close() error {

// InitJaeger - returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func InitJaeger(ctx context.Context, service string) io.Closer {
if !IsOpentracingEnabled() {
if !log.IsOpentracingEnabled() {
return &emptyCloser{}
}
if opentracing.IsGlobalTracerRegistered() {
Expand Down
26 changes: 26 additions & 0 deletions pkg/tools/log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,41 @@ package log

import (
"context"
"os"
)

type contextKeyType string

const (
logKey contextKeyType = "Logger"
logFieldsKey contextKeyType = "LoggerFields"

telemetryEnv = "TELEMETRY"
telemetryOT = "opentracing"
telemetryOTel = "opentelemetry"

// Opentracing enabled by default
telemetryDefault = telemetryOT
)

// IsOpentracingEnabled returns true if opentracing enabled
func IsOpentracingEnabled() bool {
return telemetryOT == getTelemetryEnv()
}

// IsOpentelemetryEnabled returns true if opentelemetry enabled
func IsOpentelemetryEnabled() bool {
return telemetryOTel == getTelemetryEnv()
}

func getTelemetryEnv() string {
val := os.Getenv(telemetryEnv)
if val == "" {
return telemetryDefault
}
return val
}

var (
isTracingEnabled = false
)
Expand Down
14 changes: 8 additions & 6 deletions pkg/tools/log/logruslogger/logruslogger.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ import (
"sync"

"github.com/google/uuid"
"github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/metadata"

"github.com/networkservicemesh/sdk/pkg/tools/log"
"github.com/networkservicemesh/sdk/pkg/tools/log/spanlogger"
)

type loggerKeyType string
Expand Down Expand Up @@ -93,15 +93,17 @@ func fromContext(ctx context.Context) *traceCtxInfo {

type logrusLogger struct {
entry *logrus.Entry
span opentracing.Span
span spanlogger.Span
info *traceCtxInfo
operation string
}

func (s *logrusLogger) getSpan() string {
spanStr := fmt.Sprintf("%v", s.span)
if len(spanStr) > 0 && spanStr != "{}" && s.span != nil {
return fmt.Sprintf(" span=%v", spanStr)
if s.span != nil {
spanStr := s.span.ToString()
if len(spanStr) > 0 && spanStr != "{}" {
return fmt.Sprintf(" span=%v", spanStr)
}
}
return ""
}
Expand Down Expand Up @@ -194,7 +196,7 @@ func New(ctx context.Context) log.Logger {

// FromSpan - creates a new logruslogger from context, operation and span
// and returns context with it, logger, and a function to defer
func FromSpan(ctx context.Context, span opentracing.Span, operation string) (context.Context, log.Logger, func()) {
func FromSpan(ctx context.Context, span spanlogger.Span, operation string) (context.Context, log.Logger, func()) {
entry := logrus.WithFields(log.Fields(ctx))

var info *traceCtxInfo
Expand Down
133 changes: 133 additions & 0 deletions pkg/tools/log/spanlogger/span.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (c) 2021 Doc.ai and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed 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 spanlogger

import (
"context"
"fmt"

"github.com/opentracing/opentracing-go"
opentracinglog "github.com/opentracing/opentracing-go/log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
opentelemetry "go.opentelemetry.io/otel/trace"

opentelemetrynsm "github.com/networkservicemesh/sdk/pkg/tools/opentelemetry"
)

// Span - unified interface for opentracing/opentelemetry spans
type Span interface {
Log(level, format string, v ...interface{})
LogObject(k, v interface{})
WithField(k, v interface{}) Span
Finish()

ToString() string
}

// Opentracing span
type otSpan struct {
span opentracing.Span
}

func (otsp *otSpan) Log(level, format string, v ...interface{}) {
otsp.span.LogFields(
opentracinglog.String("event", level),
opentracinglog.String("message", fmt.Sprintf(format, v...)),
)
}

func (otsp *otSpan) LogObject(k, v interface{}) {
otsp.span.LogFields(opentracinglog.Object(k.(string), v))
}

func (otsp *otSpan) WithField(k, v interface{}) Span {
otsp.span = otsp.span.SetTag(k.(string), v)
return otsp
}

func (otsp *otSpan) ToString() string {
if spanStr := fmt.Sprintf("%v", otsp.span); spanStr != "{}" {
return spanStr
}
return ""
}

func (otsp *otSpan) Finish() {
otsp.span.Finish()
}

func newOTSpan(ctx context.Context, operationName string, additionalFields map[string]interface{}) (c context.Context, s Span) {
span, ctx := opentracing.StartSpanFromContext(ctx, operationName)
for k, v := range additionalFields {
span = span.SetTag(k, v)
}
return ctx, &otSpan{span: span}
}

// Opentelemetry span
type otelSpan struct {
span opentelemetry.Span
}

func (otelsp *otelSpan) Log(level, format string, v ...interface{}) {
otelsp.span.AddEvent(
"",
opentelemetry.WithAttributes([]attribute.KeyValue{
attribute.String("event", level),
attribute.String("message", fmt.Sprintf(format, v...)),
}...),
)
}

func (otelsp *otelSpan) LogObject(k, v interface{}) {
otelsp.span.AddEvent(
"",
opentelemetry.WithAttributes([]attribute.KeyValue{
attribute.String(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)),
}...),
)
}

func (otelsp *otelSpan) WithField(k, v interface{}) Span {
otelsp.span.SetAttributes(attribute.Any(k.(string), v))
return otelsp
}

func (otelsp *otelSpan) ToString() string {
if spanID := otelsp.span.SpanContext().SpanID(); spanID.IsValid() {
return spanID.String()
}
return ""
}

func (otelsp *otelSpan) Finish() {
otelsp.span.End()
}

func newOTELSpan(ctx context.Context, operationName string, additionalFields map[string]interface{}) (c context.Context, s Span) {
var add []attribute.KeyValue

for k, v := range additionalFields {
add = append(add, attribute.Any(k, v))
}

ctx, span := otel.Tracer(opentelemetrynsm.InstrumentationName).Start(ctx, operationName)
span.SetAttributes(add...)

return ctx, &otelSpan{span: span}
}
Loading

0 comments on commit b09998d

Please sign in to comment.