MicrosoftDocs / openapi-docs

Creative Commons Attribution 4.0 International
13 stars 34 forks source link

Document the use of Open Telemetry #12

Open baywet opened 1 year ago

baywet commented 1 year ago

The use of Open Telemetry for Kiota clients is currently undocumented. We regularly get questions about observability and showing for each language how you can export traces to the console or to a service would be helpful. Originally created by @baywet at https://github.com/microsoftdocs/openapi-docs-pr/issues/21

baywet commented 8 months ago

here is an example on how to do it in dotnet, exporting to zipkin, in a console application

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource(serviceName)
    .AddSource("Microsoft.Kiota.Http.HttpClientLibrary") // really important behavior is different than go/java
    .AddSource("Microsoft.Kiota.Authentication.Azure") // really important behavior is different than go/java
    .AddSource("Microsoft.Kiota.Abstractions") // really important behavior is different than go/java
    .SetResourceBuilder(
        ResourceBuilder.CreateDefault()
            .AddService(serviceName: serviceName))
    .AddConsoleExporter()
    .AddZipkinExporter(o =>
    {
        o.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
    })
    .Build();
using var myActivitySource = new ActivitySource("my-app");
using var activity = myActivitySource.StartActivity("main");
var tokenCredential = new DeviceCodeCredential(
    clientId: "clientId",
    tenantId: "tenantId",
    deviceCodeCallback: (deviceCodeInfo, _) =>
    {
        Console.WriteLine(deviceCodeInfo.Message);
        return Task.FromResult(0);
    });
var authenticationProvider = new AzureIdentityAuthenticationProvider(tokenCredential, new string[] {"graph.microsoft.com"}, scopes: new string[] { "Mail.Read"});
var requestAdapter = new HttpClientRequestAdapter(authenticationProvider);
var apiClient = new ApiClient(requestAdapter);
// GET /users/jane@constoso.com/messages
var messagesResponse = await apiClient.Users["jane@constoso.com"].Messages.PostAsync(new Message {
    Subject = "Hello world"
});
baywet commented 8 months ago

CC @philip-young @svrooij @tonipohl in the case of a graph client initialization you don't need all the ceremony for the authenticationProvider, the request adapter, etc... You can just new up the Graph client from the SDK where I new up the API client.

baywet commented 5 months ago

Here is the equivalent for Java

 private static OpenTelemetry initializeOpenTelemetry(String ip, int port) {
        String endpoint = String.format("http://%s:%s/api/v2/spans", ip, port);
        ZipkinSpanExporter zipkinExporter = ZipkinSpanExporter.builder().setEndpoint(endpoint).build();

        Resource serviceNameResource =
            Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "my-app"));

        // Set to process the spans by the Zipkin Exporter
        SdkTracerProvider tracerProvider =
            SdkTracerProvider.builder()
                .addSpanProcessor(SimpleSpanProcessor.create(zipkinExporter))
                .setResource(Resource.getDefault().merge(serviceNameResource))
                .build();
        OpenTelemetrySdk openTelemetry =
            OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal();

        // add a shutdown hook to shut down the SDK
        Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close));

        // return the configured instance so it can be used for instrumentation.
        return openTelemetry;
    }

public static void main(String[] args) throws Exception {
        OpenTelemetry openTelemetry = initializeOpenTelemetry("127.0.0.1", 9411);

        final TokenCredential tokenCredential = new DeviceCodeCredentialBuilder()
            .clientId("clientid")
            .tenantId("tenantId")
            .challengeConsumer(deviceCodeInfo -> {
                System.out.println(deviceCodeInfo.getMessage());
            }).build();
        final AzureIdentityAuthenticationProvider authProvider = new AzureIdentityAuthenticationProvider(tokenCredential, new String[] {"graph.microsoft.com"}, new String[] {"Mail.Read", "Mail.Send"});
        final ApiClient client = new ApiClient(new OkHttpRequestAdapter(authProvider));
        final Message result = client.users("jane@contoso.com").messages().post(new Message(){{
            setSubject("Hello world");
        }}).get();
        System.out.println(result.getSubject());
     }
