github.com/observiq/bindplane-agent@v1.51.0/internal/report/snapshot/filter_traces.go (about)

     1  // Copyright observIQ, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package snapshot
    16  
    17  import (
    18  	"strings"
    19  	"time"
    20  
    21  	"go.opentelemetry.io/collector/pdata/pcommon"
    22  	"go.opentelemetry.io/collector/pdata/ptrace"
    23  )
    24  
    25  // filterTraces filters the traces by the given query and timestamp.
    26  // The returned payload cannot be assumed to be a copy, so it should not be modified.
    27  func filterTraces(traces ptrace.Traces, searchQuery *string, minimumTimestamp *time.Time) ptrace.Traces {
    28  	// No filters specified, filtered traces are trivially the same as input traces
    29  	if searchQuery == nil && minimumTimestamp == nil {
    30  		return traces
    31  	}
    32  
    33  	filteredTraces := ptrace.NewTraces()
    34  
    35  	resourceSpans := traces.ResourceSpans()
    36  	for i := 0; i < resourceSpans.Len(); i++ {
    37  		filteredResourceSpans := filterResourceSpans(resourceSpans.At(i), searchQuery, minimumTimestamp)
    38  
    39  		// Don't append empty resource traces
    40  		if filteredResourceSpans.ScopeSpans().Len() != 0 {
    41  			filteredResourceSpans.MoveTo(filteredTraces.ResourceSpans().AppendEmpty())
    42  		}
    43  	}
    44  
    45  	return filteredTraces
    46  }
    47  
    48  func filterResourceSpans(resourceSpan ptrace.ResourceSpans, searchQuery *string, minimumTimestamp *time.Time) ptrace.ResourceSpans {
    49  	filteredResourceSpans := ptrace.NewResourceSpans()
    50  
    51  	// Copy old resource to filtered resource
    52  	resource := resourceSpan.Resource()
    53  	resource.CopyTo(filteredResourceSpans.Resource())
    54  
    55  	// Apply query to resource
    56  	queryMatchesResource := true // default to true if no query specified
    57  	if searchQuery != nil {
    58  		queryMatchesResource = queryMatchesMap(resource.Attributes(), *searchQuery)
    59  	}
    60  
    61  	scopeSpans := resourceSpan.ScopeSpans()
    62  	for i := 0; i < scopeSpans.Len(); i++ {
    63  		filteredScopeSpans := filterScopeSpans(scopeSpans.At(i), queryMatchesResource, searchQuery, minimumTimestamp)
    64  
    65  		// Don't append empty scope spans
    66  		if filteredScopeSpans.Spans().Len() != 0 {
    67  			filteredScopeSpans.MoveTo(filteredResourceSpans.ScopeSpans().AppendEmpty())
    68  		}
    69  	}
    70  
    71  	return filteredResourceSpans
    72  }
    73  
    74  // filterScopeSpans filters out spans that do not match the query and minimumTimestamp, returning a new ptrace.ScopeSpans without the filtered spans.
    75  // queryMatchesResource indicates if the query string matches the resource associated with this ScopeSpans.
    76  func filterScopeSpans(scopeSpans ptrace.ScopeSpans, queryMatchesResource bool, searchQuery *string, minimumTimestamp *time.Time) ptrace.ScopeSpans {
    77  	filteredTraceSpans := ptrace.NewScopeSpans()
    78  	spans := scopeSpans.Spans()
    79  	for i := 0; i < spans.Len(); i++ {
    80  		span := spans.At(i)
    81  		if spanMatches(span, queryMatchesResource, searchQuery, minimumTimestamp) {
    82  			span.CopyTo(filteredTraceSpans.Spans().AppendEmpty())
    83  		}
    84  	}
    85  
    86  	return filteredTraceSpans
    87  }
    88  
    89  // spanMatches returns true if the query matches either the resource or span, AND the min timestamp.
    90  func spanMatches(s ptrace.Span, queryMatchesResource bool, searchQuery *string, minimumTimestamp *time.Time) bool {
    91  	queryMatchesSpan := true // default to true if no query specified
    92  	// Skip this check if we already know the query matches the resource
    93  	if !queryMatchesResource && searchQuery != nil {
    94  		queryMatchesSpan = spanMatchesQuery(s, *searchQuery)
    95  	}
    96  
    97  	timestampMatches := true // default to true if no timestamp specified
    98  	if minimumTimestamp != nil {
    99  		timestampMatches = spanMatchesTimestamp(s, *minimumTimestamp)
   100  	}
   101  
   102  	queryMatches := queryMatchesResource || queryMatchesSpan
   103  
   104  	return queryMatches && timestampMatches
   105  }
   106  
   107  // spanMatchesTimestamp determines if the span came after the provided timestamp
   108  func spanMatchesTimestamp(s ptrace.Span, minTime time.Time) bool {
   109  	return s.EndTimestamp() > pcommon.NewTimestampFromTime(minTime)
   110  }
   111  
   112  // spanMatchesQuery determines if the given span matches the given query string
   113  func spanMatchesQuery(span ptrace.Span, searchQuery string) bool {
   114  	return queryMatchesMap(span.Attributes(), searchQuery) ||
   115  		strings.Contains(span.Name(), searchQuery) ||
   116  		strings.Contains(span.TraceID().String(), searchQuery) ||
   117  		strings.Contains(span.SpanID().String(), searchQuery) ||
   118  		strings.Contains(span.ParentSpanID().String(), searchQuery) ||
   119  		strings.Contains(span.Kind().String(), searchQuery)
   120  }