Skip to content

Commit

Permalink
Merge branch 'main' into replace-ot-wid-otel-hotrod
Browse files Browse the repository at this point in the history
Signed-off-by: Afzal <94980910+afzalbin64@users.noreply.github.com>
  • Loading branch information
afzalbin64 authored Aug 12, 2023
2 parents 4f454ea + 5b9e7e1 commit cefff81
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ updates:
schedule:
interval: "weekly"
groups:
otel:
all:
patterns:
- "opentelemetry-*"
- "*"
2 changes: 1 addition & 1 deletion cmd/tracegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
Expand Down
8 changes: 4 additions & 4 deletions docker-compose/monitor/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
backoff==2.2.1
certifi==2023.7.22
charset-normalizer==3.2.0
Deprecated==1.2.13
Deprecated==1.2.14
googleapis-common-protos==1.60.0
grpcio==1.56.2
idna==3.4
Expand All @@ -16,6 +16,6 @@ protobuf==4.23.4
requests==2.31.0
six==1.16.0
thrift==0.16.0
typing_extensions==4.4.0
urllib3==1.26.12
wrapt==1.14.1
typing_extensions==4.7.1
urllib3==2.0.4
wrapt==1.15.0
2 changes: 1 addition & 1 deletion examples/hotrod/pkg/tracing/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

Expand Down
2 changes: 1 addition & 1 deletion examples/hotrod/pkg/tracing/rpcmetrics/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"

