opentelemetry-collector/processor/metrics.go

247 lines
7.2 KiB
Go

// Copyright The OpenTelemetry Authors
//
// 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 processor
import (
"context"
commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"go.opentelemetry.io/collector/consumer/pdata"
"go.opentelemetry.io/collector/internal/collector/telemetry"
"go.opentelemetry.io/collector/obsreport"
"go.opentelemetry.io/collector/translator/conventions"
)
// Keys and stats for telemetry.
var (
TagServiceNameKey, _ = tag.NewKey("service")
TagProcessorNameKey, _ = tag.NewKey(obsreport.ProcessorKey)
StatReceivedSpanCount = stats.Int64(
"spans_received",
"counts the number of spans received",
stats.UnitDimensionless)
StatDroppedSpanCount = stats.Int64(
"spans_dropped",
"counts the number of spans dropped",
stats.UnitDimensionless)
StatBadBatchDroppedSpanCount = stats.Int64(
"bad_batch_spans_dropped",
"counts the number of spans dropped due to being in bad batches",
stats.UnitDimensionless)
StatTraceBatchesDroppedCount = stats.Int64(
"trace_batches_dropped",
"counts the number of trace batches dropped",
stats.UnitDimensionless)
StatDroppedMetricCount = stats.Int64(
"metrics_dropped",
"counts the number of metrics dropped",
stats.UnitDimensionless)
StatMetricBatchesDroppedCount = stats.Int64(
"metric_batches_dropped",
"counts the number of metric batches dropped",
stats.UnitDimensionless)
)
// SpanCountStats represents span count stats grouped by service if DETAILED telemetry level is set,
// otherwise only overall span count is stored in serviceSpansCounts.
type SpanCountStats struct {
processorName string
serviceSpansCounts map[string]int
allSpansCount int
isDetailed bool
}
func NewSpanCountStats(td pdata.Traces, processorName string) *SpanCountStats {
scm := &SpanCountStats{
processorName: processorName,
allSpansCount: td.SpanCount(),
}
if serviceTagsEnabled() {
scm.serviceSpansCounts = spanCountByResourceStringAttribute(td, conventions.AttributeServiceName)
scm.isDetailed = true
}
return scm
}
func (scm *SpanCountStats) GetAllSpansCount() int {
return scm.allSpansCount
}
// MetricTagKeys returns the metric tag keys according to the given telemetry level.
func MetricTagKeys(level telemetry.Level) []tag.Key {
var tagKeys []tag.Key
switch level {
case telemetry.Detailed:
tagKeys = append(tagKeys, TagServiceNameKey)
fallthrough
case telemetry.Normal, telemetry.Basic:
tagKeys = append(tagKeys, TagProcessorNameKey)
default:
return nil
}
return tagKeys
}
// MetricViews return the metrics views according to given telemetry level.
func MetricViews(level telemetry.Level) []*view.View {
tagKeys := MetricTagKeys(level)
if tagKeys == nil {
return nil
}
// There are some metrics enabled, return the views.
receivedBatchesView := &view.View{
Name: "batches_received",
Measure: StatReceivedSpanCount,
Description: "The number of span batches received.",
TagKeys: tagKeys,
Aggregation: view.Count(),
}
droppedBatchesView := &view.View{
Measure: StatTraceBatchesDroppedCount,
Description: "The number of span batches dropped.",
TagKeys: tagKeys,
Aggregation: view.Sum(),
}
droppedBadBatchesView := &view.View{
Name: "bad_batches_dropped",
Measure: StatBadBatchDroppedSpanCount,
Description: "The number of span batches with bad data that were dropped.",
TagKeys: tagKeys,
Aggregation: view.Count(),
}
receivedSpansView := &view.View{
Name: StatReceivedSpanCount.Name(),
Measure: StatReceivedSpanCount,
Description: "The number of spans received.",
TagKeys: tagKeys,
Aggregation: view.Sum(),
}
droppedSpansView := &view.View{
Name: StatDroppedSpanCount.Name(),
Measure: StatDroppedSpanCount,
Description: "The number of spans dropped.",
TagKeys: tagKeys,
Aggregation: view.Sum(),
}
droppedSpansFromBadBatchesView := &view.View{
Name: StatBadBatchDroppedSpanCount.Name(),
Measure: StatBadBatchDroppedSpanCount,
Description: "The number of spans dropped from span batches with bad data.",
TagKeys: tagKeys,
Aggregation: view.Sum(),
}
legacyViews := []*view.View{
receivedBatchesView,
droppedBatchesView,
receivedSpansView,
droppedSpansView,
droppedBadBatchesView,
droppedSpansFromBadBatchesView,
}
return obsreport.ProcessorMetricViews("", legacyViews)
}
// ServiceNameForNode gets the service name for a specified node.
func ServiceNameForNode(node *commonpb.Node) string {
var serviceName string
if node == nil {
serviceName = "<nil-batch-node>"
} else if node.ServiceInfo == nil {
serviceName = "<nil-service-info>"
} else if node.ServiceInfo.Name == "" {
serviceName = "<empty-service-info-name>"
} else {
serviceName = node.ServiceInfo.Name
}
return serviceName
}
// ServiceNameForResource gets the service name for a specified Resource.
// TODO: Find a better package for this function.
func ServiceNameForResource(resource pdata.Resource) string {
if resource.IsNil() {
return "<nil-resource>"
}
service, found := resource.Attributes().Get(conventions.AttributeServiceName)
if !found {
return "<nil-service-name>"
}
return service.StringVal()
}
// RecordsSpanCountMetrics reports span count metrics for specified measure.
func RecordsSpanCountMetrics(ctx context.Context, scm *SpanCountStats, measure *stats.Int64Measure) {
if scm.isDetailed {
for serviceName, spanCount := range scm.serviceSpansCounts {
statsTags := []tag.Mutator{
tag.Insert(TagProcessorNameKey, scm.processorName),
tag.Insert(TagServiceNameKey, serviceName),
}
_ = stats.RecordWithTags(ctx, statsTags, measure.M(int64(spanCount)))
}
return
}
statsTags := []tag.Mutator{tag.Insert(TagProcessorNameKey, scm.processorName)}
_ = stats.RecordWithTags(ctx, statsTags, measure.M(int64(scm.allSpansCount)))
}
func serviceTagsEnabled() bool {
level, err := telemetry.GetLevel()
return err == nil && level == telemetry.Detailed
}
// spanCountByResourceStringAttribute calculates the number of spans by resource specified by
// provided string attribute attrKey.
func spanCountByResourceStringAttribute(td pdata.Traces, attrKey string) map[string]int {
spanCounts := make(map[string]int)
rss := td.ResourceSpans()
for i := 0; i < rss.Len(); i++ {
rs := rss.At(i)
if rs.IsNil() {
continue
}
var attrStringVal string
if attrVal, ok := rs.Resource().Attributes().Get(attrKey); ok {
attrStringVal = attrVal.StringVal()
}
ilss := rs.InstrumentationLibrarySpans()
for j := 0; j < ilss.Len(); j++ {
ils := ilss.At(j)
if ils.IsNil() {
continue
}
spanCounts[attrStringVal] += ilss.At(j).Spans().Len()
}
}
return spanCounts
}