diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests.csproj b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests.csproj
index a4849c97fd17..95deb59484c2 100644
--- a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests.csproj
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests/Google.Cloud.Diagnostics.AspNetCore.IntegrationTests.csproj
@@ -19,6 +19,7 @@
+
diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/Google.Cloud.Diagnostics.AspNetCore.Snippets.csproj b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/Google.Cloud.Diagnostics.AspNetCore.Snippets.csproj
index 9fa422e2edaa..8b805a3ae0cb 100644
--- a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/Google.Cloud.Diagnostics.AspNetCore.Snippets.csproj
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/Google.Cloud.Diagnostics.AspNetCore.Snippets.csproj
@@ -20,6 +20,7 @@
+
diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/StandaloneTraceSnippets.cs b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/StandaloneTraceSnippets.cs
new file mode 100644
index 000000000000..9116a051ce8c
--- /dev/null
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Snippets/StandaloneTraceSnippets.cs
@@ -0,0 +1,109 @@
+// 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.ClientTesting;
+using Google.Cloud.Diagnostics.Common;
+using Google.Cloud.Diagnostics.Common.IntegrationTests;
+using Google.Protobuf.WellKnownTypes;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Google.Cloud.Diagnostics.AspNetCore.Snippets
+{
+ public class StandaloneTraceSnippets
+ {
+ private static readonly string ProjectId = TestEnvironment.GetTestProjectId();
+
+ private static readonly TraceEntryPolling s_polling = new TraceEntryPolling();
+
+ private readonly string _testId;
+
+ private readonly Timestamp _startTime;
+
+ public StandaloneTraceSnippets()
+ {
+ _testId = IdGenerator.FromDateTime();
+ _startTime = Timestamp.FromDateTime(DateTime.UtcNow);
+
+ // The rate limiter instance is static and only set once. If we do not reset it at the
+ // beginning of each tests the qps will not change. This is dependent on the tests not
+ // running in parallel.
+ RateLimiter.Reset();
+ }
+
+ // Sample: Configure
+ public static IHostBuilder CreateHostBuilder() =>
+ Host.CreateDefaultBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddGoogleTrace(options => options.ProjectId = ProjectId);
+ // Register other services here if you need them.
+ });
+ // End sample
+
+ [Fact]
+ public async Task TraceAsync()
+ {
+ // Naming it like an instance variable so that it looks like that on sample code.
+ IHost _host = null;
+
+ try
+ {
+ // Sample: Start
+ _host = CreateHostBuilder().Build();
+ await _host.StartAsync();
+ // End sample
+
+ // Sample: IncomingContext
+ ITraceContext traceContext = GetTraceContextFromIncomingRequest();
+ var tracerFactory = _host.Services.GetRequiredService>();
+ IManagedTracer tracer = tracerFactory(traceContext);
+ ContextTracerManager.SetCurrentTracer(tracer);
+ // End sample
+
+ // Let's just start a span with the test ID so we can find it faster.
+ // But we don't show this in sample code.
+ using (tracer.StartSpan(_testId))
+ {
+ // Sample: Trace
+ IManagedTracer currentTracer = _host.Services.GetRequiredService();
+ using (currentTracer.StartSpan("standalone_tracing"))
+ {
+ Console.WriteLine("Using Cloud Trace from a non ASP.NET Core app");
+ }
+ // End sample
+ }
+
+ var trace = s_polling.GetTrace(_testId, _startTime);
+ TraceEntryVerifiers.AssertParentChildSpan(trace, _testId, "standalone_tracing");
+ Assert.Equal(traceContext.TraceId, trace.TraceId);
+ }
+ finally
+ {
+ if (_host != null)
+ {
+ await _host.StopAsync();
+ }
+ }
+ }
+
+ private static ITraceContext GetTraceContextFromIncomingRequest() =>
+ new SimpleTraceContext(TraceIdFactory.Create().NextId(), null, true);
+ }
+}
+
+
diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Tests/Google.Cloud.Diagnostics.AspNetCore.Tests.csproj b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Tests/Google.Cloud.Diagnostics.AspNetCore.Tests.csproj
index c12981263fd5..71c769ea0dee 100644
--- a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Tests/Google.Cloud.Diagnostics.AspNetCore.Tests.csproj
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore.Tests/Google.Cloud.Diagnostics.AspNetCore.Tests.csproj
@@ -14,6 +14,7 @@
+
diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore/Trace/CloudTraceExtension.cs b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore/Trace/CloudTraceExtension.cs
index c733290c01b4..7711f613fb9d 100644
--- a/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore/Trace/CloudTraceExtension.cs
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/Google.Cloud.Diagnostics.AspNetCore/Trace/CloudTraceExtension.cs
@@ -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
@@ -112,32 +110,27 @@ 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((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(ProvideGoogleTraceHeaderContext);
services.TryAddSingleton>(PropagateGoogleTraceHeaders);
- 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.
- services.AddSingleton>(tracerFactory);
+ services.AddSingleton>(sp => sp.GetRequiredService>());
+
services.AddHttpContextAccessor();
- services.AddSingleton(ManagedTracer.CreateDelegatingTracer(ContextTracerManager.GetCurrentTracer));
services.AddTransient();
- // 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.
@@ -173,15 +166,5 @@ internal static void PropagateGoogleTraceHeaders(HttpResponse response, ITraceCo
var googleHeader = TraceHeaderContext.Create(traceContext.TraceId, traceContext.SpanId ?? 0, traceContext.ShouldTrace);
response.Headers.Add(TraceHeaderContext.TraceHeader, googleHeader.ToString());
}
-
- ///
- /// Returns an 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.
- ///
- internal static UnchainedTraceHeaderPropagatingHandler UnchainedTraceHeaderPropagatingHandlerFactory(IServiceProvider serviceProvider) =>
- serviceProvider.GetService>() is Action traceContextOutgoingPropagator ?
- new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer, traceContextOutgoingPropagator) :
- new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer);
}
}
diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore/docs/index.md b/apis/Google.Cloud.Diagnostics.AspNetCore/docs/index.md
index ef4c0cca0302..50f2255970db 100644
--- a/apis/Google.Cloud.Diagnostics.AspNetCore/docs/index.md
+++ b/apis/Google.Cloud.Diagnostics.AspNetCore/docs/index.md
@@ -247,3 +247,34 @@ that is propagated in a `custom_trace_id` header. This is of no use in the real
{{sample:Trace.CustomTraceContext}}
+## Using Google Trace in applications not based in ASP.NET Core
+
+If you want to use Google Cloud Trace in applications not based on ASP.NET Core you may use the
+`Google.Cloud.Diagnostics.Common` package directly and .NET's dependency injection mechanism.
+You can read more about .NET dependency injection in non ASP.NET Core apps in the
+[Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage).
+
+Note that this is useful for both installed applications and services that process incoming messages
+other than HTTP requests, such as a service reacting to Pub/Sub messages.
+
+Following you'll see a very simplified example of how you could set up Google Cloud Trace for these
+types of applications.
+
+- Configure Google Cloud Trace. You can set tracing options the same as you would do for ASP.NET Core apps.
+
+{{sample:StandaloneTrace.Configure}}
+
+- Build and start a `Microsoft.Extensions.Hosting.IHost`.
+
+{{sample:StandaloneTrace.Start}}
+
+- Create a tracing context when appropiate, for instance, when you receive a Pub/Sub message. You can create
+an empty tracing context (with all null values) if there's none. The tracer will create a tracing context
+depending on configuration options like QPS, etc.
+
+{{sample:StandaloneTrace.IncomingContext}}
+
+- Trace normally in your code
+
+{{sample:StandaloneTrace.Trace}}
+
diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/CloudTraceExtensions.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/CloudTraceExtensions.cs
new file mode 100644
index 000000000000..5d56c9b73149
--- /dev/null
+++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/CloudTraceExtensions.cs
@@ -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
+{
+ ///
+ /// Extension methods for registering Google Cloud Trace for
+ /// dependency injection.
+ ///
+ public static class CloudTraceExtensions
+ {
+ ///
+ /// Configures Google Cloud Trace for dependency injection.
+ ///
+ public static IServiceCollection AddGoogleTrace(
+ this IServiceCollection services, Action 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 DelegatingHandler with no InnerHandler set. This is the recommended way.
+ // It should be registered as follows.
+ return services.AddTransient(UnchainedTraceHeaderPropagatingHandlerFactory);
+ }
+
+ ///
+ /// Returns an 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.
+ ///
+ internal static UnchainedTraceHeaderPropagatingHandler UnchainedTraceHeaderPropagatingHandlerFactory(IServiceProvider serviceProvider) =>
+ serviceProvider.GetService>() is Action traceContextOutgoingPropagator ?
+ new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer, traceContextOutgoingPropagator) :
+ new UnchainedTraceHeaderPropagatingHandler(ContextTracerManager.GetCurrentTracer);
+ }
+}
diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/TraceServiceOptions.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/TraceServiceOptions.cs
new file mode 100644
index 000000000000..b78732966133
--- /dev/null
+++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/Trace/TraceServiceOptions.cs
@@ -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
+{
+ ///
+ /// Options for initializing tracing.
+ ///
+ public sealed class TraceServiceOptions
+ {
+ ///
+ /// The Google Cloud Platform project ID. If unspecified and running on GAE or GCE the project
+ /// ID will be detected from the platform.
+ ///
+ public string ProjectId { get; set; }
+
+ ///
+ /// Trace options. May be null.
+ ///
+ public TraceOptions Options { get; set; }
+
+ ///
+ /// A client to send traces with. May be null.
+ ///
+ public TraceServiceClient Client { get; set; }
+ }
+}
diff --git a/apis/apis.json b/apis/apis.json
index fafe32ffce0f..8b474688aa77 100644
--- a/apis/apis.json
+++ b/apis/apis.json
@@ -714,7 +714,8 @@
"Google.Cloud.Diagnostics.Common.IntegrationTests": "project",
"Google.Cloud.Diagnostics.Common.Tests": "project",
"Microsoft.AspNetCore.Mvc": "2.1.3",
- "Microsoft.AspNetCore.TestHost": "2.1.1"
+ "Microsoft.AspNetCore.TestHost": "2.1.1",
+ "Microsoft.Extensions.Hosting": "3.0.0"
},
"additionalAnalyzerTestDependencies": {
"Microsoft.AspNetCore.Mvc.Core": "1.0.4",