1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-01-30 04:40:41 +02:00

Fix duplicate logs across resources (#5803)

1. Create scope map with resource key to map the correct log record. 
2. Add test case with different resource and scope combination

Fixes #5782 

### Benchmarks

```
goos: darwin
goarch: arm64
pkg: go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform
               │   old.txt   │               new.txt               │
               │   sec/op    │   sec/op     vs base                │
ResourceLogs-8   3.266µ ± 3%   1.100µ ± 5%  -66.33% (p=0.000 n=10)

               │   old.txt    │               new.txt                │
               │     B/op     │     B/op      vs base                │
ResourceLogs-8   8.297Ki ± 0%   2.430Ki ± 0%  -70.72% (p=0.000 n=10)

               │   old.txt   │              new.txt               │
               │  allocs/op  │ allocs/op   vs base                │
ResourceLogs-8   178.00 ± 0%   52.00 ± 0%  -70.79% (p=0.000 n=10)
```

---------

Co-authored-by: Sam Xie <sam@samxie.me>
This commit is contained in:
pree-dew 2024-09-17 16:42:49 +05:30 committed by GitHub
parent 42fd8fe325
commit 534ce5ab09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 691 additions and 606 deletions

View File

@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Fixed ### Fixed
- The race condition for multiple `FixedSize` exemplar reservoirs identified in #5814 is resolved. (#5819) - The race condition for multiple `FixedSize` exemplar reservoirs identified in #5814 is resolved. (#5819)
- Fix log records duplication in case of heterogeneous resource attributes by correctly mapping each log record to it's resource and scope. (#5803)
<!-- Released section --> <!-- Released section -->
<!-- Don't change this section unless doing release --> <!-- Don't change this section unless doing release -->

View File

@ -9,7 +9,6 @@
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform" package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform"
import ( import (
"sync"
"time" "time"
cpb "go.opentelemetry.io/proto/otlp/common/v1" cpb "go.opentelemetry.io/proto/otlp/common/v1"
@ -28,71 +27,25 @@ func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
return nil return nil
} }
resMap := resourceLogsMapPool.Get().(map[attribute.Distinct]*lpb.ResourceLogs) resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
defer func() {
clear(resMap)
resourceLogsMapPool.Put(resMap)
}()
resourceLogsMap(&resMap, records)
out := make([]*lpb.ResourceLogs, 0, len(resMap)) type key struct {
for _, rl := range resMap { r attribute.Distinct
out = append(out, rl) is instrumentation.Scope
} }
return out scopeMap := make(map[key]*lpb.ScopeLogs)
}
var resourceLogsMapPool = sync.Pool{ var resources int
New: func() any {
return make(map[attribute.Distinct]*lpb.ResourceLogs)
},
}
func resourceLogsMap(dst *map[attribute.Distinct]*lpb.ResourceLogs, records []log.Record) {
for _, r := range records { for _, r := range records {
res := r.Resource() res := r.Resource()
rl, ok := (*dst)[res.Equivalent()] rKey := res.Equivalent()
if !ok {
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
(*dst)[res.Equivalent()] = rl
}
rl.ScopeLogs = ScopeLogs(records)
}
}
// ScopeLogs returns a slice of OTLP ScopeLogs generated from records.
func ScopeLogs(records []log.Record) []*lpb.ScopeLogs {
scopeMap := scopeLogsMapPool.Get().(map[instrumentation.Scope]*lpb.ScopeLogs)
defer func() {
clear(scopeMap)
scopeLogsMapPool.Put(scopeMap)
}()
scopeLogsMap(&scopeMap, records)
out := make([]*lpb.ScopeLogs, 0, len(scopeMap))
for _, sl := range scopeMap {
out = append(out, sl)
}
return out
}
var scopeLogsMapPool = sync.Pool{
New: func() any {
return make(map[instrumentation.Scope]*lpb.ScopeLogs)
},
}
func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.Record) {
for _, r := range records {
scope := r.InstrumentationScope() scope := r.InstrumentationScope()
sl, ok := (*dst)[scope] k := key{
if !ok { r: rKey,
is: scope,
}
sl, iOk := scopeMap[k]
if !iOk {
sl = new(lpb.ScopeLogs) sl = new(lpb.ScopeLogs)
var emptyScope instrumentation.Scope var emptyScope instrumentation.Scope
if scope != emptyScope { if scope != emptyScope {
@ -102,10 +55,34 @@ func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.R
} }
sl.SchemaUrl = scope.SchemaURL sl.SchemaUrl = scope.SchemaURL
} }
(*dst)[scope] = sl scopeMap[k] = sl
} }
sl.LogRecords = append(sl.LogRecords, LogRecord(r)) sl.LogRecords = append(sl.LogRecords, LogRecord(r))
rl, rOk := resMap[rKey]
if !rOk {
resources++
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
resMap[rKey] = rl
}
if !iOk {
rl.ScopeLogs = append(rl.ScopeLogs, sl)
}
} }
// Transform the categorized map into a slice
resLogs := make([]*lpb.ResourceLogs, 0, resources)
for _, rl := range resMap {
resLogs = append(resLogs, rl)
}
return resLogs
} }
// LogRecord returns an OTLP LogRecord generated from record. // LogRecord returns an OTLP LogRecord generated from record.

