-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Write prototype implementation and benchmarks
- Loading branch information
Showing
12 changed files
with
659 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Copyright 2022 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package benchmark | ||
|
||
import ( | ||
"context" | ||
"io" | ||
"testing" | ||
"time" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/log" | ||
"go.opentelemetry.io/otel/log/noop" | ||
) | ||
|
||
var ( | ||
ctx = context.Background() | ||
testTimestamp = time.Date(1988, time.November, 17, 0, 0, 0, 0, time.UTC) | ||
testBody = "log message" | ||
testSeverity = log.SeverityInfo | ||
testFloat = 1.2345 | ||
testString = "7e3b3b2aaeff56a7108fe11e154200dd/7819479873059528190" | ||
testInt = 32768 | ||
testBool = true | ||
) | ||
|
||
// These benchmarks are based on slog/internal/benchmarks. | ||
// | ||
// They test a complete log record, from the user's call to its return. | ||
// | ||
// WriterLogger is an optimistic version of a real logger, doing real-world | ||
// tasks as fast as possible . This gives us an upper bound | ||
// on handler performance, so we can evaluate the (logger-independent) core | ||
// activity of the package in an end-to-end context without concern that a | ||
// slow logger implementation is skewing the results. The writerLogger | ||
// allocates memory only when using strconv. | ||
func BenchmarkEmit(b *testing.B) { | ||
for _, tc := range []struct { | ||
name string | ||
logger log.Logger | ||
}{ | ||
{"noop", noop.Logger{}}, | ||
{"writer", &writerLogger{w: io.Discard}}, | ||
} { | ||
b.Run(tc.name, func(b *testing.B) { | ||
for _, call := range []struct { | ||
name string | ||
f func() | ||
}{ | ||
{ | ||
"no attrs", | ||
func() { | ||
r := log.Record{Timestamp: testTimestamp, Severity: testSeverity, Body: testBody} | ||
tc.logger.Emit(ctx, r) | ||
}, | ||
}, | ||
{ | ||
"3 attrs", | ||
func() { | ||
r := log.Record{Timestamp: testTimestamp, Severity: testSeverity, Body: testBody} | ||
r.AddAttributes( | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
) | ||
tc.logger.Emit(ctx, r) | ||
}, | ||
}, | ||
{ | ||
// The number should match nAttrsInline in record.go. | ||
// This should exercise the code path where no allocations | ||
// happen in Record or Attr. If there are allocations, they | ||
// should only be from strconv used in writerLogger. | ||
"5 attrs", | ||
func() { | ||
r := log.Record{Timestamp: testTimestamp, Severity: testSeverity, Body: testBody} | ||
r.AddAttributes( | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
) | ||
tc.logger.Emit(ctx, r) | ||
}, | ||
}, | ||
{ | ||
"10 attrs", | ||
func() { | ||
r := log.Record{Timestamp: testTimestamp, Severity: testSeverity, Body: testBody} | ||
r.AddAttributes( | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
) | ||
tc.logger.Emit(ctx, r) | ||
}, | ||
}, | ||
{ | ||
"40 attrs", | ||
func() { | ||
r := log.Record{Timestamp: testTimestamp, Severity: testSeverity, Body: testBody} | ||
r.AddAttributes( | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
attribute.String("string", testString), | ||
attribute.Float64("float", testFloat), | ||
attribute.Int("int", testInt), | ||
attribute.Bool("bool", testBool), | ||
attribute.String("string", testString), | ||
) | ||
tc.logger.Emit(ctx, r) | ||
}, | ||
}, | ||
} { | ||
b.Run(call.name, func(b *testing.B) { | ||
b.ReportAllocs() | ||
for i := 0; i < b.N; i++ { | ||
call.f() | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Copyright 2022 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package benchmark | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"strconv" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/log" | ||
"go.opentelemetry.io/otel/log/embedded" | ||
) | ||
|
||
// writerLogger is a logger that writes to a provided io.Writer without any locking. | ||
// It is intended to represent a high-performance logger that synchronously | ||
// writes text. | ||
type writerLogger struct { | ||
embedded.Logger | ||
w io.Writer | ||
} | ||
|
||
func (l *writerLogger) Emit(_ context.Context, r log.Record) { | ||
if !r.Timestamp.IsZero() { | ||
l.write("timestamp=") | ||
l.write(strconv.FormatInt(r.Timestamp.Unix(), 10)) | ||
l.write(" ") | ||
} | ||
l.write("severity=") | ||
l.write(strconv.FormatInt(int64(r.Severity), 10)) | ||
l.write(" ") | ||
l.write("body=") | ||
l.write(r.Body) | ||
r.Attributes(func(kv attribute.KeyValue) bool { | ||
l.write(" ") | ||
l.write(string(kv.Key)) | ||
l.write("=") | ||
l.appendValue(kv.Value) | ||
return true | ||
}) | ||
l.write("\n") | ||
} | ||
|
||
func (l *writerLogger) appendValue(v attribute.Value) { | ||
switch v.Type() { | ||
case attribute.STRING: | ||
l.write(v.AsString()) | ||
case attribute.INT64: | ||
l.write(strconv.FormatInt(v.AsInt64(), 10)) // strconv.FormatInt allocates memory. | ||
case attribute.FLOAT64: | ||
l.write(strconv.FormatFloat(v.AsFloat64(), 'g', -1, 64)) // strconv.FormatFloat allocates memory. | ||
case attribute.BOOL: | ||
l.write(strconv.FormatBool(v.AsBool())) | ||
default: | ||
panic(fmt.Sprintf("unhandled attribute type: %s", v.Type())) | ||
} | ||
} | ||
|
||
func (l *writerLogger) write(s string) { | ||
_, _ = io.WriteString(l.w, s) | ||
} |
Oops, something went wrong.