Skip to content

Commit

Permalink
add metrics APIs and corresponding otel adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
lucix-aws committed Sep 13, 2024
1 parent 868fb89 commit 5584fd9
Show file tree
Hide file tree
Showing 27 changed files with 1,124 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ private void init() {
putContext("fmt.Errorf", SmithyGoDependency.FMT.func("Errorf"));
putContext("errors.As", SmithyGoDependency.ERRORS.func("As"));
putContext("context.Context", SmithyGoDependency.CONTEXT.func("Context"));
putContext("time.Now", SmithyGoDependency.TIME.func("Now"));

if (!innerWriter) {
packageDocs = new GoWriter(this.fullPackageName, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.go.codegen.auth.AuthSchemeResolverGenerator;
import software.amazon.smithy.go.codegen.auth.GetIdentityMiddlewareGenerator;
Expand All @@ -38,6 +37,7 @@
import software.amazon.smithy.go.codegen.integration.ClientMemberResolver;
import software.amazon.smithy.go.codegen.integration.ConfigFieldResolver;
import software.amazon.smithy.go.codegen.integration.GoIntegration;
import software.amazon.smithy.go.codegen.integration.OperationMetricsStruct;
import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.knowledge.ServiceIndex;
Expand Down Expand Up @@ -95,7 +95,7 @@ public void run() {
private GoWriter.Writable generate() {
return GoWriter.ChainWritable.of(
generateMetadata(),
generateObservabilityHelpers(),
generateObservabilityComponents(),
generateClient(),
generateNew(),
generateGetOptions(),
Expand All @@ -105,16 +105,19 @@ private GoWriter.Writable generate() {
).compose();
}

private GoWriter.Writable generateObservabilityHelpers() {
private GoWriter.Writable generateObservabilityComponents() {
return goTemplate("""
$operationMetrics:W
func operationTracer(p $tracerProvider:T) $tracer:T {
return p.Tracer($scope:S)
}
""",
Map.of(
"tracerProvider", SmithyGoDependency.SMITHY_TRACING.interfaceSymbol("TracerProvider"),
"tracer", SmithyGoDependency.SMITHY_TRACING.interfaceSymbol("Tracer"),
"scope", settings.getModuleName()
"scope", settings.getModuleName(),
"operationMetrics", new OperationMetricsStruct(settings.getModuleName())
));
}

Expand Down Expand Up @@ -339,8 +342,16 @@ func resolveAuthSchemes(options *Options) {
@SuppressWarnings("checkstyle:LineLength")
private GoWriter.Writable generateInvokeOperation() {
return goTemplate("""
func (c *Client) invokeOperation(ctx $context.Context:T, opID string, params interface{}, optFns []func(*Options), stackFns ...func($stack:P, Options) error) (result interface{}, metadata $metadata:T, err error) {
ctx = $clearStackValues:T(ctx)
$middleware:D $tracing:D
func (c *Client) invokeOperation(
ctx context.Context, opID string, params interface{}, optFns []func(*Options), stackFns ...func(*middleware.Stack, Options) error,
) (
result interface{}, metadata middleware.Metadata, err error,
) {
ctx = middleware.ClearStackValues(ctx)
ctx = middleware.WithServiceID(ctx, ServiceID)
ctx = middleware.WithOperationName(ctx, opID)
$newStack:W
options := c.options.Copy()
$resolvers:W
Expand All @@ -363,24 +374,30 @@ private GoWriter.Writable generateInvokeOperation() {
}
}
ctx, err = withOperationMetrics(ctx, options.MeterProvider)
if err != nil {
return nil, metadata, err
}
tracer := operationTracer(options.TracerProvider)
spanName := $fmt.Sprintf:T("$tracingServiceId:L.%s", opID)
spanName := fmt.Sprintf("%s.%s", ServiceID, opID)
ctx = $withOperationTracer:T(ctx, tracer)
ctx = tracing.WithOperationTracer(ctx, tracer)
ctx, span := tracer.StartSpan(ctx, spanName, func (o $spanOptions:P) {
o.Kind = $spanKindClient:T
ctx, span := tracer.StartSpan(ctx, spanName, func (o *tracing.SpanOptions) {
o.Kind = tracing.SpanKindClient
o.Properties.Set("rpc.system", "aws-api")
o.Properties.Set("rpc.method", opID)
o.Properties.Set("rpc.service", $tracingServiceId:S)
o.Properties.Set("rpc.service", ServiceID)
})
defer startMetricTimer(ctx, "client.call.duration")()
defer span.End()
handler := $newClientHandler:T(options.HTTPClient)
decorated := $decorateHandler:T(handler, stack)
decorated := middleware.DecorateHandler(handler, stack)
result, metadata, err = decorated.Handle(ctx, params)
if err != nil {
span.SetProperty("error.go.type", $fmt.Sprintf:T("%T", err))
span.SetProperty("error.go.type", fmt.Sprintf("%T", err))
span.SetProperty("error.go.error", err.Error())
var aerr smithy.APIError
Expand All @@ -399,18 +416,17 @@ private GoWriter.Writable generateInvokeOperation() {
span.SetProperty("error", err != nil)
if err == nil {
span.SetStatus($spanStatusOK:T)
span.SetStatus(tracing.SpanStatusOK)
} else {
span.SetStatus($spanStatusError:T)
span.SetStatus(tracing.SpanStatusError)
}
return result, metadata, err
}
""",
MapUtils.of(
"stack", SmithyGoTypes.Middleware.Stack,
"metadata", SmithyGoTypes.Middleware.Metadata,
"clearStackValues", SmithyGoTypes.Middleware.ClearStackValues,
"middleware", SmithyGoDependency.SMITHY_MIDDLEWARE,
"tracing", SmithyGoDependency.SMITHY_TRACING,
"newStack", generateNewStack(),
"operationError", SmithyGoTypes.Smithy.OperationError,
"resolvers", GoWriter.ChainWritable.of(
Expand All @@ -425,16 +441,7 @@ private GoWriter.Writable generateInvokeOperation() {
ConfigFieldResolver.Target.FINALIZATION
).map(this::generateConfigFieldResolver).toList()
).compose(),
"newClientHandler", SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("NewClientHandler"),
"decorateHandler", SmithyGoDependency.SMITHY_MIDDLEWARE.func("DecorateHandler")
),
Map.of(
"tracingServiceId", getTracingServiceId(),
"spanOptions", SmithyGoDependency.SMITHY_TRACING.struct("SpanOptions"),
"spanKindClient", SmithyGoDependency.SMITHY_TRACING.constSymbol("SpanKindClient"),
"withOperationTracer", SmithyGoDependency.SMITHY_TRACING.constSymbol("WithOperationTracer"),
"spanStatusOK", SmithyGoDependency.SMITHY_TRACING.constSymbol("SpanStatusOK"),
"spanStatusError", SmithyGoDependency.SMITHY_TRACING.constSymbol("SpanStatusError")
"newClientHandler", SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("NewClientHandler")
));
}

Expand Down Expand Up @@ -497,10 +504,4 @@ func addProtocolFinalizerMiddlewares(stack $P, options $L, operation string) err
SignRequestMiddlewareGenerator.generateAddToProtocolFinalizers()
).compose(false));
}

private String getTracingServiceId() {
return service.hasTrait(ServiceTrait.class)
? service.expectTrait(ServiceTrait.class).getSdkId().replaceAll("\\s", "")
: service.getId().getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public final class SmithyGoDependency {
public static final GoDependency SMITHY_ENDPOINTS = smithy("endpoints", "smithyendpoints");
public static final GoDependency SMITHY_ENDPOINT_RULESFN = smithy("endpoints/private/rulesfn");
public static final GoDependency SMITHY_TRACING = smithy("tracing");
public static final GoDependency SMITHY_METRICS = smithy("metrics");

public static final GoDependency GO_JMESPATH = goJmespath(null);
public static final GoDependency MATH = stdlib("math");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ private GoWriter.Writable generateBody() {
return out, metadata, $fmt.Errorf:T("no identity resolver")
}
identity, err := resolver.GetIdentity(innerCtx, rscheme.IdentityProperties)
identity, err := timeOperationMetric(ctx, "client.call.resolve_identity_duration",
func() ($identity:T, error) {
return resolver.GetIdentity(innerCtx, rscheme.IdentityProperties)
},
func (o $recordMetricOptions:P) {
o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID())
})
if err != nil {
return out, metadata, $fmt.Errorf:T("get identity: %w", err)
}
Expand All @@ -89,7 +95,9 @@ private GoWriter.Writable generateBody() {
return next.HandleFinalize(ctx, in)
""",
MapUtils.of(
"startSpan", SmithyGoDependency.SMITHY_TRACING.func("StartSpan")
"startSpan", SmithyGoDependency.SMITHY_TRACING.func("StartSpan"),
"identity", SmithyGoDependency.SMITHY_AUTH.interfaceSymbol("Identity"),
"recordMetricOptions", SmithyGoDependency.SMITHY_METRICS.struct("RecordMetricOptions")
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ private GoWriter.Writable generateBody() {
return out, metadata, $fmt.Errorf:T("no signer")
}
if err := signer.SignRequest(ctx, req, identity, rscheme.SignerProperties); err != nil {
_, err = timeOperationMetric(ctx, "client.call.signing_duration", func() (any, error) {
return nil, signer.SignRequest(ctx, req, identity, rscheme.SignerProperties)
}, func(o $recordMetricOptions:P) {
o.Properties.Set("auth.scheme_id", rscheme.Scheme.SchemeID())
})
if err != nil {
return out, metadata, $fmt.Errorf:T("sign request: %w", err)
}
Expand All @@ -96,7 +101,8 @@ private GoWriter.Writable generateBody() {
MapUtils.of(
// FUTURE(#458) protocol generator should specify the transport type
"request", SmithyGoTypes.Transport.Http.Request,
"startSpan", SmithyGoDependency.SMITHY_TRACING.func("StartSpan")
"startSpan", SmithyGoDependency.SMITHY_TRACING.func("StartSpan"),
"recordMetricOptions", SmithyGoDependency.SMITHY_METRICS.struct("RecordMetricOptions")
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,12 @@ private GoWriter.Writable generateAssertResolver() {
private GoWriter.Writable generateResolveEndpoint() {
return goTemplate("""
params := bindEndpointParams(ctx, getOperationInput(ctx), m.options)
endpt, err := m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params)
endpt, err := timeOperationMetric(ctx, "client.call.resolve_endpoint_duration",
func() (smithyendpoints.Endpoint, error) {
return m.options.EndpointResolverV2.ResolveEndpoint(ctx, *params)
})
if err != nil {
return out, metadata, $1T("failed to resolve service endpoint, %w", err)
return out, metadata, $fmt.Errorf:T("failed to resolve service endpoint, %w", err)
}
span.SetProperty("operation.resolved_endpoint", endpt.URI.String())
Expand All @@ -145,13 +148,12 @@ private GoWriter.Writable generateResolveEndpoint() {
}
req.URL.Scheme = endpt.URI.Scheme
req.URL.Host = endpt.URI.Host
req.URL.Path = $2T(endpt.URI.Path, req.URL.Path)
req.URL.RawPath = $2T(endpt.URI.RawPath, req.URL.RawPath)
req.URL.Path = $1T(endpt.URI.Path, req.URL.Path)
req.URL.RawPath = $1T(endpt.URI.RawPath, req.URL.RawPath)
for k := range endpt.Headers {
req.Header.Set(k, endpt.Headers.Get(k))
}
""",
GoStdlibTypes.Fmt.Errorf,
SmithyGoTypes.Transport.Http.JoinPath);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ private void generateOperationSerializerMiddleware(GenerationContext context, Op

writer.write(goTemplate("""
_, span := $T(ctx, "OperationSerializer")
endTimer := startMetricTimer(ctx, "client.call.serialization_duration")
defer endTimer()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));

Expand Down Expand Up @@ -358,6 +360,7 @@ private void generateOperationSerializerMiddleware(GenerationContext context, Op
writer.write("in.Request = request");
writer.write("");

writer.write("endTimer()");
writer.write("span.End()");
writer.write("return next.$L(ctx, in)", generator.getHandleMethodName());
});
Expand Down Expand Up @@ -395,6 +398,7 @@ private void generateOperationDeserializerMiddleware(GenerationContext context,

writer.write(goTemplate("""
_, span := $T(ctx, "OperationDeserializer")
defer startMetricTimer(ctx, "client.call.deserialization_duration")()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ private void generateOperationSerializer(GenerationContext context, OperationSha

writer.write(goTemplate("""
_, span := $T(ctx, "OperationSerializer")
endTimer := startMetricTimer(ctx, "client.call.serialization_duration")
defer endTimer()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));

Expand Down Expand Up @@ -220,6 +222,7 @@ private void generateOperationSerializer(GenerationContext context, OperationSha
writer.write("in.Request = request");

writer.write("");
writer.write("endTimer()");
writer.write("span.End()");
writer.write("return next.$L(ctx, in)", generator.getHandleMethodName());
});
Expand Down Expand Up @@ -341,6 +344,7 @@ private void generateOperationDeserializer(GenerationContext context, OperationS

writer.write(goTemplate("""
_, span := $T(ctx, "OperationDeserializer")
defer startMetricTimer(ctx, "client.call.deserialization_duration")()
defer span.End()
""", SMITHY_TRACING.func("StartSpan")));

Expand Down Expand Up @@ -378,7 +382,6 @@ private void generateOperationDeserializer(GenerationContext context, OperationS
}
writer.write("");

writer.write("span.End()");
writer.write("return out, metadata, err");
});
writer.write("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,32 @@ public class ObservabilityOptions implements GoIntegration {
.documentation("The client tracer provider.")
.build();

private static final ConfigField METER_PROVIDER = ConfigField.builder()
.name("MeterProvider")
.type(SmithyGoDependency.SMITHY_METRICS.interfaceSymbol("MeterProvider"))
.documentation("The client meter provider.")
.build();

private static final ConfigFieldResolver RESOLVE_TRACER_PROVIDER = ConfigFieldResolver.builder()
.resolver(buildPackageSymbol("resolveTracerProvider"))
.location(ConfigFieldResolver.Location.CLIENT)
.target(ConfigFieldResolver.Target.INITIALIZATION)
.build();

// TODO
// private static final ConfigField METER_PROVIDER = ConfigField.builder()
// .name("MeterProvider")
// .type(SmithyGoDependency.SMITHY_METRICS.interfaceSymbol("MeterProvider"))
// .documentation("The client meter provider.")
// .build();
private static final ConfigFieldResolver RESOLVE_METER_PROVIDER = ConfigFieldResolver.builder()
.resolver(buildPackageSymbol("resolveMeterProvider"))
.location(ConfigFieldResolver.Location.CLIENT)
.target(ConfigFieldResolver.Target.INITIALIZATION)
.build();

@Override
public List<RuntimeClientPlugin> getClientPlugins() {
return List.of(
RuntimeClientPlugin.builder()
.addConfigField(TRACER_PROVIDER)
.addConfigField(METER_PROVIDER)
.addConfigFieldResolver(RESOLVE_TRACER_PROVIDER)
.addConfigFieldResolver(RESOLVE_METER_PROVIDER)
.build()
);
}
Expand All @@ -63,6 +70,15 @@ func resolveTracerProvider(options *Options) {
options.TracerProvider = &$T{}
}
}
""", SmithyGoDependency.SMITHY_TRACING.struct("NopTracerProvider")));
func resolveMeterProvider(options *Options) {
if options.MeterProvider == nil {
options.MeterProvider = $T{}
}
}
""",
SmithyGoDependency.SMITHY_TRACING.struct("NopTracerProvider"),
SmithyGoDependency.SMITHY_METRICS.struct("NopMeterProvider")
));
}
}
Loading

0 comments on commit 5584fd9

Please sign in to comment.