View File

@ -30,73 +30,106 @@ var (
ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0)) ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0))
obs = ts.Add(30 * time.Second) obs = ts.Add(30 * time.Second)
tom = api.String("user", "tom")
jerry = api.String("user", "jerry")
// A time before unix 0. // A time before unix 0.
negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC) negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC)
alice = api.String("user", "alice") pbTom = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
bob = api.String("user", "bob") Value: &cpb.AnyValue_StringValue{StringValue: "tom"},
pbAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "alice"},
}} }}
pbBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{ pbJerry = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "bob"}, Value: &cpb.AnyValue_StringValue{StringValue: "jerry"},
}} }}
sevA = api.SeverityInfo sevC = api.SeverityInfo
sevB = api.SeverityError sevD = api.SeverityError
pbSevA = lpb.SeverityNumber_SEVERITY_NUMBER_INFO pbSevC = lpb.SeverityNumber_SEVERITY_NUMBER_INFO
pbSevB = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR pbSevD = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
bodyA = api.StringValue("a") bodyC = api.StringValue("c")
bodyB = api.StringValue("b") bodyD = api.StringValue("d")
pbBodyA = &cpb.AnyValue{ pbBodyC = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "a", StringValue: "c",
}, },
} }
pbBodyB = &cpb.AnyValue{ pbBodyD = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "b", StringValue: "d",
}, },
} }
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1} spanIDC = []byte{0, 0, 0, 0, 0, 0, 0, 1}
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2} spanIDD = []byte{0, 0, 0, 0, 0, 0, 0, 2}
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} traceIDC = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} traceIDD = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
flagsA = byte(1) flagsC = byte(1)
flagsB = byte(0) flagsD = byte(0)
scope = instrumentation.Scope{ scope = instrumentation.Scope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
SchemaURL: semconv.SchemaURL, SchemaURL: semconv.SchemaURL,
} }
scope2 = instrumentation.Scope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
SchemaURL: semconv.SchemaURL,
}
scopeList = []instrumentation.Scope{scope, scope2}
pbScope = &cpb.InstrumentationScope{ pbScope = &cpb.InstrumentationScope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
}
pbScope2 = &cpb.InstrumentationScope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
} }
res = resource.NewWithAttributes( res = resource.NewWithAttributes(
semconv.SchemaURL, semconv.SchemaURL,
semconv.ServiceName("test server"), semconv.ServiceName("service1"),
semconv.ServiceVersion("v0.1.0"), semconv.ServiceVersion("v0.1.1"),
) )
res2 = resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("service2"),
semconv.ServiceVersion("v0.2.2"),
)
resList = []*resource.Resource{res, res2}
pbRes = &rpb.Resource{ pbRes = &rpb.Resource{
Attributes: []*cpb.KeyValue{ Attributes: []*cpb.KeyValue{
{ {
Key: "service.name", Key: "service.name",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "test server"}, Value: &cpb.AnyValue_StringValue{StringValue: "service1"},
}, },
}, },
{ {
Key: "service.version", Key: "service.version",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"}, Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.1"},
},
},
},
}
pbRes2 = &rpb.Resource{
Attributes: []*cpb.KeyValue{
{
Key: "service.name",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "service2"},
},
},
{
Key: "service.version",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.2.2"},
}, },
}, },
}, },
@ -105,75 +138,79 @@ var (
records = func() []log.Record { records = func() []log.Record {
var out []log.Record var out []log.Record
out = append(out, logtest.RecordFactory{ for _, r := range resList {
Timestamp: ts, for _, s := range scopeList {
ObservedTimestamp: obs, out = append(out, logtest.RecordFactory{
Severity: sevA, Timestamp: ts,
SeverityText: "A", ObservedTimestamp: obs,
Body: bodyA, Severity: sevC,
Attributes: []api.KeyValue{alice}, SeverityText: "C",
TraceID: trace.TraceID(traceIDA), Body: bodyC,
SpanID: trace.SpanID(spanIDA), Attributes: []api.KeyValue{tom},
TraceFlags: trace.TraceFlags(flagsA), TraceID: trace.TraceID(traceIDC),
InstrumentationScope: &scope, SpanID: trace.SpanID(spanIDC),
Resource: res, TraceFlags: trace.TraceFlags(flagsC),
}.NewRecord()) InstrumentationScope: &s,
Resource: r,
}.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevA, Severity: sevC,
SeverityText: "A", SeverityText: "C",
Body: bodyA, Body: bodyC,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDA), TraceID: trace.TraceID(traceIDC),
SpanID: trace.SpanID(spanIDA), SpanID: trace.SpanID(spanIDC),
TraceFlags: trace.TraceFlags(flagsA), TraceFlags: trace.TraceFlags(flagsC),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{alice}, Attributes: []api.KeyValue{tom},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: negativeTs, Timestamp: negativeTs,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
}
}
return out return out
}() }()
@ -182,76 +219,90 @@ var (
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: 0, TimeUnixNano: 0,
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
} }
pbScopeLogs = &lpb.ScopeLogs{ pbScopeLogsList = []*lpb.ScopeLogs{
Scope: pbScope, {
SchemaUrl: semconv.SchemaURL, Scope: pbScope,
LogRecords: pbLogRecords, SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
{
Scope: pbScope2,
SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
} }
pbResourceLogs = &lpb.ResourceLogs{ pbResourceLogsList = []*lpb.ResourceLogs{
Resource: pbRes, {
SchemaUrl: semconv.SchemaURL, Resource: pbRes,
ScopeLogs: []*lpb.ScopeLogs{pbScopeLogs}, SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
{
Resource: pbRes2,
SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
} }
) )
func TestResourceLogs(t *testing.T) { func TestResourceLogs(t *testing.T) {
want := []*lpb.ResourceLogs{pbResourceLogs} want := pbResourceLogsList
assert.Equal(t, want, ResourceLogs(records)) assert.ElementsMatch(t, want, ResourceLogs(records))
} }
func TestSeverityNumber(t *testing.T) { func TestSeverityNumber(t *testing.T) {

View File

@ -9,7 +9,6 @@
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform" package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
import ( import (
"sync"
"time" "time"
cpb "go.opentelemetry.io/proto/otlp/common/v1" cpb "go.opentelemetry.io/proto/otlp/common/v1"
@ -28,71 +27,25 @@ func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
return nil return nil
} }
resMap := resourceLogsMapPool.Get().(map[attribute.Distinct]*lpb.ResourceLogs) resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
defer func() {
clear(resMap)
resourceLogsMapPool.Put(resMap)
}()
resourceLogsMap(&resMap, records)
out := make([]*lpb.ResourceLogs, 0, len(resMap)) type key struct {
for _, rl := range resMap { r attribute.Distinct
out = append(out, rl) is instrumentation.Scope
} }
return out scopeMap := make(map[key]*lpb.ScopeLogs)
}
var resourceLogsMapPool = sync.Pool{ var resources int
New: func() any {
return make(map[attribute.Distinct]*lpb.ResourceLogs)
},
}
func resourceLogsMap(dst *map[attribute.Distinct]*lpb.ResourceLogs, records []log.Record) {
for _, r := range records { for _, r := range records {
res := r.Resource() res := r.Resource()
rl, ok := (*dst)[res.Equivalent()] rKey := res.Equivalent()
if !ok {
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
(*dst)[res.Equivalent()] = rl
}
rl.ScopeLogs = ScopeLogs(records)
}
}
// ScopeLogs returns a slice of OTLP ScopeLogs generated from records.
func ScopeLogs(records []log.Record) []*lpb.ScopeLogs {
scopeMap := scopeLogsMapPool.Get().(map[instrumentation.Scope]*lpb.ScopeLogs)
defer func() {
clear(scopeMap)
scopeLogsMapPool.Put(scopeMap)
}()
scopeLogsMap(&scopeMap, records)
out := make([]*lpb.ScopeLogs, 0, len(scopeMap))
for _, sl := range scopeMap {
out = append(out, sl)
}
return out
}
var scopeLogsMapPool = sync.Pool{
New: func() any {
return make(map[instrumentation.Scope]*lpb.ScopeLogs)
},
}
func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.Record) {
for _, r := range records {
scope := r.InstrumentationScope() scope := r.InstrumentationScope()
sl, ok := (*dst)[scope] k := key{
if !ok { r: rKey,
is: scope,
}
sl, iOk := scopeMap[k]
if !iOk {
sl = new(lpb.ScopeLogs) sl = new(lpb.ScopeLogs)
var emptyScope instrumentation.Scope var emptyScope instrumentation.Scope
if scope != emptyScope { if scope != emptyScope {
@ -102,10 +55,34 @@ func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.R
} }
sl.SchemaUrl = scope.SchemaURL sl.SchemaUrl = scope.SchemaURL
} }
(*dst)[scope] = sl scopeMap[k] = sl
} }
sl.LogRecords = append(sl.LogRecords, LogRecord(r)) sl.LogRecords = append(sl.LogRecords, LogRecord(r))
rl, rOk := resMap[rKey]
if !rOk {
resources++
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
resMap[rKey] = rl
}
if !iOk {
rl.ScopeLogs = append(rl.ScopeLogs, sl)
}
} }
// Transform the categorized map into a slice
resLogs := make([]*lpb.ResourceLogs, 0, resources)
for _, rl := range resMap {
resLogs = append(resLogs, rl)
}
return resLogs
} }
// LogRecord returns an OTLP LogRecord generated from record. // LogRecord returns an OTLP LogRecord generated from record.

