Unverified Commit 1eec926c authored by Hong Minhee's avatar Hong Minhee
Browse files

Instrument `lookupWebFinger()`

parent 93030850
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ To be released.
 -  Fedify now supports OpenTelemetry for tracing.  [[#170]]

     -  Added `CreateFederationOptions.tracerProvider` option.
     -  Added `LookupWebFingerOptions.tracerProvider` option.

 -  Added `@fedify/fedify/x/sveltekit` module for integrating with [SvelteKit]
    hook.  [[#171], [#183] by Jiyu Park]
+6 −4
Original line number Diff line number Diff line
@@ -115,10 +115,11 @@ Instrumented spans
Fedify automatically instruments the following operations with OpenTelemetry
spans:

| Operation            | Description                       |
|----------------------|-----------------------------------|
| `Federation.fetch()` | Serves the incoming HTTP request. |
| `handleWebFinger()`  | Handles the WebFinger request.    |
| Operation            | Span type | Description                       |
|----------------------|-----------|-----------------------------------|
| `Federation.fetch()` | Server    | Serves the incoming HTTP request. |
| `lookupWebFinger()`  | Client    | Looks up the WebFinger resource.  |
| `handleWebFinger()`  | Server    | Handles the WebFinger request.    |

More operations will be instrumented in the future releases.

@@ -146,5 +147,6 @@ for ActivityPub:
| `activitypub.inboxes`            | int      | The number of inboxes the activity is sent to.                                  | `12`                                               |
| `activitypub.shared_inbox`       | boolean  | Whether the activity is sent to the shared inbox.                               | `true`                                             |
| `webfinger.resource`             | string   | The queried resource URI.                                                       | `"acct:fedify@hollo.social"`                       |
| `webfinger.resource.scheme`      | string   | The scheme of the queried resource URI.                                         | `"acct"`                                           |

[OpenTelemetry Semantic Conventions]: https://opentelemetry.io/docs/specs/semconv/
+4 −0
Original line number Diff line number Diff line
@@ -110,6 +110,10 @@ async function handleWebFingerInternal<TContextData>(
    }
    throw e;
  }
  span?.setAttribute(
    "webfinger.resource.scheme",
    resourceUrl.protocol.replace(/:$/, ""),
  );
  if (actorDispatcher == null) {
    logger.error("Actor dispatcher is not set.");
    return await onNotFound(request);
+54 −0
Original line number Diff line number Diff line
import { getLogger } from "@logtape/logtape";
import {
  SpanKind,
  SpanStatusCode,
  type TracerProvider,
} from "@opentelemetry/api";
import metadata from "../deno.json" with { type: "json" };
import {
  getUserAgent,
  type GetUserAgentOptions,
@@ -19,6 +25,12 @@ export interface LookupWebFingerOptions {
   * the `User-Agent` header value.
   */
  userAgent?: GetUserAgentOptions | string;

  /**
   * The OpenTelemetry tracer provider.
   * @since 1.3.0
   */
  tracerProvider?: TracerProvider;
}

/**
@@ -31,6 +43,48 @@ export interface LookupWebFingerOptions {
export async function lookupWebFinger(
  resource: URL | string,
  options: LookupWebFingerOptions = {},
): Promise<ResourceDescriptor | null> {
  if (options.tracerProvider == null) {
    return await lookupWebFingerInternal(resource, options);
  }
  const tracer = options.tracerProvider.getTracer(
    metadata.name,
    metadata.version,
  );
  return await tracer.startActiveSpan(
    "WebFinger",
    {
      kind: SpanKind.CLIENT,
      attributes: {
        "webfinger.resource": resource.toString(),
        "webfinger.resource.scheme": typeof resource === "string"
          ? resource.replace(/:.*$/, "")
          : resource.protocol.replace(/:$/, ""),
      },
    },
    async (span) => {
      try {
        const result = await lookupWebFingerInternal(resource, options);
        span.setStatus({
          code: result === null ? SpanStatusCode.ERROR : SpanStatusCode.OK,
        });
        return result;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: String(error),
        });
        throw error;
      } finally {
        span.end();
      }
    },
  );
}

async function lookupWebFingerInternal(
  resource: URL | string,
  options: LookupWebFingerOptions = {},
): Promise<ResourceDescriptor | null> {
  if (typeof resource === "string") resource = new URL(resource);
  let protocol = "https:";