Skip to content

Commit

Permalink
feat: Makes it easier to use tracing from non ASP.NET Core applications
Browse files Browse the repository at this point in the history
  • Loading branch information
amanda-tarafa committed May 26, 2021
1 parent b427a00 commit ad42b2e
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@

using Google.Api.Gax;
using Google.Cloud.Diagnostics.Common;
using Google.Cloud.Trace.V1;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Net.Http;

#if NETCOREAPP3_1
namespace Google.Cloud.Diagnostics.AspNetCore3
Expand Down Expand Up @@ -112,32 +110,28 @@ public static IServiceCollection AddGoogleTrace(
var serviceOptions = new TraceServiceOptions();
setupAction(serviceOptions);

var client = serviceOptions.Client ?? TraceServiceClient.Create();
var options = serviceOptions.Options ?? TraceOptions.Create();
var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;
var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId);
services.AddGoogleTrace(SetupCommonOptions);
void SetupCommonOptions(Common.TraceServiceOptions commonOptions)
{
commonOptions.ProjectId = serviceOptions.ProjectId;
commonOptions.Options = serviceOptions.Options;
commonOptions.Client = serviceOptions.Client;
}

var consumer = ManagedTracer.CreateConsumer(client, options);
var tracerFactory = ManagedTracer.CreateFactory(projectId, consumer, options);
var traceFallbackPredicate = serviceOptions.TraceFallbackPredicate ?? TraceDecisionPredicate.Default;

// We use TryAdd... here to allow user code to inject their own trace context provider
// and matching trace context response propagator. We use Google trace header otherwise.
services.TryAddScoped<ITraceContext>(GoogleTraceHeaderContextProvider);
services.TryAddSingleton<Action<HttpResponse, ITraceContext>>(GoogleTraceHeaderResponsePropagator);

services.AddSingleton(tracerFactory);
// Obsolete: Adding this for backwards compatibility in case someone is using the old factory type.
// The new and prefered factory type is Func<ITraceContext, IManagedTracer>.
services.AddSingleton<Func<TraceHeaderContext, IManagedTracer>>(tracerFactory);
services.AddSingleton<Func<TraceHeaderContext, IManagedTracer>>(sp => sp.GetRequiredService<Func<ITraceContext, IManagedTracer>>());

services.AddHttpContextAccessor();
services.AddSingleton(ManagedTracer.CreateDelegatingTracer(ContextTracerManager.GetCurrentTracer));
services.AddTransient<ICloudTraceNameProvider, DefaultCloudTraceNameProvider>();

// On .Net Standard 2.0 or higher, we can use the System.Net.Http.IHttpClientFactory defined in Microsoft.Extensions.Http,
// for which we need a DelagatingHandler with no InnerHandler set. This is the recommended way.
// It should be registered as follows.
services.AddTransient(UnchainedTraceHeaderPropagatingHandlerFactory);

// This is to be used for explicitly creating an HttpClient instance. Valid for all platforms but subject to
// issues with HttpClient lifetime as described in
// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests.
Expand Down Expand Up @@ -173,15 +167,5 @@ internal static void GoogleTraceHeaderResponsePropagator(HttpResponse response,
var googleHeader = TraceHeaderContext.Create(traceContext.TraceId, traceContext.SpanId ?? 0, traceContext.ShouldTrace);
response.Headers.Add(TraceHeaderContext.TraceHeader, googleHeader.ToString());
}

/// <summary>
/// Returns an <see cref="UnchainedTraceHeaderPropagatingHandler"/> configured with the user specified trace context
/// outgoing propagator. If user code has not specified a trace context outgoing propagator, the Google header will
/// be propagated.
/// </summary>
internal static UnchainedTraceHeaderPropagatingHandler UnchainedTraceHeaderPropagatingHandlerFactory(IServiceProvider serviceProvider) =>
serviceProvider.GetService<Action<HttpRequestMessage, ITraceContext>>() is Action<HttpRequestMessage, ITraceContext> traceContextOutgoingPropagator ?
new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer, traceContextOutgoingPropagator) :
new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Api.Gax;
using Google.Cloud.Trace.V1;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net.Http;

namespace Google.Cloud.Diagnostics.Common
{
/// <summary>
/// Extension methods for registering Google Cloud Trace for
/// dependency injection.
/// </summary>
public static class CloudTraceExtensions
{
/// <summary>
/// Cofigures Google Cloud Trace for dependency injection.
/// </summary>
public static IServiceCollection AddGoogleTrace(
this IServiceCollection services, Action<TraceServiceOptions> setupAction)
{
GaxPreconditions.CheckNotNull(services, nameof(services));
GaxPreconditions.CheckNotNull(setupAction, nameof(setupAction));

var serviceOptions = new TraceServiceOptions();
setupAction(serviceOptions);

var client = serviceOptions.Client ?? TraceServiceClient.Create();
var options = serviceOptions.Options ?? TraceOptions.Create();
var projectId = Project.GetAndCheckProjectId(serviceOptions.ProjectId);

var consumer = ManagedTracer.CreateConsumer(client, options);
var tracerFactory = ManagedTracer.CreateFactory(projectId, consumer, options);

services.AddSingleton(tracerFactory);
services.AddSingleton(ManagedTracer.CreateDelegatingTracer(ContextTracerManager.GetCurrentTracer));

// On .Net Standard 2.0 or higher, we can use the System.Net.Http.IHttpClientFactory defined in Microsoft.Extensions.Http,
// for which we need a DelagatingHandler with no InnerHandler set. This is the recommended way.
// It should be registered as follows.
return services.AddTransient(UnchainedTraceHeaderPropagatingHandlerFactory);
}

/// <summary>
/// Returns an <see cref="UnchainedTraceHeaderPropagatingHandler"/> configured with the user specified trace context
/// outgoing propagator. If user code has not specified a trace context outgoing propagator, the Google header will
/// be propagated.
/// </summary>
internal static UnchainedTraceHeaderPropagatingHandler UnchainedTraceHeaderPropagatingHandlerFactory(IServiceProvider serviceProvider) =>
serviceProvider.GetService<Action<HttpRequestMessage, ITraceContext>>() is Action<HttpRequestMessage, ITraceContext> traceContextOutgoingPropagator ?
new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer, traceContextOutgoingPropagator) :
new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Google.Cloud.Trace.V1;

namespace Google.Cloud.Diagnostics.Common
{
/// <summary>
/// Options for initializing tracing.
/// </summary>
public sealed class TraceServiceOptions
{
/// <summary>
/// The Google Cloud Platform project ID. If unspecified and running on GAE or GCE the project
/// ID will be detected from the platform.
/// </summary>
public string ProjectId { get; set; }

/// <summary>
/// Trace options. May be null.
/// </summary>
public TraceOptions Options { get; set; }

/// <summary>
/// A client to send traces with. May be null.
/// </summary>
public TraceServiceClient Client { get; set; }
}
}

0 comments on commit ad42b2e

Please sign in to comment.