View File

@ -30,73 +30,106 @@ var (
ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0)) ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0))
obs = ts.Add(30 * time.Second) obs = ts.Add(30 * time.Second)
tom = api.String("user", "tom")
jerry = api.String("user", "jerry")
// A time before unix 0. // A time before unix 0.
negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC) negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC)
alice = api.String("user", "alice") pbTom = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
bob = api.String("user", "bob") Value: &cpb.AnyValue_StringValue{StringValue: "tom"},
pbAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "alice"},
}} }}
pbBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{ pbJerry = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "bob"}, Value: &cpb.AnyValue_StringValue{StringValue: "jerry"},
}} }}
sevA = api.SeverityInfo sevC = api.SeverityInfo
sevB = api.SeverityError sevD = api.SeverityError
pbSevA = lpb.SeverityNumber_SEVERITY_NUMBER_INFO pbSevC = lpb.SeverityNumber_SEVERITY_NUMBER_INFO
pbSevB = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR pbSevD = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
bodyA = api.StringValue("a") bodyC = api.StringValue("c")
bodyB = api.StringValue("b") bodyD = api.StringValue("d")
pbBodyA = &cpb.AnyValue{ pbBodyC = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "a", StringValue: "c",
}, },
} }
pbBodyB = &cpb.AnyValue{ pbBodyD = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "b", StringValue: "d",
}, },
} }
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1} spanIDC = []byte{0, 0, 0, 0, 0, 0, 0, 1}
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2} spanIDD = []byte{0, 0, 0, 0, 0, 0, 0, 2}
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} traceIDC = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} traceIDD = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
flagsA = byte(1) flagsC = byte(1)
flagsB = byte(0) flagsD = byte(0)
scope = instrumentation.Scope{ scope = instrumentation.Scope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
SchemaURL: semconv.SchemaURL, SchemaURL: semconv.SchemaURL,
} }
scope2 = instrumentation.Scope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
SchemaURL: semconv.SchemaURL,
}
scopeList = []instrumentation.Scope{scope, scope2}
pbScope = &cpb.InstrumentationScope{ pbScope = &cpb.InstrumentationScope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
}
pbScope2 = &cpb.InstrumentationScope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
} }
res = resource.NewWithAttributes( res = resource.NewWithAttributes(
semconv.SchemaURL, semconv.SchemaURL,
semconv.ServiceName("test server"), semconv.ServiceName("service1"),
semconv.ServiceVersion("v0.1.0"), semconv.ServiceVersion("v0.1.1"),
) )
res2 = resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("service2"),
semconv.ServiceVersion("v0.2.2"),
)
resList = []*resource.Resource{res, res2}
pbRes = &rpb.Resource{ pbRes = &rpb.Resource{
Attributes: []*cpb.KeyValue{ Attributes: []*cpb.KeyValue{
{ {
Key: "service.name", Key: "service.name",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "test server"}, Value: &cpb.AnyValue_StringValue{StringValue: "service1"},
}, },
}, },
{ {
Key: "service.version", Key: "service.version",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"}, Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.1"},
},
},
},
}
pbRes2 = &rpb.Resource{
Attributes: []*cpb.KeyValue{
{
Key: "service.name",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "service2"},
},
},
{
Key: "service.version",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.2.2"},
}, },
}, },
}, },
@ -105,75 +138,79 @@ var (
records = func() []log.Record { records = func() []log.Record {
var out []log.Record var out []log.Record
out = append(out, logtest.RecordFactory{ for _, r := range resList {
Timestamp: ts, for _, s := range scopeList {
ObservedTimestamp: obs, out = append(out, logtest.RecordFactory{
Severity: sevA, Timestamp: ts,
SeverityText: "A", ObservedTimestamp: obs,
Body: bodyA, Severity: sevC,
Attributes: []api.KeyValue{alice}, SeverityText: "C",
TraceID: trace.TraceID(traceIDA), Body: bodyC,
SpanID: trace.SpanID(spanIDA), Attributes: []api.KeyValue{tom},
TraceFlags: trace.TraceFlags(flagsA), TraceID: trace.TraceID(traceIDC),
InstrumentationScope: &scope, SpanID: trace.SpanID(spanIDC),
Resource: res, TraceFlags: trace.TraceFlags(flagsC),
}.NewRecord()) InstrumentationScope: &s,
Resource: r,
}.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevA, Severity: sevC,
SeverityText: "A", SeverityText: "C",
Body: bodyA, Body: bodyC,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDA), TraceID: trace.TraceID(traceIDC),
SpanID: trace.SpanID(spanIDA), SpanID: trace.SpanID(spanIDC),
TraceFlags: trace.TraceFlags(flagsA), TraceFlags: trace.TraceFlags(flagsC),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{alice}, Attributes: []api.KeyValue{tom},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: negativeTs, Timestamp: negativeTs,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
}
}
return out return out
}() }()
@ -182,76 +219,90 @@ var (
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: 0, TimeUnixNano: 0,
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
} }
pbScopeLogs = &lpb.ScopeLogs{ pbScopeLogsList = []*lpb.ScopeLogs{
Scope: pbScope, {
SchemaUrl: semconv.SchemaURL, Scope: pbScope,
LogRecords: pbLogRecords, SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
{
Scope: pbScope2,
SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
} }
pbResourceLogs = &lpb.ResourceLogs{ pbResourceLogsList = []*lpb.ResourceLogs{
Resource: pbRes, {
SchemaUrl: semconv.SchemaURL, Resource: pbRes,
ScopeLogs: []*lpb.ScopeLogs{pbScopeLogs}, SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
{
Resource: pbRes2,
SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
} }
) )
func TestResourceLogs(t *testing.T) { func TestResourceLogs(t *testing.T) {
want := []*lpb.ResourceLogs{pbResourceLogs} want := pbResourceLogsList
assert.Equal(t, want, ResourceLogs(records)) assert.ElementsMatch(t, want, ResourceLogs(records))
} }
func TestSeverityNumber(t *testing.T) { func TestSeverityNumber(t *testing.T) {

View File

@ -9,7 +9,6 @@
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform" package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
import ( import (
"sync"
"time" "time"
cpb "go.opentelemetry.io/proto/otlp/common/v1" cpb "go.opentelemetry.io/proto/otlp/common/v1"
@ -28,71 +27,25 @@ func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
return nil return nil
} }
resMap := resourceLogsMapPool.Get().(map[attribute.Distinct]*lpb.ResourceLogs) resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
defer func() {
clear(resMap)
resourceLogsMapPool.Put(resMap)
}()
resourceLogsMap(&resMap, records)
out := make([]*lpb.ResourceLogs, 0, len(resMap)) type key struct {
for _, rl := range resMap { r attribute.Distinct
out = append(out, rl) is instrumentation.Scope
} }
return out scopeMap := make(map[key]*lpb.ScopeLogs)
}
var resourceLogsMapPool = sync.Pool{ var resources int
New: func() any {
return make(map[attribute.Distinct]*lpb.ResourceLogs)
},
}
func resourceLogsMap(dst *map[attribute.Distinct]*lpb.ResourceLogs, records []log.Record) {
for _, r := range records { for _, r := range records {
res := r.Resource() res := r.Resource()
rl, ok := (*dst)[res.Equivalent()] rKey := res.Equivalent()
if !ok {
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
(*dst)[res.Equivalent()] = rl
}
rl.ScopeLogs = ScopeLogs(records)
}
}
// ScopeLogs returns a slice of OTLP ScopeLogs generated from records.
func ScopeLogs(records []log.Record) []*lpb.ScopeLogs {
scopeMap := scopeLogsMapPool.Get().(map[instrumentation.Scope]*lpb.ScopeLogs)
defer func() {
clear(scopeMap)
scopeLogsMapPool.Put(scopeMap)
}()
scopeLogsMap(&scopeMap, records)
out := make([]*lpb.ScopeLogs, 0, len(scopeMap))
for _, sl := range scopeMap {
out = append(out, sl)
}
return out
}
var scopeLogsMapPool = sync.Pool{
New: func() any {
return make(map[instrumentation.Scope]*lpb.ScopeLogs)
},
}
func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.Record) {
for _, r := range records {
scope := r.InstrumentationScope() scope := r.InstrumentationScope()
sl, ok := (*dst)[scope] k := key{
if !ok { r: rKey,
is: scope,
}
sl, iOk := scopeMap[k]
if !iOk {
sl = new(lpb.ScopeLogs) sl = new(lpb.ScopeLogs)
var emptyScope instrumentation.Scope var emptyScope instrumentation.Scope
if scope != emptyScope { if scope != emptyScope {
@ -102,10 +55,34 @@ func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.R
} }
sl.SchemaUrl = scope.SchemaURL sl.SchemaUrl = scope.SchemaURL
} }
(*dst)[scope] = sl scopeMap[k] = sl
} }
sl.LogRecords = append(sl.LogRecords, LogRecord(r)) sl.LogRecords = append(sl.LogRecords, LogRecord(r))
rl, rOk := resMap[rKey]
if !rOk {
resources++
rl = new(lpb.ResourceLogs)
if res.Len() > 0 {
rl.Resource = &rpb.Resource{
Attributes: AttrIter(res.Iter()),
}
}
rl.SchemaUrl = res.SchemaURL()
resMap[rKey] = rl
}
if !iOk {
rl.ScopeLogs = append(rl.ScopeLogs, sl)
}
} }
// Transform the categorized map into a slice
resLogs := make([]*lpb.ResourceLogs, 0, resources)
for _, rl := range resMap {
resLogs = append(resLogs, rl)
}
return resLogs
} }
// LogRecord returns an OTLP LogRecord generated from record. // LogRecord returns an OTLP LogRecord generated from record.