"github.com/jaegertracing/jaeger/pkg/metrics"
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ require (
go.uber.org/atomic v1.11.0
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.25.0
golang.org/x/net v0.13.0
golang.org/x/sys v0.10.0
golang.org/x/net v0.14.0
golang.org/x/sys v0.11.0
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
Expand Down Expand Up @@ -172,8 +172,8 @@ require (
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/text v0.12.0 // indirect
google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -827,8 +827,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -916,8 +916,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1017,8 +1017,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -1032,8 +1032,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
6 changes: 4 additions & 2 deletions internal/tracegen/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Config struct {
Traces int
ChildSpans int
Attributes int
AttrKeys int
AttrValues int
Marshal bool
Debug bool
Expand All @@ -47,8 +48,9 @@ func (c *Config) Flags(fs *flag.FlagSet) {
fs.IntVar(&c.Workers, "workers", 1, "Number of workers (goroutines) to run")
fs.IntVar(&c.Traces, "traces", 1, "Number of traces to generate in each worker (ignored if duration is provided)")
fs.IntVar(&c.ChildSpans, "spans", 1, "Number of child spans to generate for each trace")
fs.IntVar(&c.Attributes, "attrs", 1, "Number of attributes to generate for each child span")
fs.IntVar(&c.AttrValues, "attr-values", 5, "Number of distinct values to allow for each attribute")
fs.IntVar(&c.Attributes, "attrs", 11, "Number of attributes to generate for each child span")
fs.IntVar(&c.AttrKeys, "attr-keys", 97, "Number of distinct attributes keys to use")
fs.IntVar(&c.AttrValues, "attr-values", 1000, "Number of distinct values to allow for each attribute")
fs.BoolVar(&c.Debug, "debug", false, "Whether to set DEBUG flag on the spans to force sampling")
fs.BoolVar(&c.Firehose, "firehose", false, "Whether to set FIREHOSE flag on the spans to skip indexing")
fs.DurationVar(&c.Pause, "pause", time.Microsecond, "How long to sleep before finishing each span. If set to 0s then a fake 123µs duration is used.")
Expand Down
6 changes: 4 additions & 2 deletions internal/tracegen/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type worker struct {

// internal counters
traceNo int
attrKeyNo int
attrValNo int
}

Expand Down Expand Up @@ -94,10 +95,11 @@ func (w *worker) simulateChildSpans(ctx context.Context, start time.Time, tracer
for c := 0; c < w.ChildSpans; c++ {
var attrs []attribute.KeyValue
for a := 0; a < w.Attributes; a++ {
key := fmt.Sprintf("attr_%02d", a)
key := fmt.Sprintf("attr_%02d", w.attrKeyNo)
val := fmt.Sprintf("val_%02d", w.attrValNo)
w.attrValNo = (w.attrValNo + 1) % w.AttrValues
attrs = append(attrs, attribute.String(key, val))
w.attrKeyNo = (w.attrKeyNo + 1) % w.AttrKeys
w.attrValNo = (w.attrValNo + 1) % w.AttrValues
}
opts := []trace.SpanStartOption{
trace.WithSpanKind(trace.SpanKindClient),
Expand Down
32 changes: 19 additions & 13 deletions model/converter/thrift/zipkin/to_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"errors"
"fmt"

"github.com/opentracing/opentracing-go/ext"
"go.opentelemetry.io/otel/trace"

"github.com/jaegertracing/jaeger/model"
"github.com/jaegertracing/jaeger/thrift-gen/zipkincore"
Expand All @@ -32,14 +32,20 @@ import (
const (
// UnknownServiceName is serviceName we give to model.Process if we cannot find it anywhere in a Zipkin span
UnknownServiceName = "unknown-service-name"
keySpanKind = "span.kind"
component = "component"
peerservice = "peer.service"
peerHostIPv4 = "peer.ipv4"
peerHostIPv6 = "peer.ipv6"
peerPort = "peer.port"
)

var (
coreAnnotations = map[string]string{
zipkincore.SERVER_RECV: string(ext.SpanKindRPCServerEnum),
zipkincore.SERVER_SEND: string(ext.SpanKindRPCServerEnum),
zipkincore.CLIENT_RECV: string(ext.SpanKindRPCClientEnum),
zipkincore.CLIENT_SEND: string(ext.SpanKindRPCClientEnum),
zipkincore.SERVER_RECV: trace.SpanKindServer.String(),
zipkincore.SERVER_SEND: trace.SpanKindServer.String(),
zipkincore.CLIENT_RECV: trace.SpanKindClient.String(),
zipkincore.CLIENT_SEND: trace.SpanKindClient.String(),
}

// Some tags on Zipkin spans really describe the process emitting them rather than an individual span.
Expand Down Expand Up @@ -165,13 +171,13 @@ func (td toDomain) transformSpan(zSpan *zipkincore.Span) []*model.Span {
}
// if the first span is a client span we create server span and vice-versa.
if result[0].IsRPCClient() {
s.Tags = []model.KeyValue{model.String(string(ext.SpanKind), string(ext.SpanKindRPCServerEnum))}
s.Tags = []model.KeyValue{model.String(keySpanKind, trace.SpanKindServer.String())}
s.StartTime = model.EpochMicrosecondsAsTime(uint64(sr.Timestamp))
if ss := td.findAnnotation(zSpan, zipkincore.SERVER_SEND); ss != nil {
s.Duration = model.MicrosecondsAsDuration(uint64(ss.Timestamp - sr.Timestamp))
}
} else {
s.Tags = []model.KeyValue{model.String(string(ext.SpanKind), string(ext.SpanKindRPCClientEnum))}
s.Tags = []model.KeyValue{model.String(keySpanKind, trace.SpanKindClient.String())}
s.StartTime = model.EpochMicrosecondsAsTime(uint64(cs.Timestamp))
if cr := td.findAnnotation(zSpan, zipkincore.CLIENT_RECV); cr != nil {
s.Duration = model.MicrosecondsAsDuration(uint64(cr.Timestamp - cs.Timestamp))
Expand Down Expand Up @@ -286,7 +292,7 @@ func (td toDomain) getTags(binAnnotations []*zipkincore.BinaryAnnotation, tagInc
switch annotation.Key {
case zipkincore.LOCAL_COMPONENT:
value := string(annotation.Value)
tag := model.String(string(ext.Component), value)
tag := model.String(component, value)
retMe = append(retMe, tag)
case zipkincore.SERVER_ADDR, zipkincore.CLIENT_ADDR, zipkincore.MESSAGE_ADDR:
retMe = td.getPeerTags(annotation.Host, retMe)
Expand Down Expand Up @@ -385,7 +391,7 @@ func (td toDomain) getLogFields(annotation *zipkincore.Annotation) []model.KeyVa
func (td toDomain) getSpanKindTag(annotations []*zipkincore.Annotation) (model.KeyValue, bool) {
for _, a := range annotations {
if spanKind, ok := coreAnnotations[a.Value]; ok {
return model.String(string(ext.SpanKind), spanKind), true
return model.String(keySpanKind, spanKind), true
}
}
return model.KeyValue{}, false
Expand All @@ -395,19 +401,19 @@ func (td toDomain) getPeerTags(endpoint *zipkincore.Endpoint, tags []model.KeyVa
if endpoint == nil {
return tags
}
tags = append(tags, model.String(string(ext.PeerService), endpoint.ServiceName))
tags = append(tags, model.String(peerservice, endpoint.ServiceName))
if endpoint.Ipv4 != 0 {
ipv4 := int64(uint32(endpoint.Ipv4))
tags = append(tags, model.Int64(string(ext.PeerHostIPv4), ipv4))
tags = append(tags, model.Int64(peerHostIPv4, ipv4))
}
if endpoint.Ipv6 != nil {
// Zipkin defines Ipv6 field as: "IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes()".
// https://github.com/openzipkin/zipkin-api/blob/master/thrift/zipkinCore.thrift#L305
tags = append(tags, model.Binary(string(ext.PeerHostIPv6), endpoint.Ipv6))
tags = append(tags, model.Binary(peerHostIPv6, endpoint.Ipv6))
}
if endpoint.Port != 0 {
port := int64(uint16(endpoint.Port))
tags = append(tags, model.Int64(string(ext.PeerPort), port))
tags = append(tags, model.Int64(peerPort, port))
}
return tags
}
31 changes: 18 additions & 13 deletions model/converter/thrift/zipkin/to_domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ import (
"github.com/gogo/protobuf/jsonpb"
"github.com/gogo/protobuf/proto"
"github.com/kr/pretty"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"

"github.com/jaegertracing/jaeger/model"
z "github.com/jaegertracing/jaeger/thrift-gen/zipkincore"
Expand Down Expand Up @@ -116,27 +115,33 @@ func TestToDomainWithDurationFromClientAnnotations(t *testing.T) {

func TestToDomainMultipleSpanKinds(t *testing.T) {
tests := []struct {
json string
tagFirst opentracing.Tag
tagSecond opentracing.Tag
json string
tagFirstKey string
tagSecondKey string
tagFirstVal trace.SpanKind
tagSecondVal trace.SpanKind
}{
{
json: `[{ "trace_id": -1, "id": 31, "annotations": [
{"value": "cs", "host": {"service_name": "bar", "ipv4": 23456}},
{"value": "sr", "timestamp": 1, "host": {"service_name": "bar", "ipv4": 23456}},
{"value": "ss", "timestamp": 2, "host": {"service_name": "bar", "ipv4": 23456}}
]}]`,
tagFirst: ext.SpanKindRPCClient,
tagSecond: ext.SpanKindRPCServer,
tagFirstKey: keySpanKind,
tagSecondKey: keySpanKind,
tagFirstVal: trace.SpanKindClient,
tagSecondVal: trace.SpanKindServer,
},
{
json: `[{ "trace_id": -1, "id": 31, "annotations": [
{"value": "sr", "host": {"service_name": "bar", "ipv4": 23456}},
{"value": "cs", "timestamp": 1, "host": {"service_name": "bar", "ipv4": 23456}},
{"value": "cr", "timestamp": 2, "host": {"service_name": "bar", "ipv4": 23456}}
]}]`,
tagFirst: ext.SpanKindRPCServer,
tagSecond: ext.SpanKindRPCClient,
tagFirstKey: keySpanKind,
tagSecondKey: keySpanKind,
tagFirstVal: trace.SpanKindServer,
tagSecondVal: trace.SpanKindClient,
},
}

Expand All @@ -146,13 +151,13 @@ func TestToDomainMultipleSpanKinds(t *testing.T) {

assert.Equal(t, 2, len(trace.Spans))
assert.Equal(t, 1, len(trace.Spans[0].Tags))
assert.Equal(t, test.tagFirst.Key, trace.Spans[0].Tags[0].Key)
assert.Equal(t, string(test.tagFirst.Value.(ext.SpanKindEnum)), trace.Spans[0].Tags[0].VStr)
assert.Equal(t, test.tagFirstKey, trace.Spans[0].Tags[0].Key)
assert.Equal(t, test.tagFirstVal.String(), trace.Spans[0].Tags[0].VStr)

assert.Equal(t, 1, len(trace.Spans[1].Tags))
assert.Equal(t, test.tagSecond.Key, trace.Spans[1].Tags[0].Key)
assert.Equal(t, test.tagSecondKey, trace.Spans[1].Tags[0].Key)
assert.Equal(t, time.Duration(1000), trace.Spans[1].Duration)
assert.Equal(t, string(test.tagSecond.Value.(ext.SpanKindEnum)), trace.Spans[1].Tags[0].VStr)
assert.Equal(t, test.tagSecondVal.String(), trace.Spans[1].Tags[0].VStr)
}
}

Expand Down
2 changes: 1 addition & 1 deletion plugin/storage/cassandra/spanstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

Expand Down

0 comments on commit cefff81

Please sign in to comment.