baywet commented 5 months ago

Here is the equivalent for Go

var logger = log.New(os.Stderr, "zipkin-example", log.Ldate|log.Ltime|log.Llongfile)

func initTracer(url string) (func(context.Context) error, error) {
       // Create Zipkin Exporter and install it as a global tracer.
       //
       // For demoing purposes, always sample. In a production application, you should
       // configure the sampler to a trace.ParentBased(trace.TraceIDRatioBased) set at the desired
       // ratio.
       exporter, err := zipkin.New(
               url,
               zipkin.WithLogger(logger),
       )
       if err != nil {
               return nil, err
       }

       batcher := sdktrace.NewBatchSpanProcessor(exporter)

       tp := sdktrace.NewTracerProvider(
               sdktrace.WithSpanProcessor(batcher),
               sdktrace.WithResource(
                       resource.NewWithAttributes(
                               semconv.SchemaURL,
                               semconv.ServiceNameKey.String("zipkin-test"),
                       )),
       )
       otel.SetTracerProvider(tp)

       return tp.Shutdown, nil
}
 func main() {
       url := flag.String("zipkin", "http://localhost:9411/api/v2/spans", "zipkin url")
       flag.Parse()

       ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
       defer cancel()

       shutdown, err := initTracer(*url)
       if err != nil {
               log.Fatal(err)
       }
       defer func() {
               if err := shutdown(ctx); err != nil {
                       log.Fatal("failed to shutdown TracerProvider: %w", err)
       }
       tr := otel.GetTracerProvider().Tracer("kiota-http-go")
       ctx, span := tr.Start(ctx, "main", trace.WithSpanKind(trace.SpanKindServer))
       cred, err := azidentity.NewDeviceCodeCredential(&azidentity.DeviceCodeCredentialOptions{
                TenantID: "tenantId",
                ClientID: "clientId",
                return
               log.Fatal(err)
       }
      client := u.NewApiClient(adapter)
      response, err := client.UsersById("jane@contoso.com").Messages().Get()
      for _, message := range response.GetValue() {
               fmt.Printf("Message: %v\n", *(message.GetSubject()))
       }
       span.End()
baywet commented 5 months ago

Here is the equivalent for TypeScript

      diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
      const resource =
      Resource.default().merge(
        new Resource({
          [SemanticResourceAttributes.SERVICE_NAME]: "service-name-here",
          [SemanticResourceAttributes.SERVICE_VERSION]: "0.1.0",
        })
      );

    const provider = new NodeTracerProvider({
        resource: resource,
    });
    const exporter = new ZipkinExporter({
    });
    provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
    provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
    provider.register();

    await trace.getTracer(
      'service-name-here'
    ).startActiveSpan('my-span', async (span) => {

      const cred = new DeviceCodeCredential({
        tenantId: "tenantId",
        clientId: "clientId",
        userPromptCallback: (deviceCodeInfo) => {
          // eslint-disable-next-line no-console
          console.log(deviceCodeInfo.message);
        },
      });
      const authProvider = new AzureIdentityAuthenticationProvider(cred, ["Mail.Read"]);
      const requestAdapter = new FetchRequestAdapter(authProvider);
      const client = new ApiClient(requestAdapter);
      const result = await client.usersById("jane@contoso.com").messages.get();
      for (const message of result?.value!) {
        Logger.logTask(`${message.subject}`);
      }
      span.end();
    });
    exporter.shutdown();
sebastienlevert commented 5 months ago

@baywet I assume these will land as snippets in the docs directly, right? Just using this one as a reminder?

baywet commented 5 months ago

@sebastienlevert yeah I've been salvaging my stashes here for now in case something happens to them. @ndiritu has had a customer issue for Java asking about that information and he volunteered to write the documentation page. I'm hoping those will help fast track things.