View File

@ -30,73 +30,106 @@ var (
ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0)) ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0))
obs = ts.Add(30 * time.Second) obs = ts.Add(30 * time.Second)
tom = api.String("user", "tom")
jerry = api.String("user", "jerry")
// A time before unix 0. // A time before unix 0.
negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC) negativeTs = time.Date(1969, 7, 20, 20, 17, 0, 0, time.UTC)
alice = api.String("user", "alice") pbTom = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
bob = api.String("user", "bob") Value: &cpb.AnyValue_StringValue{StringValue: "tom"},
pbAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "alice"},
}} }}
pbBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{ pbJerry = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "bob"}, Value: &cpb.AnyValue_StringValue{StringValue: "jerry"},
}} }}
sevA = api.SeverityInfo sevC = api.SeverityInfo
sevB = api.SeverityError sevD = api.SeverityError
pbSevA = lpb.SeverityNumber_SEVERITY_NUMBER_INFO pbSevC = lpb.SeverityNumber_SEVERITY_NUMBER_INFO
pbSevB = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR pbSevD = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
bodyA = api.StringValue("a") bodyC = api.StringValue("c")
bodyB = api.StringValue("b") bodyD = api.StringValue("d")
pbBodyA = &cpb.AnyValue{ pbBodyC = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "a", StringValue: "c",
}, },
} }
pbBodyB = &cpb.AnyValue{ pbBodyD = &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{ Value: &cpb.AnyValue_StringValue{
StringValue: "b", StringValue: "d",
}, },
} }
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1} spanIDC = []byte{0, 0, 0, 0, 0, 0, 0, 1}
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2} spanIDD = []byte{0, 0, 0, 0, 0, 0, 0, 2}
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} traceIDC = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2} traceIDD = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
flagsA = byte(1) flagsC = byte(1)
flagsB = byte(0) flagsD = byte(0)
scope = instrumentation.Scope{ scope = instrumentation.Scope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
SchemaURL: semconv.SchemaURL, SchemaURL: semconv.SchemaURL,
} }
scope2 = instrumentation.Scope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
SchemaURL: semconv.SchemaURL,
}
scopeList = []instrumentation.Scope{scope, scope2}
pbScope = &cpb.InstrumentationScope{ pbScope = &cpb.InstrumentationScope{
Name: "test/code/path", Name: "otel/test/code/path1",
Version: "v0.1.0", Version: "v0.1.1",
}
pbScope2 = &cpb.InstrumentationScope{
Name: "otel/test/code/path2",
Version: "v0.2.2",
} }
res = resource.NewWithAttributes( res = resource.NewWithAttributes(
semconv.SchemaURL, semconv.SchemaURL,
semconv.ServiceName("test server"), semconv.ServiceName("service1"),
semconv.ServiceVersion("v0.1.0"), semconv.ServiceVersion("v0.1.1"),
) )
res2 = resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("service2"),
semconv.ServiceVersion("v0.2.2"),
)
resList = []*resource.Resource{res, res2}
pbRes = &rpb.Resource{ pbRes = &rpb.Resource{
Attributes: []*cpb.KeyValue{ Attributes: []*cpb.KeyValue{
{ {
Key: "service.name", Key: "service.name",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "test server"}, Value: &cpb.AnyValue_StringValue{StringValue: "service1"},
}, },
}, },
{ {
Key: "service.version", Key: "service.version",
Value: &cpb.AnyValue{ Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"}, Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.1"},
},
},
},
}
pbRes2 = &rpb.Resource{
Attributes: []*cpb.KeyValue{
{
Key: "service.name",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "service2"},
},
},
{
Key: "service.version",
Value: &cpb.AnyValue{
Value: &cpb.AnyValue_StringValue{StringValue: "v0.2.2"},
}, },
}, },
}, },
@ -105,75 +138,79 @@ var (
records = func() []log.Record { records = func() []log.Record {
var out []log.Record var out []log.Record
out = append(out, logtest.RecordFactory{ for _, r := range resList {
Timestamp: ts, for _, s := range scopeList {
ObservedTimestamp: obs, out = append(out, logtest.RecordFactory{
Severity: sevA, Timestamp: ts,
SeverityText: "A", ObservedTimestamp: obs,
Body: bodyA, Severity: sevC,
Attributes: []api.KeyValue{alice}, SeverityText: "C",
TraceID: trace.TraceID(traceIDA), Body: bodyC,
SpanID: trace.SpanID(spanIDA), Attributes: []api.KeyValue{tom},
TraceFlags: trace.TraceFlags(flagsA), TraceID: trace.TraceID(traceIDC),
InstrumentationScope: &scope, SpanID: trace.SpanID(spanIDC),
Resource: res, TraceFlags: trace.TraceFlags(flagsC),
}.NewRecord()) InstrumentationScope: &s,
Resource: r,
}.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevA, Severity: sevC,
SeverityText: "A", SeverityText: "C",
Body: bodyA, Body: bodyC,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDA), TraceID: trace.TraceID(traceIDC),
SpanID: trace.SpanID(spanIDA), SpanID: trace.SpanID(spanIDC),
TraceFlags: trace.TraceFlags(flagsA), TraceFlags: trace.TraceFlags(flagsC),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{alice}, Attributes: []api.KeyValue{tom},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: ts, Timestamp: ts,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
out = append(out, logtest.RecordFactory{ out = append(out, logtest.RecordFactory{
Timestamp: negativeTs, Timestamp: negativeTs,
ObservedTimestamp: obs, ObservedTimestamp: obs,
Severity: sevB, Severity: sevD,
SeverityText: "B", SeverityText: "D",
Body: bodyB, Body: bodyD,
Attributes: []api.KeyValue{bob}, Attributes: []api.KeyValue{jerry},
TraceID: trace.TraceID(traceIDB), TraceID: trace.TraceID(traceIDD),
SpanID: trace.SpanID(spanIDB), SpanID: trace.SpanID(spanIDD),
TraceFlags: trace.TraceFlags(flagsB), TraceFlags: trace.TraceFlags(flagsD),
InstrumentationScope: &scope, InstrumentationScope: &s,
Resource: res, Resource: r,
}.NewRecord()) }.NewRecord())
}
}
return out return out
}() }()
@ -182,76 +219,90 @@ var (
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevA, SeverityNumber: pbSevC,
SeverityText: "A", SeverityText: "C",
Body: pbBodyA, Body: pbBodyC,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsA), Flags: uint32(flagsC),
TraceId: traceIDA, TraceId: traceIDC,
SpanId: spanIDA, SpanId: spanIDC,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbAlice}, Attributes: []*cpb.KeyValue{pbTom},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: uint64(ts.UnixNano()), TimeUnixNano: uint64(ts.UnixNano()),
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
{ {
TimeUnixNano: 0, TimeUnixNano: 0,
ObservedTimeUnixNano: uint64(obs.UnixNano()), ObservedTimeUnixNano: uint64(obs.UnixNano()),
SeverityNumber: pbSevB, SeverityNumber: pbSevD,
SeverityText: "B", SeverityText: "D",
Body: pbBodyB, Body: pbBodyD,
Attributes: []*cpb.KeyValue{pbBob}, Attributes: []*cpb.KeyValue{pbJerry},
Flags: uint32(flagsB), Flags: uint32(flagsD),
TraceId: traceIDB, TraceId: traceIDD,
SpanId: spanIDB, SpanId: spanIDD,
}, },
} }
pbScopeLogs = &lpb.ScopeLogs{ pbScopeLogsList = []*lpb.ScopeLogs{
Scope: pbScope, {
SchemaUrl: semconv.SchemaURL, Scope: pbScope,
LogRecords: pbLogRecords, SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
{
Scope: pbScope2,
SchemaUrl: semconv.SchemaURL,
LogRecords: pbLogRecords,
},
} }
pbResourceLogs = &lpb.ResourceLogs{ pbResourceLogsList = []*lpb.ResourceLogs{
Resource: pbRes, {
SchemaUrl: semconv.SchemaURL, Resource: pbRes,
ScopeLogs: []*lpb.ScopeLogs{pbScopeLogs}, SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
{
Resource: pbRes2,
SchemaUrl: semconv.SchemaURL,
ScopeLogs: pbScopeLogsList,
},
} }
) )
func TestResourceLogs(t *testing.T) { func TestResourceLogs(t *testing.T) {
want := []*lpb.ResourceLogs{pbResourceLogs} want := pbResourceLogsList
assert.Equal(t, want, ResourceLogs(records)) assert.ElementsMatch(t, want, ResourceLogs(records))
} }
func TestSeverityNumber(t *testing.T) { func TestSeverityNumber(t *testing.T) {