Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document the use of Open Telemetry #12

Open
baywet opened this issue Aug 1, 2023 · 7 comments
Open

Document the use of Open Telemetry #12

baywet opened this issue Aug 1, 2023 · 7 comments
Assignees

Comments

@baywet
Copy link
Member

baywet commented Aug 1, 2023

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
Copy link
Member Author

baywet commented Jan 10, 2024

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/[email protected]/messages
var messagesResponse = await apiClient.Users["[email protected]"].Messages.PostAsync(new Message {
    Subject = "Hello world"
});

@baywet
Copy link
Member Author

baywet commented Jan 10, 2024

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
Copy link
Member Author

baywet commented Apr 29, 2024

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("[email protected]").messages().post(new Message(){{
            setSubject("Hello world");
        }}).get();
        System.out.println(result.getSubject());
     }

@baywet
Copy link
Member Author

baywet commented Apr 30, 2024

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("[email protected]").Messages().Get()
      for _, message := range response.GetValue() {
               fmt.Printf("Message: %v\n", *(message.GetSubject()))
       }
       span.End()

@baywet
Copy link
Member Author

baywet commented Apr 30, 2024

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("[email protected]").messages.get();
      for (const message of result?.value!) {
        Logger.logTask(`${message.subject}`);
      }
      span.end();
    });
    exporter.shutdown();

@sebastienlevert
Copy link
Contributor

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

@baywet
Copy link
Member Author

baywet commented Apr 30, 2024

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants