From c131413e6c6bd7d3e09bb2317c2f977616427093 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 02:10:32 +0100 Subject: [PATCH 01/14] feat: add semconv generator for `semantic-conventions`-package Adds a build script which generate the files for the `semenatic-conventions`-package so it always in sync with the definitions in the `specifications`-repo A few unspecified attributes are added manually to the semconv template to allow existing code to pass, these are: // HTTP HTTP_ERROR_NAME: 'http.error_name', HTTP_ERROR_MESSAGE: 'http.error_message', HTTP_STATUS_TEXT: 'http.status_text', // GRPC GRPC_KIND: 'grpc.kind', // SERVER or CLIENT GRPC_METHOD: 'grpc.method', GRPC_STATUS_CODE: 'grpc.status_code', GRPC_ERROR_NAME: 'grpc.error_name', GRPC_ERROR_MESSAGE: 'grpc.error_message', --- scripts/semconv/.gitignore | 1 + scripts/semconv/generate.sh | 41 ++++++++++ .../templates/SemanticAttributes.ts.j2 | 80 +++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 scripts/semconv/.gitignore create mode 100755 scripts/semconv/generate.sh create mode 100644 scripts/semconv/templates/SemanticAttributes.ts.j2 diff --git a/scripts/semconv/.gitignore b/scripts/semconv/.gitignore new file mode 100644 index 00000000000..a93b221beb5 --- /dev/null +++ b/scripts/semconv/.gitignore @@ -0,0 +1 @@ +opentelemetry-specification/ diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh new file mode 100755 index 00000000000..ad4b7062324 --- /dev/null +++ b/scripts/semconv/generate.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="${SCRIPT_DIR}/../../" + +# freeze the spec version to make SemanticAttributes generation reproducible +SPEC_VERSION=main + +cd ${SCRIPT_DIR} + +rm -rf opentelemetry-specification || true +mkdir opentelemetry-specification +cd opentelemetry-specification + +git init +git remote add origin https://github.com/open-telemetry/opentelemetry-specification.git +git fetch origin "$SPEC_VERSION" +git reset --hard FETCH_HEAD +cd ${SCRIPT_DIR} + +docker run --rm \ + -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/trace:/source \ + -v ${SCRIPT_DIR}/templates:/templates \ + -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/trace/:/output \ + otel/semconvgen \ + -f /source code \ + --template /templates/SemanticAttributes.ts.j2 \ + --output /output/SemanticAttribute.ts \ + -Dclass=SemanticAttribute + +docker run --rm \ + -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/resource:/source \ + -v ${SCRIPT_DIR}/templates:/templates \ + -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/resource/:/output \ + otel/semconvgen \ + -f /source code \ + --template /templates/SemanticAttributes.ts.j2 \ + --output /output/ResourceAttribute.ts \ + -Dclass=ResourceAttributes + +cd "$ROOT_DIR" diff --git a/scripts/semconv/templates/SemanticAttributes.ts.j2 b/scripts/semconv/templates/SemanticAttributes.ts.j2 new file mode 100644 index 00000000000..a8bd9fdda22 --- /dev/null +++ b/scripts/semconv/templates/SemanticAttributes.ts.j2 @@ -0,0 +1,80 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + + +{%- macro print_value(type, value) -%} + {{ "\"" if type == "string"}}{{value}}{{ "\"" if type == "string"}} +{%- endmacro %} +{%- macro upFirst(text) -%} + {{ text[0]|upper}}{{text[1:] }} +{%- endmacro %} +{%- macro lowerFirst(text) -%} + {{ text[0]|lower}}{{text[1:] }} +{%- endmacro %} + +// DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates/{{template}} +export const {{class}} = { + {%- for attribute in attributes | unique(attribute="fqn") %} + + /** + * {% filter escape %}{{attribute.brief | to_doc_brief}}.{% endfilter %} + {%- if attribute.note %} + * + * Note: {% filter escape %}{{attribute.note | to_doc_brief}}.{% endfilter %} + {%- endif %} + {%- if attribute.deprecated %} + * + * @deprecated {{attribute.deprecated | to_doc_brief}}. + {%- endif %} + */ + {%- if attribute.deprecated %} + @Deprecated + {%- endif %} + {{attribute.fqn | to_const_name}}: "{{attribute.fqn}}", + {%- endfor %} + + {%- if class == "SemanticAttribute" %} + + // Manually defined and not YET in the YAML + + // HTTP + HTTP_ERROR_NAME: 'http.error_name', + HTTP_ERROR_MESSAGE: 'http.error_message', + HTTP_STATUS_TEXT: 'http.status_text', + + // GRPC + GRPC_KIND: 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD: 'grpc.method', + GRPC_STATUS_CODE: 'grpc.status_code', + GRPC_ERROR_NAME: 'grpc.error_name', + GRPC_ERROR_MESSAGE: 'grpc.error_message', + {%- endif %} +} + +// Enum definitions +{%- for attribute in attributes | unique(attribute="fqn") %} +{%- if attribute.is_enum %} +{%- set class_name = attribute.fqn | to_camelcase(True) ~ "Values" %} +{%- set type = attribute.attr_type.enum_type %} + +export enum {{class_name}} { + {%- for member in attribute.attr_type.members %} + /** {% filter escape %}{{member.brief | to_doc_brief}}.{% endfilter %} */ + {{ member.member_id | to_const_name }} = {{ print_value(type, member.value) }}, + {%- endfor %} +} +{% endif %} +{%- endfor %} From 47243055039cc507a46b1f2f302995fe72d59f60 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 02:12:20 +0100 Subject: [PATCH 02/14] feat: update `semantic-conventations`-package by generating using new build script --- .../src/{trace/exception.ts => events.ts} | 9 +- .../src/index.ts | 4 +- .../src/resource/ResourceAttribute.ts | 540 +++++++++++ .../src/resource/index.ts | 1 + .../src/trace/SemanticAttribute.ts | 874 ++++++++++++++++++ .../src/trace/database.ts | 156 ---- .../src/trace/faas.ts | 170 ---- .../src/trace/general.ts | 41 - .../src/trace/http.ts | 39 - .../src/trace/index.ts | 25 +- .../src/trace/messaging.ts | 151 --- .../src/trace/os.ts | 63 -- .../src/trace/rpc.ts | 47 - 13 files changed, 1421 insertions(+), 699 deletions(-) rename packages/opentelemetry-semantic-conventions/src/{trace/exception.ts => events.ts} (76%) create mode 100644 packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts create mode 100644 packages/opentelemetry-semantic-conventions/src/resource/index.ts create mode 100644 packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/database.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/faas.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/general.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/http.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/messaging.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/os.ts delete mode 100644 packages/opentelemetry-semantic-conventions/src/trace/rpc.ts diff --git a/packages/opentelemetry-semantic-conventions/src/trace/exception.ts b/packages/opentelemetry-semantic-conventions/src/events.ts similarity index 76% rename from packages/opentelemetry-semantic-conventions/src/trace/exception.ts rename to packages/opentelemetry-semantic-conventions/src/events.ts index cf7dc596bef..1b766da3d65 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/exception.ts +++ b/packages/opentelemetry-semantic-conventions/src/events.ts @@ -14,10 +14,5 @@ * limitations under the License. */ -export const ExceptionAttribute = { - MESSAGE: 'exception.message', - STACKTRACE: 'exception.stacktrace', - TYPE: 'exception.type', -}; - -export const ExceptionEventName = 'exception'; +// Event name definitions +export const ExceptionEventName = "exception"; diff --git a/packages/opentelemetry-semantic-conventions/src/index.ts b/packages/opentelemetry-semantic-conventions/src/index.ts index ae2998a38ce..97f755e402f 100644 --- a/packages/opentelemetry-semantic-conventions/src/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/index.ts @@ -14,4 +14,6 @@ * limitations under the License. */ -export * from './trace'; +export * from "./trace"; +export * from "./resource"; +export * from "./events"; diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts new file mode 100644 index 00000000000..679243e8484 --- /dev/null +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts @@ -0,0 +1,540 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +// DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 +export const ResourceAttributes = { + + /** + * Name of the cloud provider. + */ + CLOUD_PROVIDER: "cloud.provider", + + /** + * The cloud account ID the resource is assigned to. + */ + CLOUD_ACCOUNT_ID: "cloud.account.id", + + /** + * The geographical region the resource is running. Refer to your provider's docs to see the available regions, for example [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), or [Google Cloud regions](https://cloud.google.com/about/locations). + */ + CLOUD_REGION: "cloud.region", + + /** + * Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. + * + * Note: Availability zones are called "zones" on Google Cloud. + */ + CLOUD_AVAILABILITY_ZONE: "cloud.availability_zone", + + /** + * The cloud platform in use. + * + * Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. + */ + CLOUD_PLATFORM: "cloud.platform", + + /** + * The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). + */ + AWS_ECS_CONTAINER_ARN: "aws.ecs.container.arn", + + /** + * The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). + */ + AWS_ECS_CLUSTER_ARN: "aws.ecs.cluster.arn", + + /** + * The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. + */ + AWS_ECS_LAUNCHTYPE: "aws.ecs.launchtype", + + /** + * The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). + */ + AWS_ECS_TASK_ARN: "aws.ecs.task.arn", + + /** + * The task definition family this task definition is a member of. + */ + AWS_ECS_TASK_FAMILY: "aws.ecs.task.family", + + /** + * The revision for this task definition. + */ + AWS_ECS_TASK_REVISION: "aws.ecs.task.revision", + + /** + * The ARN of an EKS cluster. + */ + AWS_EKS_CLUSTER_ARN: "aws.eks.cluster.arn", + + /** + * The name(s) of the AWS log group(s) an application is writing to. + * + * Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. + */ + AWS_LOG_GROUP_NAMES: "aws.log.group.names", + + /** + * The Amazon Resource Name(s) (ARN) of the AWS log group(s). + * + * Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). + */ + AWS_LOG_GROUP_ARNS: "aws.log.group.arns", + + /** + * The name(s) of the AWS log stream(s) an application is writing to. + */ + AWS_LOG_STREAM_NAMES: "aws.log.stream.names", + + /** + * The ARN(s) of the AWS log stream(s). + * + * Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. + */ + AWS_LOG_STREAM_ARNS: "aws.log.stream.arns", + + /** + * Container name. + */ + CONTAINER_NAME: "container.name", + + /** + * Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/reference/run/#container-identification). The UUID might be abbreviated. + */ + CONTAINER_ID: "container.id", + + /** + * The container runtime managing this container. + */ + CONTAINER_RUNTIME: "container.runtime", + + /** + * Name of the image the container was built on. + */ + CONTAINER_IMAGE_NAME: "container.image.name", + + /** + * Container image tag. + */ + CONTAINER_IMAGE_TAG: "container.image.tag", + + /** + * Name of the [deployment environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka deployment tier). + */ + DEPLOYMENT_ENVIRONMENT: "deployment.environment", + + /** + * The name of the function being executed. + */ + FAAS_NAME: "faas.name", + + /** + * The unique ID of the function being executed. + * + * Note: For example, in AWS Lambda this field corresponds to the [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) value, in GCP to the URI of the resource, and in Azure to the [FunctionDirectory](https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function) field. + */ + FAAS_ID: "faas.id", + + /** + * The version string of the function being executed as defined in [Version Attributes](../../resource/semantic_conventions/README.md#version-attributes). + */ + FAAS_VERSION: "faas.version", + + /** + * The execution environment ID as a string. + */ + FAAS_INSTANCE: "faas.instance", + + /** + * The amount of memory available to the serverless function in MiB. + * + * Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information. + */ + FAAS_MAX_MEMORY: "faas.max_memory", + + /** + * Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. + */ + HOST_ID: "host.id", + + /** + * Name of the host. On Unix systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. + */ + HOST_NAME: "host.name", + + /** + * Type of host. For Cloud, this must be the machine type. + */ + HOST_TYPE: "host.type", + + /** + * The CPU architecture the host system is running on. + */ + HOST_ARCH: "host.arch", + + /** + * Name of the VM image or OS install the host was instantiated from. + */ + HOST_IMAGE_NAME: "host.image.name", + + /** + * VM image ID. For Cloud, this value is from the provider. + */ + HOST_IMAGE_ID: "host.image.id", + + /** + * The version string of the VM image as defined in [Version Attributes](README.md#version-attributes). + */ + HOST_IMAGE_VERSION: "host.image.version", + + /** + * The name of the cluster. + */ + K8S_CLUSTER_NAME: "k8s.cluster.name", + + /** + * The name of the Node. + */ + K8S_NODE_NAME: "k8s.node.name", + + /** + * The UID of the Node. + */ + K8S_NODE_UID: "k8s.node.uid", + + /** + * The name of the namespace that the pod is running in. + */ + K8S_NAMESPACE_NAME: "k8s.namespace.name", + + /** + * The UID of the Pod. + */ + K8S_POD_UID: "k8s.pod.uid", + + /** + * The name of the Pod. + */ + K8S_POD_NAME: "k8s.pod.name", + + /** + * The name of the Container in a Pod template. + */ + K8S_CONTAINER_NAME: "k8s.container.name", + + /** + * The UID of the ReplicaSet. + */ + K8S_REPLICASET_UID: "k8s.replicaset.uid", + + /** + * The name of the ReplicaSet. + */ + K8S_REPLICASET_NAME: "k8s.replicaset.name", + + /** + * The UID of the Deployment. + */ + K8S_DEPLOYMENT_UID: "k8s.deployment.uid", + + /** + * The name of the Deployment. + */ + K8S_DEPLOYMENT_NAME: "k8s.deployment.name", + + /** + * The UID of the StatefulSet. + */ + K8S_STATEFULSET_UID: "k8s.statefulset.uid", + + /** + * The name of the StatefulSet. + */ + K8S_STATEFULSET_NAME: "k8s.statefulset.name", + + /** + * The UID of the DaemonSet. + */ + K8S_DAEMONSET_UID: "k8s.daemonset.uid", + + /** + * The name of the DaemonSet. + */ + K8S_DAEMONSET_NAME: "k8s.daemonset.name", + + /** + * The UID of the Job. + */ + K8S_JOB_UID: "k8s.job.uid", + + /** + * The name of the Job. + */ + K8S_JOB_NAME: "k8s.job.name", + + /** + * The UID of the CronJob. + */ + K8S_CRONJOB_UID: "k8s.cronjob.uid", + + /** + * The name of the CronJob. + */ + K8S_CRONJOB_NAME: "k8s.cronjob.name", + + /** + * The operating system type. + */ + OS_TYPE: "os.type", + + /** + * Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. + */ + OS_DESCRIPTION: "os.description", + + /** + * Process identifier (PID). + */ + PROCESS_PID: "process.pid", + + /** + * The name of the process executable. On Linux based systems, can be set to the `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_NAME: "process.executable.name", + + /** + * The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_PATH: "process.executable.path", + + /** + * The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. + */ + PROCESS_COMMAND: "process.command", + + /** + * The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. + */ + PROCESS_COMMAND_LINE: "process.command_line", + + /** + * All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. + */ + PROCESS_COMMAND_ARGS: "process.command_args", + + /** + * The username of the user that owns the process. + */ + PROCESS_OWNER: "process.owner", + + /** + * The name of the runtime of this process. For compiled native binaries, this SHOULD be the name of the compiler. + */ + PROCESS_RUNTIME_NAME: "process.runtime.name", + + /** + * The version of the runtime of this process, as returned by the runtime without modification. + */ + PROCESS_RUNTIME_VERSION: "process.runtime.version", + + /** + * An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. + */ + PROCESS_RUNTIME_DESCRIPTION: "process.runtime.description", + + /** + * Logical name of the service. + * + * Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md#process), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`. + */ + SERVICE_NAME: "service.name", + + /** + * A namespace for `service.name`. + * + * Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. + */ + SERVICE_NAMESPACE: "service.namespace", + + /** + * The string ID of the service instance. + * + * Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words `service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service). It is preferable for the ID to be persistent and stay the same for the lifetime of the service instance, however it is acceptable that the ID is ephemeral and changes during important lifetime events for the service (e.g. service restarts). If the service has no inherent unique ID that can be used as the value of this attribute it is recommended to generate a random Version 1 or Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use Version 5, see RFC 4122 for more recommendations). + */ + SERVICE_INSTANCE_ID: "service.instance.id", + + /** + * The version string of the service API or implementation. + */ + SERVICE_VERSION: "service.version", + + /** + * The name of the telemetry SDK as defined above. + */ + TELEMETRY_SDK_NAME: "telemetry.sdk.name", + + /** + * The language of the telemetry SDK. + */ + TELEMETRY_SDK_LANGUAGE: "telemetry.sdk.language", + + /** + * The version string of the telemetry SDK. + */ + TELEMETRY_SDK_VERSION: "telemetry.sdk.version", + + /** + * The version string of the auto instrumentation agent, if used. + */ + TELEMETRY_AUTO_VERSION: "telemetry.auto.version", + + /** + * The name of the web engine. + */ + WEBENGINE_NAME: "webengine.name", + + /** + * The version of the web engine. + */ + WEBENGINE_VERSION: "webengine.version", + + /** + * Additional description of the web engine (e.g. detailed version and edition information). + */ + WEBENGINE_DESCRIPTION: "webengine.description", +} + +// Enum definitions + +export enum CloudProviderValues { + /** Amazon Web Services. */ + AWS = "aws", + /** Microsoft Azure. */ + AZURE = "azure", + /** Google Cloud Platform. */ + GCP = "gcp", +} + + +export enum CloudPlatformValues { + /** AWS Elastic Compute Cloud. */ + AWS_EC2 = "aws_ec2", + /** AWS Elastic Container Service. */ + AWS_ECS = "aws_ecs", + /** AWS Elastic Kubernetes Service. */ + AWS_EKS = "aws_eks", + /** AWS Lambda. */ + AWS_LAMBDA = "aws_lambda", + /** AWS Elastic Beanstalk. */ + AWS_ELASTICBEANSTALK = "aws_elastic_beanstalk", + /** Azure Virtual Machines. */ + AZURE_VM = "azure_vm", + /** Azure Container Instances. */ + AZURE_CONTAINERINSTANCES = "azure_container_instances", + /** Azure Kubernetes Service. */ + AZURE_AKS = "azure_aks", + /** Azure Functions. */ + AZURE_FUNCTIONS = "azure_functions", + /** Azure App Service. */ + AZURE_APPSERVICE = "azure_app_service", + /** Google Cloud Compute Engine (GCE). */ + GCP_COMPUTEENGINE = "gcp_compute_engine", + /** Google Cloud Run. */ + GCP_CLOUDRUN = "gcp_cloud_run", + /** Google Cloud Kubernetes Engine (GKE). */ + GCP_KUBERNETESENGINE = "gcp_kubernetes_engine", + /** Google Cloud Functions (GCF). */ + GCP_CLOUDFUNCTIONS = "gcp_cloud_functions", + /** Google Cloud App Engine (GAE). */ + GCP_APPENGINE = "gcp_app_engine", +} + + +export enum AwsEcsLaunchtypeValues { + /** ec2. */ + EC2 = "ec2", + /** fargate. */ + FARGATE = "fargate", +} + + +export enum HostArchValues { + /** AMD64. */ + AMD64 = "amd64", + /** ARM32. */ + ARM32 = "arm32", + /** ARM64. */ + ARM64 = "arm64", + /** Itanium. */ + IA64 = "ia64", + /** 32-bit PowerPC. */ + PPC32 = "ppc32", + /** 64-bit PowerPC. */ + PPC64 = "ppc64", + /** 32-bit x86. */ + X86 = "x86", +} + + +export enum OsTypeValues { + /** Microsoft Windows. */ + WINDOWS = "WINDOWS", + /** Linux. */ + LINUX = "LINUX", + /** Apple Darwin. */ + DARWIN = "DARWIN", + /** FreeBSD. */ + FREEBSD = "FREEBSD", + /** NetBSD. */ + NETBSD = "NETBSD", + /** OpenBSD. */ + OPENBSD = "OPENBSD", + /** DragonFly BSD. */ + DRAGONFLYBSD = "DRAGONFLYBSD", + /** HP-UX (Hewlett Packard Unix). */ + HPUX = "HPUX", + /** AIX (Advanced Interactive eXecutive). */ + AIX = "AIX", + /** Oracle Solaris. */ + SOLARIS = "SOLARIS", + /** IBM z/OS. */ + ZOS = "ZOS", +} + + +export enum TelemetrySdkLanguageValues { + /** cpp. */ + CPP = "cpp", + /** dotnet. */ + DOTNET = "dotnet", + /** erlang. */ + ERLANG = "erlang", + /** go. */ + GO = "go", + /** java. */ + JAVA = "java", + /** nodejs. */ + NODEJS = "nodejs", + /** php. */ + PHP = "php", + /** python. */ + PYTHON = "python", + /** ruby. */ + RUBY = "ruby", + /** webjs. */ + WEBJS = "webjs", +} diff --git a/packages/opentelemetry-semantic-conventions/src/resource/index.ts b/packages/opentelemetry-semantic-conventions/src/resource/index.ts new file mode 100644 index 00000000000..ba7030581f2 --- /dev/null +++ b/packages/opentelemetry-semantic-conventions/src/resource/index.ts @@ -0,0 +1 @@ +export * from "./ResourceAttribute"; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts new file mode 100644 index 00000000000..a4fad76a9b7 --- /dev/null +++ b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts @@ -0,0 +1,874 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +// DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 +export const SemanticAttribute = { + + /** + * An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. + */ + DB_SYSTEM: "db.system", + + /** + * The connection string used to connect to the database. It is recommended to remove embedded credentials. + */ + DB_CONNECTION_STRING: "db.connection_string", + + /** + * Username for accessing the database. + */ + DB_USER: "db.user", + + /** + * The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect. + */ + DB_JDBC_DRIVER_CLASSNAME: "db.jdbc.driver_classname", + + /** + * If no [tech-specific attribute](#call-level-attributes-for-specific-technologies) is defined, this attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). + * + * Note: In some SQL databases, the database name to be used is called "schema name". + */ + DB_NAME: "db.name", + + /** + * The database statement being executed. + * + * Note: The value may be sanitized to exclude sensitive information. + */ + DB_STATEMENT: "db.statement", + + /** + * The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword. + * + * Note: When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted. + */ + DB_OPERATION: "db.operation", + + /** + * Remote hostname or similar, see note below. + */ + NET_PEER_NAME: "net.peer.name", + + /** + * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). + */ + NET_PEER_IP: "net.peer.ip", + + /** + * Remote port number. + */ + NET_PEER_PORT: "net.peer.port", + + /** + * Transport protocol used. See note below. + */ + NET_TRANSPORT: "net.transport", + + /** + * The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. + * + * Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer required (but still recommended if non-standard). + */ + DB_MSSQL_INSTANCE_NAME: "db.mssql.instance_name", + + /** + * The name of the keyspace being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_CASSANDRA_KEYSPACE: "db.cassandra.keyspace", + + /** + * The fetch size used for paging, i.e. how many rows will be returned at once. + */ + DB_CASSANDRA_PAGE_SIZE: "db.cassandra.page_size", + + /** + * The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). + */ + DB_CASSANDRA_CONSISTENCY_LEVEL: "db.cassandra.consistency_level", + + /** + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: This mirrors the db.sql.table attribute but references cassandra rather than sql. It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_CASSANDRA_TABLE: "db.cassandra.table", + + /** + * Whether or not the query is idempotent. + */ + DB_CASSANDRA_IDEMPOTENCE: "db.cassandra.idempotence", + + /** + * The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. + */ + DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT: "db.cassandra.speculative_execution_count", + + /** + * The ID of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_ID: "db.cassandra.coordinator.id", + + /** + * The data center of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_DC: "db.cassandra.coordinator.dc", + + /** + * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_HBASE_NAMESPACE: "db.hbase.namespace", + + /** + * The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute. + */ + DB_REDIS_DATABASE_INDEX: "db.redis.database_index", + + /** + * The collection being accessed within the database stated in `db.name`. + */ + DB_MONGODB_COLLECTION: "db.mongodb.collection", + + /** + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_SQL_TABLE: "db.sql.table", + + /** + * The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. + */ + EXCEPTION_TYPE: "exception.type", + + /** + * The exception message. + */ + EXCEPTION_MESSAGE: "exception.message", + + /** + * A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. + */ + EXCEPTION_STACKTRACE: "exception.stacktrace", + + /** + * SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span. + * + * Note: An exception is considered to have escaped (or left) the scope of a span, +if that span is ended while the exception is still logically "in flight". +This may be actually "in flight" in some languages (e.g. if the exception +is passed to a Context manager's `__exit__` method in Python) but will +usually be caught at the point of recording the exception in most languages. + +It is usually not possible to determine at the point where an exception is thrown +whether it will escape the scope of a span. +However, it is trivial to know that an exception +will escape, if one checks for an active exception just before ending the span, +as done in the [example above](#exception-end-example). + +It follows that an exception may still escape the scope of the span +even if the `exception.escaped` attribute was not set or set to false, +since the event might have been recorded at a time where it was not +clear whether the exception will escape. + */ + EXCEPTION_ESCAPED: "exception.escaped", + + /** + * Type of the trigger on which the function is executed. + */ + FAAS_TRIGGER: "faas.trigger", + + /** + * The execution ID of the current function execution. + */ + FAAS_EXECUTION: "faas.execution", + + /** + * The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. + */ + FAAS_DOCUMENT_COLLECTION: "faas.document.collection", + + /** + * Describes the type of the operation that was performed on the data. + */ + FAAS_DOCUMENT_OPERATION: "faas.document.operation", + + /** + * A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_DOCUMENT_TIME: "faas.document.time", + + /** + * The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. + */ + FAAS_DOCUMENT_NAME: "faas.document.name", + + /** + * HTTP request method. + */ + HTTP_METHOD: "http.method", + + /** + * Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless. + * + * Note: `http.url` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case the attribute's value should be `https://www.example.com/`. + */ + HTTP_URL: "http.url", + + /** + * The full request target as passed in a HTTP request line or equivalent. + */ + HTTP_TARGET: "http.target", + + /** + * The value of the [HTTP host header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is empty or not present, this attribute should be the same. + */ + HTTP_HOST: "http.host", + + /** + * The URI scheme identifying the used protocol. + */ + HTTP_SCHEME: "http.scheme", + + /** + * [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + */ + HTTP_STATUS_CODE: "http.status_code", + + /** + * Kind of HTTP protocol used. + * + * Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. + */ + HTTP_FLAVOR: "http.flavor", + + /** + * Value of the [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the client. + */ + HTTP_USER_AGENT: "http.user_agent", + + /** + * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_REQUEST_CONTENT_LENGTH: "http.request_content_length", + + /** + * The size of the uncompressed request payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: "http.request_content_length_uncompressed", + + /** + * The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_RESPONSE_CONTENT_LENGTH: "http.response_content_length", + + /** + * The size of the uncompressed response payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: "http.response_content_length_uncompressed", + + /** + * The primary server name of the matched virtual host. This should be obtained via configuration. If no such configuration can be obtained, this attribute MUST NOT be set ( `net.host.name` should be used instead). + * + * Note: `http.url` is usually not readily available on the server side but would have to be assembled in a cumbersome and sometimes lossy process from other information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus preferred to supply the raw data that is available. + */ + HTTP_SERVER_NAME: "http.server_name", + + /** + * The matched route (path template). + */ + HTTP_ROUTE: "http.route", + + /** + * The IP address of the original client behind all proxies, if known (e.g. from [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). + * + * Note: This is not necessarily the same as `net.peer.ip`, which would identify the network-level peer, which may be a proxy. + */ + HTTP_CLIENT_IP: "http.client_ip", + + /** + * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. + */ + NET_HOST_IP: "net.host.ip", + + /** + * Like `net.peer.port` but for the host port. + */ + NET_HOST_PORT: "net.host.port", + + /** + * Local hostname or similar, see note below. + */ + NET_HOST_NAME: "net.host.name", + + /** + * A string identifying the messaging system. + */ + MESSAGING_SYSTEM: "messaging.system", + + /** + * The message destination name. This might be equal to the span name but is required nevertheless. + */ + MESSAGING_DESTINATION: "messaging.destination", + + /** + * The kind of message destination. + */ + MESSAGING_DESTINATION_KIND: "messaging.destination_kind", + + /** + * A boolean that is true if the message destination is temporary. + */ + MESSAGING_TEMP_DESTINATION: "messaging.temp_destination", + + /** + * The name of the transport protocol. + */ + MESSAGING_PROTOCOL: "messaging.protocol", + + /** + * The version of the transport protocol. + */ + MESSAGING_PROTOCOL_VERSION: "messaging.protocol_version", + + /** + * Connection string. + */ + MESSAGING_URL: "messaging.url", + + /** + * A value used by the messaging system as an identifier for the message, represented as a string. + */ + MESSAGING_MESSAGE_ID: "messaging.message_id", + + /** + * The [conversation ID](#conversations) identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". + */ + MESSAGING_CONVERSATION_ID: "messaging.conversation_id", + + /** + * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. + */ + MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: "messaging.message_payload_size_bytes", + + /** + * The compressed size of the message payload in bytes. + */ + MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: "messaging.message_payload_compressed_size_bytes", + + /** + * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_TIME: "faas.time", + + /** + * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + */ + FAAS_CRON: "faas.cron", + + /** + * A boolean that is true if the serverless function is executed for the first time (aka cold-start). + */ + FAAS_COLDSTART: "faas.coldstart", + + /** + * The name of the invoked function. + * + * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. + */ + FAAS_INVOKED_NAME: "faas.invoked_name", + + /** + * The cloud provider of the invoked function. + * + * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. + */ + FAAS_INVOKED_PROVIDER: "faas.invoked_provider", + + /** + * The cloud region of the invoked function. + * + * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. + */ + FAAS_INVOKED_REGION: "faas.invoked_region", + + /** + * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. + */ + PEER_SERVICE: "peer.service", + + /** + * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. + */ + ENDUSER_ID: "enduser.id", + + /** + * Actual/assumed role the client is making the request under extracted from token or application security context. + */ + ENDUSER_ROLE: "enduser.role", + + /** + * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + */ + ENDUSER_SCOPE: "enduser.scope", + + /** + * Current "managed" thread ID (as opposed to OS thread ID). + */ + THREAD_ID: "thread.id", + + /** + * Current thread name. + */ + THREAD_NAME: "thread.name", + + /** + * The method or function name, or equivalent (usually rightmost part of the code unit's name). + */ + CODE_FUNCTION: "code.function", + + /** + * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + */ + CODE_NAMESPACE: "code.namespace", + + /** + * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + */ + CODE_FILEPATH: "code.filepath", + + /** + * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + */ + CODE_LINENO: "code.lineno", + + /** + * The value `aws-api`. + */ + RPC_SYSTEM: "rpc.system", + + /** + * The name of the service to which a request is made, as returned by the AWS SDK. + */ + RPC_SERVICE: "rpc.service", + + /** + * The name of the operation corresponding to the request, as returned by the AWS SDK. + */ + RPC_METHOD: "rpc.method", + + /** + * The keys in the `RequestItems` object field. + */ + AWS_DYNAMODB_TABLE_NAMES: "aws.dynamodb.table_names", + + /** + * The JSON-serialized value of each item in the `ConsumedCapacity` response field. + */ + AWS_DYNAMODB_CONSUMED_CAPACITY: "aws.dynamodb.consumed_capacity", + + /** + * The JSON-serialized value of the `ItemCollectionMetrics` response field. + */ + AWS_DYNAMODB_ITEM_COLLECTION_METRICS: "aws.dynamodb.item_collection_metrics", + + /** + * The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. + */ + AWS_DYNAMODB_PROVISIONED_READ_CAPACITY: "aws.dynamodb.provisioned_read_capacity", + + /** + * The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. + */ + AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY: "aws.dynamodb.provisioned_write_capacity", + + /** + * The value of the `ConsistentRead` request parameter. + */ + AWS_DYNAMODB_CONSISTENT_READ: "aws.dynamodb.consistent_read", + + /** + * The value of the `ProjectionExpression` request parameter. + */ + AWS_DYNAMODB_PROJECTION: "aws.dynamodb.projection", + + /** + * The value of the `Limit` request parameter. + */ + AWS_DYNAMODB_LIMIT: "aws.dynamodb.limit", + + /** + * The value of the `AttributesToGet` request parameter. + */ + AWS_DYNAMODB_ATTRIBUTES_TO_GET: "aws.dynamodb.attributes_to_get", + + /** + * The value of the `IndexName` request parameter. + */ + AWS_DYNAMODB_INDEX_NAME: "aws.dynamodb.index_name", + + /** + * The value of the `Select` request parameter. + */ + AWS_DYNAMODB_SELECT: "aws.dynamodb.select", + + /** + * The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. + */ + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES: "aws.dynamodb.global_secondary_indexes", + + /** + * The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. + */ + AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES: "aws.dynamodb.local_secondary_indexes", + + /** + * The value of the `ExclusiveStartTableName` request parameter. + */ + AWS_DYNAMODB_EXCLUSIVE_START_TABLE: "aws.dynamodb.exclusive_start_table", + + /** + * The the number of items in the `TableNames` response parameter. + */ + AWS_DYNAMODB_TABLE_COUNT: "aws.dynamodb.table_count", + + /** + * The value of the `ScanIndexForward` request parameter. + */ + AWS_DYNAMODB_SCAN_FORWARD: "aws.dynamodb.scan_forward", + + /** + * The value of the `Segment` request parameter. + */ + AWS_DYNAMODB_SEGMENT: "aws.dynamodb.segment", + + /** + * The value of the `TotalSegments` request parameter. + */ + AWS_DYNAMODB_TOTAL_SEGMENTS: "aws.dynamodb.total_segments", + + /** + * The value of the `Count` response parameter. + */ + AWS_DYNAMODB_COUNT: "aws.dynamodb.count", + + /** + * The value of the `ScannedCount` response parameter. + */ + AWS_DYNAMODB_SCANNED_COUNT: "aws.dynamodb.scanned_count", + + /** + * The JSON-serialized value of each item in the `AttributeDefinitions` request field. + */ + AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS: "aws.dynamodb.attribute_definitions", + + /** + * The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` request field. + */ + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES: "aws.dynamodb.global_secondary_index_updates", + + /** + * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. + */ + MESSAGING_OPERATION: "messaging.operation", + + /** + * Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message_id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. + * + * Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. + */ + MESSAGING_KAFKA_MESSAGE_KEY: "messaging.kafka.message_key", + + /** + * Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. + */ + MESSAGING_KAFKA_CONSUMER_GROUP: "messaging.kafka.consumer_group", + + /** + * Client Id for the Consumer or Producer that is handling the message. + */ + MESSAGING_KAFKA_CLIENT_ID: "messaging.kafka.client_id", + + /** + * Partition the message is sent to. + */ + MESSAGING_KAFKA_PARTITION: "messaging.kafka.partition", + + /** + * A boolean that is true if the message is a tombstone. + */ + MESSAGING_KAFKA_TOMBSTONE: "messaging.kafka.tombstone", + + /** + * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. + */ + RPC_GRPC_STATUS_CODE: "rpc.grpc.status_code", + + // Manually defined and not YET in the YAML + + // HTTP + HTTP_ERROR_NAME: 'http.error_name', + HTTP_ERROR_MESSAGE: 'http.error_message', + HTTP_STATUS_TEXT: 'http.status_text', + + // GRPC + GRPC_KIND: 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD: 'grpc.method', + GRPC_STATUS_CODE: 'grpc.status_code', + GRPC_ERROR_NAME: 'grpc.error_name', + GRPC_ERROR_MESSAGE: 'grpc.error_message', +} + +// Enum definitions + +export enum DbSystemValues { + /** Some other SQL database. Fallback only. See notes. */ + OTHER_SQL = "other_sql", + /** Microsoft SQL Server. */ + MSSQL = "mssql", + /** MySQL. */ + MYSQL = "mysql", + /** Oracle Database. */ + ORACLE = "oracle", + /** IBM Db2. */ + DB2 = "db2", + /** PostgreSQL. */ + POSTGRESQL = "postgresql", + /** Amazon Redshift. */ + REDSHIFT = "redshift", + /** Apache Hive. */ + HIVE = "hive", + /** Cloudscape. */ + CLOUDSCAPE = "cloudscape", + /** HyperSQL DataBase. */ + HSQLDB = "hsqldb", + /** Progress Database. */ + PROGRESS = "progress", + /** SAP MaxDB. */ + MAXDB = "maxdb", + /** SAP HANA. */ + HANADB = "hanadb", + /** Ingres. */ + INGRES = "ingres", + /** FirstSQL. */ + FIRSTSQL = "firstsql", + /** EnterpriseDB. */ + EDB = "edb", + /** InterSystems Caché. */ + CACHE = "cache", + /** Adabas (Adaptable Database System). */ + ADABAS = "adabas", + /** Firebird. */ + FIREBIRD = "firebird", + /** Apache Derby. */ + DERBY = "derby", + /** FileMaker. */ + FILEMAKER = "filemaker", + /** Informix. */ + INFORMIX = "informix", + /** InstantDB. */ + INSTANTDB = "instantdb", + /** InterBase. */ + INTERBASE = "interbase", + /** MariaDB. */ + MARIADB = "mariadb", + /** Netezza. */ + NETEZZA = "netezza", + /** Pervasive PSQL. */ + PERVASIVE = "pervasive", + /** PointBase. */ + POINTBASE = "pointbase", + /** SQLite. */ + SQLITE = "sqlite", + /** Sybase. */ + SYBASE = "sybase", + /** Teradata. */ + TERADATA = "teradata", + /** Vertica. */ + VERTICA = "vertica", + /** H2. */ + H2 = "h2", + /** ColdFusion IMQ. */ + COLDFUSION = "coldfusion", + /** Apache Cassandra. */ + CASSANDRA = "cassandra", + /** Apache HBase. */ + HBASE = "hbase", + /** MongoDB. */ + MONGODB = "mongodb", + /** Redis. */ + REDIS = "redis", + /** Couchbase. */ + COUCHBASE = "couchbase", + /** CouchDB. */ + COUCHDB = "couchdb", + /** Microsoft Azure Cosmos DB. */ + COSMOSDB = "cosmosdb", + /** Amazon DynamoDB. */ + DYNAMODB = "dynamodb", + /** Neo4j. */ + NEO4J = "neo4j", + /** Apache Geode. */ + GEODE = "geode", + /** Elasticsearch. */ + ELASTICSEARCH = "elasticsearch", +} + + +export enum NetTransportValues { + /** IP.TCP. */ + IP_TCP = "IP.TCP", + /** IP.UDP. */ + IP_UDP = "IP.UDP", + /** Another IP-based protocol. */ + IP = "IP", + /** Unix Domain socket. See below. */ + UNIX = "Unix", + /** Named or anonymous pipe. See note below. */ + PIPE = "pipe", + /** In-process communication. */ + INPROC = "inproc", + /** Something else (non IP-based). */ + OTHER = "other", +} + + +export enum DbCassandraConsistencyLevelValues { + /** ALL. */ + ALL = "ALL", + /** EACH_QUORUM. */ + EACH_QUORUM = "EACH_QUORUM", + /** QUORUM. */ + QUORUM = "QUORUM", + /** LOCAL_QUORUM. */ + LOCAL_QUORUM = "LOCAL_QUORUM", + /** ONE. */ + ONE = "ONE", + /** TWO. */ + TWO = "TWO", + /** THREE. */ + THREE = "THREE", + /** LOCAL_ONE. */ + LOCAL_ONE = "LOCAL_ONE", + /** ANY. */ + ANY = "ANY", + /** SERIAL. */ + SERIAL = "SERIAL", + /** LOCAL_SERIAL. */ + LOCAL_SERIAL = "LOCAL_SERIAL", +} + + +export enum FaasTriggerValues { + /** A response to some data source operation such as a database or filesystem read/write. */ + DATASOURCE = "datasource", + /** To provide an answer to an inbound HTTP request. */ + HTTP = "http", + /** A function is set to be executed when messages are sent to a messaging system. */ + PUBSUB = "pubsub", + /** A function is scheduled to be executed regularly. */ + TIMER = "timer", + /** If none of the others apply. */ + OTHER = "other", +} + + +export enum FaasDocumentOperationValues { + /** When a new object is created. */ + INSERT = "insert", + /** When an object is modified. */ + EDIT = "edit", + /** When an object is deleted. */ + DELETE = "delete", +} + + +export enum HttpFlavorValues { + /** HTTP 1.0. */ + HTTP_1_0 = "1.0", + /** HTTP 1.1. */ + HTTP_1_1 = "1.1", + /** HTTP 2. */ + HTTP_2_0 = "2.0", + /** SPDY protocol. */ + SPDY = "SPDY", + /** QUIC protocol. */ + QUIC = "QUIC", +} + + +export enum MessagingDestinationKindValues { + /** A message sent to a queue. */ + QUEUE = "queue", + /** A message sent to a topic. */ + TOPIC = "topic", +} + + +export enum FaasInvokedProviderValues { + /** Amazon Web Services. */ + AWS = "aws", + /** Microsoft Azure. */ + AZURE = "azure", + /** Google Cloud Platform. */ + GCP = "gcp", +} + + +export enum MessagingOperationValues { + /** receive. */ + RECEIVE = "receive", + /** process. */ + PROCESS = "process", +} + + +export enum RpcGrpcStatusCodeValues { + /** OK. */ + OK = 0, + /** CANCELLED. */ + CANCELLED = 1, + /** UNKNOWN. */ + UNKNOWN = 2, + /** INVALID_ARGUMENT. */ + INVALID_ARGUMENT = 3, + /** DEADLINE_EXCEEDED. */ + DEADLINE_EXCEEDED = 4, + /** NOT_FOUND. */ + NOT_FOUND = 5, + /** ALREADY_EXISTS. */ + ALREADY_EXISTS = 6, + /** PERMISSION_DENIED. */ + PERMISSION_DENIED = 7, + /** RESOURCE_EXHAUSTED. */ + RESOURCE_EXHAUSTED = 8, + /** FAILED_PRECONDITION. */ + FAILED_PRECONDITION = 9, + /** ABORTED. */ + ABORTED = 10, + /** OUT_OF_RANGE. */ + OUT_OF_RANGE = 11, + /** UNIMPLEMENTED. */ + UNIMPLEMENTED = 12, + /** INTERNAL. */ + INTERNAL = 13, + /** UNAVAILABLE. */ + UNAVAILABLE = 14, + /** DATA_LOSS. */ + DATA_LOSS = 15, + /** UNAUTHENTICATED. */ + UNAUTHENTICATED = 16, +} diff --git a/packages/opentelemetry-semantic-conventions/src/trace/database.ts b/packages/opentelemetry-semantic-conventions/src/trace/database.ts deleted file mode 100644 index b8e4bfa6de3..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/database.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * Database attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md - */ -export const DatabaseAttribute = { - // Connection-level attributes - - /** - * An identifier for the database management system (DBMS) product being used. - * - * @remarks - * Required. - */ - DB_SYSTEM: 'db.system', - - /** - * The connection string used to connect to the database. - * It is recommended to remove embedded credentials. - * - * @remarks - * Optional. - */ - DB_CONNECTION_STRING: 'db.connection_string', - - /** - * Username for accessing the database, e.g., "readonly_user" or "reporting_user". - * - * @remarks - * Optional. - */ - DB_USER: 'db.user', - - // Please see ./general.ts for NET_PEER_NAME, NET_PEER_IP, NET_PEER_PORT, NET_TRANSPORT - - // Call-level attributes - - /** - * If no [tech-specific attribute](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md#call-level-attributes-for-specific-technologies) - * is defined in the list below, - * this attribute is used to report the name of the database being accessed. - * For commands that switch the database,this should be set to the - * target database (even if the command fails). - * - * @remarks - * Required if applicable and no more specific attribute is defined. - */ - DB_NAME: 'db.name', - - /** - * The database statement being executed. - * Note that the value may be sanitized to exclude sensitive information. - * E.g., for db.system="other_sql", "SELECT * FROM wuser_table"; - * for db.system="redis", "SET mykey 'WuValue'". - * - * @remarks - * Required if applicable. - */ - DB_STATEMENT: 'db.statement', - - /** - * The name of the operation being executed, - * e.g. the MongoDB command name such as findAndModify. - * While it would semantically make sense to set this, - * e.g., to an SQL keyword like SELECT or INSERT, - * it is not recommended to attempt any client-side parsing of - * db.statement just to get this property (the back end can do that if required). - * - * @remarks - * Required if db.statement is not applicable. - */ - DB_OPERATION: 'db.operation', - - // Connection-level attributes for specific technologies - - /** - * The instance name connecting to. - * This name is used to determine the port of a named instance. - * - * @remarks - * If setting a `db.mssql.instance_name`, - * `net.peer.port` is no longer required (but still recommended if non-standard) - */ - DB_MSSSQL_INSTANCE_NAME: 'db.mssql.instance_name', - - /** - * The fully-qualified class name of the Java Database Connectivity (JDBC) driver used to connect, - * e.g., "org.postgresql.Driver" or "com.microsoft.sqlserver.jdbc.SQLServerDriver". - * - * @remarks - * Optional. - */ - DB_JDBC_DRIVER_CLASSNAME: 'db.jdbc.driver_classname', - - // Call-level attributes for specific technologies - - /** - * The name of the keyspace being accessed. To be used instead of the generic db.name attribute. - * - * @remarks - * Required. - */ - DB_CASSANDRA_KEYSPACE: 'db.cassandra.keyspace', - - /** - * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. - * To be used instead of the generic db.name attribute. - * - * @remarks - * Required. - */ - DB_HBASE_NAMESPACE: 'db.hbase.namespace', - - /** - * The index of the database being accessed as used in the [SELECT command](https://redis.io/commands/select), - * provided as an integer. To be used instead of the generic db.name attribute. - * - * @remarks - * Required if other than the default database (0). - */ - DB_REDIS_DATABASE_INDEX: 'db.redis.database_index', - - /** - * The collection being accessed within the database stated in db.name. - * - * @remarks - * Required. - */ - DB_MONGODB_COLLECTION: 'db.mongodb.collection', - - // Not in spec. - - /** Deprecated. Not in spec. */ - DB_TYPE: 'db.type', - - /** Deprecated. Not in spec. */ - DB_INSTANCE: 'db.instance', - - /** Deprecated. Not in spec. */ - DB_URL: 'db.url', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/faas.ts b/packages/opentelemetry-semantic-conventions/src/trace/faas.ts deleted file mode 100644 index aff39d78c91..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/faas.ts +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * FaaS (Function as a Service) attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/faas.md - */ -export const FaasAttribute = { - // General attributes - - /** - * The name of the function being executed. - * - * @remarks - * Required. - */ - FAAS_NAME: 'faas.name', - - /** - * The unique ID of the function being executed. - * For example, in AWS Lambda this field corresponds to the ARN value, in GCP to the URI of the resource, and in Azure to the FunctionDirectory field. - * - * @remarks - * Required. - */ - FAAS_ID: 'faas.id', - - /** - * The version string of the function being executed. - * - * @remarks - * Optional. - */ - FAAS_VERSION: 'faas.version', - - /** - * The execution environment ID as a string. - * - * @remarks - * Optional. - */ - FAAS_INSTANCE: 'faas.instance', - - /** - * Type of the trigger on which the function is executed. - * Possible values: datasource, http, pubsub, timer, other - * - * @remarks - * Required if applicable. - */ - FAAS_TRIGGER: 'faas.trigger', - - /** - * The execution ID of the current function execution. - * - * @remarks - * Optional. - */ - FAAS_EXECUTION: 'faas.execution', - - // Incoming Invocations - - /** - * A boolean that is true if the serverless function is executed for the first time (aka cold-start). - * - * @remarks - * Optional. - */ - FAAS_COLD_START: 'faas.coldstart', - - // Outgoing Invocations - // This section describes outgoing FaaS invocations as they are reported by a client calling a FaaS instance. - - /** - * The name of the invoked function. - * SHOULD be equal to the faas.name resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_NAME: 'faas.invoked_name', - - /** - * The cloud provider of the invoked function. - * SHOULD be equal to the cloud.provider resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', - - /** - * The cloud region of the invoked function. - * SHOULD be equal to the cloud.region resource attribute of the invoked function. - * - * @remarks - * Optional. - */ - FAAS_INVOKED_REGION: 'faas.invoked_region', - - // Datesource Trigger - - /** - * The name of the source on which the triggering operation was performed. - * For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_COLLECTION: 'faas.document.collection', - - /** - * Describes the type of the operation that was performed on the data. - * MUST be one of the following or, if none of the listed values apply, a custom value: insert, edit, delete - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_OPERATION: 'faas.document.operation', - - /** - * A string containing the time when the data was accessed in the ISO 8601 format expressed in UTC. - * - * @remarks - * Required if applicable. - */ - FAAS_DOC_TIME: 'faas.document.time', - - /** - * The document name/table subjected to the operation. - * For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. - * - * @remarks - * Optional. - */ - FAAS_DOC_NAME: 'faas.document.name', - - // Timer Trigger - - /** - * A string containing the function invocation time in the ISO 8601 format expressed in UTC. - * Example: "2020-01-23T13:47:06Z" - * - * @remarks - * Required if applicable. - */ - FAAS_TIME: 'faas.time', - - /** - * A string containing the schedule period as Cron Expression. - * Example: "0/5 * * * ? *" - * - * @remarks - * Optional. - */ - FAAS_CRON: 'faas.cron', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/general.ts b/packages/opentelemetry-semantic-conventions/src/trace/general.ts deleted file mode 100644 index 6fa64be3aaf..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/general.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * General purpose networking attributes defined by the OpenTelemetry Semantic Conventions Specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md - */ -export const GeneralAttribute = { - NET_PEER_IP: 'net.peer.ip', - NET_PEER_ADDRESS: 'net.peer.address', - NET_PEER_HOSTNAME: 'net.peer.host', - NET_PEER_PORT: 'net.peer.port', - NET_PEER_NAME: 'net.peer.name', - NET_PEER_IPV4: 'net.peer.ipv4', - NET_PEER_IPV6: 'net.peer.ipv6', - NET_PEER_SERVICE: 'net.peer.service', - NET_HOST_IP: 'net.host.ip', - NET_HOST_PORT: 'net.host.port', - NET_HOST_NAME: 'net.host.name', - NET_TRANSPORT: 'net.transport', - - // These are used as potential values to NET_TRANSPORT - IP_TCP: 'IP.TCP', - IP_UDP: 'IP.UDP', - INPROC: 'inproc', - PIPE: 'pipe', - UNIX: 'Unix', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/http.ts b/packages/opentelemetry-semantic-conventions/src/trace/http.ts deleted file mode 100644 index 04618c5fd91..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/http.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -export const HttpAttribute = { - HTTP_HOST: 'http.host', - HTTP_METHOD: 'http.method', - HTTP_TARGET: 'http.target', - HTTP_ROUTE: 'http.route', - HTTP_URL: 'http.url', - HTTP_STATUS_CODE: 'http.status_code', - HTTP_STATUS_TEXT: 'http.status_text', - HTTP_FLAVOR: 'http.flavor', - HTTP_SERVER_NAME: 'http.server_name', - HTTP_CLIENT_IP: 'http.client_ip', - HTTP_SCHEME: 'http.scheme', - HTTP_RESPONSE_CONTENT_LENGTH: 'http.response_content_length', - HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: - 'http.response_content_length_uncompressed', - HTTP_REQUEST_CONTENT_LENGTH: 'http.request_content_length', - HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: - 'http.request_content_length_uncompressed', - - // NOT ON OFFICIAL SPEC - HTTP_ERROR_NAME: 'http.error_name', - HTTP_ERROR_MESSAGE: 'http.error_message', - HTTP_USER_AGENT: 'http.user_agent', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/index.ts b/packages/opentelemetry-semantic-conventions/src/trace/index.ts index 20b5ebd5d57..322332d9050 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/index.ts @@ -1,24 +1 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -export * from './database'; -export * from './exception'; -export * from './general'; -export * from './http'; -export * from './os'; -export * from './rpc'; -export * from './faas'; -export * from './messaging'; +export * from "./SemanticAttribute"; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts b/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts deleted file mode 100644 index c5266a60a2d..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/messaging.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * messaging attribute names defined by the Opetelemetry Semantic Conventions specification - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md - */ -export const MessagingAttribute = { - /** - * A string identifying the messaging system. - * example: kafka, rabbitmq, sqs, sns - * - * @remarks - * Required. - */ - MESSAGING_SYSTEM: 'messaging.system', - - /** - * The message destination name. This might be equal to the span name but is required nevertheless. - * example: MyQueue, MyTopic - * - * @remarks - * Required. - */ - MESSAGING_DESTINATION: 'messaging.destination', - - /** - * The kind of message destination - * allowed values: queue, topic, - * - * @remarks - * Required only if the message destination is either a queue or topic. - */ - MESSAGING_DESTINATION_KIND: 'messaging.destination_kind', - - /** - * A boolean that is true if the message destination is temporary - * - * @remarks - * Conditional If missing, it is assumed to be false. - */ - MESSAGING_TEMP_DESTINATION: 'messaging.temp_destination', - - /** - * The kind of message destination - * allowed values: queue, topic, - * - * @remarks - * Required only if the message destination is either a queue or topic. - */ - MESSAGING_PROTOCOL: 'messaging.protocol', - - /** - * The version of the transport protocol. - * - * @remarks - * Optional. - */ - MESSAGING_PROTOCOL_VERSION: 'messaging.protocol_version', - - /** - * Connection string. - * example: https://queue.amazonaws.com/80398EXAMPLE/MyQueue - * - * @remarks - * Optional. - */ - MESSAGING_URL: 'messaging.url', - - /** - * A value used by the messaging system as an identifier for the message, represented as a string. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_ID: 'messaging.message_id', - - /** - * The conversation ID identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". - * - * @remarks - * Optional. - */ - MESSAGING_CONVERSATION_ID: 'messaging.conversation_id', - - /** - * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. - * Should be number. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: 'messaging.message_payload_size_bytes', - - /** - * The compressed size of the message payload in bytes. - * Should be number. - * - * @remarks - * Optional. - */ - MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: - 'messaging.message_payload_compressed_size_bytes', - - /** - * For message consumers only. - * allowed values: receive, process, - * - * @remarks - * Optional. - */ - MESSAGING_OPERATION: 'messaging.operation', - - // System specific attributes - MESSAGING_RABBITMQ_ROUTING_KEY: 'messaging.rabbitmq.routing_key', - MESSAGING_KAFKA_MESSAGE_KEY: 'messaging.kafka.message_key', - MESSAGING_KAFKA_CONSUMER_GROUP: 'messaging.kafka.consumer_group', - MESSAGING_KAFKA_CLIENT_ID: 'messaging.kafka.client_id', - MESSAGING_KAFKA_PARTITION: 'messaging.kafka.partition', - MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone', -}; - -export const MessagingOperationName = { - /** - * A message is sent to a destination by a message producer/client. - */ - SEND: 'send', - - /** - * A message is received from a destination by a message consumer/server. - */ - RECEIVE: 'receive', - - /** - * A message that was previously received from a destination is processed by a message consumer/server. - */ - PROCESS: 'process', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/os.ts b/packages/opentelemetry-semantic-conventions/src/trace/os.ts deleted file mode 100644 index 1ffa94ff560..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/os.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * The operating system (OS) on which the process represented by this resource is running. - * - * In case of virtualized environments, this is the operating system as it - * is observed by the process, i.e., the virtualized guest rather than the - * underlying host. - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/os.md - */ -export const OperatingSystem = { - /** - * The operating system type. - * This should be set to one of {@link OperatingSystemValues} - * E.g., "WINDOWS" - * - * @remarks - * Required. - */ - TYPE: 'os.type', - /** - * Human readable (not intended to be parsed) OS version information. - * E.g., "Microsoft Windows [Version 10.0.18363.778]" - * - * @remarks - * Required if applicable. - */ - DESCRIPTION: 'os.description', -}; - -/** - * {@link OperatingSystem.TYPE} SHOULD be set to one of the values - * listed below. - * If none of the listed values apply, a custom value best describing - * the family the operating system belongs to CAN be used. - */ -export const OperatingSystemValues = { - WINDOWS: 'WINDOWS', - LINUX: 'LINUX', - DARWIN: 'DARWIN', - FREEBSD: 'FREEBSD', - NETBSD: 'NETBSD', - OPENBSD: 'OPENBSD', - DRAGONFLYBSD: 'DRAGONFLYBSD', - HPUX: 'HPUX', - AIX: 'AIX', - SOLARIS: 'SOLARIS', - ZOS: 'ZOS', -}; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts b/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts deleted file mode 100644 index ec23d93bf37..00000000000 --- a/packages/opentelemetry-semantic-conventions/src/trace/rpc.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -export const RpcAttribute = { - /** - * A string identifying the remoting system. - * - * @remarks - * Required - */ - RPC_SYSTEM: 'rpc.system', - - /** - * The full name of the service being called, including its package name, if applicable. - * - * @remarks - * Not required, but recommended - */ - RPC_SERVICE: 'rpc.service', - - /** - * The name of the method being called, must be equal to the $method part in the span name. - * - * @remarks - * Not required, but recommended - */ - RPC_METHOD: 'rpc.method', - - // GRPC (no spec) - GRPC_KIND: 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD: 'grpc.method', - GRPC_STATUS_CODE: 'grpc.status_code', - GRPC_ERROR_NAME: 'grpc.error_name', - GRPC_ERROR_MESSAGE: 'grpc.error_message', -}; From 310d124460e43d3b4ce0df6d6fe90603e13040a0 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 02:17:15 +0100 Subject: [PATCH 03/14] fix: update packages to use the new `SemanticAttribute`-class Updates the different packages to fix the breaking change were there is only `SemanticAttribute` and `ResourceAttribute`-class defined in the `@opentelemetry/semantic-conventations`-package --- .../src/fetch.ts | 19 +- .../test/fetch.test.ts | 20 +- .../src/grpc-js/clientUtils.ts | 21 +- .../src/grpc-js/index.ts | 4 +- .../src/grpc-js/serverUtils.ts | 19 +- .../src/grpc/clientUtils.ts | 23 +- .../src/grpc/index.ts | 4 +- .../src/grpc/serverUtils.ts | 19 +- .../src/utils.ts | 76 +- .../test/functionals/http-enable.test.ts | 25 +- .../test/functionals/https-enable.test.ts | 25 +- .../test/functionals/utils.test.ts | 34 +- .../test/integrations/http-enable.test.ts | 14 +- .../test/integrations/https-enable.test.ts | 14 +- .../test/utils/assertSpan.ts | 39 +- .../src/xhr.ts | 19 +- .../test/unmocked.test.ts | 7 +- .../test/xhr.test.ts | 78 +- .../src/client/utils.ts | 21 +- .../src/server/clientStreamAndUnary.ts | 13 +- .../src/server/patchServer.ts | 4 +- .../src/server/serverStreamAndBidi.ts | 8 +- .../opentelemetry-plugin-grpc/src/grpc.ts | 39 +- .../opentelemetry-plugin-http/src/utils.ts | 76 +- .../test/functionals/http-enable.test.ts | 25 +- .../test/functionals/utils.test.ts | 34 +- .../test/integrations/http-enable.test.ts | 14 +- .../test/utils/assertSpan.ts | 39 +- .../test/functionals/https-enable.test.ts | 26 +- .../test/integrations/https-enable.test.ts | 14 +- .../test/utils/assertSpan.ts | 31 +- .../src/events.ts | 2 +- .../src/index.ts | 6 +- .../src/resource/ResourceAttribute.ts | 696 +++++----- .../src/resource/index.ts | 17 +- .../src/trace/SemanticAttribute.ts | 1138 ++++++++--------- .../src/trace/index.ts | 17 +- packages/opentelemetry-tracing/src/Span.ts | 16 +- .../opentelemetry-tracing/test/Span.test.ts | 8 +- packages/opentelemetry-web/src/utils.ts | 4 +- 40 files changed, 1391 insertions(+), 1317 deletions(-) diff --git a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index b263c110152..93d46e573ad 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -23,7 +23,7 @@ import { import * as core from '@opentelemetry/core'; import * as web from '@opentelemetry/web'; import { AttributeNames } from './enums/AttributeNames'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { FetchError, FetchResponse, SpanData } from './types'; import { VERSION } from './version'; @@ -121,16 +121,19 @@ export class FetchInstrumentation extends InstrumentationBase< response: FetchResponse ): void { const parsedUrl = web.parseUrl(response.url); - span.setAttribute(HttpAttribute.HTTP_STATUS_CODE, response.status); + span.setAttribute(SemanticAttribute.HTTP_STATUS_CODE, response.status); if (response.statusText != null) { - span.setAttribute(HttpAttribute.HTTP_STATUS_TEXT, response.statusText); + span.setAttribute( + SemanticAttribute.HTTP_STATUS_TEXT, + response.statusText + ); } - span.setAttribute(HttpAttribute.HTTP_HOST, parsedUrl.host); + span.setAttribute(SemanticAttribute.HTTP_HOST, parsedUrl.host); span.setAttribute( - HttpAttribute.HTTP_SCHEME, + SemanticAttribute.HTTP_SCHEME, parsedUrl.protocol.replace(':', '') ); - span.setAttribute(HttpAttribute.HTTP_USER_AGENT, navigator.userAgent); + span.setAttribute(SemanticAttribute.HTTP_USER_AGENT, navigator.userAgent); } /** @@ -191,8 +194,8 @@ export class FetchInstrumentation extends InstrumentationBase< kind: api.SpanKind.CLIENT, attributes: { [AttributeNames.COMPONENT]: this.moduleName, - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_URL]: url, + [SemanticAttribute.HTTP_METHOD]: method, + [SemanticAttribute.HTTP_URL]: url, }, }); } diff --git a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 9212774ca10..59cc0dd41f4 100644 --- a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -37,7 +37,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { FetchInstrumentation, FetchInstrumentationConfig } from '../src'; import { AttributeNames } from '../src/enums/AttributeNames'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -335,37 +335,37 @@ describe('fetch', () => { assert.strictEqual( attributes[keys[1]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[2]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.ok( attributes[keys[4]] === 'OK' || attributes[keys[4]] === '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.ok( (attributes[keys[5]] as string).indexOf('localhost') === 0, - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.ok( (attributes[keys[8]] as number) > 0, - `attributes ${HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` + `attributes ${SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` ); assert.strictEqual(keys.length, 9, 'number of attributes is wrong'); @@ -749,7 +749,7 @@ describe('fetch', () => { assert.strictEqual( attributes[keys[3]], 200, - `Missing basic attribute ${HttpAttribute.HTTP_STATUS_CODE}` + `Missing basic attribute ${SemanticAttribute.HTTP_STATUS_CODE}` ); }); }); diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts index 645149c051b..dbd20f8b3e3 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts @@ -24,7 +24,7 @@ import { propagation, context, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import { _grpcStatusCodeToSpanStatus, @@ -89,16 +89,19 @@ export function makeGrpcClientRemoteCall( if (err) { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.UNSET.toString() ); } @@ -124,8 +127,8 @@ export function makeGrpcClientRemoteCall( } span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [SemanticAttribute.GRPC_METHOD]: original.path, + [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -154,8 +157,8 @@ export function makeGrpcClientRemoteCall( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index cfb83288bb0..891c0169562 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -42,7 +42,7 @@ import { setSpan, diag, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { shouldNotTraceServerCall, handleServerFunction, @@ -197,7 +197,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [SemanticAttribute.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts index eac6238350d..91bc12ef6cf 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts @@ -21,7 +21,7 @@ */ import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import type { ServerCallWithMeta, @@ -70,7 +70,7 @@ function serverStreamAndBidiHandler( code: SpanStatusCode.UNSET, }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); @@ -90,8 +90,8 @@ function serverStreamAndBidiHandler( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -121,16 +121,19 @@ function clientStreamAndUnaryHandler( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts index fda7b4c7ed6..dca2d7ebcdb 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts @@ -17,7 +17,7 @@ import type * as grpcTypes from 'grpc'; import type * as events from 'events'; import { SendUnaryDataCallback, GrpcClientFunc } from './types'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { context, Span, @@ -55,16 +55,19 @@ export const makeGrpcClientRemoteCall = function ( if (err) { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.RPC_GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -96,8 +99,8 @@ export const makeGrpcClientRemoteCall = function ( span.addEvent('sent'); span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [SemanticAttribute.GRPC_METHOD]: original.path, + [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -123,8 +126,8 @@ export const makeGrpcClientRemoteCall = function ( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); } @@ -135,7 +138,7 @@ export const makeGrpcClientRemoteCall = function ( (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, status.code.toString() ); endSpan(); diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 0fe0d30bab9..12887ed5c58 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -29,7 +29,7 @@ import { GrpcClientFunc, } from './types'; import { GrpcInstrumentationConfig } from '../types'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { context, propagation, @@ -205,7 +205,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [SemanticAttribute.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts index cd928a4665f..5b2f30c9d0a 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts @@ -17,7 +17,7 @@ import type * as grpcTypes from 'grpc'; import { SendUnaryDataCallback, ServerCallWithMeta } from './types'; import { GrpcNativeInstrumentation } from './'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { context, Span, SpanStatusCode } from '@opentelemetry/api'; import { _grpcStatusCodeToOpenTelemetryStatusCode, @@ -47,16 +47,19 @@ export const clientStreamAndUnaryHandler = function ( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -89,7 +92,7 @@ export const serverStreamAndBidiHandler = function ( call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, call.status.code.toString() ); @@ -107,8 +110,8 @@ export const serverStreamAndBidiHandler = function ( }); span.addEvent('finished with error'); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); diff --git a/packages/opentelemetry-instrumentation-http/src/utils.ts b/packages/opentelemetry-instrumentation-http/src/utils.ts index d0ac02cabab..5494f49f175 100644 --- a/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -20,8 +20,8 @@ import { SpanStatus, } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import { ClientRequest, @@ -158,8 +158,8 @@ export const setSpanWithError = ( const message = error.message; span.setAttributes({ - [HttpAttribute.HTTP_ERROR_NAME]: error.name, - [HttpAttribute.HTTP_ERROR_MESSAGE]: message, + [SemanticAttribute.HTTP_ERROR_NAME]: error.name, + [SemanticAttribute.HTTP_ERROR_MESSAGE]: message, }); if (!obj) { @@ -194,9 +194,11 @@ export const setRequestContentLengthAttribute = ( if (length === null) return; if (isCompressed(request.headers)) { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; + attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; } else { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED] = length; + attributes[ + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + ] = length; } }; @@ -213,10 +215,10 @@ export const setResponseContentLengthAttribute = ( if (length === null) return; if (isCompressed(response.headers)) { - attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; + attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; } else { attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -341,18 +343,18 @@ export const getOutgoingRequestAttributes = ( const headers = requestOptions.headers || {}; const userAgent = headers['user-agent']; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( requestOptions, headers, `${options.component}:` ), - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_TARGET]: requestOptions.path || '/', - [GeneralAttribute.NET_PEER_NAME]: hostname, + [SemanticAttribute.HTTP_METHOD]: method, + [SemanticAttribute.HTTP_TARGET]: requestOptions.path || '/', + [SemanticAttribute.NET_PEER_NAME]: hostname, }; if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; } return attributes; }; @@ -364,11 +366,11 @@ export const getOutgoingRequestAttributes = ( export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { - attributes[HttpAttribute.HTTP_FLAVOR] = kind; + attributes[SemanticAttribute.HTTP_FLAVOR] = kind; if (kind.toUpperCase() !== 'QUIC') { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_TCP; + attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_UDP; + attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_UDP; } } return attributes; @@ -386,15 +388,15 @@ export const getOutgoingRequestAttributesOnResponse = ( const { statusCode, statusMessage, httpVersion, socket } = response; const { remoteAddress, remotePort } = socket; const attributes: SpanAttributes = { - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, + [SemanticAttribute.NET_PEER_IP]: remoteAddress, + [SemanticAttribute.NET_PEER_PORT]: remotePort, + [SemanticAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; setResponseContentLengthAttribute(response, attributes); if (statusCode) { - attributes[HttpAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[HttpAttribute.HTTP_STATUS_TEXT] = ( + attributes[SemanticAttribute.HTTP_STATUS_CODE] = statusCode; + attributes[SemanticAttribute.HTTP_STATUS_TEXT] = ( statusMessage || '' ).toUpperCase(); } @@ -425,31 +427,31 @@ export const getIncomingRequestAttributes = ( 'localhost'; const serverName = options.serverName; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( requestUrl, headers, `${options.component}:` ), - [HttpAttribute.HTTP_HOST]: host, - [GeneralAttribute.NET_HOST_NAME]: hostname, - [HttpAttribute.HTTP_METHOD]: method, + [SemanticAttribute.HTTP_HOST]: host, + [SemanticAttribute.NET_HOST_NAME]: hostname, + [SemanticAttribute.HTTP_METHOD]: method, }; if (typeof ips === 'string') { - attributes[HttpAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; + attributes[SemanticAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; } if (typeof serverName === 'string') { - attributes[HttpAttribute.HTTP_SERVER_NAME] = serverName; + attributes[SemanticAttribute.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[HttpAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[HttpAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; + attributes[SemanticAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; } if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; } setRequestContentLengthAttribute(request, attributes); @@ -483,16 +485,16 @@ export const getIncomingRequestAttributesOnResponse = ( : undefined; const attributes: SpanAttributes = { - [GeneralAttribute.NET_HOST_IP]: localAddress, - [GeneralAttribute.NET_HOST_PORT]: localPort, - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_STATUS_CODE]: statusCode, - [HttpAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), + [SemanticAttribute.NET_HOST_IP]: localAddress, + [SemanticAttribute.NET_HOST_PORT]: localPort, + [SemanticAttribute.NET_PEER_IP]: remoteAddress, + [SemanticAttribute.NET_PEER_PORT]: remotePort, + [SemanticAttribute.HTTP_STATUS_CODE]: statusCode, + [SemanticAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), }; if (route !== undefined) { - attributes[HttpAttribute.HTTP_ROUTE] = route; + attributes[SemanticAttribute.HTTP_ROUTE] = route; } return attributes; }; diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index 736daa62331..bd3cf611973 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -28,8 +28,8 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as nock from 'nock'; @@ -172,11 +172,11 @@ describe('HttpInstrumentation', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); }); @@ -249,25 +249,28 @@ describe('HttpInstrumentation', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); [ { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -658,7 +661,7 @@ describe('HttpInstrumentation', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index 86de0fbefa9..33083d3a01d 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -30,8 +30,8 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - GeneralAttribute, - HttpAttribute, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as fs from 'fs'; @@ -160,11 +160,11 @@ describe('HttpsInstrumentation', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); }); @@ -240,15 +240,15 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); @@ -256,10 +256,13 @@ describe('HttpsInstrumentation', () => { { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -658,7 +661,7 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index 26bef9061ce..a5b845d400f 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -21,7 +21,7 @@ import { TraceFlags, } from '@opentelemetry/api'; import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import { IncomingMessage, ServerResponse } from 'http'; @@ -258,10 +258,10 @@ describe('Utility', () => { utils.setSpanWithError(span, new Error(errorMessage), obj as any); const attributes = span.attributes; assert.strictEqual( - attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], errorMessage ); - assert.ok(attributes[HttpAttribute.HTTP_ERROR_NAME]); + assert.ok(attributes[SemanticAttribute.HTTP_ERROR_NAME]); } }); }); @@ -293,7 +293,7 @@ describe('Utility', () => { const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], '/test/toto'); + assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], '/test/toto'); }); it('should succesfully process without middleware stack', () => { @@ -303,7 +303,7 @@ describe('Utility', () => { const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], undefined); + assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], undefined); }); }); // Verify the key in the given attributes is set to the given value, @@ -313,14 +313,14 @@ describe('Utility', () => { key: string | undefined, value: number ) { - const httpAttributes = [ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + const SemanticAttributes = [ + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, ]; - for (const attr of httpAttributes) { + for (const attr of SemanticAttributes) { if (attr === key) { assert.strictEqual(attributes[attr], value); } else { @@ -341,7 +341,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -357,7 +357,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -373,7 +373,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, 1200 ); }); @@ -392,7 +392,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -411,7 +411,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -430,7 +430,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, 1200 ); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts index 9995cd985c9..1d89d9cee32 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts @@ -16,8 +16,9 @@ import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as url from 'url'; @@ -229,10 +230,13 @@ describe('HttpInstrumentation Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTP GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts index 7df9f3e771b..af60b3af346 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts @@ -16,8 +16,9 @@ import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; @@ -238,10 +239,13 @@ describe('HttpsInstrumentation Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTPS GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index 97e67f7557a..3fbd7d8f329 100644 --- a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -16,10 +16,7 @@ import { SpanKind, SpanStatus } from '@opentelemetry/api'; import { hrTimeToNanoseconds } from '@opentelemetry/core'; import { ReadableSpan } from '@opentelemetry/tracing'; -import { - GeneralAttribute, - HttpAttribute, -} from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import * as utils from '../../src/utils'; @@ -49,19 +46,19 @@ export const assertSpan = ( `${validations.component.toUpperCase()} ${validations.httpMethod}` ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], + span.attributes[SemanticAttribute.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], + span.attributes[SemanticAttribute.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], validations.httpStatusCode ); @@ -81,7 +78,7 @@ export const assertSpan = ( const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttribute.HTTP_USER_AGENT], userAgent ); } @@ -95,34 +92,34 @@ export const assertSpan = ( validations.resHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + span.attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); } } assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], + span.attributes[SemanticAttribute.NET_PEER_NAME], validations.hostname, 'must be consistent (PEER_NAME and hostname)' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], + span.attributes[SemanticAttribute.NET_PEER_IP], 'must have PEER_IP' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], + span.attributes[SemanticAttribute.NET_PEER_PORT], 'must have PEER_PORT' ); assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttribute.NET_PEER_NAME] as string ) > -1, 'must be consistent' ); @@ -136,13 +133,13 @@ export const assertSpan = ( validations.reqHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], + span.attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -150,16 +147,16 @@ export const assertSpan = ( } if (validations.serverName) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttribute.HTTP_SERVER_NAME], validations.serverName, ' must have serverName attribute' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], + span.attributes[SemanticAttribute.NET_HOST_PORT], 'must have HOST_PORT' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], + span.attributes[SemanticAttribute.NET_HOST_IP], 'must have HOST_IP' ); } diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts index 84c0b18c83f..29fc53f1c59 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts @@ -21,7 +21,7 @@ import { InstrumentationConfig, } from '@opentelemetry/instrumentation'; import { hrTime, isUrlIgnored, otperformance } from '@opentelemetry/core'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { addSpanNetworkEvents, getResource, @@ -145,20 +145,23 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { // content length comes from the PerformanceTiming resource; this ensures that our // matching logic found the right one assert.ok( - (span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] as any) > - 0 + (span.attributes[ + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH + ] as any) > 0 ); done(); }, 500); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts index 689e299aa8a..82750d7f43e 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts @@ -25,7 +25,7 @@ import { } from '@opentelemetry/propagator-b3'; import { ZoneContextManager } from '@opentelemetry/context-zone'; import * as tracing from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { PerformanceTimingNames as PTN, WebTracerProvider, @@ -289,39 +289,39 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.ok( (attributes[keys[2]] as number) > 0, - 'attributes ${HttpAttributes.HTTP_RESPONSE_CONTENT_SIZE} <= 0' + 'attributes ${SemanticAttributes.HTTP_RESPONSE_CONTENT_SIZE} <= 0' ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], 'OK', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], parseUrl(url).host, - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); @@ -681,7 +681,7 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[1]], secondUrl, - `attribute ${HttpAttribute.HTTP_URL} is wrong` + `attribute ${SemanticAttribute.HTTP_URL} is wrong` ); }); }); @@ -777,40 +777,40 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH} is wrong` + `attributes ${SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH} is wrong` ); assert.strictEqual( attributes[keys[3]], 400, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], 'Bad Request', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[7]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); @@ -912,35 +912,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); @@ -1004,35 +1004,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); @@ -1098,35 +1098,35 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[0]], 'GET', - `attributes ${HttpAttribute.HTTP_METHOD} is wrong` + `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${HttpAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttribute.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${HttpAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], '', - `attributes ${HttpAttribute.HTTP_STATUS_TEXT} is wrong` + `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], 'raw.githubusercontent.com', - `attributes ${HttpAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttribute.HTTP_HOST} is wrong` ); assert.ok( attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${HttpAttribute.HTTP_SCHEME} is wrong` + `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` ); assert.ok( attributes[keys[6]] !== '', - `attributes ${HttpAttribute.HTTP_USER_AGENT} is not defined` + `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` ); assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts index dc696b88fb6..13f2c08357c 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts @@ -26,7 +26,7 @@ import { setSpan, diag, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import { grpcStatusCodeToSpanStatus, @@ -114,16 +114,19 @@ export function makeGrpcClientRemoteCall( if (err) { if (err.code) { span.setStatus(grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } @@ -149,8 +152,8 @@ export function makeGrpcClientRemoteCall( } span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [SemanticAttribute.GRPC_METHOD]: original.path, + [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -179,8 +182,8 @@ export function makeGrpcClientRemoteCall( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts index 431f659966e..15a60757123 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts @@ -17,7 +17,7 @@ import { context, Span, SpanStatusCode } from '@opentelemetry/api'; import type { ServerCallWithMeta, SendUnaryDataCallback } from '../types'; import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import type { GrpcJsPlugin } from '../grpcJs'; import type * as grpcJs from '@grpc/grpc-js'; @@ -43,16 +43,19 @@ export function clientStreamAndUnaryHandler( code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts index 06ff464dfe5..c2c93947132 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts @@ -33,7 +33,7 @@ import { setSpan, diag, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { clientStreamAndUnaryHandler } from './clientStreamAndUnary'; import { serverStreamAndBidiHandler } from './serverStreamAndBidi'; import { methodIsIgnored } from '../utils'; @@ -112,7 +112,7 @@ export function patchServer( const span = plugin.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [SemanticAttribute.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts index 18341ff0eeb..215c4787641 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts @@ -15,7 +15,7 @@ */ import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import type * as grpcJs from '@grpc/grpc-js'; import type { GrpcJsPlugin } from '../grpcJs'; import { GrpcEmitter } from '../types'; @@ -58,7 +58,7 @@ export function serverStreamAndBidiHandler( code: SpanStatusCode.OK, }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); @@ -78,8 +78,8 @@ export function serverStreamAndBidiHandler( message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 9836d8bd229..ef598b4558d 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -26,7 +26,7 @@ import { setSpan, diag, } from '@opentelemetry/api'; -import { RpcAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import { BasePlugin } from '@opentelemetry/core'; import * as events from 'events'; import * as grpcTypes from 'grpc'; @@ -182,7 +182,7 @@ export class GrpcPlugin extends BasePlugin { const span = plugin._tracer .startSpan(spanName, spanOptions) .setAttributes({ - [RpcAttribute.GRPC_KIND]: spanOptions.kind, + [SemanticAttribute.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { @@ -257,16 +257,19 @@ export class GrpcPlugin extends BasePlugin { code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(RpcAttribute.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttribute.GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } @@ -300,7 +303,7 @@ export class GrpcPlugin extends BasePlugin { call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, call.status.code.toString() ); @@ -318,8 +321,8 @@ export class GrpcPlugin extends BasePlugin { }); span.addEvent('finished with error'); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -420,18 +423,18 @@ export class GrpcPlugin extends BasePlugin { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } @@ -463,8 +466,8 @@ export class GrpcPlugin extends BasePlugin { span.addEvent('sent'); span.setAttributes({ - [RpcAttribute.GRPC_METHOD]: original.path, - [RpcAttribute.GRPC_KIND]: SpanKind.CLIENT, + [SemanticAttribute.GRPC_METHOD]: original.path, + [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, }); this._setSpanContext(metadata); @@ -490,8 +493,8 @@ export class GrpcPlugin extends BasePlugin { message: err.message, }); span.setAttributes({ - [RpcAttribute.GRPC_ERROR_NAME]: err.name, - [RpcAttribute.GRPC_ERROR_MESSAGE]: err.message, + [SemanticAttribute.GRPC_ERROR_NAME]: err.name, + [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); } @@ -502,7 +505,7 @@ export class GrpcPlugin extends BasePlugin { (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - RpcAttribute.GRPC_STATUS_CODE, + SemanticAttribute.GRPC_STATUS_CODE, status.code.toString() ); endSpan(); diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index e23b5aeb0f2..ea9c75d258b 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -20,8 +20,8 @@ import { SpanStatus, } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import { ClientRequest, @@ -157,8 +157,8 @@ export const setSpanWithError = ( const message = error.message; span.setAttributes({ - [HttpAttribute.HTTP_ERROR_NAME]: error.name, - [HttpAttribute.HTTP_ERROR_MESSAGE]: message, + [SemanticAttribute.HTTP_ERROR_NAME]: error.name, + [SemanticAttribute.HTTP_ERROR_MESSAGE]: message, }); if (!obj) { @@ -193,9 +193,11 @@ export const setRequestContentLengthAttribute = ( if (length === null) return; if (isCompressed(request.headers)) { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; + attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; } else { - attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED] = length; + attributes[ + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + ] = length; } }; @@ -212,10 +214,10 @@ export const setResponseContentLengthAttribute = ( if (length === null) return; if (isCompressed(response.headers)) { - attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; + attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; } else { attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -337,18 +339,18 @@ export const getOutgoingRequestAttributes = ( const headers = requestOptions.headers || {}; const userAgent = headers['user-agent']; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( requestOptions, headers, `${options.component}:` ), - [HttpAttribute.HTTP_METHOD]: method, - [HttpAttribute.HTTP_TARGET]: requestOptions.path || '/', - [GeneralAttribute.NET_PEER_NAME]: hostname, + [SemanticAttribute.HTTP_METHOD]: method, + [SemanticAttribute.HTTP_TARGET]: requestOptions.path || '/', + [SemanticAttribute.NET_PEER_NAME]: hostname, }; if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; } return attributes; }; @@ -360,11 +362,11 @@ export const getOutgoingRequestAttributes = ( export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { - attributes[HttpAttribute.HTTP_FLAVOR] = kind; + attributes[SemanticAttribute.HTTP_FLAVOR] = kind; if (kind.toUpperCase() !== 'QUIC') { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_TCP; + attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { - attributes[GeneralAttribute.NET_TRANSPORT] = GeneralAttribute.IP_UDP; + attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_UDP; } } return attributes; @@ -383,16 +385,16 @@ export const getOutgoingRequestAttributesOnResponse = ( const { remoteAddress, remotePort } = socket; const attributes: SpanAttributes = { - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, + [SemanticAttribute.NET_PEER_IP]: remoteAddress, + [SemanticAttribute.NET_PEER_PORT]: remotePort, + [SemanticAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; setResponseContentLengthAttribute(response, attributes); if (statusCode) { - attributes[HttpAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[HttpAttribute.HTTP_STATUS_TEXT] = ( + attributes[SemanticAttribute.HTTP_STATUS_CODE] = statusCode; + attributes[SemanticAttribute.HTTP_STATUS_TEXT] = ( statusMessage || '' ).toUpperCase(); } @@ -423,31 +425,31 @@ export const getIncomingRequestAttributes = ( 'localhost'; const serverName = options.serverName; const attributes: SpanAttributes = { - [HttpAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( requestUrl, headers, `${options.component}:` ), - [HttpAttribute.HTTP_HOST]: host, - [GeneralAttribute.NET_HOST_NAME]: hostname, - [HttpAttribute.HTTP_METHOD]: method, + [SemanticAttribute.HTTP_HOST]: host, + [SemanticAttribute.NET_HOST_NAME]: hostname, + [SemanticAttribute.HTTP_METHOD]: method, }; if (typeof ips === 'string') { - attributes[HttpAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; + attributes[SemanticAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; } if (typeof serverName === 'string') { - attributes[HttpAttribute.HTTP_SERVER_NAME] = serverName; + attributes[SemanticAttribute.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[HttpAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[HttpAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; + attributes[SemanticAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; } if (userAgent !== undefined) { - attributes[HttpAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; } setRequestContentLengthAttribute(request, attributes); @@ -482,16 +484,16 @@ export const getIncomingRequestAttributesOnResponse = ( : undefined; const attributes: SpanAttributes = { - [GeneralAttribute.NET_HOST_IP]: localAddress, - [GeneralAttribute.NET_HOST_PORT]: localPort, - [GeneralAttribute.NET_PEER_IP]: remoteAddress, - [GeneralAttribute.NET_PEER_PORT]: remotePort, - [HttpAttribute.HTTP_STATUS_CODE]: statusCode, - [HttpAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), + [SemanticAttribute.NET_HOST_IP]: localAddress, + [SemanticAttribute.NET_HOST_PORT]: localPort, + [SemanticAttribute.NET_PEER_IP]: remoteAddress, + [SemanticAttribute.NET_PEER_PORT]: remotePort, + [SemanticAttribute.HTTP_STATUS_CODE]: statusCode, + [SemanticAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), }; if (route !== undefined) { - attributes[HttpAttribute.HTTP_ROUTE] = route; + attributes[SemanticAttribute.HTTP_ROUTE] = route; } return attributes; }; diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index 310908abea0..bc3fd6eac51 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -28,8 +28,8 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - HttpAttribute, - GeneralAttribute, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; @@ -174,11 +174,11 @@ describe('HttpPlugin', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); }); @@ -258,25 +258,28 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); [ { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + '1.1' + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -667,7 +670,7 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index c14ee4bbb7c..d16d7064124 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -21,7 +21,7 @@ import { TraceFlags, } from '@opentelemetry/api'; import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import { IncomingMessage, ServerResponse } from 'http'; @@ -257,10 +257,10 @@ describe('Utility', () => { utils.setSpanWithError(span, new Error(errorMessage), obj as any); const attributes = span.attributes; assert.strictEqual( - attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], errorMessage ); - assert.ok(attributes[HttpAttribute.HTTP_ERROR_NAME]); + assert.ok(attributes[SemanticAttribute.HTTP_ERROR_NAME]); } }); }); @@ -293,7 +293,7 @@ describe('Utility', () => { request, response ); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], '/test/toto'); + assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], '/test/toto'); }); it('should succesfully process without middleware stack', () => { @@ -303,7 +303,7 @@ describe('Utility', () => { request, response ); - assert.deepEqual(attributes[HttpAttribute.HTTP_ROUTE], undefined); + assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], undefined); }); }); @@ -314,14 +314,14 @@ describe('Utility', () => { key: string | undefined, value: number ) { - const httpAttributes = [ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + const SemanticAttributes = [ + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, ]; - for (const attr of httpAttributes) { + for (const attr of SemanticAttributes) { if (attr === key) { assert.strictEqual(attributes[attr], value); } else { @@ -342,7 +342,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -358,7 +358,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -374,7 +374,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH, + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, 1200 ); }); @@ -393,7 +393,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -412,7 +412,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -431,7 +431,7 @@ describe('Utility', () => { verifyValueInAttributes( attributes, - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, 1200 ); }); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 65b0040eabe..49cc71712a9 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -16,8 +16,9 @@ import { SpanKind, Span, context } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; @@ -221,10 +222,13 @@ describe('HttpPlugin Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTP GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts index 4db2a99b279..128a08037da 100644 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts @@ -16,10 +16,7 @@ import { SpanKind, SpanStatus } from '@opentelemetry/api'; import { hrTimeToNanoseconds } from '@opentelemetry/core'; import { ReadableSpan } from '@opentelemetry/tracing'; -import { - GeneralAttribute, - HttpAttribute, -} from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import * as utils from '../../src/utils'; @@ -46,19 +43,19 @@ export const assertSpan = ( assert.strictEqual(span.kind, kind); assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], + span.attributes[SemanticAttribute.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], + span.attributes[SemanticAttribute.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], validations.httpStatusCode ); @@ -78,7 +75,7 @@ export const assertSpan = ( const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttribute.HTTP_USER_AGENT], userAgent ); } @@ -93,13 +90,13 @@ export const assertSpan = ( validations.resHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + span.attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -107,21 +104,21 @@ export const assertSpan = ( } assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], + span.attributes[SemanticAttribute.NET_PEER_NAME], validations.hostname, 'must be consistent (PEER_NAME and hostname)' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], + span.attributes[SemanticAttribute.NET_PEER_IP], 'must have PEER_IP' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], + span.attributes[SemanticAttribute.NET_PEER_PORT], 'must have PEER_PORT' ); assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttribute.NET_PEER_NAME] as string ) > -1, 'must be consistent' ); @@ -135,13 +132,13 @@ export const assertSpan = ( validations.reqHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH], + span.attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - HttpAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -150,16 +147,16 @@ export const assertSpan = ( if (validations.serverName) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttribute.HTTP_SERVER_NAME], validations.serverName, ' must have serverName attribute' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], + span.attributes[SemanticAttribute.NET_HOST_PORT], 'must have HOST_PORT' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], + span.attributes[SemanticAttribute.NET_HOST_IP], 'must have HOST_IP' ); } diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts index a4ad62f09e7..a4394a80704 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -31,8 +31,9 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; import { - GeneralAttribute, - HttpAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as fs from 'fs'; @@ -170,11 +171,11 @@ describe('HttpsPlugin', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); }); @@ -259,15 +260,15 @@ describe('HttpsPlugin', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[HttpAttribute.HTTP_CLIENT_IP], + incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], '' ); assert.strictEqual( - incomingSpan.attributes[GeneralAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[GeneralAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], serverPort ); @@ -275,10 +276,13 @@ describe('HttpsPlugin', () => { { span: incomingSpan, kind: SpanKind.SERVER }, { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); }); @@ -643,7 +647,7 @@ describe('HttpsPlugin', () => { assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts index 74a247e909e..6f4227ce8a0 100644 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -17,8 +17,9 @@ import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; import { SpanKind, Span, context } from '@opentelemetry/api'; import { - HttpAttribute, - GeneralAttribute, + HttpFlavorValues, + NetTransportValues, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; @@ -234,10 +235,13 @@ describe('HttpsPlugin Integration tests', () => { assert.strictEqual(spans.length, 2); assert.strictEqual(span.name, 'HTTP GET'); assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual(span.attributes[HttpAttribute.HTTP_FLAVOR], '1.1'); assert.strictEqual( - span.attributes[GeneralAttribute.NET_TRANSPORT], - GeneralAttribute.IP_TCP + span.attributes[SemanticAttribute.HTTP_FLAVOR], + HttpFlavorValues.HTTP_1_1 + ); + assert.strictEqual( + span.attributes[SemanticAttribute.NET_TRANSPORT], + NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts index 5921f47eb63..79af0a121fd 100644 --- a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts @@ -16,10 +16,7 @@ import { SpanKind } from '@opentelemetry/api'; import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { - HttpAttribute, - GeneralAttribute, -} from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import * as http from 'http'; import { DummyPropagation } from './DummyPropagation'; @@ -46,19 +43,19 @@ export const assertSpan = ( assert.strictEqual(span.kind, kind); assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_ERROR_MESSAGE], + span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_METHOD], + span.attributes[SemanticAttribute.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_TARGET], + span.attributes[SemanticAttribute.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[HttpAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttribute.HTTP_STATUS_CODE], validations.httpStatusCode ); assert.ok(span.endTime); @@ -76,28 +73,28 @@ export const assertSpan = ( const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttribute.HTTP_USER_AGENT], userAgent ); } } if (span.kind === SpanKind.CLIENT) { assert.strictEqual( - span.attributes[GeneralAttribute.NET_PEER_NAME], + span.attributes[SemanticAttribute.NET_PEER_NAME], validations.hostname, 'must be consistent (PEER_NAME and hostname)' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_IP], + span.attributes[SemanticAttribute.NET_PEER_IP], 'must have PEER_IP' ); assert.ok( - span.attributes[GeneralAttribute.NET_PEER_PORT], + span.attributes[SemanticAttribute.NET_PEER_PORT], 'must have PEER_PORT' ); assert.ok( - (span.attributes[HttpAttribute.HTTP_URL] as string).indexOf( - span.attributes[GeneralAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttribute.NET_PEER_NAME] as string ) > -1, 'must be consistent' ); @@ -105,17 +102,17 @@ export const assertSpan = ( if (span.kind === SpanKind.SERVER) { if (validations.serverName) { assert.strictEqual( - span.attributes[HttpAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttribute.HTTP_SERVER_NAME], validations.serverName, ' must have serverName attribute' ); } assert.ok( - span.attributes[GeneralAttribute.NET_HOST_PORT], + span.attributes[SemanticAttribute.NET_HOST_PORT], 'must have HOST_PORT' ); assert.ok( - span.attributes[GeneralAttribute.NET_HOST_IP], + span.attributes[SemanticAttribute.NET_HOST_IP], 'must have HOST_IP' ); assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); diff --git a/packages/opentelemetry-semantic-conventions/src/events.ts b/packages/opentelemetry-semantic-conventions/src/events.ts index 1b766da3d65..af2909dc68e 100644 --- a/packages/opentelemetry-semantic-conventions/src/events.ts +++ b/packages/opentelemetry-semantic-conventions/src/events.ts @@ -15,4 +15,4 @@ */ // Event name definitions -export const ExceptionEventName = "exception"; +export const ExceptionEventName = 'exception'; diff --git a/packages/opentelemetry-semantic-conventions/src/index.ts b/packages/opentelemetry-semantic-conventions/src/index.ts index 97f755e402f..3d979fd4df7 100644 --- a/packages/opentelemetry-semantic-conventions/src/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export * from "./trace"; -export * from "./resource"; -export * from "./events"; +export * from './trace'; +export * from './resource'; +export * from './events'; diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts index 679243e8484..4157bafeff1 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts @@ -16,525 +16,519 @@ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 export const ResourceAttributes = { - /** - * Name of the cloud provider. - */ - CLOUD_PROVIDER: "cloud.provider", + * Name of the cloud provider. + */ + CLOUD_PROVIDER: 'cloud.provider', /** - * The cloud account ID the resource is assigned to. - */ - CLOUD_ACCOUNT_ID: "cloud.account.id", + * The cloud account ID the resource is assigned to. + */ + CLOUD_ACCOUNT_ID: 'cloud.account.id', /** - * The geographical region the resource is running. Refer to your provider's docs to see the available regions, for example [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), or [Google Cloud regions](https://cloud.google.com/about/locations). - */ - CLOUD_REGION: "cloud.region", + * The geographical region the resource is running. Refer to your provider's docs to see the available regions, for example [AWS regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), or [Google Cloud regions](https://cloud.google.com/about/locations). + */ + CLOUD_REGION: 'cloud.region', /** - * Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. - * - * Note: Availability zones are called "zones" on Google Cloud. - */ - CLOUD_AVAILABILITY_ZONE: "cloud.availability_zone", + * Cloud regions often have multiple, isolated locations known as zones to increase availability. Availability zone represents the zone where the resource is running. + * + * Note: Availability zones are called "zones" on Google Cloud. + */ + CLOUD_AVAILABILITY_ZONE: 'cloud.availability_zone', /** - * The cloud platform in use. - * - * Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. - */ - CLOUD_PLATFORM: "cloud.platform", + * The cloud platform in use. + * + * Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. + */ + CLOUD_PLATFORM: 'cloud.platform', /** - * The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). - */ - AWS_ECS_CONTAINER_ARN: "aws.ecs.container.arn", + * The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). + */ + AWS_ECS_CONTAINER_ARN: 'aws.ecs.container.arn', /** - * The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). - */ - AWS_ECS_CLUSTER_ARN: "aws.ecs.cluster.arn", + * The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). + */ + AWS_ECS_CLUSTER_ARN: 'aws.ecs.cluster.arn', /** - * The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. - */ - AWS_ECS_LAUNCHTYPE: "aws.ecs.launchtype", + * The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) for an ECS task. + */ + AWS_ECS_LAUNCHTYPE: 'aws.ecs.launchtype', /** - * The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). - */ - AWS_ECS_TASK_ARN: "aws.ecs.task.arn", + * The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). + */ + AWS_ECS_TASK_ARN: 'aws.ecs.task.arn', /** - * The task definition family this task definition is a member of. - */ - AWS_ECS_TASK_FAMILY: "aws.ecs.task.family", + * The task definition family this task definition is a member of. + */ + AWS_ECS_TASK_FAMILY: 'aws.ecs.task.family', /** - * The revision for this task definition. - */ - AWS_ECS_TASK_REVISION: "aws.ecs.task.revision", + * The revision for this task definition. + */ + AWS_ECS_TASK_REVISION: 'aws.ecs.task.revision', /** - * The ARN of an EKS cluster. - */ - AWS_EKS_CLUSTER_ARN: "aws.eks.cluster.arn", + * The ARN of an EKS cluster. + */ + AWS_EKS_CLUSTER_ARN: 'aws.eks.cluster.arn', /** - * The name(s) of the AWS log group(s) an application is writing to. - * - * Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. - */ - AWS_LOG_GROUP_NAMES: "aws.log.group.names", + * The name(s) of the AWS log group(s) an application is writing to. + * + * Note: Multiple log groups must be supported for cases like multi-container applications, where a single application has sidecar containers, and each write to their own log group. + */ + AWS_LOG_GROUP_NAMES: 'aws.log.group.names', /** - * The Amazon Resource Name(s) (ARN) of the AWS log group(s). - * - * Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). - */ - AWS_LOG_GROUP_ARNS: "aws.log.group.arns", + * The Amazon Resource Name(s) (ARN) of the AWS log group(s). + * + * Note: See the [log group ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). + */ + AWS_LOG_GROUP_ARNS: 'aws.log.group.arns', /** - * The name(s) of the AWS log stream(s) an application is writing to. - */ - AWS_LOG_STREAM_NAMES: "aws.log.stream.names", + * The name(s) of the AWS log stream(s) an application is writing to. + */ + AWS_LOG_STREAM_NAMES: 'aws.log.stream.names', /** - * The ARN(s) of the AWS log stream(s). - * - * Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. - */ - AWS_LOG_STREAM_ARNS: "aws.log.stream.arns", + * The ARN(s) of the AWS log stream(s). + * + * Note: See the [log stream ARN format documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain several log streams, so these ARNs necessarily identify both a log group and a log stream. + */ + AWS_LOG_STREAM_ARNS: 'aws.log.stream.arns', /** - * Container name. - */ - CONTAINER_NAME: "container.name", + * Container name. + */ + CONTAINER_NAME: 'container.name', /** - * Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/reference/run/#container-identification). The UUID might be abbreviated. - */ - CONTAINER_ID: "container.id", + * Container ID. Usually a UUID, as for example used to [identify Docker containers](https://docs.docker.com/engine/reference/run/#container-identification). The UUID might be abbreviated. + */ + CONTAINER_ID: 'container.id', /** - * The container runtime managing this container. - */ - CONTAINER_RUNTIME: "container.runtime", + * The container runtime managing this container. + */ + CONTAINER_RUNTIME: 'container.runtime', /** - * Name of the image the container was built on. - */ - CONTAINER_IMAGE_NAME: "container.image.name", + * Name of the image the container was built on. + */ + CONTAINER_IMAGE_NAME: 'container.image.name', /** - * Container image tag. - */ - CONTAINER_IMAGE_TAG: "container.image.tag", + * Container image tag. + */ + CONTAINER_IMAGE_TAG: 'container.image.tag', /** - * Name of the [deployment environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka deployment tier). - */ - DEPLOYMENT_ENVIRONMENT: "deployment.environment", + * Name of the [deployment environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka deployment tier). + */ + DEPLOYMENT_ENVIRONMENT: 'deployment.environment', /** - * The name of the function being executed. - */ - FAAS_NAME: "faas.name", + * The name of the function being executed. + */ + FAAS_NAME: 'faas.name', /** - * The unique ID of the function being executed. - * - * Note: For example, in AWS Lambda this field corresponds to the [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) value, in GCP to the URI of the resource, and in Azure to the [FunctionDirectory](https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function) field. - */ - FAAS_ID: "faas.id", + * The unique ID of the function being executed. + * + * Note: For example, in AWS Lambda this field corresponds to the [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) value, in GCP to the URI of the resource, and in Azure to the [FunctionDirectory](https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function) field. + */ + FAAS_ID: 'faas.id', /** - * The version string of the function being executed as defined in [Version Attributes](../../resource/semantic_conventions/README.md#version-attributes). - */ - FAAS_VERSION: "faas.version", + * The version string of the function being executed as defined in [Version Attributes](../../resource/semantic_conventions/README.md#version-attributes). + */ + FAAS_VERSION: 'faas.version', /** - * The execution environment ID as a string. - */ - FAAS_INSTANCE: "faas.instance", + * The execution environment ID as a string. + */ + FAAS_INSTANCE: 'faas.instance', /** - * The amount of memory available to the serverless function in MiB. - * - * Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information. - */ - FAAS_MAX_MEMORY: "faas.max_memory", + * The amount of memory available to the serverless function in MiB. + * + * Note: It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information. + */ + FAAS_MAX_MEMORY: 'faas.max_memory', /** - * Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. - */ - HOST_ID: "host.id", + * Unique host ID. For Cloud, this must be the instance_id assigned by the cloud provider. + */ + HOST_ID: 'host.id', /** - * Name of the host. On Unix systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. - */ - HOST_NAME: "host.name", + * Name of the host. On Unix systems, it may contain what the hostname command returns, or the fully qualified hostname, or another name specified by the user. + */ + HOST_NAME: 'host.name', /** - * Type of host. For Cloud, this must be the machine type. - */ - HOST_TYPE: "host.type", + * Type of host. For Cloud, this must be the machine type. + */ + HOST_TYPE: 'host.type', /** - * The CPU architecture the host system is running on. - */ - HOST_ARCH: "host.arch", + * The CPU architecture the host system is running on. + */ + HOST_ARCH: 'host.arch', /** - * Name of the VM image or OS install the host was instantiated from. - */ - HOST_IMAGE_NAME: "host.image.name", + * Name of the VM image or OS install the host was instantiated from. + */ + HOST_IMAGE_NAME: 'host.image.name', /** - * VM image ID. For Cloud, this value is from the provider. - */ - HOST_IMAGE_ID: "host.image.id", + * VM image ID. For Cloud, this value is from the provider. + */ + HOST_IMAGE_ID: 'host.image.id', /** - * The version string of the VM image as defined in [Version Attributes](README.md#version-attributes). - */ - HOST_IMAGE_VERSION: "host.image.version", + * The version string of the VM image as defined in [Version Attributes](README.md#version-attributes). + */ + HOST_IMAGE_VERSION: 'host.image.version', /** - * The name of the cluster. - */ - K8S_CLUSTER_NAME: "k8s.cluster.name", + * The name of the cluster. + */ + K8S_CLUSTER_NAME: 'k8s.cluster.name', /** - * The name of the Node. - */ - K8S_NODE_NAME: "k8s.node.name", + * The name of the Node. + */ + K8S_NODE_NAME: 'k8s.node.name', /** - * The UID of the Node. - */ - K8S_NODE_UID: "k8s.node.uid", + * The UID of the Node. + */ + K8S_NODE_UID: 'k8s.node.uid', /** - * The name of the namespace that the pod is running in. - */ - K8S_NAMESPACE_NAME: "k8s.namespace.name", + * The name of the namespace that the pod is running in. + */ + K8S_NAMESPACE_NAME: 'k8s.namespace.name', /** - * The UID of the Pod. - */ - K8S_POD_UID: "k8s.pod.uid", + * The UID of the Pod. + */ + K8S_POD_UID: 'k8s.pod.uid', /** - * The name of the Pod. - */ - K8S_POD_NAME: "k8s.pod.name", + * The name of the Pod. + */ + K8S_POD_NAME: 'k8s.pod.name', /** - * The name of the Container in a Pod template. - */ - K8S_CONTAINER_NAME: "k8s.container.name", + * The name of the Container in a Pod template. + */ + K8S_CONTAINER_NAME: 'k8s.container.name', /** - * The UID of the ReplicaSet. - */ - K8S_REPLICASET_UID: "k8s.replicaset.uid", + * The UID of the ReplicaSet. + */ + K8S_REPLICASET_UID: 'k8s.replicaset.uid', /** - * The name of the ReplicaSet. - */ - K8S_REPLICASET_NAME: "k8s.replicaset.name", + * The name of the ReplicaSet. + */ + K8S_REPLICASET_NAME: 'k8s.replicaset.name', /** - * The UID of the Deployment. - */ - K8S_DEPLOYMENT_UID: "k8s.deployment.uid", + * The UID of the Deployment. + */ + K8S_DEPLOYMENT_UID: 'k8s.deployment.uid', /** - * The name of the Deployment. - */ - K8S_DEPLOYMENT_NAME: "k8s.deployment.name", + * The name of the Deployment. + */ + K8S_DEPLOYMENT_NAME: 'k8s.deployment.name', /** - * The UID of the StatefulSet. - */ - K8S_STATEFULSET_UID: "k8s.statefulset.uid", + * The UID of the StatefulSet. + */ + K8S_STATEFULSET_UID: 'k8s.statefulset.uid', /** - * The name of the StatefulSet. - */ - K8S_STATEFULSET_NAME: "k8s.statefulset.name", + * The name of the StatefulSet. + */ + K8S_STATEFULSET_NAME: 'k8s.statefulset.name', /** - * The UID of the DaemonSet. - */ - K8S_DAEMONSET_UID: "k8s.daemonset.uid", + * The UID of the DaemonSet. + */ + K8S_DAEMONSET_UID: 'k8s.daemonset.uid', /** - * The name of the DaemonSet. - */ - K8S_DAEMONSET_NAME: "k8s.daemonset.name", + * The name of the DaemonSet. + */ + K8S_DAEMONSET_NAME: 'k8s.daemonset.name', /** - * The UID of the Job. - */ - K8S_JOB_UID: "k8s.job.uid", + * The UID of the Job. + */ + K8S_JOB_UID: 'k8s.job.uid', /** - * The name of the Job. - */ - K8S_JOB_NAME: "k8s.job.name", + * The name of the Job. + */ + K8S_JOB_NAME: 'k8s.job.name', /** - * The UID of the CronJob. - */ - K8S_CRONJOB_UID: "k8s.cronjob.uid", + * The UID of the CronJob. + */ + K8S_CRONJOB_UID: 'k8s.cronjob.uid', /** - * The name of the CronJob. - */ - K8S_CRONJOB_NAME: "k8s.cronjob.name", + * The name of the CronJob. + */ + K8S_CRONJOB_NAME: 'k8s.cronjob.name', /** - * The operating system type. - */ - OS_TYPE: "os.type", + * The operating system type. + */ + OS_TYPE: 'os.type', /** - * Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. - */ - OS_DESCRIPTION: "os.description", + * Human readable (not intended to be parsed) OS version information, like e.g. reported by `ver` or `lsb_release -a` commands. + */ + OS_DESCRIPTION: 'os.description', /** - * Process identifier (PID). - */ - PROCESS_PID: "process.pid", + * Process identifier (PID). + */ + PROCESS_PID: 'process.pid', /** - * The name of the process executable. On Linux based systems, can be set to the `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of `GetProcessImageFileNameW`. - */ - PROCESS_EXECUTABLE_NAME: "process.executable.name", + * The name of the process executable. On Linux based systems, can be set to the `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_NAME: 'process.executable.name', /** - * The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. - */ - PROCESS_EXECUTABLE_PATH: "process.executable.path", + * The full path to the process executable. On Linux based systems, can be set to the target of `proc/[pid]/exe`. On Windows, can be set to the result of `GetProcessImageFileNameW`. + */ + PROCESS_EXECUTABLE_PATH: 'process.executable.path', /** - * The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. - */ - PROCESS_COMMAND: "process.command", + * The command used to launch the process (i.e. the command name). On Linux based systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to the first parameter extracted from `GetCommandLineW`. + */ + PROCESS_COMMAND: 'process.command', /** - * The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. - */ - PROCESS_COMMAND_LINE: "process.command_line", + * The full command used to launch the process as a single string representing the full command. On Windows, can be set to the result of `GetCommandLineW`. Do not set this if you have to assemble it just for monitoring; use `process.command_args` instead. + */ + PROCESS_COMMAND_LINE: 'process.command_line', /** - * All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. - */ - PROCESS_COMMAND_ARGS: "process.command_args", + * All the command arguments (including the command/executable itself) as received by the process. On Linux-based systems (and some other Unixoid systems supporting procfs), can be set according to the list of null-delimited strings extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be the full argv vector passed to `main`. + */ + PROCESS_COMMAND_ARGS: 'process.command_args', /** - * The username of the user that owns the process. - */ - PROCESS_OWNER: "process.owner", + * The username of the user that owns the process. + */ + PROCESS_OWNER: 'process.owner', /** - * The name of the runtime of this process. For compiled native binaries, this SHOULD be the name of the compiler. - */ - PROCESS_RUNTIME_NAME: "process.runtime.name", + * The name of the runtime of this process. For compiled native binaries, this SHOULD be the name of the compiler. + */ + PROCESS_RUNTIME_NAME: 'process.runtime.name', /** - * The version of the runtime of this process, as returned by the runtime without modification. - */ - PROCESS_RUNTIME_VERSION: "process.runtime.version", + * The version of the runtime of this process, as returned by the runtime without modification. + */ + PROCESS_RUNTIME_VERSION: 'process.runtime.version', /** - * An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. - */ - PROCESS_RUNTIME_DESCRIPTION: "process.runtime.description", + * An additional description about the runtime of the process, for example a specific vendor customization of the runtime environment. + */ + PROCESS_RUNTIME_DESCRIPTION: 'process.runtime.description', /** - * Logical name of the service. - * - * Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md#process), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`. - */ - SERVICE_NAME: "service.name", + * Logical name of the service. + * + * Note: MUST be the same for all instances of horizontally scaled services. If the value was not specified, SDKs MUST fallback to `unknown_service:` concatenated with [`process.executable.name`](process.md#process), e.g. `unknown_service:bash`. If `process.executable.name` is not available, the value MUST be set to `unknown_service`. + */ + SERVICE_NAME: 'service.name', /** - * A namespace for `service.name`. - * - * Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. - */ - SERVICE_NAMESPACE: "service.namespace", + * A namespace for `service.name`. + * + * Note: A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services. `service.name` is expected to be unique within the same namespace. If `service.namespace` is not specified in the Resource then `service.name` is expected to be unique for all services that have no explicit namespace defined (so the empty/unspecified namespace is simply one more valid namespace). Zero-length namespace string is assumed equal to unspecified namespace. + */ + SERVICE_NAMESPACE: 'service.namespace', /** - * The string ID of the service instance. - * - * Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words `service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service). It is preferable for the ID to be persistent and stay the same for the lifetime of the service instance, however it is acceptable that the ID is ephemeral and changes during important lifetime events for the service (e.g. service restarts). If the service has no inherent unique ID that can be used as the value of this attribute it is recommended to generate a random Version 1 or Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use Version 5, see RFC 4122 for more recommendations). - */ - SERVICE_INSTANCE_ID: "service.instance.id", + * The string ID of the service instance. + * + * Note: MUST be unique for each instance of the same `service.namespace,service.name` pair (in other words `service.namespace,service.name,service.instance.id` triplet MUST be globally unique). The ID helps to distinguish instances of the same service that exist at the same time (e.g. instances of a horizontally scaled service). It is preferable for the ID to be persistent and stay the same for the lifetime of the service instance, however it is acceptable that the ID is ephemeral and changes during important lifetime events for the service (e.g. service restarts). If the service has no inherent unique ID that can be used as the value of this attribute it is recommended to generate a random Version 1 or Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use Version 5, see RFC 4122 for more recommendations). + */ + SERVICE_INSTANCE_ID: 'service.instance.id', /** - * The version string of the service API or implementation. - */ - SERVICE_VERSION: "service.version", + * The version string of the service API or implementation. + */ + SERVICE_VERSION: 'service.version', /** - * The name of the telemetry SDK as defined above. - */ - TELEMETRY_SDK_NAME: "telemetry.sdk.name", + * The name of the telemetry SDK as defined above. + */ + TELEMETRY_SDK_NAME: 'telemetry.sdk.name', /** - * The language of the telemetry SDK. - */ - TELEMETRY_SDK_LANGUAGE: "telemetry.sdk.language", + * The language of the telemetry SDK. + */ + TELEMETRY_SDK_LANGUAGE: 'telemetry.sdk.language', /** - * The version string of the telemetry SDK. - */ - TELEMETRY_SDK_VERSION: "telemetry.sdk.version", + * The version string of the telemetry SDK. + */ + TELEMETRY_SDK_VERSION: 'telemetry.sdk.version', /** - * The version string of the auto instrumentation agent, if used. - */ - TELEMETRY_AUTO_VERSION: "telemetry.auto.version", + * The version string of the auto instrumentation agent, if used. + */ + TELEMETRY_AUTO_VERSION: 'telemetry.auto.version', /** - * The name of the web engine. - */ - WEBENGINE_NAME: "webengine.name", + * The name of the web engine. + */ + WEBENGINE_NAME: 'webengine.name', /** - * The version of the web engine. - */ - WEBENGINE_VERSION: "webengine.version", + * The version of the web engine. + */ + WEBENGINE_VERSION: 'webengine.version', /** - * Additional description of the web engine (e.g. detailed version and edition information). - */ - WEBENGINE_DESCRIPTION: "webengine.description", -} + * Additional description of the web engine (e.g. detailed version and edition information). + */ + WEBENGINE_DESCRIPTION: 'webengine.description', +}; // Enum definitions export enum CloudProviderValues { - /** Amazon Web Services. */ - AWS = "aws", - /** Microsoft Azure. */ - AZURE = "azure", - /** Google Cloud Platform. */ - GCP = "gcp", + /** Amazon Web Services. */ + AWS = 'aws', + /** Microsoft Azure. */ + AZURE = 'azure', + /** Google Cloud Platform. */ + GCP = 'gcp', } - export enum CloudPlatformValues { - /** AWS Elastic Compute Cloud. */ - AWS_EC2 = "aws_ec2", - /** AWS Elastic Container Service. */ - AWS_ECS = "aws_ecs", - /** AWS Elastic Kubernetes Service. */ - AWS_EKS = "aws_eks", - /** AWS Lambda. */ - AWS_LAMBDA = "aws_lambda", - /** AWS Elastic Beanstalk. */ - AWS_ELASTICBEANSTALK = "aws_elastic_beanstalk", - /** Azure Virtual Machines. */ - AZURE_VM = "azure_vm", - /** Azure Container Instances. */ - AZURE_CONTAINERINSTANCES = "azure_container_instances", - /** Azure Kubernetes Service. */ - AZURE_AKS = "azure_aks", - /** Azure Functions. */ - AZURE_FUNCTIONS = "azure_functions", - /** Azure App Service. */ - AZURE_APPSERVICE = "azure_app_service", - /** Google Cloud Compute Engine (GCE). */ - GCP_COMPUTEENGINE = "gcp_compute_engine", - /** Google Cloud Run. */ - GCP_CLOUDRUN = "gcp_cloud_run", - /** Google Cloud Kubernetes Engine (GKE). */ - GCP_KUBERNETESENGINE = "gcp_kubernetes_engine", - /** Google Cloud Functions (GCF). */ - GCP_CLOUDFUNCTIONS = "gcp_cloud_functions", - /** Google Cloud App Engine (GAE). */ - GCP_APPENGINE = "gcp_app_engine", + /** AWS Elastic Compute Cloud. */ + AWS_EC2 = 'aws_ec2', + /** AWS Elastic Container Service. */ + AWS_ECS = 'aws_ecs', + /** AWS Elastic Kubernetes Service. */ + AWS_EKS = 'aws_eks', + /** AWS Lambda. */ + AWS_LAMBDA = 'aws_lambda', + /** AWS Elastic Beanstalk. */ + AWS_ELASTICBEANSTALK = 'aws_elastic_beanstalk', + /** Azure Virtual Machines. */ + AZURE_VM = 'azure_vm', + /** Azure Container Instances. */ + AZURE_CONTAINERINSTANCES = 'azure_container_instances', + /** Azure Kubernetes Service. */ + AZURE_AKS = 'azure_aks', + /** Azure Functions. */ + AZURE_FUNCTIONS = 'azure_functions', + /** Azure App Service. */ + AZURE_APPSERVICE = 'azure_app_service', + /** Google Cloud Compute Engine (GCE). */ + GCP_COMPUTEENGINE = 'gcp_compute_engine', + /** Google Cloud Run. */ + GCP_CLOUDRUN = 'gcp_cloud_run', + /** Google Cloud Kubernetes Engine (GKE). */ + GCP_KUBERNETESENGINE = 'gcp_kubernetes_engine', + /** Google Cloud Functions (GCF). */ + GCP_CLOUDFUNCTIONS = 'gcp_cloud_functions', + /** Google Cloud App Engine (GAE). */ + GCP_APPENGINE = 'gcp_app_engine', } - export enum AwsEcsLaunchtypeValues { - /** ec2. */ - EC2 = "ec2", - /** fargate. */ - FARGATE = "fargate", + /** ec2. */ + EC2 = 'ec2', + /** fargate. */ + FARGATE = 'fargate', } - export enum HostArchValues { - /** AMD64. */ - AMD64 = "amd64", - /** ARM32. */ - ARM32 = "arm32", - /** ARM64. */ - ARM64 = "arm64", - /** Itanium. */ - IA64 = "ia64", - /** 32-bit PowerPC. */ - PPC32 = "ppc32", - /** 64-bit PowerPC. */ - PPC64 = "ppc64", - /** 32-bit x86. */ - X86 = "x86", + /** AMD64. */ + AMD64 = 'amd64', + /** ARM32. */ + ARM32 = 'arm32', + /** ARM64. */ + ARM64 = 'arm64', + /** Itanium. */ + IA64 = 'ia64', + /** 32-bit PowerPC. */ + PPC32 = 'ppc32', + /** 64-bit PowerPC. */ + PPC64 = 'ppc64', + /** 32-bit x86. */ + X86 = 'x86', } - export enum OsTypeValues { - /** Microsoft Windows. */ - WINDOWS = "WINDOWS", - /** Linux. */ - LINUX = "LINUX", - /** Apple Darwin. */ - DARWIN = "DARWIN", - /** FreeBSD. */ - FREEBSD = "FREEBSD", - /** NetBSD. */ - NETBSD = "NETBSD", - /** OpenBSD. */ - OPENBSD = "OPENBSD", - /** DragonFly BSD. */ - DRAGONFLYBSD = "DRAGONFLYBSD", - /** HP-UX (Hewlett Packard Unix). */ - HPUX = "HPUX", - /** AIX (Advanced Interactive eXecutive). */ - AIX = "AIX", - /** Oracle Solaris. */ - SOLARIS = "SOLARIS", - /** IBM z/OS. */ - ZOS = "ZOS", + /** Microsoft Windows. */ + WINDOWS = 'WINDOWS', + /** Linux. */ + LINUX = 'LINUX', + /** Apple Darwin. */ + DARWIN = 'DARWIN', + /** FreeBSD. */ + FREEBSD = 'FREEBSD', + /** NetBSD. */ + NETBSD = 'NETBSD', + /** OpenBSD. */ + OPENBSD = 'OPENBSD', + /** DragonFly BSD. */ + DRAGONFLYBSD = 'DRAGONFLYBSD', + /** HP-UX (Hewlett Packard Unix). */ + HPUX = 'HPUX', + /** AIX (Advanced Interactive eXecutive). */ + AIX = 'AIX', + /** Oracle Solaris. */ + SOLARIS = 'SOLARIS', + /** IBM z/OS. */ + ZOS = 'ZOS', } - export enum TelemetrySdkLanguageValues { - /** cpp. */ - CPP = "cpp", - /** dotnet. */ - DOTNET = "dotnet", - /** erlang. */ - ERLANG = "erlang", - /** go. */ - GO = "go", - /** java. */ - JAVA = "java", - /** nodejs. */ - NODEJS = "nodejs", - /** php. */ - PHP = "php", - /** python. */ - PYTHON = "python", - /** ruby. */ - RUBY = "ruby", - /** webjs. */ - WEBJS = "webjs", + /** cpp. */ + CPP = 'cpp', + /** dotnet. */ + DOTNET = 'dotnet', + /** erlang. */ + ERLANG = 'erlang', + /** go. */ + GO = 'go', + /** java. */ + JAVA = 'java', + /** nodejs. */ + NODEJS = 'nodejs', + /** php. */ + PHP = 'php', + /** python. */ + PYTHON = 'python', + /** ruby. */ + RUBY = 'ruby', + /** webjs. */ + WEBJS = 'webjs', } diff --git a/packages/opentelemetry-semantic-conventions/src/resource/index.ts b/packages/opentelemetry-semantic-conventions/src/resource/index.ts index ba7030581f2..34ed6e77941 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/index.ts @@ -1 +1,16 @@ -export * from "./ResourceAttribute"; +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ +export * from './ResourceAttribute'; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts index a4fad76a9b7..2a12b2244d3 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts @@ -16,153 +16,153 @@ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 export const SemanticAttribute = { - /** - * An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. - */ - DB_SYSTEM: "db.system", + * An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. + */ + DB_SYSTEM: 'db.system', /** - * The connection string used to connect to the database. It is recommended to remove embedded credentials. - */ - DB_CONNECTION_STRING: "db.connection_string", + * The connection string used to connect to the database. It is recommended to remove embedded credentials. + */ + DB_CONNECTION_STRING: 'db.connection_string', /** - * Username for accessing the database. - */ - DB_USER: "db.user", + * Username for accessing the database. + */ + DB_USER: 'db.user', /** - * The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect. - */ - DB_JDBC_DRIVER_CLASSNAME: "db.jdbc.driver_classname", + * The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect. + */ + DB_JDBC_DRIVER_CLASSNAME: 'db.jdbc.driver_classname', /** - * If no [tech-specific attribute](#call-level-attributes-for-specific-technologies) is defined, this attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). - * - * Note: In some SQL databases, the database name to be used is called "schema name". - */ - DB_NAME: "db.name", + * If no [tech-specific attribute](#call-level-attributes-for-specific-technologies) is defined, this attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). + * + * Note: In some SQL databases, the database name to be used is called "schema name". + */ + DB_NAME: 'db.name', /** - * The database statement being executed. - * - * Note: The value may be sanitized to exclude sensitive information. - */ - DB_STATEMENT: "db.statement", + * The database statement being executed. + * + * Note: The value may be sanitized to exclude sensitive information. + */ + DB_STATEMENT: 'db.statement', /** - * The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword. - * - * Note: When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted. - */ - DB_OPERATION: "db.operation", + * The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword. + * + * Note: When setting this to an SQL keyword, it is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if the operation name is provided by the library being instrumented. If the SQL statement has an ambiguous operation, or performs more than one operation, this value may be omitted. + */ + DB_OPERATION: 'db.operation', /** - * Remote hostname or similar, see note below. - */ - NET_PEER_NAME: "net.peer.name", + * Remote hostname or similar, see note below. + */ + NET_PEER_NAME: 'net.peer.name', /** - * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). - */ - NET_PEER_IP: "net.peer.ip", + * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). + */ + NET_PEER_IP: 'net.peer.ip', /** - * Remote port number. - */ - NET_PEER_PORT: "net.peer.port", + * Remote port number. + */ + NET_PEER_PORT: 'net.peer.port', /** - * Transport protocol used. See note below. - */ - NET_TRANSPORT: "net.transport", + * Transport protocol used. See note below. + */ + NET_TRANSPORT: 'net.transport', /** - * The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. - * - * Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer required (but still recommended if non-standard). - */ - DB_MSSQL_INSTANCE_NAME: "db.mssql.instance_name", + * The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. + * + * Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no longer required (but still recommended if non-standard). + */ + DB_MSSQL_INSTANCE_NAME: 'db.mssql.instance_name', /** - * The name of the keyspace being accessed. To be used instead of the generic `db.name` attribute. - */ - DB_CASSANDRA_KEYSPACE: "db.cassandra.keyspace", + * The name of the keyspace being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_CASSANDRA_KEYSPACE: 'db.cassandra.keyspace', /** - * The fetch size used for paging, i.e. how many rows will be returned at once. - */ - DB_CASSANDRA_PAGE_SIZE: "db.cassandra.page_size", + * The fetch size used for paging, i.e. how many rows will be returned at once. + */ + DB_CASSANDRA_PAGE_SIZE: 'db.cassandra.page_size', /** - * The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). - */ - DB_CASSANDRA_CONSISTENCY_LEVEL: "db.cassandra.consistency_level", + * The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). + */ + DB_CASSANDRA_CONSISTENCY_LEVEL: 'db.cassandra.consistency_level', /** - * The name of the primary table that the operation is acting upon, including the schema name (if applicable). - * - * Note: This mirrors the db.sql.table attribute but references cassandra rather than sql. It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. - */ - DB_CASSANDRA_TABLE: "db.cassandra.table", + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: This mirrors the db.sql.table attribute but references cassandra rather than sql. It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_CASSANDRA_TABLE: 'db.cassandra.table', /** - * Whether or not the query is idempotent. - */ - DB_CASSANDRA_IDEMPOTENCE: "db.cassandra.idempotence", + * Whether or not the query is idempotent. + */ + DB_CASSANDRA_IDEMPOTENCE: 'db.cassandra.idempotence', /** - * The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. - */ - DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT: "db.cassandra.speculative_execution_count", + * The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively. + */ + DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT: + 'db.cassandra.speculative_execution_count', /** - * The ID of the coordinating node for a query. - */ - DB_CASSANDRA_COORDINATOR_ID: "db.cassandra.coordinator.id", + * The ID of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_ID: 'db.cassandra.coordinator.id', /** - * The data center of the coordinating node for a query. - */ - DB_CASSANDRA_COORDINATOR_DC: "db.cassandra.coordinator.dc", + * The data center of the coordinating node for a query. + */ + DB_CASSANDRA_COORDINATOR_DC: 'db.cassandra.coordinator.dc', /** - * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. To be used instead of the generic `db.name` attribute. - */ - DB_HBASE_NAMESPACE: "db.hbase.namespace", + * The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. To be used instead of the generic `db.name` attribute. + */ + DB_HBASE_NAMESPACE: 'db.hbase.namespace', /** - * The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute. - */ - DB_REDIS_DATABASE_INDEX: "db.redis.database_index", + * The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute. + */ + DB_REDIS_DATABASE_INDEX: 'db.redis.database_index', /** - * The collection being accessed within the database stated in `db.name`. - */ - DB_MONGODB_COLLECTION: "db.mongodb.collection", + * The collection being accessed within the database stated in `db.name`. + */ + DB_MONGODB_COLLECTION: 'db.mongodb.collection', /** - * The name of the primary table that the operation is acting upon, including the schema name (if applicable). - * - * Note: It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. - */ - DB_SQL_TABLE: "db.sql.table", + * The name of the primary table that the operation is acting upon, including the schema name (if applicable). + * + * Note: It is not recommended to attempt any client-side parsing of `db.statement` just to get this property, but it should be set if it is provided by the library being instrumented. If the operation is acting upon an anonymous table, or more than one table, this value MUST NOT be set. + */ + DB_SQL_TABLE: 'db.sql.table', /** - * The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. - */ - EXCEPTION_TYPE: "exception.type", + * The type of the exception (its fully-qualified class name, if applicable). The dynamic type of the exception should be preferred over the static type in languages that support it. + */ + EXCEPTION_TYPE: 'exception.type', /** - * The exception message. - */ - EXCEPTION_MESSAGE: "exception.message", + * The exception message. + */ + EXCEPTION_MESSAGE: 'exception.message', /** - * A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. - */ - EXCEPTION_STACKTRACE: "exception.stacktrace", + * A stacktrace as a string in the natural representation for the language runtime. The representation is to be determined and documented by each language SIG. + */ + EXCEPTION_STACKTRACE: 'exception.stacktrace', /** * SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span. @@ -184,438 +184,445 @@ even if the `exception.escaped` attribute was not set or set to false, since the event might have been recorded at a time where it was not clear whether the exception will escape. */ - EXCEPTION_ESCAPED: "exception.escaped", + EXCEPTION_ESCAPED: 'exception.escaped', /** - * Type of the trigger on which the function is executed. - */ - FAAS_TRIGGER: "faas.trigger", + * Type of the trigger on which the function is executed. + */ + FAAS_TRIGGER: 'faas.trigger', /** - * The execution ID of the current function execution. - */ - FAAS_EXECUTION: "faas.execution", + * The execution ID of the current function execution. + */ + FAAS_EXECUTION: 'faas.execution', /** - * The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. - */ - FAAS_DOCUMENT_COLLECTION: "faas.document.collection", + * The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name. + */ + FAAS_DOCUMENT_COLLECTION: 'faas.document.collection', /** - * Describes the type of the operation that was performed on the data. - */ - FAAS_DOCUMENT_OPERATION: "faas.document.operation", + * Describes the type of the operation that was performed on the data. + */ + FAAS_DOCUMENT_OPERATION: 'faas.document.operation', /** - * A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). - */ - FAAS_DOCUMENT_TIME: "faas.document.time", + * A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_DOCUMENT_TIME: 'faas.document.time', /** - * The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. - */ - FAAS_DOCUMENT_NAME: "faas.document.name", + * The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name. + */ + FAAS_DOCUMENT_NAME: 'faas.document.name', /** - * HTTP request method. - */ - HTTP_METHOD: "http.method", + * HTTP request method. + */ + HTTP_METHOD: 'http.method', /** - * Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless. - * - * Note: `http.url` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case the attribute's value should be `https://www.example.com/`. - */ - HTTP_URL: "http.url", + * Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless. + * + * Note: `http.url` MUST NOT contain credentials passed via URL in form of `https://username:password@www.example.com/`. In such case the attribute's value should be `https://www.example.com/`. + */ + HTTP_URL: 'http.url', /** - * The full request target as passed in a HTTP request line or equivalent. - */ - HTTP_TARGET: "http.target", + * The full request target as passed in a HTTP request line or equivalent. + */ + HTTP_TARGET: 'http.target', /** - * The value of the [HTTP host header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is empty or not present, this attribute should be the same. - */ - HTTP_HOST: "http.host", + * The value of the [HTTP host header](https://tools.ietf.org/html/rfc7230#section-5.4). When the header is empty or not present, this attribute should be the same. + */ + HTTP_HOST: 'http.host', /** - * The URI scheme identifying the used protocol. - */ - HTTP_SCHEME: "http.scheme", + * The URI scheme identifying the used protocol. + */ + HTTP_SCHEME: 'http.scheme', /** - * [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). - */ - HTTP_STATUS_CODE: "http.status_code", + * [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). + */ + HTTP_STATUS_CODE: 'http.status_code', /** - * Kind of HTTP protocol used. - * - * Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. - */ - HTTP_FLAVOR: "http.flavor", + * Kind of HTTP protocol used. + * + * Note: If `net.transport` is not specified, it can be assumed to be `IP.TCP` except if `http.flavor` is `QUIC`, in which case `IP.UDP` is assumed. + */ + HTTP_FLAVOR: 'http.flavor', /** - * Value of the [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the client. - */ - HTTP_USER_AGENT: "http.user_agent", + * Value of the [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3) header sent by the client. + */ + HTTP_USER_AGENT: 'http.user_agent', /** - * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. - */ - HTTP_REQUEST_CONTENT_LENGTH: "http.request_content_length", + * The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_REQUEST_CONTENT_LENGTH: 'http.request_content_length', /** - * The size of the uncompressed request payload body after transport decoding. Not set if transport encoding not used. - */ - HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: "http.request_content_length_uncompressed", + * The size of the uncompressed request payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED: + 'http.request_content_length_uncompressed', /** - * The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. - */ - HTTP_RESPONSE_CONTENT_LENGTH: "http.response_content_length", + * The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) header. For requests using transport encoding, this should be the compressed size. + */ + HTTP_RESPONSE_CONTENT_LENGTH: 'http.response_content_length', /** - * The size of the uncompressed response payload body after transport decoding. Not set if transport encoding not used. - */ - HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: "http.response_content_length_uncompressed", + * The size of the uncompressed response payload body after transport decoding. Not set if transport encoding not used. + */ + HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED: + 'http.response_content_length_uncompressed', /** - * The primary server name of the matched virtual host. This should be obtained via configuration. If no such configuration can be obtained, this attribute MUST NOT be set ( `net.host.name` should be used instead). - * - * Note: `http.url` is usually not readily available on the server side but would have to be assembled in a cumbersome and sometimes lossy process from other information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus preferred to supply the raw data that is available. - */ - HTTP_SERVER_NAME: "http.server_name", + * The primary server name of the matched virtual host. This should be obtained via configuration. If no such configuration can be obtained, this attribute MUST NOT be set ( `net.host.name` should be used instead). + * + * Note: `http.url` is usually not readily available on the server side but would have to be assembled in a cumbersome and sometimes lossy process from other information (see e.g. open-telemetry/opentelemetry-python/pull/148). It is thus preferred to supply the raw data that is available. + */ + HTTP_SERVER_NAME: 'http.server_name', /** - * The matched route (path template). - */ - HTTP_ROUTE: "http.route", + * The matched route (path template). + */ + HTTP_ROUTE: 'http.route', /** - * The IP address of the original client behind all proxies, if known (e.g. from [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). - * - * Note: This is not necessarily the same as `net.peer.ip`, which would identify the network-level peer, which may be a proxy. - */ - HTTP_CLIENT_IP: "http.client_ip", + * The IP address of the original client behind all proxies, if known (e.g. from [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). + * + * Note: This is not necessarily the same as `net.peer.ip`, which would identify the network-level peer, which may be a proxy. + */ + HTTP_CLIENT_IP: 'http.client_ip', /** - * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. - */ - NET_HOST_IP: "net.host.ip", + * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. + */ + NET_HOST_IP: 'net.host.ip', /** - * Like `net.peer.port` but for the host port. - */ - NET_HOST_PORT: "net.host.port", + * Like `net.peer.port` but for the host port. + */ + NET_HOST_PORT: 'net.host.port', /** - * Local hostname or similar, see note below. - */ - NET_HOST_NAME: "net.host.name", + * Local hostname or similar, see note below. + */ + NET_HOST_NAME: 'net.host.name', /** - * A string identifying the messaging system. - */ - MESSAGING_SYSTEM: "messaging.system", + * A string identifying the messaging system. + */ + MESSAGING_SYSTEM: 'messaging.system', /** - * The message destination name. This might be equal to the span name but is required nevertheless. - */ - MESSAGING_DESTINATION: "messaging.destination", + * The message destination name. This might be equal to the span name but is required nevertheless. + */ + MESSAGING_DESTINATION: 'messaging.destination', /** - * The kind of message destination. - */ - MESSAGING_DESTINATION_KIND: "messaging.destination_kind", + * The kind of message destination. + */ + MESSAGING_DESTINATION_KIND: 'messaging.destination_kind', /** - * A boolean that is true if the message destination is temporary. - */ - MESSAGING_TEMP_DESTINATION: "messaging.temp_destination", + * A boolean that is true if the message destination is temporary. + */ + MESSAGING_TEMP_DESTINATION: 'messaging.temp_destination', /** - * The name of the transport protocol. - */ - MESSAGING_PROTOCOL: "messaging.protocol", + * The name of the transport protocol. + */ + MESSAGING_PROTOCOL: 'messaging.protocol', /** - * The version of the transport protocol. - */ - MESSAGING_PROTOCOL_VERSION: "messaging.protocol_version", + * The version of the transport protocol. + */ + MESSAGING_PROTOCOL_VERSION: 'messaging.protocol_version', /** - * Connection string. - */ - MESSAGING_URL: "messaging.url", + * Connection string. + */ + MESSAGING_URL: 'messaging.url', /** - * A value used by the messaging system as an identifier for the message, represented as a string. - */ - MESSAGING_MESSAGE_ID: "messaging.message_id", + * A value used by the messaging system as an identifier for the message, represented as a string. + */ + MESSAGING_MESSAGE_ID: 'messaging.message_id', /** - * The [conversation ID](#conversations) identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". - */ - MESSAGING_CONVERSATION_ID: "messaging.conversation_id", + * The [conversation ID](#conversations) identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". + */ + MESSAGING_CONVERSATION_ID: 'messaging.conversation_id', /** - * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. - */ - MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: "messaging.message_payload_size_bytes", + * The (uncompressed) size of the message payload in bytes. Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported. + */ + MESSAGING_MESSAGE_PAYLOAD_SIZE_BYTES: 'messaging.message_payload_size_bytes', /** - * The compressed size of the message payload in bytes. - */ - MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: "messaging.message_payload_compressed_size_bytes", + * The compressed size of the message payload in bytes. + */ + MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: + 'messaging.message_payload_compressed_size_bytes', /** - * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). - */ - FAAS_TIME: "faas.time", + * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_TIME: 'faas.time', /** - * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). - */ - FAAS_CRON: "faas.cron", + * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + */ + FAAS_CRON: 'faas.cron', /** - * A boolean that is true if the serverless function is executed for the first time (aka cold-start). - */ - FAAS_COLDSTART: "faas.coldstart", + * A boolean that is true if the serverless function is executed for the first time (aka cold-start). + */ + FAAS_COLDSTART: 'faas.coldstart', /** - * The name of the invoked function. - * - * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. - */ - FAAS_INVOKED_NAME: "faas.invoked_name", + * The name of the invoked function. + * + * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. + */ + FAAS_INVOKED_NAME: 'faas.invoked_name', /** - * The cloud provider of the invoked function. - * - * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. - */ - FAAS_INVOKED_PROVIDER: "faas.invoked_provider", + * The cloud provider of the invoked function. + * + * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. + */ + FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', /** - * The cloud region of the invoked function. - * - * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. - */ - FAAS_INVOKED_REGION: "faas.invoked_region", + * The cloud region of the invoked function. + * + * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. + */ + FAAS_INVOKED_REGION: 'faas.invoked_region', /** - * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. - */ - PEER_SERVICE: "peer.service", + * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. + */ + PEER_SERVICE: 'peer.service', /** - * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. - */ - ENDUSER_ID: "enduser.id", + * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. + */ + ENDUSER_ID: 'enduser.id', /** - * Actual/assumed role the client is making the request under extracted from token or application security context. - */ - ENDUSER_ROLE: "enduser.role", + * Actual/assumed role the client is making the request under extracted from token or application security context. + */ + ENDUSER_ROLE: 'enduser.role', /** - * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). - */ - ENDUSER_SCOPE: "enduser.scope", + * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + */ + ENDUSER_SCOPE: 'enduser.scope', /** - * Current "managed" thread ID (as opposed to OS thread ID). - */ - THREAD_ID: "thread.id", + * Current "managed" thread ID (as opposed to OS thread ID). + */ + THREAD_ID: 'thread.id', /** - * Current thread name. - */ - THREAD_NAME: "thread.name", + * Current thread name. + */ + THREAD_NAME: 'thread.name', /** - * The method or function name, or equivalent (usually rightmost part of the code unit's name). - */ - CODE_FUNCTION: "code.function", + * The method or function name, or equivalent (usually rightmost part of the code unit's name). + */ + CODE_FUNCTION: 'code.function', /** - * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. - */ - CODE_NAMESPACE: "code.namespace", + * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + */ + CODE_NAMESPACE: 'code.namespace', /** - * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). - */ - CODE_FILEPATH: "code.filepath", + * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + */ + CODE_FILEPATH: 'code.filepath', /** - * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. - */ - CODE_LINENO: "code.lineno", + * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + */ + CODE_LINENO: 'code.lineno', /** - * The value `aws-api`. - */ - RPC_SYSTEM: "rpc.system", + * The value `aws-api`. + */ + RPC_SYSTEM: 'rpc.system', /** - * The name of the service to which a request is made, as returned by the AWS SDK. - */ - RPC_SERVICE: "rpc.service", + * The name of the service to which a request is made, as returned by the AWS SDK. + */ + RPC_SERVICE: 'rpc.service', /** - * The name of the operation corresponding to the request, as returned by the AWS SDK. - */ - RPC_METHOD: "rpc.method", + * The name of the operation corresponding to the request, as returned by the AWS SDK. + */ + RPC_METHOD: 'rpc.method', /** - * The keys in the `RequestItems` object field. - */ - AWS_DYNAMODB_TABLE_NAMES: "aws.dynamodb.table_names", + * The keys in the `RequestItems` object field. + */ + AWS_DYNAMODB_TABLE_NAMES: 'aws.dynamodb.table_names', /** - * The JSON-serialized value of each item in the `ConsumedCapacity` response field. - */ - AWS_DYNAMODB_CONSUMED_CAPACITY: "aws.dynamodb.consumed_capacity", + * The JSON-serialized value of each item in the `ConsumedCapacity` response field. + */ + AWS_DYNAMODB_CONSUMED_CAPACITY: 'aws.dynamodb.consumed_capacity', /** - * The JSON-serialized value of the `ItemCollectionMetrics` response field. - */ - AWS_DYNAMODB_ITEM_COLLECTION_METRICS: "aws.dynamodb.item_collection_metrics", + * The JSON-serialized value of the `ItemCollectionMetrics` response field. + */ + AWS_DYNAMODB_ITEM_COLLECTION_METRICS: 'aws.dynamodb.item_collection_metrics', /** - * The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. - */ - AWS_DYNAMODB_PROVISIONED_READ_CAPACITY: "aws.dynamodb.provisioned_read_capacity", + * The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. + */ + AWS_DYNAMODB_PROVISIONED_READ_CAPACITY: + 'aws.dynamodb.provisioned_read_capacity', /** - * The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. - */ - AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY: "aws.dynamodb.provisioned_write_capacity", + * The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. + */ + AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY: + 'aws.dynamodb.provisioned_write_capacity', /** - * The value of the `ConsistentRead` request parameter. - */ - AWS_DYNAMODB_CONSISTENT_READ: "aws.dynamodb.consistent_read", + * The value of the `ConsistentRead` request parameter. + */ + AWS_DYNAMODB_CONSISTENT_READ: 'aws.dynamodb.consistent_read', /** - * The value of the `ProjectionExpression` request parameter. - */ - AWS_DYNAMODB_PROJECTION: "aws.dynamodb.projection", + * The value of the `ProjectionExpression` request parameter. + */ + AWS_DYNAMODB_PROJECTION: 'aws.dynamodb.projection', /** - * The value of the `Limit` request parameter. - */ - AWS_DYNAMODB_LIMIT: "aws.dynamodb.limit", + * The value of the `Limit` request parameter. + */ + AWS_DYNAMODB_LIMIT: 'aws.dynamodb.limit', /** - * The value of the `AttributesToGet` request parameter. - */ - AWS_DYNAMODB_ATTRIBUTES_TO_GET: "aws.dynamodb.attributes_to_get", + * The value of the `AttributesToGet` request parameter. + */ + AWS_DYNAMODB_ATTRIBUTES_TO_GET: 'aws.dynamodb.attributes_to_get', /** - * The value of the `IndexName` request parameter. - */ - AWS_DYNAMODB_INDEX_NAME: "aws.dynamodb.index_name", + * The value of the `IndexName` request parameter. + */ + AWS_DYNAMODB_INDEX_NAME: 'aws.dynamodb.index_name', /** - * The value of the `Select` request parameter. - */ - AWS_DYNAMODB_SELECT: "aws.dynamodb.select", + * The value of the `Select` request parameter. + */ + AWS_DYNAMODB_SELECT: 'aws.dynamodb.select', /** - * The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. - */ - AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES: "aws.dynamodb.global_secondary_indexes", + * The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. + */ + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES: + 'aws.dynamodb.global_secondary_indexes', /** - * The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. - */ - AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES: "aws.dynamodb.local_secondary_indexes", + * The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. + */ + AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES: 'aws.dynamodb.local_secondary_indexes', /** - * The value of the `ExclusiveStartTableName` request parameter. - */ - AWS_DYNAMODB_EXCLUSIVE_START_TABLE: "aws.dynamodb.exclusive_start_table", + * The value of the `ExclusiveStartTableName` request parameter. + */ + AWS_DYNAMODB_EXCLUSIVE_START_TABLE: 'aws.dynamodb.exclusive_start_table', /** - * The the number of items in the `TableNames` response parameter. - */ - AWS_DYNAMODB_TABLE_COUNT: "aws.dynamodb.table_count", + * The the number of items in the `TableNames` response parameter. + */ + AWS_DYNAMODB_TABLE_COUNT: 'aws.dynamodb.table_count', /** - * The value of the `ScanIndexForward` request parameter. - */ - AWS_DYNAMODB_SCAN_FORWARD: "aws.dynamodb.scan_forward", + * The value of the `ScanIndexForward` request parameter. + */ + AWS_DYNAMODB_SCAN_FORWARD: 'aws.dynamodb.scan_forward', /** - * The value of the `Segment` request parameter. - */ - AWS_DYNAMODB_SEGMENT: "aws.dynamodb.segment", + * The value of the `Segment` request parameter. + */ + AWS_DYNAMODB_SEGMENT: 'aws.dynamodb.segment', /** - * The value of the `TotalSegments` request parameter. - */ - AWS_DYNAMODB_TOTAL_SEGMENTS: "aws.dynamodb.total_segments", + * The value of the `TotalSegments` request parameter. + */ + AWS_DYNAMODB_TOTAL_SEGMENTS: 'aws.dynamodb.total_segments', /** - * The value of the `Count` response parameter. - */ - AWS_DYNAMODB_COUNT: "aws.dynamodb.count", + * The value of the `Count` response parameter. + */ + AWS_DYNAMODB_COUNT: 'aws.dynamodb.count', /** - * The value of the `ScannedCount` response parameter. - */ - AWS_DYNAMODB_SCANNED_COUNT: "aws.dynamodb.scanned_count", + * The value of the `ScannedCount` response parameter. + */ + AWS_DYNAMODB_SCANNED_COUNT: 'aws.dynamodb.scanned_count', /** - * The JSON-serialized value of each item in the `AttributeDefinitions` request field. - */ - AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS: "aws.dynamodb.attribute_definitions", + * The JSON-serialized value of each item in the `AttributeDefinitions` request field. + */ + AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS: 'aws.dynamodb.attribute_definitions', /** - * The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` request field. - */ - AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES: "aws.dynamodb.global_secondary_index_updates", + * The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` request field. + */ + AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES: + 'aws.dynamodb.global_secondary_index_updates', /** - * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. - */ - MESSAGING_OPERATION: "messaging.operation", + * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. + */ + MESSAGING_OPERATION: 'messaging.operation', /** - * Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message_id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. - * - * Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. - */ - MESSAGING_KAFKA_MESSAGE_KEY: "messaging.kafka.message_key", + * Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition. They differ from `messaging.message_id` in that they're not unique. If the key is `null`, the attribute MUST NOT be set. + * + * Note: If the key type is not string, it's string representation has to be supplied for the attribute. If the key has no unambiguous, canonical string form, don't include its value. + */ + MESSAGING_KAFKA_MESSAGE_KEY: 'messaging.kafka.message_key', /** - * Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. - */ - MESSAGING_KAFKA_CONSUMER_GROUP: "messaging.kafka.consumer_group", + * Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. + */ + MESSAGING_KAFKA_CONSUMER_GROUP: 'messaging.kafka.consumer_group', /** - * Client Id for the Consumer or Producer that is handling the message. - */ - MESSAGING_KAFKA_CLIENT_ID: "messaging.kafka.client_id", + * Client Id for the Consumer or Producer that is handling the message. + */ + MESSAGING_KAFKA_CLIENT_ID: 'messaging.kafka.client_id', /** - * Partition the message is sent to. - */ - MESSAGING_KAFKA_PARTITION: "messaging.kafka.partition", + * Partition the message is sent to. + */ + MESSAGING_KAFKA_PARTITION: 'messaging.kafka.partition', /** - * A boolean that is true if the message is a tombstone. - */ - MESSAGING_KAFKA_TOMBSTONE: "messaging.kafka.tombstone", + * A boolean that is true if the message is a tombstone. + */ + MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone', /** - * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. - */ - RPC_GRPC_STATUS_CODE: "rpc.grpc.status_code", + * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. + */ + RPC_GRPC_STATUS_CODE: 'rpc.grpc.status_code', // Manually defined and not YET in the YAML @@ -630,245 +637,236 @@ clear whether the exception will escape. GRPC_STATUS_CODE: 'grpc.status_code', GRPC_ERROR_NAME: 'grpc.error_name', GRPC_ERROR_MESSAGE: 'grpc.error_message', -} +}; // Enum definitions export enum DbSystemValues { - /** Some other SQL database. Fallback only. See notes. */ - OTHER_SQL = "other_sql", - /** Microsoft SQL Server. */ - MSSQL = "mssql", - /** MySQL. */ - MYSQL = "mysql", - /** Oracle Database. */ - ORACLE = "oracle", - /** IBM Db2. */ - DB2 = "db2", - /** PostgreSQL. */ - POSTGRESQL = "postgresql", - /** Amazon Redshift. */ - REDSHIFT = "redshift", - /** Apache Hive. */ - HIVE = "hive", - /** Cloudscape. */ - CLOUDSCAPE = "cloudscape", - /** HyperSQL DataBase. */ - HSQLDB = "hsqldb", - /** Progress Database. */ - PROGRESS = "progress", - /** SAP MaxDB. */ - MAXDB = "maxdb", - /** SAP HANA. */ - HANADB = "hanadb", - /** Ingres. */ - INGRES = "ingres", - /** FirstSQL. */ - FIRSTSQL = "firstsql", - /** EnterpriseDB. */ - EDB = "edb", - /** InterSystems Caché. */ - CACHE = "cache", - /** Adabas (Adaptable Database System). */ - ADABAS = "adabas", - /** Firebird. */ - FIREBIRD = "firebird", - /** Apache Derby. */ - DERBY = "derby", - /** FileMaker. */ - FILEMAKER = "filemaker", - /** Informix. */ - INFORMIX = "informix", - /** InstantDB. */ - INSTANTDB = "instantdb", - /** InterBase. */ - INTERBASE = "interbase", - /** MariaDB. */ - MARIADB = "mariadb", - /** Netezza. */ - NETEZZA = "netezza", - /** Pervasive PSQL. */ - PERVASIVE = "pervasive", - /** PointBase. */ - POINTBASE = "pointbase", - /** SQLite. */ - SQLITE = "sqlite", - /** Sybase. */ - SYBASE = "sybase", - /** Teradata. */ - TERADATA = "teradata", - /** Vertica. */ - VERTICA = "vertica", - /** H2. */ - H2 = "h2", - /** ColdFusion IMQ. */ - COLDFUSION = "coldfusion", - /** Apache Cassandra. */ - CASSANDRA = "cassandra", - /** Apache HBase. */ - HBASE = "hbase", - /** MongoDB. */ - MONGODB = "mongodb", - /** Redis. */ - REDIS = "redis", - /** Couchbase. */ - COUCHBASE = "couchbase", - /** CouchDB. */ - COUCHDB = "couchdb", - /** Microsoft Azure Cosmos DB. */ - COSMOSDB = "cosmosdb", - /** Amazon DynamoDB. */ - DYNAMODB = "dynamodb", - /** Neo4j. */ - NEO4J = "neo4j", - /** Apache Geode. */ - GEODE = "geode", - /** Elasticsearch. */ - ELASTICSEARCH = "elasticsearch", + /** Some other SQL database. Fallback only. See notes. */ + OTHER_SQL = 'other_sql', + /** Microsoft SQL Server. */ + MSSQL = 'mssql', + /** MySQL. */ + MYSQL = 'mysql', + /** Oracle Database. */ + ORACLE = 'oracle', + /** IBM Db2. */ + DB2 = 'db2', + /** PostgreSQL. */ + POSTGRESQL = 'postgresql', + /** Amazon Redshift. */ + REDSHIFT = 'redshift', + /** Apache Hive. */ + HIVE = 'hive', + /** Cloudscape. */ + CLOUDSCAPE = 'cloudscape', + /** HyperSQL DataBase. */ + HSQLDB = 'hsqldb', + /** Progress Database. */ + PROGRESS = 'progress', + /** SAP MaxDB. */ + MAXDB = 'maxdb', + /** SAP HANA. */ + HANADB = 'hanadb', + /** Ingres. */ + INGRES = 'ingres', + /** FirstSQL. */ + FIRSTSQL = 'firstsql', + /** EnterpriseDB. */ + EDB = 'edb', + /** InterSystems Caché. */ + CACHE = 'cache', + /** Adabas (Adaptable Database System). */ + ADABAS = 'adabas', + /** Firebird. */ + FIREBIRD = 'firebird', + /** Apache Derby. */ + DERBY = 'derby', + /** FileMaker. */ + FILEMAKER = 'filemaker', + /** Informix. */ + INFORMIX = 'informix', + /** InstantDB. */ + INSTANTDB = 'instantdb', + /** InterBase. */ + INTERBASE = 'interbase', + /** MariaDB. */ + MARIADB = 'mariadb', + /** Netezza. */ + NETEZZA = 'netezza', + /** Pervasive PSQL. */ + PERVASIVE = 'pervasive', + /** PointBase. */ + POINTBASE = 'pointbase', + /** SQLite. */ + SQLITE = 'sqlite', + /** Sybase. */ + SYBASE = 'sybase', + /** Teradata. */ + TERADATA = 'teradata', + /** Vertica. */ + VERTICA = 'vertica', + /** H2. */ + H2 = 'h2', + /** ColdFusion IMQ. */ + COLDFUSION = 'coldfusion', + /** Apache Cassandra. */ + CASSANDRA = 'cassandra', + /** Apache HBase. */ + HBASE = 'hbase', + /** MongoDB. */ + MONGODB = 'mongodb', + /** Redis. */ + REDIS = 'redis', + /** Couchbase. */ + COUCHBASE = 'couchbase', + /** CouchDB. */ + COUCHDB = 'couchdb', + /** Microsoft Azure Cosmos DB. */ + COSMOSDB = 'cosmosdb', + /** Amazon DynamoDB. */ + DYNAMODB = 'dynamodb', + /** Neo4j. */ + NEO4J = 'neo4j', + /** Apache Geode. */ + GEODE = 'geode', + /** Elasticsearch. */ + ELASTICSEARCH = 'elasticsearch', } - export enum NetTransportValues { - /** IP.TCP. */ - IP_TCP = "IP.TCP", - /** IP.UDP. */ - IP_UDP = "IP.UDP", - /** Another IP-based protocol. */ - IP = "IP", - /** Unix Domain socket. See below. */ - UNIX = "Unix", - /** Named or anonymous pipe. See note below. */ - PIPE = "pipe", - /** In-process communication. */ - INPROC = "inproc", - /** Something else (non IP-based). */ - OTHER = "other", + /** IP.TCP. */ + IP_TCP = 'IP.TCP', + /** IP.UDP. */ + IP_UDP = 'IP.UDP', + /** Another IP-based protocol. */ + IP = 'IP', + /** Unix Domain socket. See below. */ + UNIX = 'Unix', + /** Named or anonymous pipe. See note below. */ + PIPE = 'pipe', + /** In-process communication. */ + INPROC = 'inproc', + /** Something else (non IP-based). */ + OTHER = 'other', } - export enum DbCassandraConsistencyLevelValues { - /** ALL. */ - ALL = "ALL", - /** EACH_QUORUM. */ - EACH_QUORUM = "EACH_QUORUM", - /** QUORUM. */ - QUORUM = "QUORUM", - /** LOCAL_QUORUM. */ - LOCAL_QUORUM = "LOCAL_QUORUM", - /** ONE. */ - ONE = "ONE", - /** TWO. */ - TWO = "TWO", - /** THREE. */ - THREE = "THREE", - /** LOCAL_ONE. */ - LOCAL_ONE = "LOCAL_ONE", - /** ANY. */ - ANY = "ANY", - /** SERIAL. */ - SERIAL = "SERIAL", - /** LOCAL_SERIAL. */ - LOCAL_SERIAL = "LOCAL_SERIAL", + /** ALL. */ + ALL = 'ALL', + /** EACH_QUORUM. */ + EACH_QUORUM = 'EACH_QUORUM', + /** QUORUM. */ + QUORUM = 'QUORUM', + /** LOCAL_QUORUM. */ + LOCAL_QUORUM = 'LOCAL_QUORUM', + /** ONE. */ + ONE = 'ONE', + /** TWO. */ + TWO = 'TWO', + /** THREE. */ + THREE = 'THREE', + /** LOCAL_ONE. */ + LOCAL_ONE = 'LOCAL_ONE', + /** ANY. */ + ANY = 'ANY', + /** SERIAL. */ + SERIAL = 'SERIAL', + /** LOCAL_SERIAL. */ + LOCAL_SERIAL = 'LOCAL_SERIAL', } - export enum FaasTriggerValues { - /** A response to some data source operation such as a database or filesystem read/write. */ - DATASOURCE = "datasource", - /** To provide an answer to an inbound HTTP request. */ - HTTP = "http", - /** A function is set to be executed when messages are sent to a messaging system. */ - PUBSUB = "pubsub", - /** A function is scheduled to be executed regularly. */ - TIMER = "timer", - /** If none of the others apply. */ - OTHER = "other", + /** A response to some data source operation such as a database or filesystem read/write. */ + DATASOURCE = 'datasource', + /** To provide an answer to an inbound HTTP request. */ + HTTP = 'http', + /** A function is set to be executed when messages are sent to a messaging system. */ + PUBSUB = 'pubsub', + /** A function is scheduled to be executed regularly. */ + TIMER = 'timer', + /** If none of the others apply. */ + OTHER = 'other', } - export enum FaasDocumentOperationValues { - /** When a new object is created. */ - INSERT = "insert", - /** When an object is modified. */ - EDIT = "edit", - /** When an object is deleted. */ - DELETE = "delete", + /** When a new object is created. */ + INSERT = 'insert', + /** When an object is modified. */ + EDIT = 'edit', + /** When an object is deleted. */ + DELETE = 'delete', } - export enum HttpFlavorValues { - /** HTTP 1.0. */ - HTTP_1_0 = "1.0", - /** HTTP 1.1. */ - HTTP_1_1 = "1.1", - /** HTTP 2. */ - HTTP_2_0 = "2.0", - /** SPDY protocol. */ - SPDY = "SPDY", - /** QUIC protocol. */ - QUIC = "QUIC", + /** HTTP 1.0. */ + HTTP_1_0 = '1.0', + /** HTTP 1.1. */ + HTTP_1_1 = '1.1', + /** HTTP 2. */ + HTTP_2_0 = '2.0', + /** SPDY protocol. */ + SPDY = 'SPDY', + /** QUIC protocol. */ + QUIC = 'QUIC', } - export enum MessagingDestinationKindValues { - /** A message sent to a queue. */ - QUEUE = "queue", - /** A message sent to a topic. */ - TOPIC = "topic", + /** A message sent to a queue. */ + QUEUE = 'queue', + /** A message sent to a topic. */ + TOPIC = 'topic', } - export enum FaasInvokedProviderValues { - /** Amazon Web Services. */ - AWS = "aws", - /** Microsoft Azure. */ - AZURE = "azure", - /** Google Cloud Platform. */ - GCP = "gcp", + /** Amazon Web Services. */ + AWS = 'aws', + /** Microsoft Azure. */ + AZURE = 'azure', + /** Google Cloud Platform. */ + GCP = 'gcp', } - export enum MessagingOperationValues { - /** receive. */ - RECEIVE = "receive", - /** process. */ - PROCESS = "process", + /** receive. */ + RECEIVE = 'receive', + /** process. */ + PROCESS = 'process', } - export enum RpcGrpcStatusCodeValues { - /** OK. */ - OK = 0, - /** CANCELLED. */ - CANCELLED = 1, - /** UNKNOWN. */ - UNKNOWN = 2, - /** INVALID_ARGUMENT. */ - INVALID_ARGUMENT = 3, - /** DEADLINE_EXCEEDED. */ - DEADLINE_EXCEEDED = 4, - /** NOT_FOUND. */ - NOT_FOUND = 5, - /** ALREADY_EXISTS. */ - ALREADY_EXISTS = 6, - /** PERMISSION_DENIED. */ - PERMISSION_DENIED = 7, - /** RESOURCE_EXHAUSTED. */ - RESOURCE_EXHAUSTED = 8, - /** FAILED_PRECONDITION. */ - FAILED_PRECONDITION = 9, - /** ABORTED. */ - ABORTED = 10, - /** OUT_OF_RANGE. */ - OUT_OF_RANGE = 11, - /** UNIMPLEMENTED. */ - UNIMPLEMENTED = 12, - /** INTERNAL. */ - INTERNAL = 13, - /** UNAVAILABLE. */ - UNAVAILABLE = 14, - /** DATA_LOSS. */ - DATA_LOSS = 15, - /** UNAUTHENTICATED. */ - UNAUTHENTICATED = 16, + /** OK. */ + OK = 0, + /** CANCELLED. */ + CANCELLED = 1, + /** UNKNOWN. */ + UNKNOWN = 2, + /** INVALID_ARGUMENT. */ + INVALID_ARGUMENT = 3, + /** DEADLINE_EXCEEDED. */ + DEADLINE_EXCEEDED = 4, + /** NOT_FOUND. */ + NOT_FOUND = 5, + /** ALREADY_EXISTS. */ + ALREADY_EXISTS = 6, + /** PERMISSION_DENIED. */ + PERMISSION_DENIED = 7, + /** RESOURCE_EXHAUSTED. */ + RESOURCE_EXHAUSTED = 8, + /** FAILED_PRECONDITION. */ + FAILED_PRECONDITION = 9, + /** ABORTED. */ + ABORTED = 10, + /** OUT_OF_RANGE. */ + OUT_OF_RANGE = 11, + /** UNIMPLEMENTED. */ + UNIMPLEMENTED = 12, + /** INTERNAL. */ + INTERNAL = 13, + /** UNAVAILABLE. */ + UNAVAILABLE = 14, + /** DATA_LOSS. */ + DATA_LOSS = 15, + /** UNAUTHENTICATED. */ + UNAUTHENTICATED = 16, } diff --git a/packages/opentelemetry-semantic-conventions/src/trace/index.ts b/packages/opentelemetry-semantic-conventions/src/trace/index.ts index 322332d9050..71503260391 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/index.ts @@ -1 +1,16 @@ -export * from "./SemanticAttribute"; +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ +export * from './SemanticAttribute'; diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index ea6ebce1823..c5d720c4608 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -25,8 +25,8 @@ import { } from '@opentelemetry/core'; import { Resource } from '@opentelemetry/resources'; import { - ExceptionAttribute, ExceptionEventName, + SemanticAttribute, } from '@opentelemetry/semantic-conventions'; import { ReadableSpan } from './export/ReadableSpan'; import { Tracer } from './Tracer'; @@ -190,25 +190,25 @@ export class Span implements api.Span, ReadableSpan { recordException(exception: api.Exception, time: api.TimeInput = hrTime()) { const attributes: api.SpanAttributes = {}; if (typeof exception === 'string') { - attributes[ExceptionAttribute.MESSAGE] = exception; + attributes[SemanticAttribute.EXCEPTION_MESSAGE] = exception; } else if (exception) { if (exception.code) { - attributes[ExceptionAttribute.TYPE] = exception.code; + attributes[SemanticAttribute.EXCEPTION_TYPE] = exception.code; } else if (exception.name) { - attributes[ExceptionAttribute.TYPE] = exception.name; + attributes[SemanticAttribute.EXCEPTION_TYPE] = exception.name; } if (exception.message) { - attributes[ExceptionAttribute.MESSAGE] = exception.message; + attributes[SemanticAttribute.EXCEPTION_MESSAGE] = exception.message; } if (exception.stack) { - attributes[ExceptionAttribute.STACKTRACE] = exception.stack; + attributes[SemanticAttribute.EXCEPTION_STACKTRACE] = exception.stack; } } // these are minimum requirements from spec if ( - attributes[ExceptionAttribute.TYPE] || - attributes[ExceptionAttribute.MESSAGE] + attributes[SemanticAttribute.EXCEPTION_TYPE] || + attributes[SemanticAttribute.EXCEPTION_MESSAGE] ) { this.addEvent(ExceptionEventName, attributes as api.SpanAttributes, time); } else { diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 5597223e026..672f2592c8a 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -29,7 +29,7 @@ import { hrTimeToMilliseconds, hrTimeToNanoseconds, } from '@opentelemetry/core'; -import { ExceptionAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; import * as assert from 'assert'; import { BasicTracerProvider, Span, SpanProcessor } from '../src'; @@ -692,10 +692,10 @@ describe('Span', () => { assert.ok(event.attributes); - const type = event.attributes[ExceptionAttribute.TYPE]; - const message = event.attributes[ExceptionAttribute.MESSAGE]; + const type = event.attributes[SemanticAttribute.EXCEPTION_TYPE]; + const message = event.attributes[SemanticAttribute.EXCEPTION_MESSAGE]; const stacktrace = String( - event.attributes[ExceptionAttribute.STACKTRACE] + event.attributes[SemanticAttribute.EXCEPTION_STACKTRACE] ); assert.strictEqual(type, 'Error'); assert.strictEqual(message, 'boom'); diff --git a/packages/opentelemetry-web/src/utils.ts b/packages/opentelemetry-web/src/utils.ts index 8c6a1a0e866..a9ad053042b 100644 --- a/packages/opentelemetry-web/src/utils.ts +++ b/packages/opentelemetry-web/src/utils.ts @@ -26,7 +26,7 @@ import { timeInputToHrTime, urlMatches, } from '@opentelemetry/core'; -import { HttpAttribute } from '@opentelemetry/semantic-conventions'; +import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; // Used to normalize relative URLs let a: HTMLAnchorElement | undefined; @@ -90,7 +90,7 @@ export function addSpanNetworkEvents( const contentLength = resource[PTN.ENCODED_BODY_SIZE]; if (contentLength !== undefined) { span.setAttribute( - HttpAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, contentLength ); } From 5b40934f391ac3ce4c0e3e0a0d7a3717dc88bcf1 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 02:22:58 +0100 Subject: [PATCH 04/14] fix: improve the `semconv`-template to better match eslint prettier rules --- scripts/semconv/templates/SemanticAttributes.ts.j2 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/semconv/templates/SemanticAttributes.ts.j2 b/scripts/semconv/templates/SemanticAttributes.ts.j2 index a8bd9fdda22..aa9081e6a31 100644 --- a/scripts/semconv/templates/SemanticAttributes.ts.j2 +++ b/scripts/semconv/templates/SemanticAttributes.ts.j2 @@ -43,7 +43,7 @@ export const {{class}} = { {%- if attribute.deprecated %} @Deprecated {%- endif %} - {{attribute.fqn | to_const_name}}: "{{attribute.fqn}}", + {{attribute.fqn | to_const_name}}: '{{attribute.fqn}}', {%- endfor %} {%- if class == "SemanticAttribute" %} @@ -72,8 +72,8 @@ export const {{class}} = { export enum {{class_name}} { {%- for member in attribute.attr_type.members %} - /** {% filter escape %}{{member.brief | to_doc_brief}}.{% endfilter %} */ - {{ member.member_id | to_const_name }} = {{ print_value(type, member.value) }}, + /** {% filter escape %}{{member.brief | to_doc_brief}}.{% endfilter %} */ + {{ member.member_id | to_const_name }} = {{ print_value(type, member.value) }}, {%- endfor %} } {% endif %} From 3e9f64e94e26f52a424c461e42d5f359291315b1 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 02:39:23 +0100 Subject: [PATCH 05/14] chore: run `lint:fix`-task after generating source files --- .../src/resource/ResourceAttribute.ts | 2 +- scripts/semconv/generate.sh | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts index 4157bafeff1..6a7ba0989df 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts @@ -15,7 +15,7 @@ */ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 -export const ResourceAttributes = { +export const ResourceAttribute = { /** * Name of the cloud provider. */ diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh index ad4b7062324..c7f1a40cc1c 100755 --- a/scripts/semconv/generate.sh +++ b/scripts/semconv/generate.sh @@ -36,6 +36,11 @@ docker run --rm \ -f /source code \ --template /templates/SemanticAttributes.ts.j2 \ --output /output/ResourceAttribute.ts \ - -Dclass=ResourceAttributes + -Dclass=ResourceAttribute + +# Run the automatic linting fixing task to ensure it will pass eslint +cd "$ROOT_DIR/packages/opentelemetry-semantic-conventions" + +npm run lint:fix cd "$ROOT_DIR" From 3302972f519f936a800adb1bf9a26a71cb5c9ed5 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 03:05:37 +0100 Subject: [PATCH 06/14] chore: move argument to its own line in `generate.sh` --- scripts/semconv/generate.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh index c7f1a40cc1c..11d04959b83 100755 --- a/scripts/semconv/generate.sh +++ b/scripts/semconv/generate.sh @@ -23,7 +23,8 @@ docker run --rm \ -v ${SCRIPT_DIR}/templates:/templates \ -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/trace/:/output \ otel/semconvgen \ - -f /source code \ + -f /source \ + code \ --template /templates/SemanticAttributes.ts.j2 \ --output /output/SemanticAttribute.ts \ -Dclass=SemanticAttribute @@ -33,7 +34,8 @@ docker run --rm \ -v ${SCRIPT_DIR}/templates:/templates \ -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/resource/:/output \ otel/semconvgen \ - -f /source code \ + -f /source \ + code \ --template /templates/SemanticAttributes.ts.j2 \ --output /output/ResourceAttribute.ts \ -Dclass=ResourceAttribute From 9b889efff306715adb6136b9e84539da3d20be0a Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 03:26:14 +0100 Subject: [PATCH 07/14] fix: ensure we generate against v1.1.0 of the spec Changed `generate.sh` to ensure it generates the files against the latest published version of the otel specification which is v1.1.0. To allow this version of the spec files to work with the `semconv` the version of this tool had to be lowered to 0.2.1 as there is a breaking change in 0.3.0 which removes support for the `number`-type. --- .../src/resource/ResourceAttribute.ts | 26 +--- .../src/trace/SemanticAttribute.ts | 146 ++---------------- scripts/semconv/generate.sh | 7 +- 3 files changed, 23 insertions(+), 156 deletions(-) diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts index 6a7ba0989df..d9ccb3e76c7 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts @@ -39,11 +39,11 @@ export const ResourceAttribute = { CLOUD_AVAILABILITY_ZONE: 'cloud.availability_zone', /** - * The cloud platform in use. + * The cloud infrastructure resource in use. * * Note: The prefix of the service SHOULD match the one specified in `cloud.provider`. */ - CLOUD_PLATFORM: 'cloud.platform', + CLOUD_INFRASTRUCTURE_SERVICE: 'cloud.infrastructure_service', /** * The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). @@ -70,11 +70,6 @@ export const ResourceAttribute = { */ AWS_ECS_TASK_FAMILY: 'aws.ecs.task.family', - /** - * The revision for this task definition. - */ - AWS_ECS_TASK_REVISION: 'aws.ecs.task.revision', - /** * The ARN of an EKS cluster. */ @@ -400,21 +395,6 @@ export const ResourceAttribute = { * The version string of the auto instrumentation agent, if used. */ TELEMETRY_AUTO_VERSION: 'telemetry.auto.version', - - /** - * The name of the web engine. - */ - WEBENGINE_NAME: 'webengine.name', - - /** - * The version of the web engine. - */ - WEBENGINE_VERSION: 'webengine.version', - - /** - * Additional description of the web engine (e.g. detailed version and edition information). - */ - WEBENGINE_DESCRIPTION: 'webengine.description', }; // Enum definitions @@ -428,7 +408,7 @@ export enum CloudProviderValues { GCP = 'gcp', } -export enum CloudPlatformValues { +export enum CloudInfrastructureServiceValues { /** AWS Elastic Compute Cloud. */ AWS_EC2 = 'aws_ec2', /** AWS Elastic Container Service. */ diff --git a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts index 2a12b2244d3..32d7b2318f9 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts @@ -458,135 +458,6 @@ clear whether the exception will escape. */ CODE_LINENO: 'code.lineno', - /** - * The value `aws-api`. - */ - RPC_SYSTEM: 'rpc.system', - - /** - * The name of the service to which a request is made, as returned by the AWS SDK. - */ - RPC_SERVICE: 'rpc.service', - - /** - * The name of the operation corresponding to the request, as returned by the AWS SDK. - */ - RPC_METHOD: 'rpc.method', - - /** - * The keys in the `RequestItems` object field. - */ - AWS_DYNAMODB_TABLE_NAMES: 'aws.dynamodb.table_names', - - /** - * The JSON-serialized value of each item in the `ConsumedCapacity` response field. - */ - AWS_DYNAMODB_CONSUMED_CAPACITY: 'aws.dynamodb.consumed_capacity', - - /** - * The JSON-serialized value of the `ItemCollectionMetrics` response field. - */ - AWS_DYNAMODB_ITEM_COLLECTION_METRICS: 'aws.dynamodb.item_collection_metrics', - - /** - * The value of the `ProvisionedThroughput.ReadCapacityUnits` request parameter. - */ - AWS_DYNAMODB_PROVISIONED_READ_CAPACITY: - 'aws.dynamodb.provisioned_read_capacity', - - /** - * The value of the `ProvisionedThroughput.WriteCapacityUnits` request parameter. - */ - AWS_DYNAMODB_PROVISIONED_WRITE_CAPACITY: - 'aws.dynamodb.provisioned_write_capacity', - - /** - * The value of the `ConsistentRead` request parameter. - */ - AWS_DYNAMODB_CONSISTENT_READ: 'aws.dynamodb.consistent_read', - - /** - * The value of the `ProjectionExpression` request parameter. - */ - AWS_DYNAMODB_PROJECTION: 'aws.dynamodb.projection', - - /** - * The value of the `Limit` request parameter. - */ - AWS_DYNAMODB_LIMIT: 'aws.dynamodb.limit', - - /** - * The value of the `AttributesToGet` request parameter. - */ - AWS_DYNAMODB_ATTRIBUTES_TO_GET: 'aws.dynamodb.attributes_to_get', - - /** - * The value of the `IndexName` request parameter. - */ - AWS_DYNAMODB_INDEX_NAME: 'aws.dynamodb.index_name', - - /** - * The value of the `Select` request parameter. - */ - AWS_DYNAMODB_SELECT: 'aws.dynamodb.select', - - /** - * The JSON-serialized value of each item of the `GlobalSecondaryIndexes` request field. - */ - AWS_DYNAMODB_GLOBAL_SECONDARY_INDEXES: - 'aws.dynamodb.global_secondary_indexes', - - /** - * The JSON-serialized value of each item of the `LocalSecondaryIndexes` request field. - */ - AWS_DYNAMODB_LOCAL_SECONDARY_INDEXES: 'aws.dynamodb.local_secondary_indexes', - - /** - * The value of the `ExclusiveStartTableName` request parameter. - */ - AWS_DYNAMODB_EXCLUSIVE_START_TABLE: 'aws.dynamodb.exclusive_start_table', - - /** - * The the number of items in the `TableNames` response parameter. - */ - AWS_DYNAMODB_TABLE_COUNT: 'aws.dynamodb.table_count', - - /** - * The value of the `ScanIndexForward` request parameter. - */ - AWS_DYNAMODB_SCAN_FORWARD: 'aws.dynamodb.scan_forward', - - /** - * The value of the `Segment` request parameter. - */ - AWS_DYNAMODB_SEGMENT: 'aws.dynamodb.segment', - - /** - * The value of the `TotalSegments` request parameter. - */ - AWS_DYNAMODB_TOTAL_SEGMENTS: 'aws.dynamodb.total_segments', - - /** - * The value of the `Count` response parameter. - */ - AWS_DYNAMODB_COUNT: 'aws.dynamodb.count', - - /** - * The value of the `ScannedCount` response parameter. - */ - AWS_DYNAMODB_SCANNED_COUNT: 'aws.dynamodb.scanned_count', - - /** - * The JSON-serialized value of each item in the `AttributeDefinitions` request field. - */ - AWS_DYNAMODB_ATTRIBUTE_DEFINITIONS: 'aws.dynamodb.attribute_definitions', - - /** - * The JSON-serialized value of each item in the the `GlobalSecondaryIndexUpdates` request field. - */ - AWS_DYNAMODB_GLOBAL_SECONDARY_INDEX_UPDATES: - 'aws.dynamodb.global_secondary_index_updates', - /** * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. */ @@ -619,6 +490,21 @@ clear whether the exception will escape. */ MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone', + /** + * A string identifying the remoting system. + */ + RPC_SYSTEM: 'rpc.system', + + /** + * The full name of the service being called, including its package name, if applicable. + */ + RPC_SERVICE: 'rpc.service', + + /** + * The name of the method being called, must be equal to the $method part in the span name. + */ + RPC_METHOD: 'rpc.method', + /** * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. */ @@ -821,7 +707,7 @@ export enum MessagingDestinationKindValues { export enum FaasInvokedProviderValues { /** Amazon Web Services. */ AWS = 'aws', - /** Microsoft Azure. */ + /** Amazon Web Services. */ AZURE = 'azure', /** Google Cloud Platform. */ GCP = 'gcp', diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh index 11d04959b83..c7d5c713059 100755 --- a/scripts/semconv/generate.sh +++ b/scripts/semconv/generate.sh @@ -4,7 +4,8 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ROOT_DIR="${SCRIPT_DIR}/../../" # freeze the spec version to make SemanticAttributes generation reproducible -SPEC_VERSION=main +SPEC_VERSION=v1.1.0 +GENERATOR_VERSION=0.2.1 cd ${SCRIPT_DIR} @@ -22,7 +23,7 @@ docker run --rm \ -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/trace:/source \ -v ${SCRIPT_DIR}/templates:/templates \ -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/trace/:/output \ - otel/semconvgen \ + otel/semconvgen:${GENERATOR_VERSION} \ -f /source \ code \ --template /templates/SemanticAttributes.ts.j2 \ @@ -33,7 +34,7 @@ docker run --rm \ -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/resource:/source \ -v ${SCRIPT_DIR}/templates:/templates \ -v ${ROOT_DIR}/packages/opentelemetry-semantic-conventions/src/resource/:/output \ - otel/semconvgen \ + otel/semconvgen:${GENERATOR_VERSION} \ -f /source \ code \ --template /templates/SemanticAttributes.ts.j2 \ From 55d2c9bebcca3ae487e27d6647eeb39e39a82681 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 18:52:12 +0100 Subject: [PATCH 08/14] fix: removed the out of spec semantic conventions from package - Removed all the semantic convention constants from the `@opentelemetry/semantic-conventions`-package that are not part of the Opentelemetry Specification v1.1.0 - Renamed the `ResourceAttribute`-class to `ResourceAttributes` - Renamed the `SemanticAttribute`-class to `SemanticAttributes` as `SpanAttributes` name conflicts with the interface defined in the `api`-package - Updated references in the packages to use the new names --- .../src/enums/AttributeNames.ts | 4 +- .../src/fetch.ts | 71 ++-- .../test/fetch.test.ts | 252 ++++++------ .../src/enums.ts | 26 ++ .../src/grpc-js/clientUtils.ts | 46 +-- .../src/grpc-js/index.ts | 76 ++-- .../src/grpc-js/serverUtils.ts | 61 ++- .../src/grpc/clientUtils.ts | 45 +-- .../src/grpc/index.ts | 80 ++-- .../src/grpc/serverUtils.ts | 41 +- .../src/enums.ts | 24 ++ .../src/utils.ts | 175 ++++----- .../test/functionals/http-enable.test.ts | 302 +++++++------- .../test/functionals/https-enable.test.ts | 244 ++++++------ .../test/functionals/utils.test.ts | 235 +++++------ .../test/integrations/http-enable.test.ts | 176 ++++----- .../test/integrations/https-enable.test.ts | 180 ++++----- .../test/utils/assertSpan.ts | 87 +++-- .../src/enums/AttributeNames.ts | 22 ++ .../src/xhr.ts | 103 ++--- .../test/unmocked.test.ts | 26 +- .../test/xhr.test.ts | 367 +++++++++--------- .../src/client/utils.ts | 48 +-- .../opentelemetry-plugin-grpc-js/src/enums.ts | 26 ++ .../src/server/clientStreamAndUnary.ts | 23 +- .../src/server/patchServer.ts | 60 +-- .../src/server/serverStreamAndBidi.ts | 22 +- .../opentelemetry-plugin-grpc/src/enums.ts | 26 ++ .../opentelemetry-plugin-grpc/src/grpc.ts | 148 +++---- .../opentelemetry-plugin-http/src/enums.ts | 24 ++ .../opentelemetry-plugin-http/src/utils.ts | 171 ++++---- .../test/functionals/http-enable.test.ts | 290 +++++++------- .../test/functionals/utils.test.ts | 233 +++++------ .../test/integrations/http-enable.test.ts | 168 ++++---- .../test/utils/assertSpan.ts | 87 +++-- .../opentelemetry-plugin-https/src/enums.ts | 22 ++ .../test/functionals/https-enable.test.ts | 224 +++++------ .../test/integrations/https-enable.test.ts | 172 ++++---- .../test/utils/assertSpan.ts | 63 +-- .../src/index.ts | 1 - ...urceAttribute.ts => ResourceAttributes.ts} | 2 +- .../src/resource/index.ts | 2 +- ...nticAttribute.ts => SemanticAttributes.ts} | 312 +++++++-------- .../src/trace/index.ts | 2 +- packages/opentelemetry-tracing/src/Span.ts | 50 ++- .../src/enums.ts} | 2 +- packages/opentelemetry-tracing/src/index.ts | 22 +- .../opentelemetry-tracing/test/Span.test.ts | 287 +++++++------- packages/opentelemetry-web/src/utils.ts | 44 +-- scripts/semconv/generate.sh | 10 +- .../templates/SemanticAttributes.ts.j2 | 23 +- 51 files changed, 2674 insertions(+), 2533 deletions(-) create mode 100644 packages/opentelemetry-instrumentation-grpc/src/enums.ts create mode 100644 packages/opentelemetry-instrumentation-http/src/enums.ts create mode 100644 packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts create mode 100644 packages/opentelemetry-plugin-grpc-js/src/enums.ts create mode 100644 packages/opentelemetry-plugin-grpc/src/enums.ts create mode 100644 packages/opentelemetry-plugin-http/src/enums.ts create mode 100644 packages/opentelemetry-plugin-https/src/enums.ts rename packages/opentelemetry-semantic-conventions/src/resource/{ResourceAttribute.ts => ResourceAttributes.ts} (99%) rename packages/opentelemetry-semantic-conventions/src/trace/{SemanticAttribute.ts => SemanticAttributes.ts} (98%) rename packages/{opentelemetry-semantic-conventions/src/events.ts => opentelemetry-tracing/src/enums.ts} (93%) diff --git a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts index 7b9c935694b..9a8c7db1bac 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts @@ -18,5 +18,7 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - COMPONENT = 'component', + COMPONENT = "component", + HTTP_ERROR_NAME = "http.error_name", + HTTP_STATUS_TEXT = "http.status_text", } diff --git a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index 93d46e573ad..e7d319c6502 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -14,18 +14,18 @@ * limitations under the License. */ -import * as api from '@opentelemetry/api'; +import * as api from "@opentelemetry/api"; import { isWrapped, InstrumentationBase, InstrumentationConfig, -} from '@opentelemetry/instrumentation'; -import * as core from '@opentelemetry/core'; -import * as web from '@opentelemetry/web'; -import { AttributeNames } from './enums/AttributeNames'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import { FetchError, FetchResponse, SpanData } from './types'; -import { VERSION } from './version'; +} from "@opentelemetry/instrumentation"; +import * as core from "@opentelemetry/core"; +import * as web from "@opentelemetry/web"; +import { AttributeNames } from "./enums/AttributeNames"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import { FetchError, FetchResponse, SpanData } from "./types"; +import { VERSION } from "./version"; // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer @@ -37,7 +37,7 @@ const OBSERVER_WAIT_TIME_MS = 300; let a: HTMLAnchorElement | undefined; const getUrlNormalizingAnchor = () => { if (!a) { - a = document.createElement('a'); + a = document.createElement("a"); } return a; @@ -69,7 +69,7 @@ export interface FetchInstrumentationConfig extends InstrumentationConfig { export class FetchInstrumentation extends InstrumentationBase< Promise > { - readonly component: string = 'fetch'; + readonly component: string = "fetch"; readonly version: string = VERSION; moduleName = this.component; private _usedResources = new WeakSet(); @@ -77,7 +77,7 @@ export class FetchInstrumentation extends InstrumentationBase< constructor(config: FetchInstrumentationConfig = {}) { super( - '@opentelemetry/instrumentation-fetch', + "@opentelemetry/instrumentation-fetch", VERSION, Object.assign({}, config) ); @@ -99,7 +99,7 @@ export class FetchInstrumentation extends InstrumentationBase< corsPreFlightRequest: PerformanceResourceTiming ): void { const childSpan = this.tracer.startSpan( - 'CORS Preflight', + "CORS Preflight", { startTime: corsPreFlightRequest[web.PerformanceTimingNames.FETCH_START], }, @@ -121,19 +121,16 @@ export class FetchInstrumentation extends InstrumentationBase< response: FetchResponse ): void { const parsedUrl = web.parseUrl(response.url); - span.setAttribute(SemanticAttribute.HTTP_STATUS_CODE, response.status); + span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, response.status); if (response.statusText != null) { - span.setAttribute( - SemanticAttribute.HTTP_STATUS_TEXT, - response.statusText - ); + span.setAttribute(AttributeNames.HTTP_STATUS_TEXT, response.statusText); } - span.setAttribute(SemanticAttribute.HTTP_HOST, parsedUrl.host); + span.setAttribute(SemanticAttributes.HTTP_HOST, parsedUrl.host); span.setAttribute( - SemanticAttribute.HTTP_SCHEME, - parsedUrl.protocol.replace(':', '') + SemanticAttributes.HTTP_SCHEME, + parsedUrl.protocol.replace(":", "") ); - span.setAttribute(SemanticAttribute.HTTP_USER_AGENT, navigator.userAgent); + span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent); } /** @@ -153,7 +150,7 @@ export class FetchInstrumentation extends InstrumentationBase< if (options instanceof Request) { api.propagation.inject(api.context.active(), options.headers, { - set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)), + set: (h, k, v) => h.set(k, typeof v === "string" ? v : String(v)), }); } else { const headers: Partial> = {}; @@ -185,17 +182,17 @@ export class FetchInstrumentation extends InstrumentationBase< options: Partial = {} ): api.Span | undefined { if (core.isUrlIgnored(url, this._getConfig().ignoreUrls)) { - api.diag.debug('ignoring span as url matches ignored url'); + api.diag.debug("ignoring span as url matches ignored url"); return; } - const method = (options.method || 'GET').toUpperCase(); + const method = (options.method || "GET").toUpperCase(); const spanName = `HTTP ${method}`; return this.tracer.startSpan(spanName, { kind: api.SpanKind.CLIENT, attributes: { [AttributeNames.COMPONENT]: this.moduleName, - [SemanticAttribute.HTTP_METHOD]: method, - [SemanticAttribute.HTTP_URL]: url, + [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_URL]: url, }, }); } @@ -220,7 +217,7 @@ export class FetchInstrumentation extends InstrumentationBase< // then OBSERVER_WAIT_TIME_MS and observer didn't collect enough // information resources = performance.getEntriesByType( - 'resource' + "resource" ) as PerformanceResourceTiming[]; } const resource = web.getResource( @@ -229,7 +226,7 @@ export class FetchInstrumentation extends InstrumentationBase< endTime, resources, this._usedResources, - 'fetch' + "fetch" ); if (resource.mainRequest) { @@ -366,17 +363,17 @@ export class FetchInstrumentation extends InstrumentationBase< private _prepareSpanData(spanUrl: string): SpanData { const startTime = core.hrTime(); const entries: PerformanceResourceTiming[] = []; - if (typeof window.PerformanceObserver === 'undefined') { + if (typeof window.PerformanceObserver === "undefined") { return { entries, startTime, spanUrl }; } - const observer: PerformanceObserver = new PerformanceObserver(list => { + const observer: PerformanceObserver = new PerformanceObserver((list) => { const perfObsEntries = list.getEntries() as PerformanceResourceTiming[]; const urlNormalizingAnchor = getUrlNormalizingAnchor(); urlNormalizingAnchor.href = spanUrl; - perfObsEntries.forEach(entry => { + perfObsEntries.forEach((entry) => { if ( - entry.initiatorType === 'fetch' && + entry.initiatorType === "fetch" && entry.name === urlNormalizingAnchor.href ) { entries.push(entry); @@ -384,7 +381,7 @@ export class FetchInstrumentation extends InstrumentationBase< }); }); observer.observe({ - entryTypes: ['resource'], + entryTypes: ["resource"], }); return { entries, observer, startTime, spanUrl }; } @@ -394,17 +391,17 @@ export class FetchInstrumentation extends InstrumentationBase< */ enable() { if (isWrapped(window.fetch)) { - this._unwrap(window, 'fetch'); - api.diag.debug('removing previous patch for constructor'); + this._unwrap(window, "fetch"); + api.diag.debug("removing previous patch for constructor"); } - this._wrap(window, 'fetch', this._patchConstructor()); + this._wrap(window, "fetch", this._patchConstructor()); } /** * implements unpatch function */ disable() { - this._unwrap(window, 'fetch'); + this._unwrap(window, "fetch"); this._usedResources = new WeakSet(); } } diff --git a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 59cc0dd41f4..08c37370759 100644 --- a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as api from '@opentelemetry/api'; -import * as core from '@opentelemetry/core'; +import * as api from "@opentelemetry/api"; +import * as core from "@opentelemetry/core"; import { isWrapped, registerInstrumentations, -} from '@opentelemetry/instrumentation'; +} from "@opentelemetry/instrumentation"; import { B3Propagator, @@ -26,18 +26,18 @@ import { X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_SAMPLED, -} from '@opentelemetry/propagator-b3'; -import { ZoneContextManager } from '@opentelemetry/context-zone'; -import * as tracing from '@opentelemetry/tracing'; +} from "@opentelemetry/propagator-b3"; +import { ZoneContextManager } from "@opentelemetry/context-zone"; +import * as tracing from "@opentelemetry/tracing"; import { PerformanceTimingNames as PTN, WebTracerProvider, -} from '@opentelemetry/web'; -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import { FetchInstrumentation, FetchInstrumentationConfig } from '../src'; -import { AttributeNames } from '../src/enums/AttributeNames'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "@opentelemetry/web"; +import * as assert from "assert"; +import * as sinon from "sinon"; +import { FetchInstrumentation, FetchInstrumentationConfig } from "../src"; +import { AttributeNames } from "../src/enums/AttributeNames"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -49,11 +49,11 @@ class DummySpanExporter implements tracing.SpanExporter { const getData = (url: string, method?: string) => fetch(url, { - method: method || 'GET', + method: method || "GET", headers: { - foo: 'bar', - Accept: 'application/json', - 'Content-Type': 'application/json', + foo: "bar", + Accept: "application/json", + "Content-Type": "application/json", }, }); @@ -65,8 +65,8 @@ const defaultResource = { domainLookupStart: 11, encodedBodySize: 0, fetchStart: 10.1, - initiatorType: 'fetch', - nextHopProtocol: '', + initiatorType: "fetch", + nextHopProtocol: "", redirectEnd: 0, redirectStart: 0, requestStart: 16, @@ -76,8 +76,8 @@ const defaultResource = { transferSize: 0, workerStart: 0, duration: 0, - entryType: '', - name: '', + entryType: "", + name: "", startTime: 0, }; @@ -92,7 +92,7 @@ function createResource(resource = {}): PerformanceResourceTiming { function createMainResource(resource = {}): PerformanceResourceTiming { const mainResource: any = createResource(resource); Object.keys(mainResource).forEach((key: string) => { - if (typeof mainResource[key] === 'number') { + if (typeof mainResource[key] === "number") { mainResource[key] = mainResource[key] + 30; } }); @@ -103,7 +103,7 @@ function createFakePerformanceObs(url: string) { class FakePerfObs implements PerformanceObserver { constructor(private readonly cb: PerformanceObserverCallback) {} observe() { - const absoluteUrl = url.startsWith('http') ? url : location.origin + url; + const absoluteUrl = url.startsWith("http") ? url : location.origin + url; const resources: PerformanceObserverEntryList = { getEntries(): PerformanceEntryList { return [ @@ -129,7 +129,7 @@ function createFakePerformanceObs(url: string) { return FakePerfObs; } -describe('fetch', () => { +describe("fetch", () => { let contextManager: ZoneContextManager; let lastResponse: any | undefined; let webTracerWithZone: api.Tracer; @@ -141,8 +141,8 @@ describe('fetch', () => { let fakeNow = 0; let fetchInstrumentation: FetchInstrumentation; - const url = 'http://localhost:8090/get'; - const badUrl = 'http://foo.bar.com/get'; + const url = "http://localhost:8090/get"; + const badUrl = "http://foo.bar.com/get"; const clearData = () => { sinon.restore(); @@ -159,8 +159,8 @@ describe('fetch', () => { ) => { sinon.useFakeTimers(); - sinon.stub(core.otperformance, 'timeOrigin').value(0); - sinon.stub(core.otperformance, 'now').callsFake(() => fakeNow); + sinon.stub(core.otperformance, "timeOrigin").value(0); + sinon.stub(core.otperformance, "now").callsFake(() => fakeNow); function fakeFetch(input: RequestInfo | Request, init: RequestInit = {}) { return new Promise((resolve, reject) => { @@ -170,23 +170,23 @@ describe('fetch', () => { }; response.headers = Object.assign({}, init.headers); - if (init.method === 'DELETE') { + if (init.method === "DELETE") { response.status = 405; - response.statusText = 'OK'; - resolve(new window.Response('foo', response)); + response.statusText = "OK"; + resolve(new window.Response("foo", response)); } else if (input === url) { response.status = 200; - response.statusText = 'OK'; + response.statusText = "OK"; resolve(new window.Response(JSON.stringify(response), response)); } else { response.status = 404; - response.statusText = 'Bad request'; + response.statusText = "Bad request"; reject(new window.Response(JSON.stringify(response), response)); } }); } - sinon.stub(window, 'fetch').callsFake(fakeFetch as any); + sinon.stub(window, "fetch").callsFake(fakeFetch as any); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -199,18 +199,18 @@ describe('fetch', () => { ); if (disablePerfObserver) { - sinon.stub(window, 'PerformanceObserver').value(undefined); + sinon.stub(window, "PerformanceObserver").value(undefined); } else { sinon - .stub(window, 'PerformanceObserver') + .stub(window, "PerformanceObserver") .value(createFakePerformanceObs(fileUrl)); } if (disableGetEntries) { - sinon.stub(performance, 'getEntriesByType').value(undefined); + sinon.stub(performance, "getEntriesByType").value(undefined); } else { - const spyEntries = sinon.stub(performance, 'getEntriesByType'); - spyEntries.withArgs('resource').returns(resources); + const spyEntries = sinon.stub(performance, "getEntriesByType"); + spyEntries.withArgs("resource").returns(resources); } fetchInstrumentation = new FetchInstrumentation(config); @@ -219,26 +219,26 @@ describe('fetch', () => { tracerProvider: webTracerProviderWithZone, instrumentations: [fetchInstrumentation], }); - webTracerWithZone = webTracerProviderWithZone.getTracer('fetch-test'); + webTracerWithZone = webTracerProviderWithZone.getTracer("fetch-test"); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, 'export'); - clearResourceTimingsSpy = sinon.stub(performance, 'clearResourceTimings'); + exportSpy = sinon.stub(dummySpanExporter, "export"); + clearResourceTimingsSpy = sinon.stub(performance, "clearResourceTimings"); webTracerProviderWithZone.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - rootSpan = webTracerWithZone.startSpan('root'); + rootSpan = webTracerWithZone.startSpan("root"); api.context.with(api.setSpan(api.context.active(), rootSpan), () => { fakeNow = 0; getData(fileUrl, method).then( - response => { + (response) => { // this is a bit tricky as the only way to get all request headers from // fetch is to use json() response.json().then( - json => { + (json) => { lastResponse = json; const headers: { [key: string]: string } = {}; - Object.keys(lastResponse.headers).forEach(key => { + Object.keys(lastResponse.headers).forEach((key) => { headers[key.toLowerCase()] = lastResponse.headers[key]; }); lastResponse.headers = headers; @@ -282,8 +282,8 @@ describe('fetch', () => { ); }); - describe('when request is successful', () => { - beforeEach(done => { + describe("when request is successful", () => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = [url]; prepareData(done, url, { propagateTraceHeaderCorsUrls }); }); @@ -292,89 +292,89 @@ describe('fetch', () => { clearData(); }); - it('should wrap methods', () => { + it("should wrap methods", () => { assert.ok(isWrapped(window.fetch)); fetchInstrumentation.enable(); assert.ok(isWrapped(window.fetch)); }); - it('should unwrap methods', () => { + it("should unwrap methods", () => { assert.ok(isWrapped(window.fetch)); fetchInstrumentation.disable(); assert.ok(!isWrapped(window.fetch)); }); - it('should create a span with correct root span', () => { + it("should create a span with correct root span", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - 'parent span is not root span' + "parent span is not root span" ); }); - it('span should have correct name', () => { + it("span should have correct name", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.name, 'HTTP GET', 'span has wrong name'); + assert.strictEqual(span.name, "HTTP GET", "span has wrong name"); }); - it('span should have correct kind', () => { + it("span should have correct kind", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.kind, api.SpanKind.CLIENT, 'span has wrong kind'); + assert.strictEqual(span.kind, api.SpanKind.CLIENT, "span has wrong kind"); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.ok( - attributes[keys[0]] !== '', + attributes[keys[0]] !== "", `attributes ${AttributeNames.COMPONENT} is not defined` ); assert.strictEqual( attributes[keys[1]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[2]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.ok( - attributes[keys[4]] === 'OK' || attributes[keys[4]] === '', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + attributes[keys[4]] === "OK" || attributes[keys[4]] === "", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.ok( - (attributes[keys[5]] as string).indexOf('localhost') === 0, - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + (attributes[keys[5]] as string).indexOf("localhost") === 0, + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[7]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.ok( (attributes[keys[8]] as number) > 0, - `attributes ${SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` + `attributes ${SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` ); - assert.strictEqual(keys.length, 9, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 9, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[0].name, @@ -423,38 +423,38 @@ describe('fetch', () => { ); }); - it('should create a span for preflight request', () => { + it("should create a span for preflight request", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const parentSpan: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, parentSpan.spanContext.spanId, - 'parent span is not root span' + "parent span is not root span" ); }); - it('preflight request span should have correct name', () => { + it("preflight request span should have correct name", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.name, - 'CORS Preflight', - 'preflight request span has wrong name' + "CORS Preflight", + "preflight request span has wrong name" ); }); - it('preflight request span should have correct kind', () => { + it("preflight request span should have correct kind", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.kind, api.SpanKind.INTERNAL, - 'span has wrong kind' + "span has wrong kind" ); }); - it('preflight request span should have correct events', () => { + it("preflight request span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[0].name, @@ -503,7 +503,7 @@ describe('fetch', () => { ); }); - it('should set trace headers', () => { + it("should set trace headers", () => { const span: api.Span = exportSpy.args[1][0][0]; assert.strictEqual( lastResponse.headers[X_B3_TRACE_ID], @@ -522,26 +522,26 @@ describe('fetch', () => { ); }); - it('should set trace headers with a request object', () => { - const r = new Request('url'); + it("should set trace headers with a request object", () => { + const r = new Request("url"); window.fetch(r); - assert.ok(typeof r.headers.get(X_B3_TRACE_ID) === 'string'); + assert.ok(typeof r.headers.get(X_B3_TRACE_ID) === "string"); }); - it('should NOT clear the resources', () => { + it("should NOT clear the resources", () => { assert.strictEqual( clearResourceTimingsSpy.args.length, 0, - 'resources have been cleared' + "resources have been cleared" ); }); - describe('when propagateTraceHeaderCorsUrls does NOT MATCH', () => { - beforeEach(done => { + describe("when propagateTraceHeaderCorsUrls does NOT MATCH", () => { + beforeEach((done) => { clearData(); prepareData(done, url, {}); }); - it('should NOT set trace headers', () => { + it("should NOT set trace headers", () => { assert.strictEqual( lastResponse.headers[X_B3_TRACE_ID], undefined, @@ -561,8 +561,8 @@ describe('fetch', () => { }); }); - describe('when url is ignored', () => { - beforeEach(done => { + describe("when url is ignored", () => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { propagateTraceHeaderCorsUrls, @@ -572,13 +572,13 @@ describe('fetch', () => { afterEach(() => { clearData(); }); - it('should NOT create any span', () => { + it("should NOT create any span", () => { assert.strictEqual(exportSpy.args.length, 0, "span shouldn't b exported"); }); }); - describe('when clearTimingResources is TRUE', () => { - beforeEach(done => { + describe("when clearTimingResources is TRUE", () => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { propagateTraceHeaderCorsUrls, @@ -588,7 +588,7 @@ describe('fetch', () => { afterEach(() => { clearData(); }); - it('should clear the resources', () => { + it("should clear the resources", () => { assert.strictEqual( clearResourceTimingsSpy.args.length, 1, @@ -597,45 +597,45 @@ describe('fetch', () => { }); }); - describe('when request is NOT successful (wrong url)', () => { - beforeEach(done => { + describe("when request is NOT successful (wrong url)", () => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = badUrl; prepareData(done, badUrl, { propagateTraceHeaderCorsUrls }); }); afterEach(() => { clearData(); }); - it('should create a span with correct root span', () => { + it("should create a span with correct root span", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - 'parent span is not root span' + "parent span is not root span" ); }); }); - describe('when request is NOT successful (405)', () => { - beforeEach(done => { + describe("when request is NOT successful (405)", () => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = url; - prepareData(done, url, { propagateTraceHeaderCorsUrls }, 'DELETE'); + prepareData(done, url, { propagateTraceHeaderCorsUrls }, "DELETE"); }); afterEach(() => { clearData(); }); - it('should create a span with correct root span', () => { + it("should create a span with correct root span", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - 'parent span is not root span' + "parent span is not root span" ); }); }); - describe('when PerformanceObserver is used by default', () => { - beforeEach(done => { + describe("when PerformanceObserver is used by default", () => { + beforeEach((done) => { // All above tests test it already but just in case // lets explicitly turn getEntriesByType off so we can be sure // that the perf entries come from the observer. @@ -644,7 +644,7 @@ describe('fetch', () => { afterEach(() => { clearData(); }); - it('should create both spans with network events', () => { + it("should create both spans with network events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -654,7 +654,7 @@ describe('fetch', () => { `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[6].name, @@ -664,14 +664,14 @@ describe('fetch', () => { }); }); - describe('when fetching with relative url', () => { - beforeEach(done => { - prepareData(done, '/get', {}, undefined, false, true); + describe("when fetching with relative url", () => { + beforeEach((done) => { + prepareData(done, "/get", {}, undefined, false, true); }); afterEach(() => { clearData(); }); - it('should create spans with network info', () => { + it("should create spans with network info", () => { // no prefetch span because mock observer uses location.origin as url when relative // and prefetch span finding compares url origins const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; @@ -683,7 +683,7 @@ describe('fetch', () => { `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[6].name, PTN.REQUEST_START, @@ -692,8 +692,8 @@ describe('fetch', () => { }); }); - describe('when PerformanceObserver is undefined', () => { - beforeEach(done => { + describe("when PerformanceObserver is undefined", () => { + beforeEach((done) => { prepareData(done, url, {}, undefined, true, false); }); @@ -701,7 +701,7 @@ describe('fetch', () => { clearData(); }); - it('should fallback to getEntries', () => { + it("should fallback to getEntries", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -710,7 +710,7 @@ describe('fetch', () => { 2, `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[6].name, PTN.REQUEST_START, @@ -719,14 +719,14 @@ describe('fetch', () => { }); }); - describe('when PerformanceObserver and performance.getEntriesByType are undefined', () => { - beforeEach(done => { + describe("when PerformanceObserver and performance.getEntriesByType are undefined", () => { + beforeEach((done) => { prepareData(done, url, {}, undefined, true, true); }); afterEach(() => { clearData(); }); - it('should still capture fetch with basic attributes', () => { + it("should still capture fetch with basic attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; const attributes = span.attributes; @@ -739,17 +739,17 @@ describe('fetch', () => { ); assert.strictEqual( exportSpy.args[0][0][0].name, - 'HTTP GET', - 'wrong span captured' + "HTTP GET", + "wrong span captured" ); - assert.strictEqual(events.length, 0, 'Should not have any events'); + assert.strictEqual(events.length, 0, "Should not have any events"); // should still have basic attributes assert.strictEqual( attributes[keys[3]], 200, - `Missing basic attribute ${SemanticAttribute.HTTP_STATUS_CODE}` + `Missing basic attribute ${SemanticAttributes.HTTP_STATUS_CODE}` ); }); }); diff --git a/packages/opentelemetry-instrumentation-grpc/src/enums.ts b/packages/opentelemetry-instrumentation-grpc/src/enums.ts new file mode 100644 index 00000000000..e573bbbaaa5 --- /dev/null +++ b/packages/opentelemetry-instrumentation-grpc/src/enums.ts @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + GRPC_KIND = "grpc.kind", // SERVER or CLIENT + GRPC_METHOD = "grpc.method", + GRPC_STATUS_CODE = "grpc.status_code", + GRPC_ERROR_NAME = "grpc.error_name", + GRPC_ERROR_MESSAGE = "grpc.error_message", +} diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts index dbd20f8b3e3..2f27b84c793 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { GrpcJsInstrumentation } from './'; -import type { GrpcClientFunc, SendUnaryDataCallback } from './types'; +import { GrpcJsInstrumentation } from "./"; +import type { GrpcClientFunc, SendUnaryDataCallback } from "./types"; import { SpanKind, Span, @@ -23,16 +23,16 @@ import { SpanStatus, propagation, context, -} from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; +} from "@opentelemetry/api"; +import type * as grpcJs from "@grpc/grpc-js"; import { _grpcStatusCodeToSpanStatus, _grpcStatusCodeToOpenTelemetryStatusCode, _methodIsIgnored, -} from '../utils'; -import { CALL_SPAN_ENDED } from './serverUtils'; -import { EventEmitter } from 'events'; +} from "../utils"; +import { CALL_SPAN_ENDED } from "./serverUtils"; +import { EventEmitter } from "events"; +import { AttributeNames } from "../enums"; /** * Parse a package method list and return a list of methods to patch @@ -90,18 +90,18 @@ export function makeGrpcClientRemoteCall( if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.UNSET.toString() ); } @@ -115,8 +115,8 @@ export function makeGrpcClientRemoteCall( return (span: Span) => { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = args.findIndex(arg => { - return typeof arg === 'function'; + const callbackFuncIndex = args.findIndex((arg) => { + return typeof arg === "function"; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -127,8 +127,8 @@ export function makeGrpcClientRemoteCall( } span.setAttributes({ - [SemanticAttribute.GRPC_METHOD]: original.path, - [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -146,7 +146,7 @@ export function makeGrpcClientRemoteCall( } }; context.bind(call); - call.on('error', (err: grpcJs.ServiceError) => { + call.on("error", (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -157,14 +157,14 @@ export function makeGrpcClientRemoteCall( message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); - call.on('status', (status: SpanStatus) => { + call.on("status", (status: SpanStatus) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -197,9 +197,9 @@ export function getMetadata( let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { return ( arg && - typeof arg === 'object' && - (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === 'function' + typeof arg === "object" && + (arg as grpcJs.Metadata)["internalRepr"] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 + typeof (arg as grpcJs.Metadata).getMap === "function" ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index 891c0169562..dd3e58f7d80 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import type * as grpcJs from '@grpc/grpc-js'; +import type * as grpcJs from "@grpc/grpc-js"; import { InstrumentationNodeModuleDefinition, isWrapped, -} from '@opentelemetry/instrumentation'; +} from "@opentelemetry/instrumentation"; import { InstrumentationBase, InstrumentationConfig, -} from '@opentelemetry/instrumentation'; -import { GrpcInstrumentationConfig } from '../types'; +} from "@opentelemetry/instrumentation"; +import { GrpcInstrumentationConfig } from "../types"; import { ServerCallWithMeta, SendUnaryDataCallback, @@ -32,7 +32,7 @@ import { MakeClientConstructorFunction, PackageDefinition, GrpcClientFunc, -} from './types'; +} from "./types"; import { context, SpanOptions, @@ -41,19 +41,19 @@ import { ROOT_CONTEXT, setSpan, diag, -} from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "@opentelemetry/api"; import { shouldNotTraceServerCall, handleServerFunction, handleUntracedServerFunction, -} from './serverUtils'; +} from "./serverUtils"; import { getMethodsToWrap, makeGrpcClientRemoteCall, getMetadata, -} from './clientUtils'; -import { EventEmitter } from 'events'; +} from "./clientUtils"; +import { EventEmitter } from "events"; +import { AttributeNames } from "../enums"; export class GrpcJsInstrumentation extends InstrumentationBase { constructor( @@ -73,42 +73,42 @@ export class GrpcJsInstrumentation extends InstrumentationBase { init() { return [ new InstrumentationNodeModuleDefinition( - '@grpc/grpc-js', - ['1.*'], + "@grpc/grpc-js", + ["1.*"], (moduleExports, version) => { diag.debug(`Applying patch for @grpc/grpc-js@${version}`); if (isWrapped(moduleExports.Server.prototype.register)) { - this._unwrap(moduleExports.Server.prototype, 'register'); + this._unwrap(moduleExports.Server.prototype, "register"); } // Patch Server methods this._wrap( moduleExports.Server.prototype, - 'register', + "register", this._patchServer() as any ); // Patch Client methods if (isWrapped(moduleExports.makeGenericClientConstructor)) { - this._unwrap(moduleExports, 'makeGenericClientConstructor'); + this._unwrap(moduleExports, "makeGenericClientConstructor"); } this._wrap( moduleExports, - 'makeGenericClientConstructor', + "makeGenericClientConstructor", this._patchClient(moduleExports) ); if (isWrapped(moduleExports.makeClientConstructor)) { - this._unwrap(moduleExports, 'makeClientConstructor'); + this._unwrap(moduleExports, "makeClientConstructor"); } this._wrap( moduleExports, - 'makeClientConstructor', + "makeClientConstructor", this._patchClient(moduleExports) ); if (isWrapped(moduleExports.loadPackageDefinition)) { - this._unwrap(moduleExports, 'loadPackageDefinition'); + this._unwrap(moduleExports, "loadPackageDefinition"); } this._wrap( moduleExports, - 'loadPackageDefinition', + "loadPackageDefinition", this._patchLoadPackageDefinition(moduleExports) ); return moduleExports; @@ -117,10 +117,10 @@ export class GrpcJsInstrumentation extends InstrumentationBase { if (moduleExports === undefined) return; diag.debug(`Removing patch for @grpc/grpc-js@${version}`); - this._unwrap(moduleExports.Server.prototype, 'register'); - this._unwrap(moduleExports, 'makeClientConstructor'); - this._unwrap(moduleExports, 'makeGenericClientConstructor'); - this._unwrap(moduleExports, 'loadPackageDefinition'); + this._unwrap(moduleExports.Server.prototype, "register"); + this._unwrap(moduleExports, "makeClientConstructor"); + this._unwrap(moduleExports, "makeGenericClientConstructor"); + this._unwrap(moduleExports, "loadPackageDefinition"); } ), ]; @@ -136,7 +136,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { const instrumentation = this; return (originalRegister: ServerRegisterFunction) => { const config = this._config; - diag.debug('patched gRPC server'); + diag.debug("patched gRPC server"); return function register( this: grpcJs.Server, name: string, @@ -153,11 +153,11 @@ export class GrpcJsInstrumentation extends InstrumentationBase { deserialize, type ); - const handlerSet = this['handlers'].get(name); + const handlerSet = this["handlers"].get(name); instrumentation._wrap( handlerSet, - 'func', + "func", (originalFunc: HandleCall) => { return function func( this: typeof handlerSet, @@ -181,23 +181,23 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ); } - const spanName = `grpc.${name.replace('/', '')}`; + const spanName = `grpc.${name.replace("/", "")}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug('patch func: %s', JSON.stringify(spanOptions)); + diag.debug("patch func: %s", JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (carrier, key) => carrier.get(key).map(String), - keys: carrier => Object.keys(carrier.getMap()), + keys: (carrier) => Object.keys(carrier.getMap()), }), () => { const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [SemanticAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { @@ -231,7 +231,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ) => MakeClientConstructorFunction { const instrumentation = this; return (original: MakeClientConstructorFunction) => { - diag.debug('patching client'); + diag.debug("patching client"); return function makeClientConstructor( this: typeof grpcJs.Client, methods: grpcJs.ServiceDefinition, @@ -255,7 +255,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { */ private _patchLoadPackageDefinition(grpcClient: typeof grpcJs) { const instrumentation = this; - diag.debug('patching loadPackageDefinition'); + diag.debug("patching loadPackageDefinition"); return (original: typeof grpcJs.loadPackageDefinition) => { return function patchedLoadPackageDefinition( this: null, @@ -279,9 +279,9 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ): (original: GrpcClientFunc) => () => EventEmitter { const instrumentation = this; return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); + diag.debug("patch all client methods"); return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace('/', '')}`; + const name = `grpc.${original.path.replace("/", "")}`; const args = [...arguments]; const metadata = getMetadata.call( instrumentation, @@ -309,14 +309,14 @@ export class GrpcJsInstrumentation extends InstrumentationBase { grpcClient: typeof grpcJs, result: grpcJs.GrpcObject ): void { - Object.values(result).forEach(service => { - if (typeof service === 'function') { + Object.values(result).forEach((service) => { + if (typeof service === "function") { this._massWrap( service.prototype, getMethodsToWrap.call(this, service, service.service), this._getPatchedClientMethods.call(this, grpcClient) ); - } else if (typeof service.format !== 'string') { + } else if (typeof service.format !== "string") { // GrpcObject this._patchLoadedPackage.call( this, diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts index 91bc12ef6cf..2aa72919113 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts @@ -20,22 +20,22 @@ * error event should be processed. */ -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; +import { context, Span, SpanStatusCode } from "@opentelemetry/api"; +import type * as grpcJs from "@grpc/grpc-js"; import type { ServerCallWithMeta, SendUnaryDataCallback, GrpcEmitter, HandleCall, -} from './types'; +} from "./types"; import { _grpcStatusCodeToOpenTelemetryStatusCode, _methodIsIgnored, -} from '../utils'; -import { IgnoreMatcher } from '../types'; +} from "../utils"; +import { IgnoreMatcher } from "../types"; +import { AttributeNames } from "../enums"; -export const CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); +export const CALL_SPAN_ENDED = Symbol("opentelemetry call span ended"); /** * Handle patching for serverStream and Bidi type server handlers @@ -56,7 +56,7 @@ function serverStreamAndBidiHandler( }; context.bind(call); - call.on('finish', () => { + call.on("finish", () => { // @grpc/js does not expose a way to check if this call also emitted an error, // e.g. call.status.code !== 0 if (call[CALL_SPAN_ENDED]) { @@ -70,14 +70,14 @@ function serverStreamAndBidiHandler( code: SpanStatusCode.UNSET, }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); endSpan(); }); - call.on('error', (err: grpcJs.ServiceError) => { + call.on("error", (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -90,8 +90,8 @@ function serverStreamAndBidiHandler( message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -121,19 +121,16 @@ function clientStreamAndUnaryHandler( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, - err.code.toString() - ); + span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } @@ -158,9 +155,9 @@ export function handleServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': + case "unary": + case "clientStream": + case "client_stream": return clientStreamAndUnaryHandler( span, call, @@ -169,9 +166,9 @@ export function handleServerFunction( | grpcJs.handleUnaryCall | grpcJs.ClientReadableStream ); - case 'serverStream': - case 'server_stream': - case 'bidi': + case "serverStream": + case "server_stream": + case "bidi": return serverStreamAndBidiHandler( span, call, @@ -195,13 +192,13 @@ export function handleUntracedServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': + case "unary": + case "clientStream": + case "client_stream": return (originalFunc as Function).call({}, call, callback); - case 'serverStream': - case 'server_stream': - case 'bidi': + case "serverStream": + case "server_stream": + case "bidi": return (originalFunc as Function).call({}, call); default: break; @@ -216,7 +213,7 @@ export function shouldNotTraceServerCall( methodName: string, ignoreGrpcMethods?: IgnoreMatcher[] ): boolean { - const parsedName = methodName.split('/'); + const parsedName = methodName.split("/"); return _methodIsIgnored( parsedName[parsedName.length - 1] || methodName, ignoreGrpcMethods diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts index dca2d7ebcdb..2aad1b653e8 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import type * as grpcTypes from 'grpc'; -import type * as events from 'events'; -import { SendUnaryDataCallback, GrpcClientFunc } from './types'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +import type * as grpcTypes from "grpc"; +import type * as events from "events"; +import { SendUnaryDataCallback, GrpcClientFunc } from "./types"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; import { context, Span, @@ -25,12 +25,13 @@ import { SpanKind, SpanStatus, propagation, -} from '@opentelemetry/api'; +} from "@opentelemetry/api"; import { _grpcStatusCodeToSpanStatus, _grpcStatusCodeToOpenTelemetryStatusCode, findIndex, -} from '../utils'; +} from "../utils"; +import { AttributeNames } from "../enums"; /** * This method handles the client remote call @@ -56,18 +57,18 @@ export const makeGrpcClientRemoteCall = function ( if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - SemanticAttribute.RPC_GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - SemanticAttribute.RPC_GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -85,8 +86,8 @@ export const makeGrpcClientRemoteCall = function ( // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, arg => { - return typeof arg === 'function'; + const callbackFuncIndex = findIndex(args, (arg) => { + return typeof arg === "function"; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -97,10 +98,10 @@ export const makeGrpcClientRemoteCall = function ( } } - span.addEvent('sent'); + span.addEvent("sent"); span.setAttributes({ - [SemanticAttribute.GRPC_METHOD]: original.path, - [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -119,26 +120,26 @@ export const makeGrpcClientRemoteCall = function ( }; context.bind(call); ((call as unknown) as events.EventEmitter).on( - 'error', + "error", (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); } ); ((call as unknown) as events.EventEmitter).on( - 'status', + "status", (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, status.code.toString() ); endSpan(); @@ -163,9 +164,9 @@ export const getMetadata = function ( let metadataIndex = findIndex(args, (arg: any) => { return ( arg && - typeof arg === 'object' && + typeof arg === "object" && arg._internal_repr && - typeof arg.getMap === 'function' + typeof arg.getMap === "function" ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 12887ed5c58..32fd4300ddc 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -14,22 +14,21 @@ * limitations under the License. */ -import type * as grpcTypes from 'grpc'; +import type * as grpcTypes from "grpc"; import { InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, InstrumentationBase, InstrumentationConfig, isWrapped, -} from '@opentelemetry/instrumentation'; +} from "@opentelemetry/instrumentation"; import { GrpcInternalClientTypes, ServerCallWithMeta, SendUnaryDataCallback, GrpcClientFunc, -} from './types'; -import { GrpcInstrumentationConfig } from '../types'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "./types"; +import { GrpcInstrumentationConfig } from "../types"; import { context, propagation, @@ -37,14 +36,15 @@ import { SpanKind, setSpan, diag, -} from '@opentelemetry/api'; +} from "@opentelemetry/api"; import { clientStreamAndUnaryHandler, shouldNotTraceServerCall, serverStreamAndBidiHandler, -} from './serverUtils'; -import { makeGrpcClientRemoteCall, getMetadata } from './clientUtils'; -import { _methodIsIgnored } from '../utils'; +} from "./serverUtils"; +import { makeGrpcClientRemoteCall, getMetadata } from "./clientUtils"; +import { _methodIsIgnored } from "../utils"; +import { AttributeNames } from "../enums"; /** * Holding reference to grpc module here to access constant of grpc modules @@ -72,26 +72,26 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< init() { return [ new InstrumentationNodeModuleDefinition( - 'grpc', - ['1.*'], + "grpc", + ["1.*"], (moduleExports, version) => { diag.debug(`Applying patch for grpc@${version}`); if (isWrapped(moduleExports.Server.prototype.register)) { - this._unwrap(moduleExports.Server.prototype, 'register'); + this._unwrap(moduleExports.Server.prototype, "register"); } grpcClient = moduleExports; this._wrap( moduleExports.Server.prototype, - 'register', + "register", this._patchServer(moduleExports) as any ); // Wrap the externally exported client constructor if (isWrapped(moduleExports.makeGenericClientConstructor)) { - this._unwrap(moduleExports, 'makeGenericClientConstructor'); + this._unwrap(moduleExports, "makeGenericClientConstructor"); } this._wrap( moduleExports, - 'makeGenericClientConstructor', + "makeGenericClientConstructor", this._patchClient(moduleExports) ); return moduleExports; @@ -100,7 +100,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< if (moduleExports === undefined) return; diag.debug(`Removing patch for grpc@${version}`); - this._unwrap(moduleExports.Server.prototype, 'register'); + this._unwrap(moduleExports.Server.prototype, "register"); }, this._getInternalPatchs() ), @@ -114,11 +114,11 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< ) => { diag.debug(`Applying internal patch for grpc@${version}`); if (isWrapped(moduleExports.makeClientConstructor)) { - this._unwrap(moduleExports, 'makeClientConstructor'); + this._unwrap(moduleExports, "makeClientConstructor"); } this._wrap( moduleExports, - 'makeClientConstructor', + "makeClientConstructor", this._patchClient(grpcClient) ); return moduleExports; @@ -129,18 +129,18 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< ) => { if (moduleExports === undefined) return; diag.debug(`Removing internal patch for grpc@${version}`); - this._unwrap(moduleExports, 'makeClientConstructor'); + this._unwrap(moduleExports, "makeClientConstructor"); }; return [ new InstrumentationNodeModuleFile( - 'grpc/src/node/src/client.js', - ['0.13 - 1.6'], + "grpc/src/node/src/client.js", + ["0.13 - 1.6"], onPatch, onUnPatch ), new InstrumentationNodeModuleFile( - 'grpc/src/client.js', - ['^1.7'], + "grpc/src/client.js", + ["^1.7"], onPatch, onUnPatch ), @@ -150,7 +150,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _patchServer(grpcModule: typeof grpcTypes) { const instrumentation = this; return (originalRegister: typeof grpcTypes.Server.prototype.register) => { - diag.debug('patched gRPC server'); + diag.debug("patched gRPC server"); return function register( this: grpcTypes.Server & { handlers: any }, @@ -165,7 +165,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< instrumentation._wrap( handlerSet, - 'func', + "func", (originalFunc: grpcTypes.handleCall) => { return function func( this: typeof handlerSet, @@ -175,43 +175,43 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< const self = this; if (shouldNotTraceServerCall.call(instrumentation, call, name)) { switch (type) { - case 'unary': - case 'client_stream': + case "unary": + case "client_stream": return (originalFunc as Function).call( self, call, callback ); - case 'server_stream': - case 'bidi': + case "server_stream": + case "bidi": return (originalFunc as Function).call(self, call); default: return originalResult; } } - const spanName = `grpc.${name.replace('/', '')}`; + const spanName = `grpc.${name.replace("/", "")}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug('patch func: %s', JSON.stringify(spanOptions)); + diag.debug("patch func: %s", JSON.stringify(spanOptions)); context.with( propagation.extract(context.active(), call.metadata, { get: (metadata, key) => metadata.get(key).map(String), - keys: metadata => Object.keys(metadata.getMap()), + keys: (metadata) => Object.keys(metadata.getMap()), }), () => { const span = instrumentation.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [SemanticAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { switch (type) { - case 'unary': - case 'client_stream': + case "unary": + case "client_stream": return clientStreamAndUnaryHandler( grpcModule, span, @@ -220,8 +220,8 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< originalFunc, self ); - case 'server_stream': - case 'bidi': + case "server_stream": + case "bidi": return serverStreamAndBidiHandler( span, call, @@ -246,7 +246,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _patchClient(grpcClient: typeof grpcTypes) { const instrumentation = this; return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug('patching client'); + diag.debug("patching client"); return function makeClientConstructor( this: typeof grpcTypes.Client, methods: { [key: string]: { originalName?: string } }, @@ -291,9 +291,9 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _getPatchedClientMethods(grpcClient: typeof grpcTypes) { const instrumentation = this; return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); + diag.debug("patch all client methods"); return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace('/', '')}`; + const name = `grpc.${original.path.replace("/", "")}`; const args = Array.prototype.slice.call(arguments); const metadata = getMetadata(grpcClient, original, args); const span = instrumentation.tracer.startSpan(name, { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts index 5b2f30c9d0a..4ea51bdf293 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import type * as grpcTypes from 'grpc'; -import { SendUnaryDataCallback, ServerCallWithMeta } from './types'; -import { GrpcNativeInstrumentation } from './'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; +import type * as grpcTypes from "grpc"; +import { SendUnaryDataCallback, ServerCallWithMeta } from "./types"; +import { GrpcNativeInstrumentation } from "./"; +import { context, Span, SpanStatusCode } from "@opentelemetry/api"; import { _grpcStatusCodeToOpenTelemetryStatusCode, _grpcStatusCodeToSpanStatus, _methodIsIgnored, -} from '../utils'; +} from "../utils"; +import { AttributeNames } from "../enums"; export const clientStreamAndUnaryHandler = function ( grpcClient: typeof grpcTypes, @@ -47,23 +47,20 @@ export const clientStreamAndUnaryHandler = function ( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, - err.code.toString() - ); + span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } - span.addEvent('received'); + span.addEvent("received"); // end the span span.end(); @@ -89,29 +86,29 @@ export const serverStreamAndBidiHandler = function ( }; context.bind(call); - call.on('finish', () => { + call.on("finish", () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, call.status.code.toString() ); // if there is an error, span will be ended on error event, otherwise end it here if (call.status.code === 0) { - span.addEvent('finished'); + span.addEvent("finished"); endSpan(); } }); - call.on('error', (err: grpcTypes.ServiceError) => { + call.on("error", (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.addEvent('finished with error'); + span.addEvent("finished with error"); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -127,7 +124,7 @@ export const shouldNotTraceServerCall = function ( call: ServerCallWithMeta, name: string ): boolean { - const parsedName = name.split('/'); + const parsedName = name.split("/"); return _methodIsIgnored( parsedName[parsedName.length - 1] || name, this._config.ignoreGrpcMethods diff --git a/packages/opentelemetry-instrumentation-http/src/enums.ts b/packages/opentelemetry-instrumentation-http/src/enums.ts new file mode 100644 index 00000000000..31c18afc460 --- /dev/null +++ b/packages/opentelemetry-instrumentation-http/src/enums.ts @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_ERROR_NAME = "http.error_name", + HTTP_ERROR_MESSAGE = "http.error_message", + HTTP_STATUS_TEXT = "http.status_text", +} diff --git a/packages/opentelemetry-instrumentation-http/src/utils.ts b/packages/opentelemetry-instrumentation-http/src/utils.ts index 5494f49f175..3d302d3aa78 100644 --- a/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -18,11 +18,11 @@ import { SpanStatusCode, Span, SpanStatus, -} from '@opentelemetry/api'; +} from "@opentelemetry/api"; import { NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; import { ClientRequest, IncomingHttpHeaders, @@ -30,10 +30,11 @@ import { OutgoingHttpHeaders, RequestOptions, ServerResponse, -} from 'http'; -import { Socket } from 'net'; -import * as url from 'url'; -import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; +} from "http"; +import { Socket } from "net"; +import * as url from "url"; +import { AttributeNames } from "./enums"; +import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; /** * Get an absolute url @@ -41,22 +42,22 @@ import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; export const getAbsoluteUrl = ( requestUrl: ParsedRequestOptions | null, headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = 'http:' + fallbackProtocol = "http:" ): string => { const reqUrlObject = requestUrl || {}; const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || '').toString(); - const path = reqUrlObject.path || '/'; + const port = (reqUrlObject.port || "").toString(); + const path = reqUrlObject.path || "/"; let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; + reqUrlObject.host || reqUrlObject.hostname || headers.host || "localhost"; // if there is no port in host and there is a port // it should be displayed if it's not 80 and 443 (default ports) if ( - (host as string).indexOf(':') === -1 && + (host as string).indexOf(":") === -1 && port && - port !== '80' && - port !== '443' + port !== "80" && + port !== "443" ) { host += `:${port}`; } @@ -68,7 +69,7 @@ export const getAbsoluteUrl = ( */ export const parseResponseStatus = ( statusCode: number -): Omit => { +): Omit => { // 1xx, 2xx, 3xx are OK if (statusCode >= 100 && statusCode < 400) { return { code: SpanStatusCode.OK }; @@ -88,7 +89,7 @@ export const hasExpectHeader = (options: RequestOptions): boolean => { } const keys = Object.keys(options.headers); - return !!keys.find(key => key.toLowerCase() === 'expect'); + return !!keys.find((key) => key.toLowerCase() === "expect"); }; /** @@ -100,14 +101,14 @@ export const satisfiesPattern = ( constant: string, pattern: IgnoreMatcher ): boolean => { - if (typeof pattern === 'string') { + if (typeof pattern === "string") { return pattern === constant; } else if (pattern instanceof RegExp) { return pattern.test(constant); - } else if (typeof pattern === 'function') { + } else if (typeof pattern === "function") { return pattern(constant); } else { - throw new TypeError('Pattern is in unsupported datatype'); + throw new TypeError("Pattern is in unsupported datatype"); } }; @@ -158,8 +159,8 @@ export const setSpanWithError = ( const message = error.message; span.setAttributes({ - [SemanticAttribute.HTTP_ERROR_NAME]: error.name, - [SemanticAttribute.HTTP_ERROR_MESSAGE]: message, + [AttributeNames.HTTP_ERROR_NAME]: error.name, + [AttributeNames.HTTP_ERROR_MESSAGE]: message, }); if (!obj) { @@ -194,10 +195,10 @@ export const setRequestContentLengthAttribute = ( if (length === null) return; if (isCompressed(request.headers)) { - attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH] = length; } else { attributes[ - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -215,10 +216,10 @@ export const setResponseContentLengthAttribute = ( if (length === null) return; if (isCompressed(response.headers)) { - attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH] = length; } else { attributes[ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -226,7 +227,7 @@ export const setResponseContentLengthAttribute = ( function getContentLength( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): number | null { - const contentLengthHeader = headers['content-length']; + const contentLengthHeader = headers["content-length"]; if (contentLengthHeader === undefined) return null; const contentLength = parseInt(contentLengthHeader as string, 10); @@ -238,9 +239,9 @@ function getContentLength( export const isCompressed = ( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): boolean => { - const encoding = headers['content-encoding']; + const encoding = headers["content-encoding"]; - return !!encoding && encoding !== 'identity'; + return !!encoding && encoding !== "identity"; }; /** @@ -253,13 +254,13 @@ export const getRequestInfo = ( options: url.URL | RequestOptions | string, extraOptions?: RequestOptions ) => { - let pathname = '/'; - let origin = ''; + let pathname = "/"; + let origin = ""; let optionsParsed: RequestOptions; - if (typeof options === 'string') { + if (typeof options === "string") { optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; - origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; + pathname = (optionsParsed as url.UrlWithStringQuery).pathname || "/"; + origin = `${optionsParsed.protocol || "http:"}//${optionsParsed.host}`; if (extraOptions !== undefined) { Object.assign(optionsParsed, extraOptions); } @@ -267,12 +268,12 @@ export const getRequestInfo = ( optionsParsed = { protocol: options.protocol, hostname: - typeof options.hostname === 'string' && options.hostname.startsWith('[') + typeof options.hostname === "string" && options.hostname.startsWith("[") ? options.hostname.slice(1, -1) : options.hostname, - path: `${options.pathname || ''}${options.search || ''}`, + path: `${options.pathname || ""}${options.search || ""}`, }; - if (options.port !== '') { + if (options.port !== "") { optionsParsed.port = Number(options.port); } if (options.username || options.password) { @@ -285,14 +286,14 @@ export const getRequestInfo = ( } } else { optionsParsed = Object.assign( - { protocol: options.host ? 'http:' : undefined }, + { protocol: options.host ? "http:" : undefined }, options ); pathname = (options as url.URL).pathname; if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || '/'; + pathname = url.parse(optionsParsed.path).pathname || "/"; } - origin = `${optionsParsed.protocol || 'http:'}//${ + origin = `${optionsParsed.protocol || "http:"}//${ optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` }`; } @@ -306,7 +307,7 @@ export const getRequestInfo = ( // ensure upperCase for consistency const method = optionsParsed.method ? optionsParsed.method.toUpperCase() - : 'GET'; + : "GET"; return { origin, pathname, method, optionsParsed }; }; @@ -321,7 +322,7 @@ export const isValidOptionsType = (options: unknown): boolean => { } const type = typeof options; - return type === 'string' || (type === 'object' && !Array.isArray(options)); + return type === "string" || (type === "object" && !Array.isArray(options)); }; /** @@ -336,25 +337,25 @@ export const getOutgoingRequestAttributes = ( const host = requestOptions.host; const hostname = requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; + host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || + "localhost"; const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; + const method = requestMethod ? requestMethod.toUpperCase() : "GET"; const headers = requestOptions.headers || {}; - const userAgent = headers['user-agent']; + const userAgent = headers["user-agent"]; const attributes: SpanAttributes = { - [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestOptions, headers, `${options.component}:` ), - [SemanticAttribute.HTTP_METHOD]: method, - [SemanticAttribute.HTTP_TARGET]: requestOptions.path || '/', - [SemanticAttribute.NET_PEER_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_TARGET]: requestOptions.path || "/", + [SemanticAttributes.NET_PEER_NAME]: hostname, }; if (userAgent !== undefined) { - attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } return attributes; }; @@ -366,11 +367,11 @@ export const getOutgoingRequestAttributes = ( export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { - attributes[SemanticAttribute.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== 'QUIC') { - attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_TCP; + attributes[SemanticAttributes.HTTP_FLAVOR] = kind; + if (kind.toUpperCase() !== "QUIC") { + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { - attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_UDP; + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; } } return attributes; @@ -388,16 +389,16 @@ export const getOutgoingRequestAttributesOnResponse = ( const { statusCode, statusMessage, httpVersion, socket } = response; const { remoteAddress, remotePort } = socket; const attributes: SpanAttributes = { - [SemanticAttribute.NET_PEER_IP]: remoteAddress, - [SemanticAttribute.NET_PEER_PORT]: remotePort, - [SemanticAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; setResponseContentLengthAttribute(response, attributes); if (statusCode) { - attributes[SemanticAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[SemanticAttribute.HTTP_STATUS_TEXT] = ( - statusMessage || '' + attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; + attributes[AttributeNames.HTTP_STATUS_TEXT] = ( + statusMessage || "" ).toUpperCase(); } @@ -415,43 +416,43 @@ export const getIncomingRequestAttributes = ( options: { component: string; serverName?: string } ): SpanAttributes => { const headers = request.headers; - const userAgent = headers['user-agent']; - const ips = headers['x-forwarded-for']; - const method = request.method || 'GET'; + const userAgent = headers["user-agent"]; + const ips = headers["x-forwarded-for"]; + const method = request.method || "GET"; const httpVersion = request.httpVersion; const requestUrl = request.url ? url.parse(request.url) : null; const host = requestUrl?.host || headers.host; const hostname = requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; + host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || + "localhost"; const serverName = options.serverName; const attributes: SpanAttributes = { - [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestUrl, headers, `${options.component}:` ), - [SemanticAttribute.HTTP_HOST]: host, - [SemanticAttribute.NET_HOST_NAME]: hostname, - [SemanticAttribute.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_HOST]: host, + [SemanticAttributes.NET_HOST_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, }; - if (typeof ips === 'string') { - attributes[SemanticAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; + if (typeof ips === "string") { + attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(",")[0]; } - if (typeof serverName === 'string') { - attributes[SemanticAttribute.HTTP_SERVER_NAME] = serverName; + if (typeof serverName === "string") { + attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[SemanticAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[SemanticAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || "/"; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || "/"; } if (userAgent !== undefined) { - attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } setRequestContentLengthAttribute(request, attributes); @@ -477,24 +478,24 @@ export const getIncomingRequestAttributesOnResponse = ( }; const route = Array.isArray(__ot_middlewares) ? __ot_middlewares - .filter(path => path !== '/') - .map(path => { - return path[0] === '/' ? path : '/' + path; + .filter((path) => path !== "/") + .map((path) => { + return path[0] === "/" ? path : "/" + path; }) - .join('') + .join("") : undefined; const attributes: SpanAttributes = { - [SemanticAttribute.NET_HOST_IP]: localAddress, - [SemanticAttribute.NET_HOST_PORT]: localPort, - [SemanticAttribute.NET_PEER_IP]: remoteAddress, - [SemanticAttribute.NET_PEER_PORT]: remotePort, - [SemanticAttribute.HTTP_STATUS_CODE]: statusCode, - [SemanticAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), + [SemanticAttributes.NET_HOST_IP]: localAddress, + [SemanticAttributes.NET_HOST_PORT]: localPort, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, + [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || "").toUpperCase(), }; if (route !== undefined) { - attributes[SemanticAttribute.HTTP_ROUTE] = route; + attributes[SemanticAttributes.HTTP_ROUTE] = route; } return attributes; }; diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index bd3cf611973..fa5fa73019f 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -21,44 +21,44 @@ import { SpanKind, getSpan, setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; +} from "@opentelemetry/api"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; +} from "@opentelemetry/tracing"; import { NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpInstrumentation } from '../../src/http'; -import { HttpInstrumentationConfig } from '../../src/types'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import { ContextManager } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import type { ClientRequest, IncomingMessage, ServerResponse } from 'http'; -import { isWrapped } from '@opentelemetry/instrumentation'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as nock from "nock"; +import * as path from "path"; +import { HttpInstrumentation } from "../../src/http"; +import { HttpInstrumentationConfig } from "../../src/types"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { httpRequest } from "../utils/httpRequest"; +import { ContextManager } from "@opentelemetry/api"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import type { ClientRequest, IncomingMessage, ServerResponse } from "http"; +import { isWrapped } from "@opentelemetry/instrumentation"; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from 'http'; +import * as http from "http"; const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; + "bad applyCustomAttributesOnSpan function"; let server: http.Server; const serverPort = 22346; -const protocol = 'http'; -const hostname = 'localhost'; -const pathname = '/test'; -const serverName = 'my.server.name'; +const protocol = "http"; +const hostname = "localhost"; +const pathname = "/test"; +const serverName = "my.server.name"; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); @@ -79,24 +79,24 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; export const requestHookFunction = ( span: ISpan, request: ClientRequest | IncomingMessage ): void => { - span.setAttribute('custom request hook attribute', 'request'); + span.setAttribute("custom request hook attribute", "request"); }; export const responseHookFunction = ( span: ISpan, response: IncomingMessage | ServerResponse ): void => { - span.setAttribute('custom response hook attribute', 'response'); + span.setAttribute("custom response hook attribute", "response"); }; -describe('HttpInstrumentation', () => { +describe("HttpInstrumentation", () => { let contextManager: ContextManager; before(() => { @@ -116,8 +116,8 @@ describe('HttpInstrumentation', () => { context.disable(); }); - describe('enable()', () => { - describe('with bad instrumentation options', () => { + describe("enable()", () => { + describe("with bad instrumentation options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -126,12 +126,12 @@ describe('HttpInstrumentation', () => { const config: HttpInstrumentationConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); + throw new Error("bad ignoreIncomingPaths function"); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); + throw new Error("bad ignoreOutgoingUrls function"); }, ], applyCustomAttributesOnSpan: () => { @@ -141,7 +141,7 @@ describe('HttpInstrumentation', () => { instrumentation.setConfig(config); instrumentation.enable(); server = http.createServer((request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort); @@ -152,7 +152,7 @@ describe('HttpInstrumentation', () => { instrumentation.disable(); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -165,24 +165,24 @@ describe('HttpInstrumentation', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); }); - describe('with good instrumentation options', () => { + describe("with good instrumentation options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -190,14 +190,14 @@ describe('HttpInstrumentation', () => { before(() => { instrumentation.setConfig({ ignoreIncomingPaths: [ - '/ignored/string', + "/ignored/string", /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], applyCustomAttributesOnSpan: customAttributeFunction, requestHook: requestHookFunction, @@ -206,10 +206,10 @@ describe('HttpInstrumentation', () => { }); instrumentation.enable(); server = http.createServer((request, response) => { - if (request.url?.includes('/ignored')) { - provider.getTracer('test').startSpan('some-span').end(); + if (request.url?.includes("/ignored")) { + provider.getTracer("test").startSpan("some-span").end(); } - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort); @@ -224,13 +224,13 @@ describe('HttpInstrumentation', () => { assert.strictEqual(isWrapped(http.Server.prototype.emit), true); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', + "x-forwarded-for": ", , ", + "user-agent": "chrome", }, } ); @@ -243,21 +243,21 @@ describe('HttpInstrumentation', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", serverName, }; assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], - '' + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], + "" ); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); [ @@ -265,11 +265,11 @@ describe('HttpInstrumentation', () => { { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], - '1.1' + span.attributes[SemanticAttributes.HTTP_FLAVOR], + "1.1" ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); @@ -292,7 +292,7 @@ describe('HttpInstrumentation', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; + const testPath = "/outgoing/rootSpan/1"; doNock( hostname, @@ -316,22 +316,22 @@ describe('HttpInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assertSpan(reqSpan, SpanKind.CLIENT, validations); }); } - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + it("should create a child span for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 200, "Ok"); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -342,16 +342,16 @@ describe('HttpInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -366,15 +366,15 @@ describe('HttpInstrumentation', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; + const testPath = "/outgoing/rootSpan/childs/1"; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -385,16 +385,16 @@ describe('HttpInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -408,17 +408,17 @@ describe('HttpInstrumentation', () => { }); } - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; + it("should create multiple child spans for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs"; const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + doNock(hostname, testPath, 200, "Ok", num); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); + assert.strictEqual(spans[i].name, "HTTP GET"); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -431,7 +431,7 @@ describe('HttpInstrumentation', () => { }); }); - for (const ignored of ['string', 'function', 'regexp']) { + for (const ignored of ["string", "function", "regexp"]) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -443,7 +443,7 @@ describe('HttpInstrumentation', () => { }); } - for (const arg of ['string', {}, new Date()]) { + for (const arg of ["string", {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -452,14 +452,14 @@ describe('HttpInstrumentation', () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); + assert.ok(error.message.startsWith("Nock: No match for request")); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, '']) { + for (const arg of [true, 1, false, 0, ""]) { it(`should not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -470,7 +470,7 @@ describe('HttpInstrumentation', () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') + path.normalize("/node_modules/nock/lib/intercept.js") ) > 0 ); } @@ -482,26 +482,26 @@ describe('HttpInstrumentation', () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - http.request({ protocol: 'telnet' }); + http.request({ protocol: "telnet" }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -522,7 +522,7 @@ describe('HttpInstrumentation', () => { nock.cleanAll(); nock.enableNetConnect(); try { - http.request({ protocol: 'telnet' }); + http.request({ protocol: "telnet" }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -530,19 +530,19 @@ describe('HttpInstrumentation', () => { } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -559,28 +559,28 @@ describe('HttpInstrumentation', () => { } }); - it('should have 1 ended span when request is aborted', async () => { + it("should have 1 ended span when request is aborted", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .socketDelay(50) - .reply(200, ''); + .reply(200, ""); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject('timeout'); + reject("timeout"); }); return req.end(); }); @@ -597,9 +597,9 @@ describe('HttpInstrumentation', () => { } }); - it('should have 1 ended span when request is aborted after receiving response', async () => { + it("should have 1 ended span when request is aborted after receiving response", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .delay({ body: 50, }) @@ -609,12 +609,12 @@ describe('HttpInstrumentation', () => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { req.abort(); data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } @@ -635,11 +635,11 @@ describe('HttpInstrumentation', () => { } }); - it("should have 1 ended span when request doesn't listening response", done => { + it("should have 1 ended span when request doesn't listening response", (done) => { nock.cleanAll(); nock.enableNetConnect(); const req = http.request(`${protocol}://${hostname}/`); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -649,19 +649,19 @@ describe('HttpInstrumentation', () => { req.end(); }); - it("should have 1 ended span when response is listened by using req.on('response')", done => { + it("should have 1 ended span when response is listened by using req.on('response')", (done) => { const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); + nock(host).get("/").reply(404); const req = http.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { + req.on("response", (response) => { + response.on("data", () => {}); + response.on("end", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); @@ -671,7 +671,7 @@ describe('HttpInstrumentation', () => { req.end(); }); - it('custom attributes should show up on client and server spans', async () => { + it("custom attributes should show up on client and server spans", async () => { await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -679,42 +679,42 @@ describe('HttpInstrumentation', () => { const [incomingSpan, outgoingSpan] = spans; assert.strictEqual( - incomingSpan.attributes['custom request hook attribute'], - 'request' + incomingSpan.attributes["custom request hook attribute"], + "request" ); assert.strictEqual( - incomingSpan.attributes['custom response hook attribute'], - 'response' + incomingSpan.attributes["custom response hook attribute"], + "response" ); assert.strictEqual( - incomingSpan.attributes['span kind'], + incomingSpan.attributes["span kind"], SpanKind.CLIENT ); assert.strictEqual( - outgoingSpan.attributes['custom request hook attribute'], - 'request' + outgoingSpan.attributes["custom request hook attribute"], + "request" ); assert.strictEqual( - outgoingSpan.attributes['custom response hook attribute'], - 'response' + outgoingSpan.attributes["custom response hook attribute"], + "response" ); assert.strictEqual( - outgoingSpan.attributes['span kind'], + outgoingSpan.attributes["span kind"], SpanKind.CLIENT ); }); - it('should not set span as active in context for outgoing request', done => { + it("should not set span as active in context for outgoing request", (done) => { assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { + http.get(`${protocol}://${hostname}:${serverPort}/test`, (res) => { assert.deepStrictEqual(getSpan(context.active()), undefined); - res.on('data', () => { + res.on("data", () => { assert.deepStrictEqual(getSpan(context.active()), undefined); }); - res.on('end', () => { + res.on("end", () => { assert.deepStrictEqual(getSpan(context.active()), undefined); done(); }); @@ -722,13 +722,13 @@ describe('HttpInstrumentation', () => { }); }); - describe('with require parent span', () => { - beforeEach(done => { + describe("with require parent span", () => { + beforeEach((done) => { memoryExporter.reset(); instrumentation.setConfig({}); instrumentation.enable(); server = http.createServer((request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort, done); }); @@ -738,14 +738,14 @@ describe('HttpInstrumentation', () => { instrumentation.disable(); }); - it('should not trace without parent with options enabled (both client & server)', async () => { + it("should not trace without parent with options enabled (both client & server)", async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = '/test/test'; + const testPath = "/test/test"; await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -753,13 +753,13 @@ describe('HttpInstrumentation', () => { assert.strictEqual(spans.length, 0); }); - it('should not trace without parent with options enabled (client only)', async () => { + it("should not trace without parent with options enabled (client only)", async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = '/test/test'; + const testPath = "/test/test"; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -772,18 +772,18 @@ describe('HttpInstrumentation', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every(span => span.kind === SpanKind.SERVER), + spans.every((span) => span.kind === SpanKind.SERVER), true ); }); - it('should not trace without parent with options enabled (server only)', async () => { + it("should not trace without parent with options enabled (server only)", async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, }); instrumentation.enable(); - const testPath = '/test/test'; + const testPath = "/test/test"; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -796,27 +796,27 @@ describe('HttpInstrumentation', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every(span => span.kind === SpanKind.CLIENT), + spans.every((span) => span.kind === SpanKind.CLIENT), true ); }); - it('should trace with parent with both requireParent options enabled', done => { + it("should trace with parent with both requireParent options enabled", (done) => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = '/test/test'; - const tracer = provider.getTracer('default'); - const span = tracer.startSpan('parentSpan', { + const testPath = "/test/test"; + const tracer = provider.getTracer("default"); + const span = tracer.startSpan("parentSpan", { kind: SpanKind.INTERNAL, }); context.with(setSpan(context.active(), span), () => { httpRequest .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then(result => { + .then((result) => { span.end(); assert( result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== @@ -829,11 +829,11 @@ describe('HttpInstrumentation', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2); assert.strictEqual( - spans.filter(span => span.kind === SpanKind.CLIENT).length, + spans.filter((span) => span.kind === SpanKind.CLIENT).length, 1 ); assert.strictEqual( - spans.filter(span => span.kind === SpanKind.INTERNAL).length, + spans.filter((span) => span.kind === SpanKind.INTERNAL).length, 1 ); return done(); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index 33083d3a01d..b27298007a1 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -21,49 +21,49 @@ import { Span as ISpan, SpanKind, setSpan, -} from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ContextManager } from '@opentelemetry/api'; +} from "@opentelemetry/api"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { ContextManager } from "@opentelemetry/api"; import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; +} from "@opentelemetry/tracing"; import { NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as semver from 'semver'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpInstrumentation } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { isWrapped } from '@opentelemetry/instrumentation'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as fs from "fs"; +import * as semver from "semver"; +import * as nock from "nock"; +import * as path from "path"; +import { HttpInstrumentation } from "../../src/http"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { isWrapped } from "@opentelemetry/instrumentation"; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from 'http'; -import * as https from 'https'; -import { httpsRequest } from '../utils/httpsRequest'; +import * as http from "http"; +import * as https from "https"; +import { httpsRequest } from "../utils/httpsRequest"; const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; + "bad applyCustomAttributesOnSpan function"; let server: https.Server; const serverPort = 32345; -const protocol = 'https'; -const hostname = 'localhost'; -const serverName = 'my.server.name'; -const pathname = '/test'; +const protocol = "https"; +const hostname = "localhost"; +const serverName = "my.server.name"; +const pathname = "/test"; const memoryExporter = new InMemorySpanExporter(); const provider = new BasicTracerProvider(); instrumentation.setTracerProvider(provider); -const tracer = provider.getTracer('test-https'); +const tracer = provider.getTracer("test-https"); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); function doNock( @@ -81,10 +81,10 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpsInstrumentation', () => { +describe("HttpsInstrumentation", () => { let contextManager: ContextManager; beforeEach(() => { @@ -99,8 +99,8 @@ describe('HttpsInstrumentation', () => { propagation.disable(); }); - describe('enable()', () => { - describe('with bad instrumentation options', () => { + describe("enable()", () => { + describe("with bad instrumentation options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -109,12 +109,12 @@ describe('HttpsInstrumentation', () => { instrumentation.setConfig({ ignoreIncomingPaths: [ (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); + throw new Error("bad ignoreIncomingPaths function"); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); + throw new Error("bad ignoreOutgoingUrls function"); }, ], applyCustomAttributesOnSpan: () => { @@ -124,11 +124,11 @@ describe('HttpsInstrumentation', () => { instrumentation.enable(); server = https.createServer( { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), + key: fs.readFileSync("test/fixtures/server-key.pem"), + cert: fs.readFileSync("test/fixtures/server-cert.pem"), }, (request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); } ); @@ -140,7 +140,7 @@ describe('HttpsInstrumentation', () => { instrumentation.disable(); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -153,23 +153,23 @@ describe('HttpsInstrumentation', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); }); - describe('with good instrumentation options', () => { + describe("with good instrumentation options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -177,14 +177,14 @@ describe('HttpsInstrumentation', () => { before(() => { instrumentation.setConfig({ ignoreIncomingPaths: [ - '/ignored/string', + "/ignored/string", /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], applyCustomAttributesOnSpan: customAttributeFunction, serverName, @@ -192,14 +192,14 @@ describe('HttpsInstrumentation', () => { instrumentation.enable(); server = https.createServer( { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), + key: fs.readFileSync("test/fixtures/server-key.pem"), + cert: fs.readFileSync("test/fixtures/server-cert.pem"), }, (request, response) => { - if (request.url?.includes('/ignored')) { - tracer.startSpan('some-span').end(); + if (request.url?.includes("/ignored")) { + tracer.startSpan("some-span").end(); } - response.end('Test Server Response'); + response.end("Test Server Response"); } ); @@ -215,13 +215,13 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(isWrapped(https.Server.prototype.emit), true); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', + "x-forwarded-for": ", , ", + "user-agent": "chrome", }, } ); @@ -234,21 +234,21 @@ describe('HttpsInstrumentation', () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", serverName, }; assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], - '' + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], + "" ); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); @@ -257,11 +257,11 @@ describe('HttpsInstrumentation', () => { { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], - '1.1' + span.attributes[SemanticAttributes.HTTP_FLAVOR], + "1.1" ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); @@ -272,7 +272,7 @@ describe('HttpsInstrumentation', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; + const testPath = "/outgoing/rootSpan/1"; doNock( hostname, @@ -296,21 +296,21 @@ describe('HttpsInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assertSpan(reqSpan, SpanKind.CLIENT, validations); }); } - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; + it("should create a child span for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 200, "Ok"); + const name = "TestRootSpan"; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -322,16 +322,16 @@ describe('HttpsInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTPS GET'); + assert.strictEqual(reqSpan.name, "HTTPS GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -346,14 +346,14 @@ describe('HttpsInstrumentation', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; + const testPath = "/outgoing/rootSpan/childs/1"; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = 'TestRootSpan'; + const name = "TestRootSpan"; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -365,16 +365,16 @@ describe('HttpsInstrumentation', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTPS GET'); + assert.strictEqual(reqSpan.name, "HTTPS GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -388,17 +388,17 @@ describe('HttpsInstrumentation', () => { }); } - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; + it("should create multiple child spans for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs"; const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; + doNock(hostname, testPath, 200, "Ok", num); + const name = "TestRootSpan"; const span = tracer.startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpsRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTPS GET'); + assert.strictEqual(spans[i].name, "HTTPS GET"); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -411,7 +411,7 @@ describe('HttpsInstrumentation', () => { }); }); - for (const ignored of ['string', 'function', 'regexp']) { + for (const ignored of ["string", "function", "regexp"]) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -423,7 +423,7 @@ describe('HttpsInstrumentation', () => { }); } - for (const arg of ['string', {}, new Date()]) { + for (const arg of ["string", {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -432,14 +432,14 @@ describe('HttpsInstrumentation', () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); + assert.ok(error.message.startsWith("Nock: No match for request")); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, '']) { + for (const arg of [true, 1, false, 0, ""]) { it(`should not throw exception in https instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -450,7 +450,7 @@ describe('HttpsInstrumentation', () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') + path.normalize("/node_modules/nock/lib/intercept.js") ) > 0 ); } @@ -462,26 +462,26 @@ describe('HttpsInstrumentation', () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - https.request({ protocol: 'telnet' }); + https.request({ protocol: "telnet" }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -502,7 +502,7 @@ describe('HttpsInstrumentation', () => { nock.cleanAll(); nock.enableNetConnect(); try { - https.request({ protocol: 'telnet' }); + https.request({ protocol: "telnet" }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -513,26 +513,26 @@ describe('HttpsInstrumentation', () => { */ assert.strictEqual( spans.length, - semver.gt(process.version, '9.0.0') ? 1 : 2 + semver.gt(process.version, "9.0.0") ? 1 : 2 ); } }); it('should have 2 ended spans when provided "options" are an object without a constructor', async () => { // Related issue: https://github.com/open-telemetry/opentelemetry-js/issues/2008 - const testPath = '/outgoing/test'; + const testPath = "/outgoing/test"; const options = Object.create(null); options.hostname = hostname; options.port = serverPort; options.path = pathname; - options.method = 'GET'; + options.method = "GET"; - doNock(hostname, testPath, 200, 'Ok'); + doNock(hostname, testPath, 200, "Ok"); const promiseRequest = new Promise((resolve, _reject) => { const req = https.request(options, (resp: http.IncomingMessage) => { - resp.on('data', () => {}); - resp.on('end', () => { + resp.on("data", () => {}); + resp.on("end", () => { resolve({}); }); }); @@ -544,19 +544,19 @@ describe('HttpsInstrumentation', () => { assert.strictEqual(spans.length, 2); }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -573,28 +573,28 @@ describe('HttpsInstrumentation', () => { } }); - it('should have 1 ended span when request is aborted', async () => { + it("should have 1 ended span when request is aborted", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .socketDelay(50) - .reply(200, ''); + .reply(200, ""); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject('timeout'); + reject("timeout"); }); return req.end(); }); @@ -611,9 +611,9 @@ describe('HttpsInstrumentation', () => { } }); - it('should have 1 ended span when request is aborted after receiving response', async () => { + it("should have 1 ended span when request is aborted after receiving response", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .delay({ body: 50, }) @@ -623,12 +623,12 @@ describe('HttpsInstrumentation', () => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { req.abort(); data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } @@ -649,19 +649,19 @@ describe('HttpsInstrumentation', () => { } }); - it("should have 1 ended span when response is listened by using req.on('response')", done => { + it("should have 1 ended span when response is listened by using req.on('response')", (done) => { const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); + nock(host).get("/").reply(404); const req = https.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { + req.on("response", (response) => { + response.on("data", () => {}); + response.on("end", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index a5b845d400f..e3e7ab24ca7 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -19,55 +19,56 @@ import { ROOT_CONTEXT, SpanKind, TraceFlags, -} from '@opentelemetry/api'; -import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { IncomingMessage, ServerResponse } from 'http'; -import { Socket } from 'net'; -import * as sinon from 'sinon'; -import * as url from 'url'; -import { IgnoreMatcher } from '../../src/types'; -import * as utils from '../../src/utils'; - -describe('Utility', () => { - describe('parseResponseStatus()', () => { - it('should return ERROR code by default', () => { +} from "@opentelemetry/api"; +import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import { IncomingMessage, ServerResponse } from "http"; +import { Socket } from "net"; +import * as sinon from "sinon"; +import * as url from "url"; +import { IgnoreMatcher } from "../../src/types"; +import * as utils from "../../src/utils"; +import { AttributeNames } from "../../src/enums"; + +describe("Utility", () => { + describe("parseResponseStatus()", () => { + it("should return ERROR code by default", () => { const status = utils.parseResponseStatus( (undefined as unknown) as number ); assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); }); - it('should return OK for Success HTTP status code', () => { + it("should return OK for Success HTTP status code", () => { for (let index = 100; index < 400; index++) { const status = utils.parseResponseStatus(index); assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); } }); - it('should not return OK for Bad HTTP status code', () => { + it("should not return OK for Bad HTTP status code", () => { for (let index = 400; index <= 600; index++) { const status = utils.parseResponseStatus(index); assert.notStrictEqual(status.code, SpanStatusCode.OK); } }); }); - describe('hasExpectHeader()', () => { - it('should throw if no option', () => { + describe("hasExpectHeader()", () => { + it("should throw if no option", () => { try { - utils.hasExpectHeader('' as http.RequestOptions); + utils.hasExpectHeader("" as http.RequestOptions); assert.fail(); } catch (ignore) {} }); - it('should not throw if no headers', () => { + it("should not throw if no headers", () => { const result = utils.hasExpectHeader({} as http.RequestOptions); assert.strictEqual(result, false); }); - it('should return true on Expect (no case sensitive)', () => { + it("should return true on Expect (no case sensitive)", () => { for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { const result = utils.hasExpectHeader({ headers, @@ -77,9 +78,9 @@ describe('Utility', () => { }); }); - describe('getRequestInfo()', () => { - it('should get options object', () => { - const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; + describe("getRequestInfo()", () => { + it("should get options object", () => { + const webUrl = "http://u:p@google.fr/aPath?qu=ry"; const urlParsed = url.parse(webUrl); const urlParsedWithoutPathname = { ...urlParsed, @@ -93,64 +94,64 @@ describe('Utility', () => { whatWgUrl, ]) { const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); - assert.strictEqual(result.optionsParsed.protocol, 'http:'); - assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); - assert.strictEqual(result.pathname, '/aPath'); - assert.strictEqual(result.origin, 'http://google.fr'); + assert.strictEqual(result.optionsParsed.hostname, "google.fr"); + assert.strictEqual(result.optionsParsed.protocol, "http:"); + assert.strictEqual(result.optionsParsed.path, "/aPath?qu=ry"); + assert.strictEqual(result.pathname, "/aPath"); + assert.strictEqual(result.origin, "http://google.fr"); } }); }); - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); + describe("satisfiesPattern()", () => { + it("string pattern", () => { + const answer1 = utils.satisfiesPattern("/test/1", "/test/1"); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); + const answer2 = utils.satisfiesPattern("/test/1", "/test/11"); assert.strictEqual(answer2, false); }); - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); + it("regex pattern", () => { + const answer1 = utils.satisfiesPattern("/TeSt/1", /\/test/i); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); + const answer2 = utils.satisfiesPattern("/2/tEst/1", /\/test/); assert.strictEqual(answer2, false); }); - it('should throw if type is unknown', () => { + it("should throw if type is unknown", () => { try { - utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); + utils.satisfiesPattern("/TeSt/1", (true as unknown) as IgnoreMatcher); assert.fail(); } catch (error) { assert.strictEqual(error instanceof TypeError, true); } }); - it('function pattern', () => { + it("function pattern", () => { const answer1 = utils.satisfiesPattern( - '/test/home', - (url: string) => url === '/test/home' + "/test/home", + (url: string) => url === "/test/home" ); assert.strictEqual(answer1, true); const answer2 = utils.satisfiesPattern( - '/test/home', - (url: string) => url !== '/test/home' + "/test/home", + (url: string) => url !== "/test/home" ); assert.strictEqual(answer2, false); }); }); - describe('isIgnored()', () => { + describe("isIgnored()", () => { beforeEach(() => { - sinon.spy(utils, 'satisfiesPattern'); + sinon.spy(utils, "satisfiesPattern"); }); afterEach(() => { sinon.restore(); }); - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('/test/1', ['/test/11']); + it("should call isSatisfyPattern, n match", () => { + const answer1 = utils.isIgnored("/test/1", ["/test/11"]); assert.strictEqual(answer1, false); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, @@ -158,24 +159,24 @@ describe('Utility', () => { ); }); - it('should call isSatisfyPattern, match for function', () => { - const answer1 = utils.isIgnored('/test/1', [ - url => url.endsWith('/test/1'), + it("should call isSatisfyPattern, match for function", () => { + const answer1 = utils.isIgnored("/test/1", [ + (url) => url.endsWith("/test/1"), ]); assert.strictEqual(answer1, true); }); - it('should not re-throw when function throws an exception', () => { + it("should not re-throw when function throws an exception", () => { const onException = (e: Error) => { // Do nothing }; for (const callback of [undefined, onException]) { assert.doesNotThrow(() => utils.isIgnored( - '/test/1', + "/test/1", [ () => { - throw new Error('test'); + throw new Error("test"); }, ], callback @@ -184,14 +185,14 @@ describe('Utility', () => { } }); - it('should call onException when function throws an exception', () => { + it("should call onException when function throws an exception", () => { const onException = sinon.spy(); assert.doesNotThrow(() => utils.isIgnored( - '/test/1', + "/test/1", [ () => { - throw new Error('test'); + throw new Error("test"); }, ], onException @@ -200,81 +201,81 @@ describe('Utility', () => { assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); }); - it('should not call isSatisfyPattern', () => { - utils.isIgnored('/test/1', []); + it("should not call isSatisfyPattern", () => { + utils.isIgnored("/test/1", []); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, 0 ); }); - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('/test/1', []); + it("should return false on empty list", () => { + const answer1 = utils.isIgnored("/test/1", []); assert.strictEqual(answer1, false); }); - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('/test/1', undefined); + it("should not throw and return false when list is undefined", () => { + const answer2 = utils.isIgnored("/test/1", undefined); assert.strictEqual(answer2, false); }); }); - describe('getAbsoluteUrl()', () => { - it('should return absolute url with localhost', () => { - const path = '/test/1'; + describe("getAbsoluteUrl()", () => { + it("should return absolute url with localhost", () => { + const path = "/test/1"; const result = utils.getAbsoluteUrl(url.parse(path), {}); assert.strictEqual(result, `http://localhost${path}`); }); - it('should return absolute url', () => { - const absUrl = 'http://www.google/test/1?query=1'; + it("should return absolute url", () => { + const absUrl = "http://www.google/test/1?query=1"; const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); assert.strictEqual(result, absUrl); }); - it('should return default url', () => { + it("should return default url", () => { const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, 'http://localhost/'); + assert.strictEqual(result, "http://localhost/"); }); it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { const result = utils.getAbsoluteUrl( - { path: '/helloworld', port: 8080 }, + { path: "/helloworld", port: 8080 }, {} ); - assert.strictEqual(result, 'http://localhost:8080/helloworld'); + assert.strictEqual(result, "http://localhost:8080/helloworld"); }); }); - describe('setSpanWithError()', () => { - it('should have error attributes', () => { - const errorMessage = 'test error'; + describe("setSpanWithError()", () => { + it("should have error attributes", () => { + const errorMessage = "test error"; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider().getTracer('default'), + new BasicTracerProvider().getTracer("default"), ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, + "test", + { spanId: "", traceId: "", traceFlags: TraceFlags.SAMPLED }, SpanKind.INTERNAL ); /* tslint:disable-next-line:no-any */ utils.setSpanWithError(span, new Error(errorMessage), obj as any); const attributes = span.attributes; assert.strictEqual( - attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], + attributes[AttributeNames.HTTP_ERROR_MESSAGE], errorMessage ); - assert.ok(attributes[SemanticAttribute.HTTP_ERROR_NAME]); + assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]); } }); }); - describe('isValidOptionsType()', () => { - ['', false, true, 1, 0, []].forEach(options => { + describe("isValidOptionsType()", () => { + ["", false, true, 1, 0, []].forEach((options) => { it(`should return false with the following value: ${JSON.stringify( options )}`, () => { assert.strictEqual(utils.isValidOptionsType(options), false); }); }); - for (const options of ['url', url.parse('http://url.com'), {}]) { + for (const options of ["url", url.parse("http://url.com"), {}]) { it(`should return true with the following value: ${JSON.stringify( options )}`, () => { @@ -283,27 +284,27 @@ describe('Utility', () => { } }); - describe('getIncomingRequestAttributesOnResponse()', () => { - it('should correctly parse the middleware stack if present', () => { + describe("getIncomingRequestAttributesOnResponse()", () => { + it("should correctly parse the middleware stack if present", () => { const request = { - __ot_middlewares: ['/test', '/toto', '/'], + __ot_middlewares: ["/test", "/toto", "/"], socket: {}, } as IncomingMessage & { __ot_middlewares?: string[] }; const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], '/test/toto'); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], "/test/toto"); }); - it('should succesfully process without middleware stack', () => { + it("should succesfully process without middleware stack", () => { const request = { socket: {}, } as IncomingMessage; const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], undefined); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); }); // Verify the key in the given attributes is set to the given value, @@ -313,14 +314,14 @@ describe('Utility', () => { key: string | undefined, value: number ) { - const SemanticAttributes = [ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, + const SemanticAttributess = [ + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, ]; - for (const attr of SemanticAttributes) { + for (const attr of SemanticAttributess) { if (attr === key) { assert.strictEqual(attributes[attr], value); } else { @@ -329,19 +330,19 @@ describe('Utility', () => { } } - describe('setRequestContentLengthAttributes()', () => { - it('should set request content-length uncompressed attribute with no content-encoding header', () => { + describe("setRequestContentLengthAttributes()", () => { + it("should set request content-length uncompressed attribute with no content-encoding header", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', + "content-length": "1200", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -350,14 +351,14 @@ describe('Utility', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', + "content-length": "1200", + "content-encoding": "identity", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -366,33 +367,33 @@ describe('Utility', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', + "content-length": "1200", + "content-encoding": "gzip", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 1200 ); }); }); - describe('setResponseContentLengthAttributes()', () => { - it('should set response content-length uncompressed attribute with no content-encoding header', () => { + describe("setResponseContentLengthAttributes()", () => { + it("should set response content-length uncompressed attribute with no content-encoding header", () => { const attributes: SpanAttributes = {}; const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', + "content-length": "1200", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -403,15 +404,15 @@ describe('Utility', () => { const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', + "content-length": "1200", + "content-encoding": "identity", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -422,25 +423,25 @@ describe('Utility', () => { const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', + "content-length": "1200", + "content-encoding": "gzip", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 1200 ); }); - it('should set no attributes with no content-length header', () => { + it("should set no attributes with no content-length header", () => { const attributes: SpanAttributes = {}; const message = {} as IncomingMessage; message.headers = { - 'content-encoding': 'gzip', + "content-encoding": "gzip", }; utils.setResponseContentLengthAttribute(message, attributes); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts index 1d89d9cee32..300919547cb 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts @@ -14,51 +14,51 @@ * limitations under the License. */ -import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; +import { SpanKind, Span, context, propagation } from "@opentelemetry/api"; import { HttpFlavorValues, NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as url from 'url'; -import { HttpInstrumentation } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as url from "url"; +import { HttpInstrumentation } from "../../src/http"; +import { assertSpan } from "../utils/assertSpan"; +import * as utils from "../utils/utils"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +} from "@opentelemetry/tracing"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from 'http'; -import { httpRequest } from '../utils/httpRequest'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { Socket } from 'net'; -import { sendRequestTwice } from '../utils/rawRequest'; +import * as http from "http"; +import { httpRequest } from "../utils/httpRequest"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { Socket } from "net"; +import { sendRequestTwice } from "../utils/rawRequest"; -const protocol = 'http'; +const protocol = "http"; const serverPort = 32345; -const hostname = 'localhost'; +const hostname = "localhost"; const memoryExporter = new InMemorySpanExporter(); const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpInstrumentation Integration tests', () => { +describe("HttpInstrumentation Integration tests", () => { let mockServerPort = 0; let mockServer: http.Server; const sockets: Array = []; - before(done => { + before((done) => { mockServer = http.createServer((req, res) => { res.statusCode = 200; - res.setHeader('content-type', 'application/json'); + res.setHeader("content-type", "application/json"); res.write( JSON.stringify({ success: true, @@ -70,17 +70,17 @@ describe('HttpInstrumentation Integration tests', () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error('unexpected addr null')); + done(new Error("unexpected addr null")); return; } - if (typeof addr === 'string') { + if (typeof addr === "string") { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error('Could not get port')); + done(new Error("Could not get port")); return; } mockServerPort = addr.port; @@ -88,8 +88,8 @@ describe('HttpInstrumentation Integration tests', () => { }); }); - after(done => { - sockets.forEach(s => s.destroy()); + after((done) => { + sockets.forEach((s) => s.destroy()); mockServer.close(done); }); @@ -106,7 +106,7 @@ describe('HttpInstrumentation Integration tests', () => { context.disable(); propagation.disable(); }); - describe('enable()', () => { + describe("enable()", () => { before(function (done) { // mandatory if (process.env.CI) { @@ -114,7 +114,7 @@ describe('HttpInstrumentation Integration tests', () => { return; } - utils.checkInternet(isConnected => { + utils.checkInternet((isConnected) => { if (!isConnected) { this.skip(); // don't disturb people @@ -134,7 +134,7 @@ describe('HttpInstrumentation Integration tests', () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ]; instrumentation.setConfig({ ignoreIncomingPaths: ignoreConfig, @@ -148,7 +148,7 @@ describe('HttpInstrumentation Integration tests', () => { instrumentation.disable(); }); - it('should create a rootSpan for GET requests and add propagation headers', async () => { + it("should create a rootSpan for GET requests and add propagation headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -157,25 +157,25 @@ describe('HttpInstrumentation Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { + it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -184,119 +184,119 @@ describe('HttpInstrumentation Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { + it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { 'x-foo': 'foo' }, + headers: { "x-foo": "foo" }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(result.reqHeaders["x-foo"], "foo"); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], + span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); - it('custom attributes should show up on client spans', async () => { + it("custom attributes should show up on client spans", async () => { const result = await httpRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { + it("should create a span for GET requests and add propagation headers with Expect headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: '100-continue' } }, + { headers: { Expect: "100-continue" } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'http', + component: "http", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, + { Expect: "100-continue", "user-agent": "http-plugin-test" }, + { "user-agent": "http-plugin-test" }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, done => { + )}`, (done) => { let validations: { hostname: string; httpStatusCode: number; @@ -305,7 +305,7 @@ describe('HttpInstrumentation Integration tests', () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ''; + let data = ""; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -317,15 +317,15 @@ describe('HttpInstrumentation Integration tests', () => { req: http.IncomingMessage; }; - resp.on('data', chunk => { + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -337,12 +337,12 @@ describe('HttpInstrumentation Integration tests', () => { } ); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); @@ -351,13 +351,13 @@ describe('HttpInstrumentation Integration tests', () => { }); } - it('should work for multiple active requests in keep-alive mode', async () => { + it("should work for multiple active requests in keep-alive mode", async () => { await sendRequestTwice(hostname, mockServerPort); const spans = memoryExporter.getFinishedSpans(); const span = spans.find((s: any) => s.kind === SpanKind.SERVER); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); }); }); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts index af60b3af346..4a257c71fdd 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts @@ -14,62 +14,62 @@ * limitations under the License. */ -import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; +import { SpanKind, Span, context, propagation } from "@opentelemetry/api"; import { HttpFlavorValues, NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as fs from 'fs'; -import * as path from 'path'; -import { Socket } from 'net'; -import { assertSpan } from '../utils/assertSpan'; -import * as url from 'url'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as fs from "fs"; +import * as path from "path"; +import { Socket } from "net"; +import { assertSpan } from "../utils/assertSpan"; +import * as url from "url"; +import * as utils from "../utils/utils"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { HttpInstrumentation } from '../../src'; +} from "@opentelemetry/tracing"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { HttpInstrumentation } from "../../src"; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as https from 'https'; -import { httpsRequest } from '../utils/httpsRequest'; -import { DummyPropagation } from '../utils/DummyPropagation'; +import * as https from "https"; +import { httpsRequest } from "../utils/httpsRequest"; +import { DummyPropagation } from "../utils/DummyPropagation"; -const protocol = 'https'; +const protocol = "https"; const serverPort = 42345; -const hostname = 'localhost'; +const hostname = "localhost"; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpsInstrumentation Integration tests', () => { +describe("HttpsInstrumentation Integration tests", () => { let mockServerPort = 0; let mockServer: https.Server; const sockets: Array = []; - before(done => { + before((done) => { mockServer = https.createServer( { key: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-key.pem') + path.join(__dirname, "..", "fixtures", "server-key.pem") ), cert: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-cert.pem') + path.join(__dirname, "..", "fixtures", "server-cert.pem") ), }, (req, res) => { res.statusCode = 200; - res.setHeader('content-type', 'application/json'); + res.setHeader("content-type", "application/json"); res.write( JSON.stringify({ success: true, @@ -82,17 +82,17 @@ describe('HttpsInstrumentation Integration tests', () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error('unexpected addr null')); + done(new Error("unexpected addr null")); return; } - if (typeof addr === 'string') { + if (typeof addr === "string") { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error('Could not get port')); + done(new Error("Could not get port")); return; } mockServerPort = addr.port; @@ -100,8 +100,8 @@ describe('HttpsInstrumentation Integration tests', () => { }); }); - after(done => { - sockets.forEach(s => s.destroy()); + after((done) => { + sockets.forEach((s) => s.destroy()); mockServer.close(done); }); @@ -114,7 +114,7 @@ describe('HttpsInstrumentation Integration tests', () => { context.disable(); }); - describe('enable()', () => { + describe("enable()", () => { before(function (done) { // mandatory if (process.env.CI) { @@ -122,7 +122,7 @@ describe('HttpsInstrumentation Integration tests', () => { return; } - utils.checkInternet(isConnected => { + utils.checkInternet((isConnected) => { if (!isConnected) { this.skip(); // don't disturb people @@ -141,7 +141,7 @@ describe('HttpsInstrumentation Integration tests', () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ]; propagation.setGlobalPropagator(new DummyPropagation()); instrumentation.setConfig({ @@ -157,7 +157,7 @@ describe('HttpsInstrumentation Integration tests', () => { propagation.disable(); }); - it('should create a rootSpan for GET requests and add propagation headers', async () => { + it("should create a rootSpan for GET requests and add propagation headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -166,25 +166,25 @@ describe('HttpsInstrumentation Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, "HTTPS GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { + it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -193,119 +193,119 @@ describe('HttpsInstrumentation Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, "HTTPS GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { + it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpsRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { 'x-foo': 'foo' }, + headers: { "x-foo": "foo" }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); + assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(result.reqHeaders["x-foo"], "foo"); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], + span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); - it('custom attributes should show up on client spans', async () => { + it("custom attributes should show up on client spans", async () => { const result = await httpsRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { + it("should create a span for GET requests and add propagation headers with Expect headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: '100-continue' } }, + { headers: { Expect: "100-continue" } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpsRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: 'https', + component: "https", }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, "HTTPS GET"); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, + { Expect: "100-continue", "user-agent": "http-plugin-test" }, + { "user-agent": "http-plugin-test" }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, done => { + )}`, (done) => { let validations: { hostname: string; httpStatusCode: number; @@ -314,7 +314,7 @@ describe('HttpsInstrumentation Integration tests', () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ''; + let data = ""; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -326,15 +326,15 @@ describe('HttpsInstrumentation Integration tests', () => { req: http.IncomingMessage; }; - resp.on('data', chunk => { + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -346,12 +346,12 @@ describe('HttpsInstrumentation Integration tests', () => { } ); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.name, "HTTPS GET"); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); diff --git a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index 3fbd7d8f329..d549f88bbc8 100644 --- a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanKind, SpanStatus } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as utils from '../../src/utils'; -import { DummyPropagation } from './DummyPropagation'; +import { SpanKind, SpanStatus } from "@opentelemetry/api"; +import { hrTimeToNanoseconds } from "@opentelemetry/core"; +import { ReadableSpan } from "@opentelemetry/tracing"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as utils from "../../src/utils"; +import { DummyPropagation } from "./DummyPropagation"; +import { AttributeNames } from "../../src/enums"; export const assertSpan = ( span: ReadableSpan, @@ -46,19 +47,19 @@ export const assertSpan = ( `${validations.component.toUpperCase()} ${validations.httpMethod}` ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], + span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_METHOD], + span.attributes[SemanticAttributes.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_TARGET], + span.attributes[SemanticAttributes.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], validations.httpStatusCode ); @@ -71,75 +72,75 @@ export const assertSpan = ( utils.parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); + assert.ok(span.endTime, "must be finished"); + assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; + const userAgent = validations.reqHeaders["user-agent"]; if (userAgent) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttributes.HTTP_USER_AGENT], userAgent ); } } if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders['content-length']) { - const contentLength = Number(validations.resHeaders['content-length']); + if (validations.resHeaders["content-length"]) { + const contentLength = Number(validations.resHeaders["content-length"]); if ( - validations.resHeaders['content-encoding'] && - validations.resHeaders['content-encoding'] !== 'identity' + validations.resHeaders["content-encoding"] && + validations.resHeaders["content-encoding"] !== "identity" ) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); } } assert.strictEqual( - span.attributes[SemanticAttribute.NET_PEER_NAME], + span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - 'must be consistent (PEER_NAME and hostname)' + "must be consistent (PEER_NAME and hostname)" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_IP], - 'must have PEER_IP' + span.attributes[SemanticAttributes.NET_PEER_IP], + "must have PEER_IP" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_PORT], - 'must have PEER_PORT' + span.attributes[SemanticAttributes.NET_PEER_PORT], + "must have PEER_PORT" ); assert.ok( - (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( - span.attributes[SemanticAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - 'must be consistent' + "must be consistent" ); } if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders['content-length']) { - const contentLength = validations.reqHeaders['content-length']; + if (validations.reqHeaders && validations.reqHeaders["content-length"]) { + const contentLength = validations.reqHeaders["content-length"]; if ( - validations.reqHeaders['content-encoding'] && - validations.reqHeaders['content-encoding'] !== 'identity' + validations.reqHeaders["content-encoding"] && + validations.reqHeaders["content-encoding"] !== "identity" ) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -147,17 +148,17 @@ export const assertSpan = ( } if (validations.serverName) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - ' must have serverName attribute' + " must have serverName attribute" ); assert.ok( - span.attributes[SemanticAttribute.NET_HOST_PORT], - 'must have HOST_PORT' + span.attributes[SemanticAttributes.NET_HOST_PORT], + "must have HOST_PORT" ); assert.ok( - span.attributes[SemanticAttribute.NET_HOST_IP], - 'must have HOST_IP' + span.attributes[SemanticAttributes.NET_HOST_IP], + "must have HOST_IP" ); } assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts new file mode 100644 index 00000000000..4b6005df653 --- /dev/null +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_STATUS_TEXT = "http.status_text", +} diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts index 29fc53f1c59..29dafdee5c4 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts @@ -14,29 +14,30 @@ * limitations under the License. */ -import * as api from '@opentelemetry/api'; +import * as api from "@opentelemetry/api"; import { isWrapped, InstrumentationBase, InstrumentationConfig, -} from '@opentelemetry/instrumentation'; -import { hrTime, isUrlIgnored, otperformance } from '@opentelemetry/core'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "@opentelemetry/instrumentation"; +import { hrTime, isUrlIgnored, otperformance } from "@opentelemetry/core"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; import { addSpanNetworkEvents, getResource, parseUrl, PerformanceTimingNames as PTN, shouldPropagateTraceHeaders, -} from '@opentelemetry/web'; -import { EventNames } from './enums/EventNames'; +} from "@opentelemetry/web"; +import { EventNames } from "./enums/EventNames"; import { OpenFunction, PropagateTraceHeaderCorsUrls, SendFunction, XhrMem, -} from './types'; -import { VERSION } from './version'; +} from "./types"; +import { VERSION } from "./version"; +import { AttributeNames } from "./enums/AttributeNames"; // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer @@ -69,7 +70,7 @@ export interface XMLHttpRequestInstrumentationConfig * This class represents a XMLHttpRequest plugin for auto instrumentation */ export class XMLHttpRequestInstrumentation extends InstrumentationBase { - readonly component: string = 'xml-http-request'; + readonly component: string = "xml-http-request"; readonly version: string = VERSION; moduleName = this.component; @@ -81,7 +82,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { + Object.keys(headers).forEach((key) => { xhr.setRequestHeader(key, String(headers[key])); }); } @@ -126,7 +127,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { - const childSpan = this.tracer.startSpan('CORS Preflight', { + const childSpan = this.tracer.startSpan("CORS Preflight", { startTime: corsPreFlightRequest[PTN.FETCH_START], }); addSpanNetworkEvents(childSpan, corsPreFlightRequest); @@ -142,26 +143,26 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { + observer: new PerformanceObserver((list) => { const entries = list.getEntries() as PerformanceResourceTiming[]; - entries.forEach(entry => { + entries.forEach((entry) => { if ( - entry.initiatorType === 'xmlhttprequest' && + entry.initiatorType === "xmlhttprequest" && entry.name === spanUrl ) { if (xhrMem.createdResources) { @@ -198,7 +199,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { unregister(this); @@ -475,30 +476,30 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase(); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts index d8ea5937f2f..bf925b3e64e 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Span } from '@opentelemetry/api'; -import { registerInstrumentations } from '@opentelemetry/instrumentation'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import { ReadableSpan, SpanProcessor } from '@opentelemetry/tracing'; -import { WebTracerProvider } from '@opentelemetry/web'; -import { XMLHttpRequestInstrumentation } from '../src'; -import assert = require('assert'); +import { Span } from "@opentelemetry/api"; +import { registerInstrumentations } from "@opentelemetry/instrumentation"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import { ReadableSpan, SpanProcessor } from "@opentelemetry/tracing"; +import { WebTracerProvider } from "@opentelemetry/web"; +import { XMLHttpRequestInstrumentation } from "../src"; +import assert = require("assert"); class TestSpanProcessor implements SpanProcessor { spans: ReadableSpan[] = []; @@ -37,7 +37,7 @@ class TestSpanProcessor implements SpanProcessor { } } -describe('unmocked xhr', () => { +describe("unmocked xhr", () => { let testSpans: TestSpanProcessor; let provider: WebTracerProvider; beforeEach(() => { @@ -53,12 +53,12 @@ describe('unmocked xhr', () => { // nop }); - it('should find resource with a relative url', done => { + it("should find resource with a relative url", (done) => { const xhr = new XMLHttpRequest(); let path = location.pathname; - path = path.substring(path.lastIndexOf('/') + 1); - xhr.open('GET', path); - xhr.addEventListener('loadend', () => { + path = path.substring(path.lastIndexOf("/") + 1); + xhr.open("GET", path); + xhr.addEventListener("loadend", () => { setTimeout(() => { assert.strictEqual(testSpans.spans.length, 1); const span = testSpans.spans[0]; @@ -66,7 +66,7 @@ describe('unmocked xhr', () => { // matching logic found the right one assert.ok( (span.attributes[ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH ] as any) > 0 ); done(); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts index 82750d7f43e..886ccff336c 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts @@ -13,28 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as api from '@opentelemetry/api'; -import { otperformance as performance, isWrapped } from '@opentelemetry/core'; -import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import * as api from "@opentelemetry/api"; +import { otperformance as performance, isWrapped } from "@opentelemetry/core"; +import { registerInstrumentations } from "@opentelemetry/instrumentation"; import { B3Propagator, B3InjectEncoding, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, -} from '@opentelemetry/propagator-b3'; -import { ZoneContextManager } from '@opentelemetry/context-zone'; -import * as tracing from '@opentelemetry/tracing'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "@opentelemetry/propagator-b3"; +import { ZoneContextManager } from "@opentelemetry/context-zone"; +import * as tracing from "@opentelemetry/tracing"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; import { PerformanceTimingNames as PTN, WebTracerProvider, parseUrl, -} from '@opentelemetry/web'; -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import { EventNames } from '../src/enums/EventNames'; -import { XMLHttpRequestInstrumentation } from '../src/xhr'; +} from "@opentelemetry/web"; +import * as assert from "assert"; +import * as sinon from "sinon"; +import { EventNames } from "../src/enums/EventNames"; +import { XMLHttpRequestInstrumentation } from "../src/xhr"; +import { AttributeNames } from "../src/enums/AttributeNames"; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -59,7 +60,7 @@ const getData = ( } req.timeout = XHR_TIMEOUT; - req.open('GET', url, async); + req.open("GET", url, async); req.onload = function () { resolve(); }; @@ -89,8 +90,8 @@ function createResource(resource = {}): PerformanceResourceTiming { domainLookupStart: 11, encodedBodySize: 0, fetchStart: 10.1, - initiatorType: 'xmlhttprequest', - nextHopProtocol: '', + initiatorType: "xmlhttprequest", + nextHopProtocol: "", redirectEnd: 0, redirectStart: 0, requestStart: 16, @@ -100,8 +101,8 @@ function createResource(resource = {}): PerformanceResourceTiming { transferSize: 0, workerStart: 0, duration: 0, - entryType: '', - name: '', + entryType: "", + name: "", startTime: 0, }; return Object.assign( @@ -114,16 +115,16 @@ function createResource(resource = {}): PerformanceResourceTiming { function createMainResource(resource = {}): PerformanceResourceTiming { const mainResource: any = createResource(resource); Object.keys(mainResource).forEach((key: string) => { - if (typeof mainResource[key] === 'number') { + if (typeof mainResource[key] === "number") { mainResource[key] = mainResource[key] + 30; } }); return mainResource; } -describe('xhr', () => { +describe("xhr", () => { const asyncTests = [{ async: true }, { async: false }]; - asyncTests.forEach(test => { + asyncTests.forEach((test) => { const testAsync = test.async; describe(`when async='${testAsync}'`, () => { let requests: any[] = []; @@ -150,7 +151,7 @@ describe('xhr', () => { api.propagation.disable(); }); - describe('when request is successful', () => { + describe("when request is successful", () => { let webTracerWithZone: api.Tracer; let webTracerProviderWithZone: WebTracerProvider; let dummySpanExporter: DummySpanExporter; @@ -158,7 +159,7 @@ describe('xhr', () => { let clearResourceTimingsSpy: any; let rootSpan: api.Span; let spyEntries: any; - const url = 'http://localhost:8090/xml-http-request.js'; + const url = "http://localhost:8090/xml-http-request.js"; let fakeNow = 0; let xmlHttpRequestInstrumentation: XMLHttpRequestInstrumentation; @@ -174,8 +175,8 @@ describe('xhr', () => { }; sinon.useFakeTimers(); - sinon.stub(performance, 'timeOrigin').value(0); - sinon.stub(performance, 'now').callsFake(() => fakeNow); + sinon.stub(performance, "timeOrigin").value(0); + sinon.stub(performance, "now").callsFake(() => fakeNow); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -189,9 +190,9 @@ describe('xhr', () => { spyEntries = sinon.stub( (performance as unknown) as Performance, - 'getEntriesByType' + "getEntriesByType" ); - spyEntries.withArgs('resource').returns(resources); + spyEntries.withArgs("resource").returns(resources); xmlHttpRequestInstrumentation = new XMLHttpRequestInstrumentation( config ); @@ -200,18 +201,18 @@ describe('xhr', () => { instrumentations: [xmlHttpRequestInstrumentation], tracerProvider: webTracerProviderWithZone, }); - webTracerWithZone = webTracerProviderWithZone.getTracer('xhr-test'); + webTracerWithZone = webTracerProviderWithZone.getTracer("xhr-test"); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, 'export'); + exportSpy = sinon.stub(dummySpanExporter, "export"); clearResourceTimingsSpy = sinon.stub( (performance as unknown) as Performance, - 'clearResourceTimings' + "clearResourceTimings" ); webTracerProviderWithZone.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - rootSpan = webTracerWithZone.startSpan('root'); + rootSpan = webTracerWithZone.startSpan("root"); api.context.with(api.setSpan(api.context.active(), rootSpan), () => { getData( new XMLHttpRequest(), @@ -225,17 +226,17 @@ describe('xhr', () => { sinon.clock.tick(1000); done(); }); - assert.strictEqual(requests.length, 1, 'request not called'); + assert.strictEqual(requests.length, 1, "request not called"); requests[0].respond( 200, - { 'Content-Type': 'application/json' }, + { "Content-Type": "application/json" }, '{"foo":"bar"}' ); }); }; - beforeEach(done => { + beforeEach((done) => { const propagateTraceHeaderCorsUrls = [window.location.origin]; prepareData(done, url, { propagateTraceHeaderCorsUrls }); }); @@ -244,90 +245,90 @@ describe('xhr', () => { clearData(); }); - it('should patch to wrap XML HTTP Requests when enabled', () => { + it("should patch to wrap XML HTTP Requests when enabled", () => { const xhttp = new XMLHttpRequest(); assert.ok(isWrapped(xhttp.send)); xmlHttpRequestInstrumentation.enable(); assert.ok(isWrapped(xhttp.send)); }); - it('should unpatch to unwrap XML HTTP Requests when disabled', () => { + it("should unpatch to unwrap XML HTTP Requests when disabled", () => { const xhttp = new XMLHttpRequest(); assert.ok(isWrapped(xhttp.send)); xmlHttpRequestInstrumentation.disable(); assert.ok(!isWrapped(xhttp.send)); }); - it('should create a span with correct root span', () => { + it("should create a span with correct root span", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - 'parent span is not root span' + "parent span is not root span" ); }); - it('span should have correct name', () => { + it("span should have correct name", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.name, 'HTTP GET', 'span has wrong name'); + assert.strictEqual(span.name, "HTTP GET", "span has wrong name"); }); - it('span should have correct kind', () => { + it("span should have correct kind", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.kind, api.SpanKind.CLIENT, - 'span has wrong kind' + "span has wrong kind" ); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.ok( (attributes[keys[2]] as number) > 0, - 'attributes ${SemanticAttributes.HTTP_RESPONSE_CONTENT_SIZE} <= 0' + "attributes ${SemanticAttributess.HTTP_RESPONSE_CONTENT_SIZE} <= 0" ); assert.strictEqual( attributes[keys[3]], 200, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], - 'OK', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + "OK", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], parseUrl(url).host, - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[7]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 8, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const events = span.events; @@ -392,41 +393,41 @@ describe('xhr', () => { `event ${EventNames.EVENT_LOAD} is not defined` ); - assert.strictEqual(events.length, 12, 'number of events is wrong'); + assert.strictEqual(events.length, 12, "number of events is wrong"); }); - it('should create a span for preflight request', () => { + it("should create a span for preflight request", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const parentSpan: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, parentSpan.spanContext.spanId, - 'parent span is not root span' + "parent span is not root span" ); }); - it('preflight request span should have correct name', () => { + it("preflight request span should have correct name", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.name, - 'CORS Preflight', - 'preflight request span has wrong name' + "CORS Preflight", + "preflight request span has wrong name" ); }); - it('preflight request span should have correct kind', () => { + it("preflight request span should have correct kind", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.kind, api.SpanKind.INTERNAL, - 'span has wrong kind' + "span has wrong kind" ); }); - it('preflight request span should have correct events', () => { + it("preflight request span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, 'number of events is wrong'); + assert.strictEqual(events.length, 9, "number of events is wrong"); assert.strictEqual( events[0].name, @@ -475,24 +476,24 @@ describe('xhr', () => { ); }); - it('should NOT clear the resources', () => { + it("should NOT clear the resources", () => { assert.ok( clearResourceTimingsSpy.notCalled, - 'resources have been cleared' + "resources have been cleared" ); }); - describe('AND origin match with window.location', () => { - beforeEach(done => { + describe("AND origin match with window.location", () => { + beforeEach((done) => { clearData(); // this won't generate a preflight span const propagateTraceHeaderCorsUrls = [url]; - prepareData(done, window.location.origin + '/xml-http-request.js', { + prepareData(done, window.location.origin + "/xml-http-request.js", { propagateTraceHeaderCorsUrls, }); }); - it('should set trace headers', () => { + it("should set trace headers", () => { const span: api.Span = exportSpy.args[0][0][0]; assert.strictEqual( requests[0].requestHeaders[X_B3_TRACE_ID], @@ -513,18 +514,18 @@ describe('xhr', () => { }); describe( - 'AND origin does NOT match window.location but match with' + - ' propagateTraceHeaderCorsUrls', + "AND origin does NOT match window.location but match with" + + " propagateTraceHeaderCorsUrls", () => { - beforeEach(done => { + beforeEach((done) => { clearData(); prepareData( done, - 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json', + "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json", { propagateTraceHeaderCorsUrls: /raw\.githubusercontent\.com/ } ); }); - it('should set trace headers', () => { + it("should set trace headers", () => { // span at exportSpy.args[0][0][0] is the preflight span const span: api.Span = exportSpy.args[1][0][0]; assert.strictEqual( @@ -546,17 +547,17 @@ describe('xhr', () => { } ); describe( - 'AND origin does NOT match window.location And does NOT match' + - ' with propagateTraceHeaderCorsUrls', + "AND origin does NOT match window.location And does NOT match" + + " with propagateTraceHeaderCorsUrls", () => { - beforeEach(done => { + beforeEach((done) => { clearData(); prepareData( done, - 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json' + "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json" ); }); - it('should NOT set trace headers', () => { + it("should NOT set trace headers", () => { assert.strictEqual( requests[0].requestHeaders[X_B3_TRACE_ID], undefined, @@ -576,8 +577,8 @@ describe('xhr', () => { } ); - describe('when url is ignored', () => { - beforeEach(done => { + describe("when url is ignored", () => { + beforeEach((done) => { clearData(); const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { @@ -586,13 +587,13 @@ describe('xhr', () => { }); }); - it('should NOT create any span', () => { + it("should NOT create any span", () => { assert.ok(exportSpy.notCalled, "span shouldn't be exported"); }); }); - describe('when clearTimingResources is set', () => { - beforeEach(done => { + describe("when clearTimingResources is set", () => { + beforeEach((done) => { clearData(); const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { @@ -601,7 +602,7 @@ describe('xhr', () => { }); }); - it('should clear the resources', () => { + it("should clear the resources", () => { assert.ok( clearResourceTimingsSpy.calledOnce, "resources haven't been cleared" @@ -609,11 +610,11 @@ describe('xhr', () => { }); }); - describe('when reusing the same XML Http request', () => { - const firstUrl = 'http://localhost:8090/get'; - const secondUrl = 'http://localhost:8099/get'; + describe("when reusing the same XML Http request", () => { + const firstUrl = "http://localhost:8090/get"; + const secondUrl = "http://localhost:8099/get"; - beforeEach(done => { + beforeEach((done) => { requests = []; const resources: PerformanceResourceTiming[] = []; resources.push( @@ -661,19 +662,19 @@ describe('xhr', () => { assert.strictEqual( requests.length, 1, - 'first request not called' + "first request not called" ); requests[0].respond( 200, - { 'Content-Type': 'application/json' }, + { "Content-Type": "application/json" }, '{"foo":"bar"}' ); } ); }); - it('should clear previous span information', () => { + it("should clear previous span information", () => { const span: tracing.ReadableSpan = exportSpy.args[2][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); @@ -681,13 +682,13 @@ describe('xhr', () => { assert.strictEqual( attributes[keys[1]], secondUrl, - `attribute ${SemanticAttribute.HTTP_URL} is wrong` + `attribute ${SemanticAttributes.HTTP_URL} is wrong` ); }); }); }); - describe('when request is NOT successful', () => { + describe("when request is NOT successful", () => { let webTracerWithZoneProvider: WebTracerProvider; let webTracerWithZone: api.Tracer; let dummySpanExporter: DummySpanExporter; @@ -695,7 +696,7 @@ describe('xhr', () => { let rootSpan: api.Span; let spyEntries: any; const url = - 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json'; + "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json"; let fakeNow = 0; beforeEach(() => { @@ -706,8 +707,8 @@ describe('xhr', () => { sinon.useFakeTimers(); - sinon.stub(performance, 'timeOrigin').value(0); - sinon.stub(performance, 'now').callsFake(() => fakeNow); + sinon.stub(performance, "timeOrigin").value(0); + sinon.stub(performance, "now").callsFake(() => fakeNow); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -718,9 +719,9 @@ describe('xhr', () => { spyEntries = sinon.stub( (performance as unknown) as Performance, - 'getEntriesByType' + "getEntriesByType" ); - spyEntries.withArgs('resource').returns(resources); + spyEntries.withArgs("resource").returns(resources); webTracerWithZoneProvider = new WebTracerProvider(); @@ -730,21 +731,21 @@ describe('xhr', () => { }); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, 'export'); + exportSpy = sinon.stub(dummySpanExporter, "export"); webTracerWithZoneProvider.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - webTracerWithZone = webTracerWithZoneProvider.getTracer('xhr-test'); + webTracerWithZone = webTracerWithZoneProvider.getTracer("xhr-test"); - rootSpan = webTracerWithZone.startSpan('root'); + rootSpan = webTracerWithZone.startSpan("root"); }); afterEach(() => { clearData(); }); - describe('when request loads and receives an error code', () => { - beforeEach(done => { + describe("when request loads and receives an error code", () => { + beforeEach((done) => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -760,63 +761,63 @@ describe('xhr', () => { sinon.clock.tick(1000); done(); }); - assert.strictEqual(requests.length, 1, 'request not called'); + assert.strictEqual(requests.length, 1, "request not called"); requests[0].respond( 400, - { 'Content-Type': 'text/plain' }, - 'Bad Request' + { "Content-Type": "text/plain" }, + "Bad Request" ); } ); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH} is wrong` + `attributes ${SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH} is wrong` ); assert.strictEqual( attributes[keys[3]], 400, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[4]], - 'Bad Request', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + "Bad Request", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], - 'raw.githubusercontent.com', - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + "raw.githubusercontent.com", + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[7]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 8, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -881,12 +882,12 @@ describe('xhr', () => { `event ${EventNames.EVENT_ERROR} is not defined` ); - assert.strictEqual(events.length, 12, 'number of events is wrong'); + assert.strictEqual(events.length, 12, "number of events is wrong"); }); }); - describe('when request encounters a network error', () => { - beforeEach(done => { + describe("when request encounters a network error", () => { + beforeEach((done) => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -898,55 +899,55 @@ describe('xhr', () => { } ); - assert.strictEqual(requests.length, 1, 'request not called'); + assert.strictEqual(requests.length, 1, "request not called"); requests[0].error(); } ); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], - '', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + "", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - 'raw.githubusercontent.com', - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + "raw.githubusercontent.com", + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[6]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 7, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -966,11 +967,11 @@ describe('xhr', () => { `event ${EventNames.EVENT_ERROR} is not defined` ); - assert.strictEqual(events.length, 3, 'number of events is wrong'); + assert.strictEqual(events.length, 3, "number of events is wrong"); }); }); - describe('when request is aborted', () => { + describe("when request is aborted", () => { before(function () { // Can only abort Async requests if (!testAsync) { @@ -978,7 +979,7 @@ describe('xhr', () => { } }); - beforeEach(done => { + beforeEach((done) => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -990,55 +991,55 @@ describe('xhr', () => { } ); - assert.strictEqual(requests.length, 1, 'request not called'); + assert.strictEqual(requests.length, 1, "request not called"); requests[0].abort(); } ); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], - '', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + "", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - 'raw.githubusercontent.com', - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + "raw.githubusercontent.com", + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[6]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 7, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -1058,11 +1059,11 @@ describe('xhr', () => { `event ${EventNames.EVENT_ABORT} is not defined` ); - assert.strictEqual(events.length, 3, 'number of events is wrong'); + assert.strictEqual(events.length, 3, "number of events is wrong"); }); }); - describe('when request times out', () => { + describe("when request times out", () => { before(function () { // Can only set timeout for Async requests if (!testAsync) { @@ -1070,7 +1071,7 @@ describe('xhr', () => { } }); - beforeEach(done => { + beforeEach((done) => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -1090,49 +1091,49 @@ describe('xhr', () => { ); }); - it('span should have correct attributes', () => { + it("span should have correct attributes", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - 'GET', - `attributes ${SemanticAttribute.HTTP_METHOD} is wrong` + "GET", + `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( attributes[keys[1]], url, - `attributes ${SemanticAttribute.HTTP_URL} is wrong` + `attributes ${SemanticAttributes.HTTP_URL} is wrong` ); assert.strictEqual( attributes[keys[2]], 0, - `attributes ${SemanticAttribute.HTTP_STATUS_CODE} is wrong` + `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.strictEqual( attributes[keys[3]], - '', - `attributes ${SemanticAttribute.HTTP_STATUS_TEXT} is wrong` + "", + `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - 'raw.githubusercontent.com', - `attributes ${SemanticAttribute.HTTP_HOST} is wrong` + "raw.githubusercontent.com", + `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', - `attributes ${SemanticAttribute.HTTP_SCHEME} is wrong` + attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== '', - `attributes ${SemanticAttribute.HTTP_USER_AGENT} is not defined` + attributes[keys[6]] !== "", + `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); + assert.strictEqual(keys.length, 7, "number of attributes is wrong"); }); - it('span should have correct events', () => { + it("span should have correct events", () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -1152,7 +1153,7 @@ describe('xhr', () => { `event ${EventNames.EVENT_TIMEOUT} is not defined` ); - assert.strictEqual(events.length, 3, 'number of events is wrong'); + assert.strictEqual(events.length, 3, "number of events is wrong"); }); }); }); diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts index 13f2c08357c..bd7d1c3b2c0 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { GrpcJsPlugin } from '../grpcJs'; -import type { GrpcClientFunc, SendUnaryDataCallback } from '../types'; +import { GrpcJsPlugin } from "../grpcJs"; +import type { GrpcClientFunc, SendUnaryDataCallback } from "../types"; import { SpanKind, Span, @@ -25,16 +25,16 @@ import { context, setSpan, diag, -} from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; +} from "@opentelemetry/api"; +import type * as grpcJs from "@grpc/grpc-js"; import { grpcStatusCodeToSpanStatus, grpcStatusCodeToOpenTelemetryStatusCode, CALL_SPAN_ENDED, methodIsIgnored, -} from '../utils'; -import { EventEmitter } from 'events'; +} from "../utils"; +import { EventEmitter } from "events"; +import { AttributeNames } from "../enums"; /** * Parse a package method list and return a list of methods to patch @@ -74,9 +74,9 @@ export function getPatchedClientMethods( ): (original: GrpcClientFunc) => () => EventEmitter { const plugin = this; return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); + diag.debug("patch all client methods"); return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace('/', '')}`; + const name = `grpc.${original.path.replace("/", "")}`; const args = [...arguments]; const metadata = getMetadata.call(plugin, original, args); const span = plugin.tracer.startSpan(name, { @@ -115,18 +115,18 @@ export function makeGrpcClientRemoteCall( if (err.code) { span.setStatus(grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } @@ -140,8 +140,8 @@ export function makeGrpcClientRemoteCall( return (span: Span) => { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = args.findIndex(arg => { - return typeof arg === 'function'; + const callbackFuncIndex = args.findIndex((arg) => { + return typeof arg === "function"; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -152,8 +152,8 @@ export function makeGrpcClientRemoteCall( } span.setAttributes({ - [SemanticAttribute.GRPC_METHOD]: original.path, - [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); setSpanContext(metadata); @@ -171,7 +171,7 @@ export function makeGrpcClientRemoteCall( } }; context.bind(call); - call.on('error', (err: grpcJs.ServiceError) => { + call.on("error", (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -182,14 +182,14 @@ export function makeGrpcClientRemoteCall( message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); - call.on('status', (status: SpanStatus) => { + call.on("status", (status: SpanStatus) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -221,9 +221,9 @@ function getMetadata( let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { return ( arg && - typeof arg === 'object' && - (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === 'function' + typeof arg === "object" && + (arg as grpcJs.Metadata)["internalRepr"] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 + typeof (arg as grpcJs.Metadata).getMap === "function" ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-plugin-grpc-js/src/enums.ts b/packages/opentelemetry-plugin-grpc-js/src/enums.ts new file mode 100644 index 00000000000..e573bbbaaa5 --- /dev/null +++ b/packages/opentelemetry-plugin-grpc-js/src/enums.ts @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + GRPC_KIND = "grpc.kind", // SERVER or CLIENT + GRPC_METHOD = "grpc.method", + GRPC_STATUS_CODE = "grpc.status_code", + GRPC_ERROR_NAME = "grpc.error_name", + GRPC_ERROR_MESSAGE = "grpc.error_message", +} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts index 15a60757123..5ba38fe2c2d 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import type { ServerCallWithMeta, SendUnaryDataCallback } from '../types'; -import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import type { GrpcJsPlugin } from '../grpcJs'; -import type * as grpcJs from '@grpc/grpc-js'; +import { context, Span, SpanStatusCode } from "@opentelemetry/api"; +import type { ServerCallWithMeta, SendUnaryDataCallback } from "../types"; +import { grpcStatusCodeToOpenTelemetryStatusCode } from "../utils"; +import type { GrpcJsPlugin } from "../grpcJs"; +import type * as grpcJs from "@grpc/grpc-js"; +import { AttributeNames } from "../enums"; /** * Handle patching for clientStream and unary type server handlers @@ -43,19 +43,16 @@ export function clientStreamAndUnaryHandler( code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, - err.code.toString() - ); + span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts index c2c93947132..f9423038a9e 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import type * as grpcJs from '@grpc/grpc-js'; -import type { HandleCall } from '@grpc/grpc-js/build/src/server-call'; -import { GrpcJsPlugin } from '../grpcJs'; -import * as shimmer from 'shimmer'; +import type * as grpcJs from "@grpc/grpc-js"; +import type { HandleCall } from "@grpc/grpc-js/build/src/server-call"; +import { GrpcJsPlugin } from "../grpcJs"; +import * as shimmer from "shimmer"; import { ServerCallWithMeta, SendUnaryDataCallback, IgnoreMatcher, -} from '../types'; +} from "../types"; import { context, SpanOptions, @@ -32,11 +32,11 @@ import { ROOT_CONTEXT, setSpan, diag, -} from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import { clientStreamAndUnaryHandler } from './clientStreamAndUnary'; -import { serverStreamAndBidiHandler } from './serverStreamAndBidi'; -import { methodIsIgnored } from '../utils'; +} from "@opentelemetry/api"; +import { clientStreamAndUnaryHandler } from "./clientStreamAndUnary"; +import { serverStreamAndBidiHandler } from "./serverStreamAndBidi"; +import { methodIsIgnored } from "../utils"; +import { AttributeNames } from "../enums"; type ServerRegisterFunction = typeof grpcJs.Server.prototype.register; @@ -51,7 +51,7 @@ export function patchServer( const plugin = this; const config = this._config; - diag.debug('patched gRPC server'); + diag.debug("patched gRPC server"); return function register( this: grpcJs.Server, name: string, @@ -68,11 +68,11 @@ export function patchServer( deserialize, type ); - const handlerSet = this['handlers'].get(name); + const handlerSet = this["handlers"].get(name); shimmer.wrap( handlerSet, - 'func', + "func", (originalFunc: HandleCall) => { return function func( this: typeof handlerSet, @@ -96,23 +96,23 @@ export function patchServer( ); } - const spanName = `grpc.${name.replace('/', '')}`; + const spanName = `grpc.${name.replace("/", "")}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug('patch func: %s', JSON.stringify(spanOptions)); + diag.debug("patch func: %s", JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (carrier, key) => carrier.get(key).map(String), - keys: carrier => Object.keys(carrier.getMap()), + keys: (carrier) => Object.keys(carrier.getMap()), }), () => { const span = plugin.tracer .startSpan(spanName, spanOptions) .setAttributes({ - [SemanticAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { @@ -144,7 +144,7 @@ function shouldNotTraceServerCall( methodName: string, ignoreGrpcMethods?: IgnoreMatcher[] ): boolean { - const parsedName = methodName.split('/'); + const parsedName = methodName.split("/"); return methodIsIgnored( parsedName[parsedName.length - 1] || methodName, ignoreGrpcMethods @@ -165,9 +165,9 @@ function handleServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': + case "unary": + case "clientStream": + case "client_stream": return clientStreamAndUnaryHandler( plugin, span, @@ -177,9 +177,9 @@ function handleServerFunction( | grpcJs.handleUnaryCall | grpcJs.ClientReadableStream ); - case 'serverStream': - case 'server_stream': - case 'bidi': + case "serverStream": + case "server_stream": + case "bidi": return serverStreamAndBidiHandler( plugin, span, @@ -204,13 +204,13 @@ function handleUntracedServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': + case "unary": + case "clientStream": + case "client_stream": return (originalFunc as Function).call({}, call, callback); - case 'serverStream': - case 'server_stream': - case 'bidi': + case "serverStream": + case "server_stream": + case "bidi": return (originalFunc as Function).call({}, call); default: break; diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts index 215c4787641..268ffbde4ae 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import type * as grpcJs from '@grpc/grpc-js'; -import type { GrpcJsPlugin } from '../grpcJs'; -import { GrpcEmitter } from '../types'; +import { context, Span, SpanStatusCode } from "@opentelemetry/api"; +import type * as grpcJs from "@grpc/grpc-js"; +import type { GrpcJsPlugin } from "../grpcJs"; +import { GrpcEmitter } from "../types"; import { CALL_SPAN_ENDED, grpcStatusCodeToOpenTelemetryStatusCode, -} from '../utils'; +} from "../utils"; +import { AttributeNames } from "../enums"; /** * Handle patching for serverStream and Bidi type server handlers @@ -44,7 +44,7 @@ export function serverStreamAndBidiHandler( }; context.bind(call); - call.on('finish', () => { + call.on("finish", () => { // @grpc/js does not expose a way to check if this call also emitted an error, // e.g. call.status.code !== 0 if (call[CALL_SPAN_ENDED]) { @@ -58,14 +58,14 @@ export function serverStreamAndBidiHandler( code: SpanStatusCode.OK, }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); endSpan(); }); - call.on('error', (err: grpcJs.ServiceError) => { + call.on("error", (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -78,8 +78,8 @@ export function serverStreamAndBidiHandler( message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); diff --git a/packages/opentelemetry-plugin-grpc/src/enums.ts b/packages/opentelemetry-plugin-grpc/src/enums.ts new file mode 100644 index 00000000000..e573bbbaaa5 --- /dev/null +++ b/packages/opentelemetry-plugin-grpc/src/enums.ts @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + GRPC_KIND = "grpc.kind", // SERVER or CLIENT + GRPC_METHOD = "grpc.method", + GRPC_STATUS_CODE = "grpc.status_code", + GRPC_ERROR_NAME = "grpc.error_name", + GRPC_ERROR_MESSAGE = "grpc.error_message", +} diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index ef598b4558d..6d7476dd8a3 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -25,13 +25,12 @@ import { ROOT_CONTEXT, setSpan, diag, -} from '@opentelemetry/api'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import { BasePlugin } from '@opentelemetry/core'; -import * as events from 'events'; -import * as grpcTypes from 'grpc'; -import * as path from 'path'; -import * as shimmer from 'shimmer'; +} from "@opentelemetry/api"; +import { BasePlugin } from "@opentelemetry/core"; +import * as events from "events"; +import * as grpcTypes from "grpc"; +import * as path from "path"; +import * as shimmer from "shimmer"; import { grpc, GrpcClientFunc, @@ -40,44 +39,45 @@ import { ModuleExportsMapping, SendUnaryDataCallback, ServerCallWithMeta, -} from './types'; +} from "./types"; import { findIndex, _grpcStatusCodeToOpenTelemetryStatusCode, _grpcStatusCodeToSpanStatus, _methodIsIgnored, -} from './utils'; -import { VERSION } from './version'; +} from "./utils"; +import { VERSION } from "./version"; +import { AttributeNames } from "./enums"; /** The metadata key under which span context is stored as a binary value. */ -export const GRPC_TRACE_KEY = 'grpc-trace-bin'; +export const GRPC_TRACE_KEY = "grpc-trace-bin"; let grpcClientModule: GrpcInternalClientTypes; export class GrpcPlugin extends BasePlugin { - static readonly component = 'grpc'; - readonly supportedVersions = ['1.*']; + static readonly component = "grpc"; + readonly supportedVersions = ["1.*"]; protected _config!: GrpcPluginOptions; constructor(readonly moduleName: string, readonly version: string) { - super('@opentelemetry/plugin-grpc', VERSION); + super("@opentelemetry/plugin-grpc", VERSION); this._config = {}; } protected readonly _internalFilesList: ModuleExportsMapping = { - '0.13 - 1.6': { client: 'src/node/src/client.js' }, - '^1.7': { client: 'src/client.js' }, + "0.13 - 1.6": { client: "src/node/src/client.js" }, + "^1.7": { client: "src/client.js" }, }; protected readonly _basedir = basedir; protected patch(): typeof grpcTypes { - diag.debug('applying patch to %s@%s', this.moduleName, this.version); + diag.debug("applying patch to %s@%s", this.moduleName, this.version); if (this._moduleExports.Server) { shimmer.wrap( this._moduleExports.Server.prototype, - 'register', + "register", this._patchServer() as any ); } @@ -85,19 +85,19 @@ export class GrpcPlugin extends BasePlugin { // Wrap the externally exported client constructor shimmer.wrap( this._moduleExports, - 'makeGenericClientConstructor', + "makeGenericClientConstructor", this._patchClient() ); - if (this._internalFilesExports['client']) { + if (this._internalFilesExports["client"]) { grpcClientModule = this._internalFilesExports[ - 'client' + "client" ] as GrpcInternalClientTypes; // Wrap the internally used client constructor shimmer.wrap( grpcClientModule, - 'makeClientConstructor', + "makeClientConstructor", this._patchClient() ); } @@ -105,16 +105,16 @@ export class GrpcPlugin extends BasePlugin { return this._moduleExports; } protected unpatch(): void { - diag.debug('removing patch to %s@%s', this.moduleName, this.version); + diag.debug("removing patch to %s@%s", this.moduleName, this.version); if (this._moduleExports.Server) { - shimmer.unwrap(this._moduleExports.Server.prototype, 'register'); + shimmer.unwrap(this._moduleExports.Server.prototype, "register"); } - shimmer.unwrap(this._moduleExports, 'makeGenericClientConstructor'); + shimmer.unwrap(this._moduleExports, "makeGenericClientConstructor"); if (grpcClientModule) { - shimmer.unwrap(grpcClientModule, 'makeClientConstructor'); + shimmer.unwrap(grpcClientModule, "makeClientConstructor"); } } @@ -127,7 +127,7 @@ export class GrpcPlugin extends BasePlugin { private _patchServer() { return (originalRegister: typeof grpcTypes.Server.prototype.register) => { const plugin = this; - diag.debug('patched gRPC server'); + diag.debug("patched gRPC server"); return function register( this: grpcTypes.Server & { handlers: any }, @@ -142,7 +142,7 @@ export class GrpcPlugin extends BasePlugin { shimmer.wrap( handlerSet, - 'func', + "func", (originalFunc: grpcTypes.handleCall) => { return function func( this: typeof handlerSet, @@ -152,43 +152,43 @@ export class GrpcPlugin extends BasePlugin { const self = this; if (plugin._shouldNotTraceServerCall(call, name)) { switch (type) { - case 'unary': - case 'client_stream': + case "unary": + case "client_stream": return (originalFunc as Function).call( self, call, callback ); - case 'server_stream': - case 'bidi': + case "server_stream": + case "bidi": return (originalFunc as Function).call(self, call); default: return originalResult; } } - const spanName = `grpc.${name.replace('/', '')}`; + const spanName = `grpc.${name.replace("/", "")}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug('patch func: %s', JSON.stringify(spanOptions)); + diag.debug("patch func: %s", JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (metadata, key) => metadata.get(key).map(String), - keys: metadata => Object.keys(metadata.getMap()), + keys: (metadata) => Object.keys(metadata.getMap()), }), () => { const span = plugin._tracer .startSpan(spanName, spanOptions) .setAttributes({ - [SemanticAttribute.GRPC_KIND]: spanOptions.kind, + [AttributeNames.GRPC_KIND]: spanOptions.kind, }); context.with(setSpan(context.active(), span), () => { switch (type) { - case 'unary': - case 'client_stream': + case "unary": + case "client_stream": return plugin._clientStreamAndUnaryHandler( plugin, span, @@ -197,8 +197,8 @@ export class GrpcPlugin extends BasePlugin { originalFunc, self ); - case 'server_stream': - case 'bidi': + case "server_stream": + case "bidi": return plugin._serverStreamAndBidiHandler( plugin, span, @@ -228,7 +228,7 @@ export class GrpcPlugin extends BasePlugin { call: ServerCallWithMeta, name: string ): boolean { - const parsedName = name.split('/'); + const parsedName = name.split("/"); return _methodIsIgnored( parsedName[parsedName.length - 1] || name, this._config.ignoreGrpcMethods @@ -258,22 +258,22 @@ export class GrpcPlugin extends BasePlugin { message: err.message, }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } - span.addEvent('received'); + span.addEvent("received"); // end the span span.end(); @@ -300,29 +300,29 @@ export class GrpcPlugin extends BasePlugin { }; context.bind(call); - call.on('finish', () => { + call.on("finish", () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, call.status.code.toString() ); // if there is an error, span will be ended on error event, otherwise end it here if (call.status.code === 0) { - span.addEvent('finished'); + span.addEvent("finished"); endSpan(); } }); - call.on('error', (err: grpcTypes.ServiceError) => { + call.on("error", (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.addEvent('finished with error'); + span.addEvent("finished with error"); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); }); @@ -333,7 +333,7 @@ export class GrpcPlugin extends BasePlugin { private _patchClient() { const plugin = this; return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug('patching client'); + diag.debug("patching client"); return function makeClientConstructor( this: typeof grpcTypes.Client, methods: { [key: string]: { originalName?: string } }, @@ -378,9 +378,9 @@ export class GrpcPlugin extends BasePlugin { private _getPatchedClientMethods() { const plugin = this; return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); + diag.debug("patch all client methods"); return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace('/', '')}`; + const name = `grpc.${original.path.replace("/", "")}`; const args = Array.prototype.slice.call(arguments); const metadata = plugin._getMetadata(original, args); const span = plugin._tracer.startSpan(name, { @@ -423,18 +423,18 @@ export class GrpcPlugin extends BasePlugin { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, err.code.toString() ); } span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } @@ -452,8 +452,8 @@ export class GrpcPlugin extends BasePlugin { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, arg => { - return typeof arg === 'function'; + const callbackFuncIndex = findIndex(args, (arg) => { + return typeof arg === "function"; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -464,10 +464,10 @@ export class GrpcPlugin extends BasePlugin { } } - span.addEvent('sent'); + span.addEvent("sent"); span.setAttributes({ - [SemanticAttribute.GRPC_METHOD]: original.path, - [SemanticAttribute.GRPC_KIND]: SpanKind.CLIENT, + [AttributeNames.GRPC_METHOD]: original.path, + [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, }); this._setSpanContext(metadata); @@ -486,26 +486,26 @@ export class GrpcPlugin extends BasePlugin { }; context.bind(call); ((call as unknown) as events.EventEmitter).on( - 'error', + "error", (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); span.setAttributes({ - [SemanticAttribute.GRPC_ERROR_NAME]: err.name, - [SemanticAttribute.GRPC_ERROR_MESSAGE]: err.message, + [AttributeNames.GRPC_ERROR_NAME]: err.name, + [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, }); endSpan(); } ); ((call as unknown) as events.EventEmitter).on( - 'status', + "status", (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - SemanticAttribute.GRPC_STATUS_CODE, + AttributeNames.GRPC_STATUS_CODE, status.code.toString() ); endSpan(); @@ -529,9 +529,9 @@ export class GrpcPlugin extends BasePlugin { let metadataIndex = findIndex(args, (arg: any) => { return ( arg && - typeof arg === 'object' && + typeof arg === "object" && arg._internal_repr && - typeof arg.getMap === 'function' + typeof arg.getMap === "function" ); }); if (metadataIndex === -1) { @@ -560,6 +560,6 @@ export class GrpcPlugin extends BasePlugin { } } -const basedir = path.dirname(require.resolve('grpc')); -const version = require(path.join(basedir, 'package.json')).version; +const basedir = path.dirname(require.resolve("grpc")); +const version = require(path.join(basedir, "package.json")).version; export const plugin = new GrpcPlugin(GrpcPlugin.component, version); diff --git a/packages/opentelemetry-plugin-http/src/enums.ts b/packages/opentelemetry-plugin-http/src/enums.ts new file mode 100644 index 00000000000..31c18afc460 --- /dev/null +++ b/packages/opentelemetry-plugin-http/src/enums.ts @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_ERROR_NAME = "http.error_name", + HTTP_ERROR_MESSAGE = "http.error_message", + HTTP_STATUS_TEXT = "http.status_text", +} diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index ea9c75d258b..da524b7d179 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -18,11 +18,11 @@ import { SpanStatusCode, Span, SpanStatus, -} from '@opentelemetry/api'; +} from "@opentelemetry/api"; import { NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; import { ClientRequest, IncomingHttpHeaders, @@ -30,9 +30,10 @@ import { OutgoingHttpHeaders, RequestOptions, ServerResponse, -} from 'http'; -import * as url from 'url'; -import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; +} from "http"; +import * as url from "url"; +import { AttributeNames } from "./enums"; +import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; /** * Get an absolute url @@ -40,22 +41,22 @@ import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; export const getAbsoluteUrl = ( requestUrl: ParsedRequestOptions | null, headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = 'http:' + fallbackProtocol = "http:" ): string => { const reqUrlObject = requestUrl || {}; const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || '').toString(); - const path = reqUrlObject.path || '/'; + const port = (reqUrlObject.port || "").toString(); + const path = reqUrlObject.path || "/"; let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; + reqUrlObject.host || reqUrlObject.hostname || headers.host || "localhost"; // if there is no port in host and there is a port // it should be displayed if it's not 80 and 443 (default ports) if ( - (host as string).indexOf(':') === -1 && + (host as string).indexOf(":") === -1 && port && - port !== '80' && - port !== '443' + port !== "80" && + port !== "443" ) { host += `:${port}`; } @@ -67,7 +68,7 @@ export const getAbsoluteUrl = ( */ export const parseResponseStatus = ( statusCode: number -): Omit => { +): Omit => { // 1xx, 2xx, 3xx are OK if (statusCode >= 100 && statusCode < 400) { return { code: SpanStatusCode.OK }; @@ -87,7 +88,7 @@ export const hasExpectHeader = (options: RequestOptions): boolean => { } const keys = Object.keys(options.headers); - return !!keys.find(key => key.toLowerCase() === 'expect'); + return !!keys.find((key) => key.toLowerCase() === "expect"); }; /** @@ -99,14 +100,14 @@ export const satisfiesPattern = ( constant: string, pattern: IgnoreMatcher ): boolean => { - if (typeof pattern === 'string') { + if (typeof pattern === "string") { return pattern === constant; } else if (pattern instanceof RegExp) { return pattern.test(constant); - } else if (typeof pattern === 'function') { + } else if (typeof pattern === "function") { return pattern(constant); } else { - throw new TypeError('Pattern is in unsupported datatype'); + throw new TypeError("Pattern is in unsupported datatype"); } }; @@ -157,8 +158,8 @@ export const setSpanWithError = ( const message = error.message; span.setAttributes({ - [SemanticAttribute.HTTP_ERROR_NAME]: error.name, - [SemanticAttribute.HTTP_ERROR_MESSAGE]: message, + [AttributeNames.HTTP_ERROR_NAME]: error.name, + [AttributeNames.HTTP_ERROR_MESSAGE]: message, }); if (!obj) { @@ -193,10 +194,10 @@ export const setRequestContentLengthAttribute = ( if (length === null) return; if (isCompressed(request.headers)) { - attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH] = length; } else { attributes[ - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -214,10 +215,10 @@ export const setResponseContentLengthAttribute = ( if (length === null) return; if (isCompressed(response.headers)) { - attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH] = length; + attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH] = length; } else { attributes[ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ] = length; } }; @@ -225,7 +226,7 @@ export const setResponseContentLengthAttribute = ( function getContentLength( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): number | null { - const contentLengthHeader = headers['content-length']; + const contentLengthHeader = headers["content-length"]; if (contentLengthHeader === undefined) return null; const contentLength = parseInt(contentLengthHeader as string, 10); @@ -237,9 +238,9 @@ function getContentLength( export const isCompressed = ( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): boolean => { - const encoding = headers['content-encoding']; + const encoding = headers["content-encoding"]; - return !!encoding && encoding !== 'identity'; + return !!encoding && encoding !== "identity"; }; /** @@ -252,13 +253,13 @@ export const getRequestInfo = ( options: url.URL | RequestOptions | string, extraOptions?: RequestOptions ) => { - let pathname = '/'; - let origin = ''; + let pathname = "/"; + let origin = ""; let optionsParsed: RequestOptions; - if (typeof options === 'string') { + if (typeof options === "string") { optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; - origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; + pathname = (optionsParsed as url.UrlWithStringQuery).pathname || "/"; + origin = `${optionsParsed.protocol || "http:"}//${optionsParsed.host}`; if (extraOptions !== undefined) { Object.assign(optionsParsed, extraOptions); } @@ -266,12 +267,12 @@ export const getRequestInfo = ( optionsParsed = { protocol: options.protocol, hostname: - typeof options.hostname === 'string' && options.hostname.startsWith('[') + typeof options.hostname === "string" && options.hostname.startsWith("[") ? options.hostname.slice(1, -1) : options.hostname, - path: `${options.pathname || ''}${options.search || ''}`, + path: `${options.pathname || ""}${options.search || ""}`, }; - if (options.port !== '') { + if (options.port !== "") { optionsParsed.port = Number(options.port); } if (options.username || options.password) { @@ -286,9 +287,9 @@ export const getRequestInfo = ( optionsParsed = Object.assign({}, options); pathname = (options as url.URL).pathname; if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || '/'; + pathname = url.parse(optionsParsed.path).pathname || "/"; } - origin = `${optionsParsed.protocol || 'http:'}//${ + origin = `${optionsParsed.protocol || "http:"}//${ optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` }`; } @@ -302,7 +303,7 @@ export const getRequestInfo = ( // ensure upperCase for consistency const method = optionsParsed.method ? optionsParsed.method.toUpperCase() - : 'GET'; + : "GET"; return { origin, pathname, method, optionsParsed }; }; @@ -317,7 +318,7 @@ export const isValidOptionsType = (options: unknown): boolean => { } const type = typeof options; - return type === 'string' || (type === 'object' && !Array.isArray(options)); + return type === "string" || (type === "object" && !Array.isArray(options)); }; /** @@ -332,25 +333,25 @@ export const getOutgoingRequestAttributes = ( const host = requestOptions.host; const hostname = requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; + host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || + "localhost"; const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; + const method = requestMethod ? requestMethod.toUpperCase() : "GET"; const headers = requestOptions.headers || {}; - const userAgent = headers['user-agent']; + const userAgent = headers["user-agent"]; const attributes: SpanAttributes = { - [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestOptions, headers, `${options.component}:` ), - [SemanticAttribute.HTTP_METHOD]: method, - [SemanticAttribute.HTTP_TARGET]: requestOptions.path || '/', - [SemanticAttribute.NET_PEER_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_TARGET]: requestOptions.path || "/", + [SemanticAttributes.NET_PEER_NAME]: hostname, }; if (userAgent !== undefined) { - attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } return attributes; }; @@ -362,11 +363,11 @@ export const getOutgoingRequestAttributes = ( export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { - attributes[SemanticAttribute.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== 'QUIC') { - attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_TCP; + attributes[SemanticAttributes.HTTP_FLAVOR] = kind; + if (kind.toUpperCase() !== "QUIC") { + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { - attributes[SemanticAttribute.NET_TRANSPORT] = NetTransportValues.IP_UDP; + attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; } } return attributes; @@ -385,17 +386,17 @@ export const getOutgoingRequestAttributesOnResponse = ( const { remoteAddress, remotePort } = socket; const attributes: SpanAttributes = { - [SemanticAttribute.NET_PEER_IP]: remoteAddress, - [SemanticAttribute.NET_PEER_PORT]: remotePort, - [SemanticAttribute.HTTP_HOST]: `${options.hostname}:${remotePort}`, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_HOST]: `${options.hostname}:${remotePort}`, }; setResponseContentLengthAttribute(response, attributes); if (statusCode) { - attributes[SemanticAttribute.HTTP_STATUS_CODE] = statusCode; - attributes[SemanticAttribute.HTTP_STATUS_TEXT] = ( - statusMessage || '' + attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; + attributes[AttributeNames.HTTP_STATUS_TEXT] = ( + statusMessage || "" ).toUpperCase(); } @@ -413,43 +414,43 @@ export const getIncomingRequestAttributes = ( options: { component: string; serverName?: string } ): SpanAttributes => { const headers = request.headers; - const userAgent = headers['user-agent']; - const ips = headers['x-forwarded-for']; - const method = request.method || 'GET'; + const userAgent = headers["user-agent"]; + const ips = headers["x-forwarded-for"]; + const method = request.method || "GET"; const httpVersion = request.httpVersion; const requestUrl = request.url ? url.parse(request.url) : null; const host = requestUrl?.host || headers.host; const hostname = requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; + host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || + "localhost"; const serverName = options.serverName; const attributes: SpanAttributes = { - [SemanticAttribute.HTTP_URL]: getAbsoluteUrl( + [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestUrl, headers, `${options.component}:` ), - [SemanticAttribute.HTTP_HOST]: host, - [SemanticAttribute.NET_HOST_NAME]: hostname, - [SemanticAttribute.HTTP_METHOD]: method, + [SemanticAttributes.HTTP_HOST]: host, + [SemanticAttributes.NET_HOST_NAME]: hostname, + [SemanticAttributes.HTTP_METHOD]: method, }; - if (typeof ips === 'string') { - attributes[SemanticAttribute.HTTP_CLIENT_IP] = ips.split(',')[0]; + if (typeof ips === "string") { + attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(",")[0]; } - if (typeof serverName === 'string') { - attributes[SemanticAttribute.HTTP_SERVER_NAME] = serverName; + if (typeof serverName === "string") { + attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[SemanticAttribute.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[SemanticAttribute.HTTP_TARGET] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || "/"; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || "/"; } if (userAgent !== undefined) { - attributes[SemanticAttribute.HTTP_USER_AGENT] = userAgent; + attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; } setRequestContentLengthAttribute(request, attributes); @@ -476,24 +477,24 @@ export const getIncomingRequestAttributesOnResponse = ( }; const route = Array.isArray(__ot_middlewares) ? __ot_middlewares - .filter(path => path !== '/') - .map(path => { - return path[0] === '/' ? path : '/' + path; + .filter((path) => path !== "/") + .map((path) => { + return path[0] === "/" ? path : "/" + path; }) - .join('') + .join("") : undefined; const attributes: SpanAttributes = { - [SemanticAttribute.NET_HOST_IP]: localAddress, - [SemanticAttribute.NET_HOST_PORT]: localPort, - [SemanticAttribute.NET_PEER_IP]: remoteAddress, - [SemanticAttribute.NET_PEER_PORT]: remotePort, - [SemanticAttribute.HTTP_STATUS_CODE]: statusCode, - [SemanticAttribute.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), + [SemanticAttributes.NET_HOST_IP]: localAddress, + [SemanticAttributes.NET_HOST_PORT]: localPort, + [SemanticAttributes.NET_PEER_IP]: remoteAddress, + [SemanticAttributes.NET_PEER_PORT]: remotePort, + [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, + [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || "").toUpperCase(), }; if (route !== undefined) { - attributes[SemanticAttribute.HTTP_ROUTE] = route; + attributes[SemanticAttributes.HTTP_ROUTE] = route; } return attributes; }; diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index bc3fd6eac51..c3bcc25327a 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -21,38 +21,38 @@ import { SpanKind, getSpan, setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; +} from "@opentelemetry/api"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; +} from "@opentelemetry/tracing"; import { NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpPlugin, plugin } from '../../src/http'; -import { Http, HttpPluginConfig } from '../../src/types'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import { ContextManager } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ClientRequest, IncomingMessage, ServerResponse } from 'http'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as nock from "nock"; +import * as path from "path"; +import { HttpPlugin, plugin } from "../../src/http"; +import { Http, HttpPluginConfig } from "../../src/types"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { httpRequest } from "../utils/httpRequest"; +import { ContextManager } from "@opentelemetry/api"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { ClientRequest, IncomingMessage, ServerResponse } from "http"; const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; + "bad applyCustomAttributesOnSpan function"; let server: http.Server; const serverPort = 22345; -const protocol = 'http'; -const hostname = 'localhost'; -const pathname = '/test'; -const serverName = 'my.server.name'; +const protocol = "http"; +const hostname = "localhost"; +const pathname = "/test"; +const serverName = "my.server.name"; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); @@ -73,24 +73,24 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; export const requestHookFunction = ( span: ISpan, request: ClientRequest | IncomingMessage ): void => { - span.setAttribute('custom request hook attribute', 'request'); + span.setAttribute("custom request hook attribute", "request"); }; export const responseHookFunction = ( span: ISpan, response: IncomingMessage | ServerResponse ): void => { - span.setAttribute('custom response hook attribute', 'response'); + span.setAttribute("custom response hook attribute", "response"); }; -describe('HttpPlugin', () => { +describe("HttpPlugin", () => { let contextManager: ContextManager; beforeEach(() => { @@ -102,11 +102,11 @@ describe('HttpPlugin', () => { context.disable(); }); - it('should return a plugin', () => { + it("should return a plugin", () => { assert.ok(plugin instanceof HttpPlugin); }); - it('should match version', () => { + it("should match version", () => { assert.strictEqual(process.versions.node, plugin.version); }); @@ -114,8 +114,8 @@ describe('HttpPlugin', () => { assert.strictEqual(protocol, plugin.moduleName); }); - describe('enable()', () => { - describe('with bad plugin options', () => { + describe("enable()", () => { + describe("with bad plugin options", () => { let pluginWithBadOptions: HttpPlugin; beforeEach(() => { memoryExporter.reset(); @@ -125,12 +125,12 @@ describe('HttpPlugin', () => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); + throw new Error("bad ignoreIncomingPaths function"); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); + throw new Error("bad ignoreOutgoingUrls function"); }, ], applyCustomAttributesOnSpan: () => { @@ -143,7 +143,7 @@ describe('HttpPlugin', () => { ); pluginWithBadOptions.enable(http, provider, config); server = http.createServer((request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort); @@ -154,7 +154,7 @@ describe('HttpPlugin', () => { pluginWithBadOptions.disable(); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -174,16 +174,16 @@ describe('HttpPlugin', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); }); - describe('with good plugin options', () => { + describe("with good plugin options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -191,14 +191,14 @@ describe('HttpPlugin', () => { before(() => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ - '/ignored/string', + "/ignored/string", /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], applyCustomAttributesOnSpan: customAttributeFunction, requestHook: requestHookFunction, @@ -207,10 +207,10 @@ describe('HttpPlugin', () => { }; plugin.enable(http, provider, config); server = http.createServer((request, response) => { - if (request.url?.includes('/ignored')) { - provider.getTracer('test').startSpan('some-span').end(); + if (request.url?.includes("/ignored")) { + provider.getTracer("test").startSpan("some-span").end(); } - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort); @@ -233,13 +233,13 @@ describe('HttpPlugin', () => { assert.strictEqual(Object.keys(httpNotPatched).length, 0); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', + "x-forwarded-for": ", , ", + "user-agent": "chrome", }, } ); @@ -258,15 +258,15 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], - '' + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], + "" ); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); [ @@ -274,11 +274,11 @@ describe('HttpPlugin', () => { { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], - '1.1' + span.attributes[SemanticAttributes.HTTP_FLAVOR], + "1.1" ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); @@ -301,7 +301,7 @@ describe('HttpPlugin', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; + const testPath = "/outgoing/rootSpan/1"; doNock( hostname, @@ -325,7 +325,7 @@ describe('HttpPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, @@ -336,11 +336,11 @@ describe('HttpPlugin', () => { }); } - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + it("should create a child span for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 200, "Ok"); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -351,16 +351,16 @@ describe('HttpPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -375,15 +375,15 @@ describe('HttpPlugin', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; + const testPath = "/outgoing/rootSpan/childs/1"; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -394,16 +394,16 @@ describe('HttpPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -417,17 +417,17 @@ describe('HttpPlugin', () => { }); } - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; + it("should create multiple child spans for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs"; const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); + doNock(hostname, testPath, 200, "Ok", num); + const name = "TestRootSpan"; + const span = provider.getTracer("default").startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); + assert.strictEqual(spans[i].name, "HTTP GET"); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -440,7 +440,7 @@ describe('HttpPlugin', () => { }); }); - for (const ignored of ['string', 'function', 'regexp']) { + for (const ignored of ["string", "function", "regexp"]) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -452,7 +452,7 @@ describe('HttpPlugin', () => { }); } - for (const arg of ['string', {}, new Date()]) { + for (const arg of ["string", {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -461,14 +461,14 @@ describe('HttpPlugin', () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); + assert.ok(error.message.startsWith("Nock: No match for request")); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, '']) { + for (const arg of [true, 1, false, 0, ""]) { it(`should not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -479,7 +479,7 @@ describe('HttpPlugin', () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') + path.normalize("/node_modules/nock/lib/intercept.js") ) > 0 ); } @@ -491,26 +491,26 @@ describe('HttpPlugin', () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - http.request({ protocol: 'telnet' }); + http.request({ protocol: "telnet" }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -531,7 +531,7 @@ describe('HttpPlugin', () => { nock.cleanAll(); nock.enableNetConnect(); try { - http.request({ protocol: 'telnet' }); + http.request({ protocol: "telnet" }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -539,19 +539,19 @@ describe('HttpPlugin', () => { } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -568,28 +568,28 @@ describe('HttpPlugin', () => { } }); - it('should have 1 ended span when request is aborted', async () => { + it("should have 1 ended span when request is aborted", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .socketDelay(50) - .reply(200, ''); + .reply(200, ""); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject('timeout'); + reject("timeout"); }); return req.end(); }); @@ -606,9 +606,9 @@ describe('HttpPlugin', () => { } }); - it('should have 1 ended span when request is aborted after receiving response', async () => { + it("should have 1 ended span when request is aborted after receiving response", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .delay({ body: 50, }) @@ -618,12 +618,12 @@ describe('HttpPlugin', () => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { req.abort(); data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } @@ -644,11 +644,11 @@ describe('HttpPlugin', () => { } }); - it("should have 1 ended span when request doesn't listening response", done => { + it("should have 1 ended span when request doesn't listening response", (done) => { nock.cleanAll(); nock.enableNetConnect(); const req = http.request(`${protocol}://${hostname}/`); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -658,19 +658,19 @@ describe('HttpPlugin', () => { req.end(); }); - it("should have 1 ended span when response is listened by using req.on('response')", done => { + it("should have 1 ended span when response is listened by using req.on('response')", (done) => { const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); + nock(host).get("/").reply(404); const req = http.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { + req.on("response", (response) => { + response.on("data", () => {}); + response.on("end", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); @@ -680,7 +680,7 @@ describe('HttpPlugin', () => { req.end(); }); - it('custom attributes should show up on client and server spans', async () => { + it("custom attributes should show up on client and server spans", async () => { await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -688,47 +688,47 @@ describe('HttpPlugin', () => { const [incomingSpan, outgoingSpan] = spans; assert.strictEqual( - incomingSpan.attributes['custom request hook attribute'], - 'request' + incomingSpan.attributes["custom request hook attribute"], + "request" ); assert.strictEqual( - incomingSpan.attributes['custom response hook attribute'], - 'response' + incomingSpan.attributes["custom response hook attribute"], + "response" ); assert.strictEqual( - incomingSpan.attributes['span kind'], + incomingSpan.attributes["span kind"], SpanKind.CLIENT ); assert.strictEqual( - outgoingSpan.attributes['custom request hook attribute'], - 'request' + outgoingSpan.attributes["custom request hook attribute"], + "request" ); assert.strictEqual( - outgoingSpan.attributes['custom response hook attribute'], - 'response' + outgoingSpan.attributes["custom response hook attribute"], + "response" ); assert.strictEqual( - outgoingSpan.attributes['span kind'], + outgoingSpan.attributes["span kind"], SpanKind.CLIENT ); }); - it('should not set span as active in context for outgoing request', done => { + it("should not set span as active in context for outgoing request", (done) => { assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { + http.get(`${protocol}://${hostname}:${serverPort}/test`, (res) => { assert.deepStrictEqual(getSpan(context.active()), undefined); done(); }); }); }); - describe('with require parent span', () => { - beforeEach(done => { + describe("with require parent span", () => { + beforeEach((done) => { memoryExporter.reset(); plugin.enable(http, provider, {}); server = http.createServer((request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); }); server.listen(serverPort, done); }); @@ -738,14 +738,14 @@ describe('HttpPlugin', () => { plugin.disable(); }); - it('should not trace without parent with options enabled (both client & server)', async () => { + it("should not trace without parent with options enabled (both client & server)", async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = '/test/test'; + const testPath = "/test/test"; await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -753,13 +753,13 @@ describe('HttpPlugin', () => { assert.strictEqual(spans.length, 0); }); - it('should not trace without parent with options enabled (client only)', async () => { + it("should not trace without parent with options enabled (client only)", async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = '/test/test'; + const testPath = "/test/test"; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -772,18 +772,18 @@ describe('HttpPlugin', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every(span => span.kind === SpanKind.SERVER), + spans.every((span) => span.kind === SpanKind.SERVER), true ); }); - it('should not trace without parent with options enabled (server only)', async () => { + it("should not trace without parent with options enabled (server only)", async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, }; plugin.enable(http, provider, config); - const testPath = '/test/test'; + const testPath = "/test/test"; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -796,27 +796,27 @@ describe('HttpPlugin', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every(span => span.kind === SpanKind.CLIENT), + spans.every((span) => span.kind === SpanKind.CLIENT), true ); }); - it('should trace with parent with both requireParent options enabled', done => { + it("should trace with parent with both requireParent options enabled", (done) => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = '/test/test'; - const tracer = provider.getTracer('default'); - const span = tracer.startSpan('parentSpan', { + const testPath = "/test/test"; + const tracer = provider.getTracer("default"); + const span = tracer.startSpan("parentSpan", { kind: SpanKind.INTERNAL, }); context.with(setSpan(context.active(), span), () => { httpRequest .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then(result => { + .then((result) => { span.end(); assert( result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== @@ -829,11 +829,11 @@ describe('HttpPlugin', () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2); assert.strictEqual( - spans.filter(span => span.kind === SpanKind.CLIENT).length, + spans.filter((span) => span.kind === SpanKind.CLIENT).length, 1 ); assert.strictEqual( - spans.filter(span => span.kind === SpanKind.INTERNAL).length, + spans.filter((span) => span.kind === SpanKind.INTERNAL).length, 1 ); return done(); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index d16d7064124..008907bf7a4 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -19,54 +19,55 @@ import { ROOT_CONTEXT, SpanKind, TraceFlags, -} from '@opentelemetry/api'; -import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { IncomingMessage, ServerResponse } from 'http'; -import * as sinon from 'sinon'; -import * as url from 'url'; -import { IgnoreMatcher } from '../../src/types'; -import * as utils from '../../src/utils'; - -describe('Utility', () => { - describe('parseResponseStatus()', () => { - it('should return ERROR code by default', () => { +} from "@opentelemetry/api"; +import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import { IncomingMessage, ServerResponse } from "http"; +import * as sinon from "sinon"; +import * as url from "url"; +import { IgnoreMatcher } from "../../src/types"; +import * as utils from "../../src/utils"; +import { AttributeNames } from "../../src/enums"; + +describe("Utility", () => { + describe("parseResponseStatus()", () => { + it("should return ERROR code by default", () => { const status = utils.parseResponseStatus( (undefined as unknown) as number ); assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); }); - it('should return OK for Success HTTP status code', () => { + it("should return OK for Success HTTP status code", () => { for (let index = 100; index < 400; index++) { const status = utils.parseResponseStatus(index); assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); } }); - it('should not return OK for Bad HTTP status code', () => { + it("should not return OK for Bad HTTP status code", () => { for (let index = 400; index <= 600; index++) { const status = utils.parseResponseStatus(index); assert.notStrictEqual(status.code, SpanStatusCode.OK); } }); }); - describe('hasExpectHeader()', () => { - it('should throw if no option', () => { + describe("hasExpectHeader()", () => { + it("should throw if no option", () => { try { - utils.hasExpectHeader('' as http.RequestOptions); + utils.hasExpectHeader("" as http.RequestOptions); assert.fail(); } catch (ignore) {} }); - it('should not throw if no headers', () => { + it("should not throw if no headers", () => { const result = utils.hasExpectHeader({} as http.RequestOptions); assert.strictEqual(result, false); }); - it('should return true on Expect (no case sensitive)', () => { + it("should return true on Expect (no case sensitive)", () => { for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { const result = utils.hasExpectHeader({ headers, @@ -76,9 +77,9 @@ describe('Utility', () => { }); }); - describe('getRequestInfo()', () => { - it('should get options object', () => { - const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; + describe("getRequestInfo()", () => { + it("should get options object", () => { + const webUrl = "http://u:p@google.fr/aPath?qu=ry"; const urlParsed = url.parse(webUrl); const urlParsedWithoutPathname = { ...urlParsed, @@ -92,64 +93,64 @@ describe('Utility', () => { whatWgUrl, ]) { const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); - assert.strictEqual(result.optionsParsed.protocol, 'http:'); - assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); - assert.strictEqual(result.pathname, '/aPath'); - assert.strictEqual(result.origin, 'http://google.fr'); + assert.strictEqual(result.optionsParsed.hostname, "google.fr"); + assert.strictEqual(result.optionsParsed.protocol, "http:"); + assert.strictEqual(result.optionsParsed.path, "/aPath?qu=ry"); + assert.strictEqual(result.pathname, "/aPath"); + assert.strictEqual(result.origin, "http://google.fr"); } }); }); - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); + describe("satisfiesPattern()", () => { + it("string pattern", () => { + const answer1 = utils.satisfiesPattern("/test/1", "/test/1"); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); + const answer2 = utils.satisfiesPattern("/test/1", "/test/11"); assert.strictEqual(answer2, false); }); - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); + it("regex pattern", () => { + const answer1 = utils.satisfiesPattern("/TeSt/1", /\/test/i); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); + const answer2 = utils.satisfiesPattern("/2/tEst/1", /\/test/); assert.strictEqual(answer2, false); }); - it('should throw if type is unknown', () => { + it("should throw if type is unknown", () => { try { - utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); + utils.satisfiesPattern("/TeSt/1", (true as unknown) as IgnoreMatcher); assert.fail(); } catch (error) { assert.strictEqual(error instanceof TypeError, true); } }); - it('function pattern', () => { + it("function pattern", () => { const answer1 = utils.satisfiesPattern( - '/test/home', - (url: string) => url === '/test/home' + "/test/home", + (url: string) => url === "/test/home" ); assert.strictEqual(answer1, true); const answer2 = utils.satisfiesPattern( - '/test/home', - (url: string) => url !== '/test/home' + "/test/home", + (url: string) => url !== "/test/home" ); assert.strictEqual(answer2, false); }); }); - describe('isIgnored()', () => { + describe("isIgnored()", () => { beforeEach(() => { - sinon.spy(utils, 'satisfiesPattern'); + sinon.spy(utils, "satisfiesPattern"); }); afterEach(() => { sinon.restore(); }); - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('/test/1', ['/test/11']); + it("should call isSatisfyPattern, n match", () => { + const answer1 = utils.isIgnored("/test/1", ["/test/11"]); assert.strictEqual(answer1, false); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, @@ -157,24 +158,24 @@ describe('Utility', () => { ); }); - it('should call isSatisfyPattern, match for function', () => { - const answer1 = utils.isIgnored('/test/1', [ - url => url.endsWith('/test/1'), + it("should call isSatisfyPattern, match for function", () => { + const answer1 = utils.isIgnored("/test/1", [ + (url) => url.endsWith("/test/1"), ]); assert.strictEqual(answer1, true); }); - it('should not re-throw when function throws an exception', () => { + it("should not re-throw when function throws an exception", () => { const onException = (e: Error) => { // Do Nothing }; for (const callback of [undefined, onException]) { assert.doesNotThrow(() => utils.isIgnored( - '/test/1', + "/test/1", [ () => { - throw new Error('test'); + throw new Error("test"); }, ], callback @@ -183,14 +184,14 @@ describe('Utility', () => { } }); - it('should call onException when function throws an exception', () => { + it("should call onException when function throws an exception", () => { const onException = sinon.spy(); assert.doesNotThrow(() => utils.isIgnored( - '/test/1', + "/test/1", [ () => { - throw new Error('test'); + throw new Error("test"); }, ], onException @@ -199,81 +200,81 @@ describe('Utility', () => { assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); }); - it('should not call isSatisfyPattern', () => { - utils.isIgnored('/test/1', []); + it("should not call isSatisfyPattern", () => { + utils.isIgnored("/test/1", []); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, 0 ); }); - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('/test/1', []); + it("should return false on empty list", () => { + const answer1 = utils.isIgnored("/test/1", []); assert.strictEqual(answer1, false); }); - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('/test/1', undefined); + it("should not throw and return false when list is undefined", () => { + const answer2 = utils.isIgnored("/test/1", undefined); assert.strictEqual(answer2, false); }); }); - describe('getAbsoluteUrl()', () => { - it('should return absolute url with localhost', () => { - const path = '/test/1'; + describe("getAbsoluteUrl()", () => { + it("should return absolute url with localhost", () => { + const path = "/test/1"; const result = utils.getAbsoluteUrl(url.parse(path), {}); assert.strictEqual(result, `http://localhost${path}`); }); - it('should return absolute url', () => { - const absUrl = 'http://www.google/test/1?query=1'; + it("should return absolute url", () => { + const absUrl = "http://www.google/test/1?query=1"; const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); assert.strictEqual(result, absUrl); }); - it('should return default url', () => { + it("should return default url", () => { const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, 'http://localhost/'); + assert.strictEqual(result, "http://localhost/"); }); it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { const result = utils.getAbsoluteUrl( - { path: '/helloworld', port: 8080 }, + { path: "/helloworld", port: 8080 }, {} ); - assert.strictEqual(result, 'http://localhost:8080/helloworld'); + assert.strictEqual(result, "http://localhost:8080/helloworld"); }); }); - describe('setSpanWithError()', () => { - it('should have error attributes', () => { - const errorMessage = 'test error'; + describe("setSpanWithError()", () => { + it("should have error attributes", () => { + const errorMessage = "test error"; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider().getTracer('default'), + new BasicTracerProvider().getTracer("default"), ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, + "test", + { spanId: "", traceId: "", traceFlags: TraceFlags.SAMPLED }, SpanKind.INTERNAL ); /* tslint:disable-next-line:no-any */ utils.setSpanWithError(span, new Error(errorMessage), obj as any); const attributes = span.attributes; assert.strictEqual( - attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], + attributes[AttributeNames.HTTP_ERROR_MESSAGE], errorMessage ); - assert.ok(attributes[SemanticAttribute.HTTP_ERROR_NAME]); + assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]); } }); }); - describe('isValidOptionsType()', () => { - ['', false, true, 1, 0, []].forEach(options => { + describe("isValidOptionsType()", () => { + ["", false, true, 1, 0, []].forEach((options) => { it(`should return false with the following value: ${JSON.stringify( options )}`, () => { assert.strictEqual(utils.isValidOptionsType(options), false); }); }); - for (const options of ['url', url.parse('http://url.com'), {}]) { + for (const options of ["url", url.parse("http://url.com"), {}]) { it(`should return true with the following value: ${JSON.stringify( options )}`, () => { @@ -282,10 +283,10 @@ describe('Utility', () => { } }); - describe('getIncomingRequestAttributesOnResponse()', () => { - it('should correctly parse the middleware stack if present', () => { + describe("getIncomingRequestAttributesOnResponse()", () => { + it("should correctly parse the middleware stack if present", () => { const request = { - __ot_middlewares: ['/test', '/toto', '/'], + __ot_middlewares: ["/test", "/toto", "/"], socket: {}, } as IncomingMessage & { __ot_middlewares?: string[] }; const response = {} as ServerResponse; @@ -293,17 +294,17 @@ describe('Utility', () => { request, response ); - assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], '/test/toto'); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], "/test/toto"); }); - it('should succesfully process without middleware stack', () => { + it("should succesfully process without middleware stack", () => { const request = { socket: {} } as IncomingMessage; const response = {} as ServerResponse; const attributes = utils.getIncomingRequestAttributesOnResponse( request, response ); - assert.deepEqual(attributes[SemanticAttribute.HTTP_ROUTE], undefined); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); }); }); @@ -314,14 +315,14 @@ describe('Utility', () => { key: string | undefined, value: number ) { - const SemanticAttributes = [ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, + const SemanticAttributess = [ + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, ]; - for (const attr of SemanticAttributes) { + for (const attr of SemanticAttributess) { if (attr === key) { assert.strictEqual(attributes[attr], value); } else { @@ -330,19 +331,19 @@ describe('Utility', () => { } } - describe('setRequestContentLengthAttributes()', () => { - it('should set request content-length uncompressed attribute with no content-encoding header', () => { + describe("setRequestContentLengthAttributes()", () => { + it("should set request content-length uncompressed attribute with no content-encoding header", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', + "content-length": "1200", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -351,14 +352,14 @@ describe('Utility', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', + "content-length": "1200", + "content-encoding": "identity", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -367,33 +368,33 @@ describe('Utility', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', + "content-length": "1200", + "content-encoding": "gzip", }; utils.setRequestContentLengthAttribute(request, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH, + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, 1200 ); }); }); - describe('setResponseContentLengthAttributes()', () => { - it('should set response content-length uncompressed attribute with no content-encoding header', () => { + describe("setResponseContentLengthAttributes()", () => { + it("should set response content-length uncompressed attribute with no content-encoding header", () => { const attributes: SpanAttributes = {}; const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', + "content-length": "1200", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -404,15 +405,15 @@ describe('Utility', () => { const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', + "content-length": "1200", + "content-encoding": "identity", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, 1200 ); }); @@ -423,25 +424,25 @@ describe('Utility', () => { const response = {} as IncomingMessage; response.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', + "content-length": "1200", + "content-encoding": "gzip", }; utils.setResponseContentLengthAttribute(response, attributes); verifyValueInAttributes( attributes, - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, 1200 ); }); - it('should set no attributes with no content-length header', () => { + it("should set no attributes with no content-length header", () => { const attributes: SpanAttributes = {}; const message = {} as IncomingMessage; message.headers = { - 'content-encoding': 'gzip', + "content-encoding": "gzip", }; utils.setResponseContentLengthAttribute(message, attributes); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 49cc71712a9..6e4805362b2 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -14,46 +14,46 @@ * limitations under the License. */ -import { SpanKind, Span, context } from '@opentelemetry/api'; +import { SpanKind, Span, context } from "@opentelemetry/api"; import { HttpFlavorValues, NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as url from 'url'; -import { plugin } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as url from "url"; +import { plugin } from "../../src/http"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { httpRequest } from "../utils/httpRequest"; +import * as utils from "../utils/utils"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { HttpPluginConfig } from '../../src/types'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; -import { sendRequestTwice } from '../utils/rawRequest'; -const protocol = 'http'; +} from "@opentelemetry/tracing"; +import { HttpPluginConfig } from "../../src/types"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { Socket } from "net"; +import { sendRequestTwice } from "../utils/rawRequest"; +const protocol = "http"; const serverPort = 32345; -const hostname = 'localhost'; +const hostname = "localhost"; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpPlugin Integration tests', () => { +describe("HttpPlugin Integration tests", () => { let mockServerPort = 0; let mockServer: http.Server; const sockets: Array = []; - before(done => { + before((done) => { mockServer = http.createServer((req, res) => { res.statusCode = 200; - res.setHeader('content-type', 'application/json'); + res.setHeader("content-type", "application/json"); res.write( JSON.stringify({ success: true, @@ -65,17 +65,17 @@ describe('HttpPlugin Integration tests', () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error('unexpected addr null')); + done(new Error("unexpected addr null")); return; } - if (typeof addr === 'string') { + if (typeof addr === "string") { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error('Could not get port')); + done(new Error("Could not get port")); return; } mockServerPort = addr.port; @@ -83,8 +83,8 @@ describe('HttpPlugin Integration tests', () => { }); }); - after(done => { - sockets.forEach(s => s.destroy()); + after((done) => { + sockets.forEach((s) => s.destroy()); mockServer.close(done); }); @@ -96,7 +96,7 @@ describe('HttpPlugin Integration tests', () => { afterEach(() => { context.disable(); }); - describe('enable()', () => { + describe("enable()", () => { before(function (done) { // mandatory if (process.env.CI) { @@ -104,7 +104,7 @@ describe('HttpPlugin Integration tests', () => { return; } - utils.checkInternet(isConnected => { + utils.checkInternet((isConnected) => { if (!isConnected) { this.skip(); // don't disturb people @@ -123,7 +123,7 @@ describe('HttpPlugin Integration tests', () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ]; const config: HttpPluginConfig = { ignoreIncomingPaths: ignoreConfig, @@ -140,7 +140,7 @@ describe('HttpPlugin Integration tests', () => { plugin.disable(); }); - it('should create a rootSpan for GET requests and add propagation headers', async () => { + it("should create a rootSpan for GET requests and add propagation headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -149,25 +149,25 @@ describe('HttpPlugin Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { + it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -176,119 +176,119 @@ describe('HttpPlugin Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { + it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { 'x-foo': 'foo' }, + headers: { "x-foo": "foo" }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(result.reqHeaders["x-foo"], "foo"); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], + span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); - it('custom attributes should show up on client spans', async () => { + it("custom attributes should show up on client spans", async () => { const result = await httpRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { + it("should create a span for GET requests and add propagation headers with Expect headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: '100-continue' } }, + { headers: { Expect: "100-continue" } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, + { Expect: "100-continue", "user-agent": "http-plugin-test" }, + { "user-agent": "http-plugin-test" }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, done => { + )}`, (done) => { let validations: { hostname: string; httpStatusCode: number; @@ -297,7 +297,7 @@ describe('HttpPlugin Integration tests', () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ''; + let data = ""; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -309,15 +309,15 @@ describe('HttpPlugin Integration tests', () => { req: http.IncomingMessage; }; - resp.on('data', chunk => { + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -329,12 +329,12 @@ describe('HttpPlugin Integration tests', () => { } ); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); @@ -343,13 +343,13 @@ describe('HttpPlugin Integration tests', () => { }); } - it('should work for multiple active requests in keep-alive mode', async () => { + it("should work for multiple active requests in keep-alive mode", async () => { await sendRequestTwice(hostname, mockServerPort); const spans = memoryExporter.getFinishedSpans(); const span = spans.find((s: any) => s.kind === SpanKind.SERVER); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); }); }); }); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts index 128a08037da..134bef5c626 100644 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanKind, SpanStatus } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as utils from '../../src/utils'; -import { DummyPropagation } from './DummyPropagation'; +import { SpanKind, SpanStatus } from "@opentelemetry/api"; +import { hrTimeToNanoseconds } from "@opentelemetry/core"; +import { ReadableSpan } from "@opentelemetry/tracing"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as utils from "../../src/utils"; +import { DummyPropagation } from "./DummyPropagation"; +import { AttributeNames } from "../../src/enums"; export const assertSpan = ( span: ReadableSpan, @@ -43,19 +44,19 @@ export const assertSpan = ( assert.strictEqual(span.kind, kind); assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], + span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_METHOD], + span.attributes[SemanticAttributes.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_TARGET], + span.attributes[SemanticAttributes.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], validations.httpStatusCode ); @@ -68,35 +69,35 @@ export const assertSpan = ( utils.parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); + assert.ok(span.endTime, "must be finished"); + assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; + const userAgent = validations.reqHeaders["user-agent"]; if (userAgent) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttributes.HTTP_USER_AGENT], userAgent ); } } if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders['content-length']) { - const contentLength = Number(validations.resHeaders['content-length']); + if (validations.resHeaders["content-length"]) { + const contentLength = Number(validations.resHeaders["content-length"]); if ( - validations.resHeaders['content-encoding'] && - validations.resHeaders['content-encoding'] !== 'identity' + validations.resHeaders["content-encoding"] && + validations.resHeaders["content-encoding"] !== "identity" ) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -104,41 +105,41 @@ export const assertSpan = ( } assert.strictEqual( - span.attributes[SemanticAttribute.NET_PEER_NAME], + span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - 'must be consistent (PEER_NAME and hostname)' + "must be consistent (PEER_NAME and hostname)" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_IP], - 'must have PEER_IP' + span.attributes[SemanticAttributes.NET_PEER_IP], + "must have PEER_IP" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_PORT], - 'must have PEER_PORT' + span.attributes[SemanticAttributes.NET_PEER_PORT], + "must have PEER_PORT" ); assert.ok( - (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( - span.attributes[SemanticAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - 'must be consistent' + "must be consistent" ); } if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders['content-length']) { - const contentLength = validations.reqHeaders['content-length']; + if (validations.reqHeaders && validations.reqHeaders["content-length"]) { + const contentLength = validations.reqHeaders["content-length"]; if ( - validations.reqHeaders['content-encoding'] && - validations.reqHeaders['content-encoding'] !== 'identity' + validations.reqHeaders["content-encoding"] && + validations.reqHeaders["content-encoding"] !== "identity" ) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH], + span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], contentLength ); } else { assert.strictEqual( span.attributes[ - SemanticAttribute.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED + SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED ], contentLength ); @@ -147,17 +148,17 @@ export const assertSpan = ( if (validations.serverName) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - ' must have serverName attribute' + " must have serverName attribute" ); assert.ok( - span.attributes[SemanticAttribute.NET_HOST_PORT], - 'must have HOST_PORT' + span.attributes[SemanticAttributes.NET_HOST_PORT], + "must have HOST_PORT" ); assert.ok( - span.attributes[SemanticAttribute.NET_HOST_IP], - 'must have HOST_IP' + span.attributes[SemanticAttributes.NET_HOST_IP], + "must have HOST_IP" ); } assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); diff --git a/packages/opentelemetry-plugin-https/src/enums.ts b/packages/opentelemetry-plugin-https/src/enums.ts new file mode 100644 index 00000000000..b0fca38cc80 --- /dev/null +++ b/packages/opentelemetry-plugin-https/src/enums.ts @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * + * 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. + */ + +/** + * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md + */ +export enum AttributeNames { + HTTP_ERROR_MESSAGE = "http.error_message", +} diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts index a4394a80704..6e20460cccf 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -21,43 +21,43 @@ import { Span as ISpan, SpanKind, setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ContextManager } from '@opentelemetry/api'; +} from "@opentelemetry/api"; +import { NodeTracerProvider } from "@opentelemetry/node"; +import { Http, HttpPluginConfig } from "@opentelemetry/plugin-http"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { ContextManager } from "@opentelemetry/api"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; +} from "@opentelemetry/tracing"; import { HttpFlavorValues, NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as https from 'https'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpsPlugin, plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as fs from "fs"; +import * as http from "http"; +import * as https from "https"; +import * as nock from "nock"; +import * as path from "path"; +import { HttpsPlugin, plugin } from "../../src/https"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { httpsRequest } from "../utils/httpsRequest"; const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; + "bad applyCustomAttributesOnSpan function"; let server: https.Server; const serverPort = 32345; -const protocol = 'https'; -const hostname = 'localhost'; -const serverName = 'my.server.name'; -const pathname = '/test'; +const protocol = "https"; +const hostname = "localhost"; +const serverName = "my.server.name"; +const pathname = "/test"; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); -const tracer = provider.getTracer('test-https'); +const tracer = provider.getTracer("test-https"); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); propagation.setGlobalPropagator(new DummyPropagation()); @@ -76,10 +76,10 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpsPlugin', () => { +describe("HttpsPlugin", () => { let contextManager: ContextManager; beforeEach(() => { @@ -92,11 +92,11 @@ describe('HttpsPlugin', () => { context.disable(); }); - it('should return a plugin', () => { + it("should return a plugin", () => { assert.ok(plugin instanceof HttpsPlugin); }); - it('should match version', () => { + it("should match version", () => { assert.strictEqual(process.versions.node, plugin.version); }); @@ -104,8 +104,8 @@ describe('HttpsPlugin', () => { assert.strictEqual(protocol, plugin.moduleName); }); - describe('enable()', () => { - describe('with bad plugin options', () => { + describe("enable()", () => { + describe("with bad plugin options", () => { let pluginWithBadOptions: HttpsPlugin; beforeEach(() => { memoryExporter.reset(); @@ -115,12 +115,12 @@ describe('HttpsPlugin', () => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); + throw new Error("bad ignoreIncomingPaths function"); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); + throw new Error("bad ignoreOutgoingUrls function"); }, ], applyCustomAttributesOnSpan: () => { @@ -135,11 +135,11 @@ describe('HttpsPlugin', () => { ); server = https.createServer( { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), + key: fs.readFileSync("test/fixtures/server-key.pem"), + cert: fs.readFileSync("test/fixtures/server-cert.pem"), }, (request, response) => { - response.end('Test Server Response'); + response.end("Test Server Response"); } ); @@ -151,7 +151,7 @@ describe('HttpsPlugin', () => { pluginWithBadOptions.disable(); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -171,16 +171,16 @@ describe('HttpsPlugin', () => { assertSpan(incomingSpan, SpanKind.SERVER, validations); assertSpan(outgoingSpan, SpanKind.CLIENT, validations); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); }); }); - describe('with good plugin options', () => { + describe("with good plugin options", () => { beforeEach(() => { memoryExporter.reset(); }); @@ -188,14 +188,14 @@ describe('HttpsPlugin', () => { before(() => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ - '/ignored/string', + "/ignored/string", /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ], applyCustomAttributesOnSpan: customAttributeFunction, serverName, @@ -203,14 +203,14 @@ describe('HttpsPlugin', () => { plugin.enable((https as unknown) as Http, provider, config); server = https.createServer( { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), + key: fs.readFileSync("test/fixtures/server-key.pem"), + cert: fs.readFileSync("test/fixtures/server-cert.pem"), }, (request, response) => { - if (request.url?.includes('/ignored')) { - tracer.startSpan('some-span').end(); + if (request.url?.includes("/ignored")) { + tracer.startSpan("some-span").end(); } - response.end('Test Server Response'); + response.end("Test Server Response"); } ); @@ -235,13 +235,13 @@ describe('HttpsPlugin', () => { assert.strictEqual(Object.keys(httpsNotPatched).length, 0); }); - it('should generate valid spans (client side and server side)', async () => { + it("should generate valid spans (client side and server side)", async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', + "x-forwarded-for": ", , ", + "user-agent": "chrome", }, } ); @@ -260,15 +260,15 @@ describe('HttpsPlugin', () => { assert.strictEqual(spans.length, 2); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.HTTP_CLIENT_IP], - '' + incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], + "" ); assert.strictEqual( - incomingSpan.attributes[SemanticAttribute.NET_HOST_PORT], + incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], serverPort ); assert.strictEqual( - outgoingSpan.attributes[SemanticAttribute.NET_PEER_PORT], + outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], serverPort ); @@ -277,11 +277,11 @@ describe('HttpsPlugin', () => { { span: outgoingSpan, kind: SpanKind.CLIENT }, ].forEach(({ span, kind }) => { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], + span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, kind, validations); @@ -292,7 +292,7 @@ describe('HttpsPlugin', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; + const testPath = "/outgoing/rootSpan/1"; doNock( hostname, @@ -316,7 +316,7 @@ describe('HttpsPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, @@ -327,10 +327,10 @@ describe('HttpsPlugin', () => { }); } - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; + it("should create a child span for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 200, "Ok"); + const name = "TestRootSpan"; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -342,16 +342,16 @@ describe('HttpsPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -366,14 +366,14 @@ describe('HttpsPlugin', () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; + const testPath = "/outgoing/rootSpan/childs/1"; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = 'TestRootSpan'; + const name = "TestRootSpan"; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -385,16 +385,16 @@ describe('HttpsPlugin', () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: 'GET', + httpMethod: "GET", pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); + assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); + assert.strictEqual(reqSpan.name, "HTTP GET"); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -408,17 +408,17 @@ describe('HttpsPlugin', () => { }); } - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; + it("should create multiple child spans for GET requests", async () => { + const testPath = "/outgoing/rootSpan/childs"; const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; + doNock(hostname, testPath, 200, "Ok", num); + const name = "TestRootSpan"; const span = tracer.startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpsRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); + assert.strictEqual(spans[i].name, "HTTP GET"); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -431,7 +431,7 @@ describe('HttpsPlugin', () => { }); }); - for (const ignored of ['string', 'function', 'regexp']) { + for (const ignored of ["string", "function", "regexp"]) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -443,7 +443,7 @@ describe('HttpsPlugin', () => { }); } - for (const arg of ['string', {}, new Date()]) { + for (const arg of ["string", {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -452,14 +452,14 @@ describe('HttpsPlugin', () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); + assert.ok(error.message.startsWith("Nock: No match for request")); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, '']) { + for (const arg of [true, 1, false, 0, ""]) { it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -470,7 +470,7 @@ describe('HttpsPlugin', () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') + path.normalize("/node_modules/nock/lib/intercept.js") ) > 0 ); } @@ -482,26 +482,26 @@ describe('HttpsPlugin', () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - https.request({ protocol: 'telnet' }); + https.request({ protocol: "telnet" }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -522,7 +522,7 @@ describe('HttpsPlugin', () => { nock.cleanAll(); nock.enableNetConnect(); try { - https.request({ protocol: 'telnet' }); + https.request({ protocol: "telnet" }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -530,19 +530,19 @@ describe('HttpsPlugin', () => { } }); - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); + it("should have 1 ended span when response.end throw an exception", async () => { + const testPath = "/outgoing/rootSpan/childs/1"; + doNock(hostname, testPath, 400, "Not Ok"); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { reject(new Error(data)); }); } @@ -559,28 +559,28 @@ describe('HttpsPlugin', () => { } }); - it('should have 1 ended span when request is aborted', async () => { + it("should have 1 ended span when request is aborted", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .socketDelay(50) - .reply(200, ''); + .reply(200, ""); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject('timeout'); + reject("timeout"); }); return req.end(); }); @@ -597,9 +597,9 @@ describe('HttpsPlugin', () => { } }); - it('should have 1 ended span when request is aborted after receiving response', async () => { + it("should have 1 ended span when request is aborted after receiving response", async () => { nock(`${protocol}://my.server.com`) - .get('/') + .get("/") .delay({ body: 50, }) @@ -609,12 +609,12 @@ describe('HttpsPlugin', () => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { + let data = ""; + resp.on("data", (chunk) => { req.abort(); data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { resolve(data); }); } @@ -635,19 +635,19 @@ describe('HttpsPlugin', () => { } }); - it("should have 1 ended span when response is listened by using req.on('response')", done => { + it("should have 1 ended span when response is listened by using req.on('response')", (done) => { const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); + nock(host).get("/").reply(404); const req = https.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { + req.on("response", (response) => { + response.on("data", () => {}); + response.on("end", () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); assert.ok(Object.keys(span.attributes).length > 6); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], 404 ); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts index 6f4227ce8a0..7f805bdd67b 100644 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -14,58 +14,58 @@ * limitations under the License. */ -import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; -import { SpanKind, Span, context } from '@opentelemetry/api'; +import { HttpPluginConfig, Http } from "@opentelemetry/plugin-http"; +import { SpanKind, Span, context } from "@opentelemetry/api"; import { HttpFlavorValues, NetTransportValues, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as https from 'https'; -import { plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; -import * as url from 'url'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; + SemanticAttributes, +} from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import * as fs from "fs"; +import * as path from "path"; +import * as https from "https"; +import { plugin } from "../../src/https"; +import { assertSpan } from "../utils/assertSpan"; +import { DummyPropagation } from "../utils/DummyPropagation"; +import { httpsRequest } from "../utils/httpsRequest"; +import * as url from "url"; +import * as utils from "../utils/utils"; +import { NodeTracerProvider } from "@opentelemetry/node"; import { InMemorySpanExporter, SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; +} from "@opentelemetry/tracing"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { Socket } from "net"; -const protocol = 'https'; +const protocol = "https"; const serverPort = 42345; -const hostname = 'localhost'; +const hostname = "localhost"; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); + span.setAttribute("span kind", SpanKind.CLIENT); }; -describe('HttpsPlugin Integration tests', () => { +describe("HttpsPlugin Integration tests", () => { let mockServerPort = 0; let mockServer: https.Server; const sockets: Array = []; - before(done => { + before((done) => { mockServer = https.createServer( { key: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-key.pem') + path.join(__dirname, "..", "fixtures", "server-key.pem") ), cert: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-cert.pem') + path.join(__dirname, "..", "fixtures", "server-cert.pem") ), }, (req, res) => { res.statusCode = 200; - res.setHeader('content-type', 'application/json'); + res.setHeader("content-type", "application/json"); res.write( JSON.stringify({ success: true, @@ -78,17 +78,17 @@ describe('HttpsPlugin Integration tests', () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error('unexpected addr null')); + done(new Error("unexpected addr null")); return; } - if (typeof addr === 'string') { + if (typeof addr === "string") { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error('Could not get port')); + done(new Error("Could not get port")); return; } mockServerPort = addr.port; @@ -96,8 +96,8 @@ describe('HttpsPlugin Integration tests', () => { }); }); - after(done => { - sockets.forEach(s => s.destroy()); + after((done) => { + sockets.forEach((s) => s.destroy()); mockServer.close(done); }); @@ -110,7 +110,7 @@ describe('HttpsPlugin Integration tests', () => { context.disable(); }); - describe('enable()', () => { + describe("enable()", () => { before(function (done) { // mandatory if (process.env.CI) { @@ -118,7 +118,7 @@ describe('HttpsPlugin Integration tests', () => { return; } - utils.checkInternet(isConnected => { + utils.checkInternet((isConnected) => { if (!isConnected) { this.skip(); // don't disturb people @@ -136,7 +136,7 @@ describe('HttpsPlugin Integration tests', () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), + (url: string) => url.endsWith("/ignored/function"), ]; const config: HttpPluginConfig = { ignoreIncomingPaths: ignoreConfig, @@ -153,7 +153,7 @@ describe('HttpsPlugin Integration tests', () => { plugin.disable(); }); - it('should create a rootSpan for GET requests and add propagation headers', async () => { + it("should create a rootSpan for GET requests and add propagation headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -162,25 +162,25 @@ describe('HttpsPlugin Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { + it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -189,119 +189,119 @@ describe('HttpsPlugin Integration tests', () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { + it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpsRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { 'x-foo': 'foo' }, + headers: { "x-foo": "foo" }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', + httpMethod: "GET", + pathname: "/", + path: "/?query=test", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(result.reqHeaders["x-foo"], "foo"); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_FLAVOR], + span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 ); assert.strictEqual( - span.attributes[SemanticAttribute.NET_TRANSPORT], + span.attributes[SemanticAttributes.NET_TRANSPORT], NetTransportValues.IP_TCP ); assertSpan(span, SpanKind.CLIENT, validations); }); - it('custom attributes should show up on client spans', async () => { + it("custom attributes should show up on client spans", async () => { const result = await httpsRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); + assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { + it("should create a span for GET requests and add propagation headers with Expect headers", async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: '100-continue' } }, + { headers: { Expect: "100-continue" } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpsRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, + { Expect: "100-continue", "user-agent": "http-plugin-test" }, + { "user-agent": "http-plugin-test" }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, done => { + )}`, (done) => { let validations: { hostname: string; httpStatusCode: number; @@ -310,7 +310,7 @@ describe('HttpsPlugin Integration tests', () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ''; + let data = ""; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -322,15 +322,15 @@ describe('HttpsPlugin Integration tests', () => { req: http.IncomingMessage; }; - resp.on('data', chunk => { + resp.on("data", (chunk) => { data += chunk; }); - resp.on('end', () => { + resp.on("end", () => { validations = { - hostname: 'localhost', + hostname: "localhost", httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', + httpMethod: "GET", + pathname: "/", resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -342,12 +342,12 @@ describe('HttpsPlugin Integration tests', () => { } ); - req.on('close', () => { + req.on("close", () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); + const span = spans.find((s) => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.name, "HTTP GET"); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts index 79af0a121fd..f4e7e95683a 100644 --- a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { SpanKind } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { DummyPropagation } from './DummyPropagation'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { parseResponseStatus } from '@opentelemetry/plugin-http'; +import { SpanKind } from "@opentelemetry/api"; +import { hrTimeToNanoseconds } from "@opentelemetry/core"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import * as http from "http"; +import { DummyPropagation } from "./DummyPropagation"; +import { ReadableSpan } from "@opentelemetry/tracing"; +import { parseResponseStatus } from "@opentelemetry/plugin-http"; +import { AttributeNames } from "../../src/enums"; export const assertSpan = ( span: ReadableSpan, @@ -43,19 +44,19 @@ export const assertSpan = ( assert.strictEqual(span.kind, kind); assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_ERROR_MESSAGE], + span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], span.status.message ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_METHOD], + span.attributes[SemanticAttributes.HTTP_METHOD], validations.httpMethod ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_TARGET], + span.attributes[SemanticAttributes.HTTP_TARGET], validations.path || validations.pathname ); assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_STATUS_CODE], + span.attributes[SemanticAttributes.HTTP_STATUS_CODE], validations.httpStatusCode ); assert.ok(span.endTime); @@ -66,54 +67,54 @@ export const assertSpan = ( parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); + assert.ok(span.endTime, "must be finished"); + assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; + const userAgent = validations.reqHeaders["user-agent"]; if (userAgent) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_USER_AGENT], + span.attributes[SemanticAttributes.HTTP_USER_AGENT], userAgent ); } } if (span.kind === SpanKind.CLIENT) { assert.strictEqual( - span.attributes[SemanticAttribute.NET_PEER_NAME], + span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - 'must be consistent (PEER_NAME and hostname)' + "must be consistent (PEER_NAME and hostname)" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_IP], - 'must have PEER_IP' + span.attributes[SemanticAttributes.NET_PEER_IP], + "must have PEER_IP" ); assert.ok( - span.attributes[SemanticAttribute.NET_PEER_PORT], - 'must have PEER_PORT' + span.attributes[SemanticAttributes.NET_PEER_PORT], + "must have PEER_PORT" ); assert.ok( - (span.attributes[SemanticAttribute.HTTP_URL] as string).indexOf( - span.attributes[SemanticAttribute.NET_PEER_NAME] as string + (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( + span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - 'must be consistent' + "must be consistent" ); } if (span.kind === SpanKind.SERVER) { if (validations.serverName) { assert.strictEqual( - span.attributes[SemanticAttribute.HTTP_SERVER_NAME], + span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - ' must have serverName attribute' + " must have serverName attribute" ); } assert.ok( - span.attributes[SemanticAttribute.NET_HOST_PORT], - 'must have HOST_PORT' + span.attributes[SemanticAttributes.NET_HOST_PORT], + "must have HOST_PORT" ); assert.ok( - span.attributes[SemanticAttribute.NET_HOST_IP], - 'must have HOST_IP' + span.attributes[SemanticAttributes.NET_HOST_IP], + "must have HOST_IP" ); assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); } else if (validations.reqHeaders) { diff --git a/packages/opentelemetry-semantic-conventions/src/index.ts b/packages/opentelemetry-semantic-conventions/src/index.ts index 3d979fd4df7..6f9f387e23c 100644 --- a/packages/opentelemetry-semantic-conventions/src/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/index.ts @@ -16,4 +16,3 @@ export * from './trace'; export * from './resource'; -export * from './events'; diff --git a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts similarity index 99% rename from packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts rename to packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts index d9ccb3e76c7..5c719f07c50 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/ResourceAttributes.ts @@ -15,7 +15,7 @@ */ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 -export const ResourceAttribute = { +export const ResourceAttributes = { /** * Name of the cloud provider. */ diff --git a/packages/opentelemetry-semantic-conventions/src/resource/index.ts b/packages/opentelemetry-semantic-conventions/src/resource/index.ts index 34ed6e77941..e3810b5eeb5 100644 --- a/packages/opentelemetry-semantic-conventions/src/resource/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/resource/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './ResourceAttribute'; +export * from './ResourceAttributes'; diff --git a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts similarity index 98% rename from packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts rename to packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts index 32d7b2318f9..9f756902fd1 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttribute.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/SemanticAttributes.ts @@ -15,7 +15,7 @@ */ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates//templates/SemanticAttributes.ts.j2 -export const SemanticAttribute = { +export const SemanticAttributes = { /** * An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers. */ @@ -57,26 +57,6 @@ export const SemanticAttribute = { */ DB_OPERATION: 'db.operation', - /** - * Remote hostname or similar, see note below. - */ - NET_PEER_NAME: 'net.peer.name', - - /** - * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). - */ - NET_PEER_IP: 'net.peer.ip', - - /** - * Remote port number. - */ - NET_PEER_PORT: 'net.peer.port', - - /** - * Transport protocol used. See note below. - */ - NET_TRANSPORT: 'net.transport', - /** * The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance. * @@ -216,6 +196,127 @@ clear whether the exception will escape. */ FAAS_DOCUMENT_NAME: 'faas.document.name', + /** + * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). + */ + FAAS_TIME: 'faas.time', + + /** + * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). + */ + FAAS_CRON: 'faas.cron', + + /** + * A boolean that is true if the serverless function is executed for the first time (aka cold-start). + */ + FAAS_COLDSTART: 'faas.coldstart', + + /** + * The name of the invoked function. + * + * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. + */ + FAAS_INVOKED_NAME: 'faas.invoked_name', + + /** + * The cloud provider of the invoked function. + * + * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. + */ + FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', + + /** + * The cloud region of the invoked function. + * + * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. + */ + FAAS_INVOKED_REGION: 'faas.invoked_region', + + /** + * Transport protocol used. See note below. + */ + NET_TRANSPORT: 'net.transport', + + /** + * Remote address of the peer (dotted decimal for IPv4 or [RFC5952](https://tools.ietf.org/html/rfc5952) for IPv6). + */ + NET_PEER_IP: 'net.peer.ip', + + /** + * Remote port number. + */ + NET_PEER_PORT: 'net.peer.port', + + /** + * Remote hostname or similar, see note below. + */ + NET_PEER_NAME: 'net.peer.name', + + /** + * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. + */ + NET_HOST_IP: 'net.host.ip', + + /** + * Like `net.peer.port` but for the host port. + */ + NET_HOST_PORT: 'net.host.port', + + /** + * Local hostname or similar, see note below. + */ + NET_HOST_NAME: 'net.host.name', + + /** + * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. + */ + PEER_SERVICE: 'peer.service', + + /** + * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. + */ + ENDUSER_ID: 'enduser.id', + + /** + * Actual/assumed role the client is making the request under extracted from token or application security context. + */ + ENDUSER_ROLE: 'enduser.role', + + /** + * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). + */ + ENDUSER_SCOPE: 'enduser.scope', + + /** + * Current "managed" thread ID (as opposed to OS thread ID). + */ + THREAD_ID: 'thread.id', + + /** + * Current thread name. + */ + THREAD_NAME: 'thread.name', + + /** + * The method or function name, or equivalent (usually rightmost part of the code unit's name). + */ + CODE_FUNCTION: 'code.function', + + /** + * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + */ + CODE_NAMESPACE: 'code.namespace', + + /** + * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + */ + CODE_FILEPATH: 'code.filepath', + + /** + * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + */ + CODE_LINENO: 'code.lineno', + /** * HTTP request method. */ @@ -301,21 +402,6 @@ clear whether the exception will escape. */ HTTP_CLIENT_IP: 'http.client_ip', - /** - * Like `net.peer.ip` but for the host IP. Useful in case of a multi-IP host. - */ - NET_HOST_IP: 'net.host.ip', - - /** - * Like `net.peer.port` but for the host port. - */ - NET_HOST_PORT: 'net.host.port', - - /** - * Local hostname or similar, see note below. - */ - NET_HOST_NAME: 'net.host.name', - /** * A string identifying the messaging system. */ @@ -372,92 +458,6 @@ clear whether the exception will escape. MESSAGING_MESSAGE_PAYLOAD_COMPRESSED_SIZE_BYTES: 'messaging.message_payload_compressed_size_bytes', - /** - * A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). - */ - FAAS_TIME: 'faas.time', - - /** - * A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). - */ - FAAS_CRON: 'faas.cron', - - /** - * A boolean that is true if the serverless function is executed for the first time (aka cold-start). - */ - FAAS_COLDSTART: 'faas.coldstart', - - /** - * The name of the invoked function. - * - * Note: SHOULD be equal to the `faas.name` resource attribute of the invoked function. - */ - FAAS_INVOKED_NAME: 'faas.invoked_name', - - /** - * The cloud provider of the invoked function. - * - * Note: SHOULD be equal to the `cloud.provider` resource attribute of the invoked function. - */ - FAAS_INVOKED_PROVIDER: 'faas.invoked_provider', - - /** - * The cloud region of the invoked function. - * - * Note: SHOULD be equal to the `cloud.region` resource attribute of the invoked function. - */ - FAAS_INVOKED_REGION: 'faas.invoked_region', - - /** - * The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any. - */ - PEER_SERVICE: 'peer.service', - - /** - * Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system. - */ - ENDUSER_ID: 'enduser.id', - - /** - * Actual/assumed role the client is making the request under extracted from token or application security context. - */ - ENDUSER_ROLE: 'enduser.role', - - /** - * Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). - */ - ENDUSER_SCOPE: 'enduser.scope', - - /** - * Current "managed" thread ID (as opposed to OS thread ID). - */ - THREAD_ID: 'thread.id', - - /** - * Current thread name. - */ - THREAD_NAME: 'thread.name', - - /** - * The method or function name, or equivalent (usually rightmost part of the code unit's name). - */ - CODE_FUNCTION: 'code.function', - - /** - * The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. - */ - CODE_NAMESPACE: 'code.namespace', - - /** - * The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). - */ - CODE_FILEPATH: 'code.filepath', - - /** - * The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. - */ - CODE_LINENO: 'code.lineno', - /** * A string identifying the kind of message consumption as defined in the [Operation names](#operation-names) section above. If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. */ @@ -509,20 +509,6 @@ clear whether the exception will escape. * The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. */ RPC_GRPC_STATUS_CODE: 'rpc.grpc.status_code', - - // Manually defined and not YET in the YAML - - // HTTP - HTTP_ERROR_NAME: 'http.error_name', - HTTP_ERROR_MESSAGE: 'http.error_message', - HTTP_STATUS_TEXT: 'http.status_text', - - // GRPC - GRPC_KIND: 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD: 'grpc.method', - GRPC_STATUS_CODE: 'grpc.status_code', - GRPC_ERROR_NAME: 'grpc.error_name', - GRPC_ERROR_MESSAGE: 'grpc.error_message', }; // Enum definitions @@ -620,22 +606,7 @@ export enum DbSystemValues { ELASTICSEARCH = 'elasticsearch', } -export enum NetTransportValues { - /** IP.TCP. */ - IP_TCP = 'IP.TCP', - /** IP.UDP. */ - IP_UDP = 'IP.UDP', - /** Another IP-based protocol. */ - IP = 'IP', - /** Unix Domain socket. See below. */ - UNIX = 'Unix', - /** Named or anonymous pipe. See note below. */ - PIPE = 'pipe', - /** In-process communication. */ - INPROC = 'inproc', - /** Something else (non IP-based). */ - OTHER = 'other', -} +export enum NetTransportValues {} export enum DbCassandraConsistencyLevelValues { /** ALL. */ @@ -684,6 +655,32 @@ export enum FaasDocumentOperationValues { DELETE = 'delete', } +export enum FaasInvokedProviderValues { + /** Amazon Web Services. */ + AWS = 'aws', + /** Amazon Web Services. */ + AZURE = 'azure', + /** Google Cloud Platform. */ + GCP = 'gcp', +} + +export enum NetTransportValues { + /** IP.TCP. */ + IP_TCP = 'IP.TCP', + /** IP.UDP. */ + IP_UDP = 'IP.UDP', + /** Another IP-based protocol. */ + IP = 'IP', + /** Unix Domain socket. See below. */ + UNIX = 'Unix', + /** Named or anonymous pipe. See note below. */ + PIPE = 'pipe', + /** In-process communication. */ + INPROC = 'inproc', + /** Something else (non IP-based). */ + OTHER = 'other', +} + export enum HttpFlavorValues { /** HTTP 1.0. */ HTTP_1_0 = '1.0', @@ -704,15 +701,6 @@ export enum MessagingDestinationKindValues { TOPIC = 'topic', } -export enum FaasInvokedProviderValues { - /** Amazon Web Services. */ - AWS = 'aws', - /** Amazon Web Services. */ - AZURE = 'azure', - /** Google Cloud Platform. */ - GCP = 'gcp', -} - export enum MessagingOperationValues { /** receive. */ RECEIVE = 'receive', @@ -720,6 +708,8 @@ export enum MessagingOperationValues { PROCESS = 'process', } +export enum NetTransportValues {} + export enum RpcGrpcStatusCodeValues { /** OK. */ OK = 0, diff --git a/packages/opentelemetry-semantic-conventions/src/trace/index.ts b/packages/opentelemetry-semantic-conventions/src/trace/index.ts index 71503260391..aa5c1024276 100644 --- a/packages/opentelemetry-semantic-conventions/src/trace/index.ts +++ b/packages/opentelemetry-semantic-conventions/src/trace/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './SemanticAttribute'; +export * from './SemanticAttributes'; diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index c5d720c4608..09cf81bb8d7 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as api from '@opentelemetry/api'; +import * as api from "@opentelemetry/api"; import { isAttributeValue, hrTime, @@ -22,17 +22,15 @@ import { InstrumentationLibrary, isTimeInput, timeInputToHrTime, -} from '@opentelemetry/core'; -import { Resource } from '@opentelemetry/resources'; -import { - ExceptionEventName, - SemanticAttribute, -} from '@opentelemetry/semantic-conventions'; -import { ReadableSpan } from './export/ReadableSpan'; -import { Tracer } from './Tracer'; -import { SpanProcessor } from './SpanProcessor'; -import { TraceParams } from './types'; -import { SpanAttributeValue, Context } from '@opentelemetry/api'; +} from "@opentelemetry/core"; +import { Resource } from "@opentelemetry/resources"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import { ReadableSpan } from "./export/ReadableSpan"; +import { Tracer } from "./Tracer"; +import { SpanProcessor } from "./SpanProcessor"; +import { TraceParams } from "./types"; +import { SpanAttributeValue, Context } from "@opentelemetry/api"; +import { ExceptionEventName } from "./enums"; /** * This class represents a span. @@ -131,16 +129,16 @@ export class Span implements api.Span, ReadableSpan { ): this { if (this._isSpanEnded()) return this; if (this.events.length >= this._traceParams.numberOfEventsPerSpan!) { - api.diag.warn('Dropping extra events.'); + api.diag.warn("Dropping extra events."); this.events.shift(); } if (isTimeInput(attributesOrStartTime)) { - if (typeof startTime === 'undefined') { + if (typeof startTime === "undefined") { startTime = attributesOrStartTime as api.TimeInput; } attributesOrStartTime = undefined; } - if (typeof startTime === 'undefined') { + if (typeof startTime === "undefined") { startTime = hrTime(); } this.events.push({ @@ -165,7 +163,7 @@ export class Span implements api.Span, ReadableSpan { end(endTime: api.TimeInput = hrTime()): void { if (this._isSpanEnded()) { - api.diag.error('You can only call end() on a span once.'); + api.diag.error("You can only call end() on a span once."); return; } this._ended = true; @@ -174,7 +172,7 @@ export class Span implements api.Span, ReadableSpan { this._duration = hrTimeDuration(this.startTime, this.endTime); if (this._duration[0] < 0) { api.diag.warn( - 'Inconsistent start and end time, startTime > endTime', + "Inconsistent start and end time, startTime > endTime", this.startTime, this.endTime ); @@ -189,26 +187,26 @@ export class Span implements api.Span, ReadableSpan { recordException(exception: api.Exception, time: api.TimeInput = hrTime()) { const attributes: api.SpanAttributes = {}; - if (typeof exception === 'string') { - attributes[SemanticAttribute.EXCEPTION_MESSAGE] = exception; + if (typeof exception === "string") { + attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception; } else if (exception) { if (exception.code) { - attributes[SemanticAttribute.EXCEPTION_TYPE] = exception.code; + attributes[SemanticAttributes.EXCEPTION_TYPE] = exception.code; } else if (exception.name) { - attributes[SemanticAttribute.EXCEPTION_TYPE] = exception.name; + attributes[SemanticAttributes.EXCEPTION_TYPE] = exception.name; } if (exception.message) { - attributes[SemanticAttribute.EXCEPTION_MESSAGE] = exception.message; + attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception.message; } if (exception.stack) { - attributes[SemanticAttribute.EXCEPTION_STACKTRACE] = exception.stack; + attributes[SemanticAttributes.EXCEPTION_STACKTRACE] = exception.stack; } } // these are minimum requirements from spec if ( - attributes[SemanticAttribute.EXCEPTION_TYPE] || - attributes[SemanticAttribute.EXCEPTION_MESSAGE] + attributes[SemanticAttributes.EXCEPTION_TYPE] || + attributes[SemanticAttributes.EXCEPTION_MESSAGE] ) { this.addEvent(ExceptionEventName, attributes as api.SpanAttributes, time); } else { @@ -227,7 +225,7 @@ export class Span implements api.Span, ReadableSpan { private _isSpanEnded(): boolean { if (this._ended) { api.diag.warn( - 'Can not execute the operation on ended Span {traceId: %s, spanId: %s}', + "Can not execute the operation on ended Span {traceId: %s, spanId: %s}", this.spanContext.traceId, this.spanContext.spanId ); diff --git a/packages/opentelemetry-semantic-conventions/src/events.ts b/packages/opentelemetry-tracing/src/enums.ts similarity index 93% rename from packages/opentelemetry-semantic-conventions/src/events.ts rename to packages/opentelemetry-tracing/src/enums.ts index af2909dc68e..1b766da3d65 100644 --- a/packages/opentelemetry-semantic-conventions/src/events.ts +++ b/packages/opentelemetry-tracing/src/enums.ts @@ -15,4 +15,4 @@ */ // Event name definitions -export const ExceptionEventName = 'exception'; +export const ExceptionEventName = "exception"; diff --git a/packages/opentelemetry-tracing/src/index.ts b/packages/opentelemetry-tracing/src/index.ts index 720241a3107..6ae68127539 100644 --- a/packages/opentelemetry-tracing/src/index.ts +++ b/packages/opentelemetry-tracing/src/index.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -export * from './Tracer'; -export * from './BasicTracerProvider'; -export * from './export/ConsoleSpanExporter'; -export * from './export/BatchSpanProcessor'; -export * from './export/InMemorySpanExporter'; -export * from './export/ReadableSpan'; -export * from './export/SimpleSpanProcessor'; -export * from './export/SpanExporter'; -export * from './Span'; -export * from './SpanProcessor'; -export * from './types'; +export * from "./Tracer"; +export * from "./BasicTracerProvider"; +export * from "./export/ConsoleSpanExporter"; +export * from "./export/BatchSpanProcessor"; +export * from "./export/InMemorySpanExporter"; +export * from "./export/ReadableSpan"; +export * from "./export/SimpleSpanProcessor"; +export * from "./export/SpanExporter"; +export * from "./Span"; +export * from "./SpanProcessor"; +export * from "./types"; diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 672f2592c8a..51b4efbabf6 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -22,38 +22,38 @@ import { SpanContext, SpanKind, TraceFlags, -} from '@opentelemetry/api'; +} from "@opentelemetry/api"; import { hrTime, hrTimeDuration, hrTimeToMilliseconds, hrTimeToNanoseconds, -} from '@opentelemetry/core'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import { BasicTracerProvider, Span, SpanProcessor } from '../src'; +} from "@opentelemetry/core"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import * as assert from "assert"; +import { BasicTracerProvider, Span, SpanProcessor } from "../src"; const performanceTimeOrigin = hrTime(); -describe('Span', () => { +describe("Span", () => { const tracer = new BasicTracerProvider({ traceParams: { numberOfAttributesPerSpan: 100, numberOfEventsPerSpan: 100, }, - }).getTracer('default'); - const name = 'span1'; + }).getTracer("default"); + const name = "span1"; const spanContext: SpanContext = { - traceId: 'd4cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', + traceId: "d4cda95b652f4a1592b449d5929fda1b", + spanId: "6e0c63257de34c92", traceFlags: TraceFlags.SAMPLED, }; const linkContext: LinkContext = { - traceId: 'e4cda95b652f4a1592b449d5929fda1b', - spanId: '7e0c63257de34c92', + traceId: "e4cda95b652f4a1592b449d5929fda1b", + spanId: "7e0c63257de34c92", }; - it('should create a Span instance', () => { + it("should create a Span instance", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -65,7 +65,7 @@ describe('Span', () => { span.end(); }); - it('should have valid startTime', () => { + it("should have valid startTime", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -79,7 +79,7 @@ describe('Span', () => { ); }); - it('should have valid endTime', () => { + it("should have valid endTime", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -90,17 +90,17 @@ describe('Span', () => { span.end(); assert.ok( hrTimeToNanoseconds(span.endTime) >= hrTimeToNanoseconds(span.startTime), - 'end time must be bigger or equal start time' + "end time must be bigger or equal start time" ); assert.ok( hrTimeToMilliseconds(span.endTime) > hrTimeToMilliseconds(performanceTimeOrigin), - 'end time must be bigger than time origin' + "end time must be bigger than time origin" ); }); - it('should have a duration', () => { + it("should have a duration", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -112,7 +112,7 @@ describe('Span', () => { assert.ok(hrTimeToNanoseconds(span.duration) >= 0); }); - it('should have valid event.time', () => { + it("should have valid event.time", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -120,14 +120,14 @@ describe('Span', () => { spanContext, SpanKind.SERVER ); - span.addEvent('my-event'); + span.addEvent("my-event"); assert.ok( hrTimeToMilliseconds(span.events[0].time) > hrTimeToMilliseconds(performanceTimeOrigin) ); }); - it('should have an entered time for event', () => { + it("should have an entered time for event", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -142,14 +142,14 @@ describe('Span', () => { const spanStartTime = hrTimeToMilliseconds(span.startTime); const eventTime = spanStartTime + timeMS; - span.addEvent('my-event', undefined, eventTime); + span.addEvent("my-event", undefined, eventTime); const diff = hrTimeDuration(span.startTime, span.events[0].time); assert.strictEqual(hrTimeToMilliseconds(diff), 123); }); describe('when 2nd param is "TimeInput" type', () => { - it('should have an entered time for event - ', () => { + it("should have an entered time for event - ", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -164,14 +164,14 @@ describe('Span', () => { const spanStartTime = hrTimeToMilliseconds(span.startTime); const eventTime = spanStartTime + timeMS; - span.addEvent('my-event', eventTime); + span.addEvent("my-event", eventTime); const diff = hrTimeDuration(span.startTime, span.events[0].time); assert.strictEqual(hrTimeToMilliseconds(diff), 123); }); }); - it('should get the span context of span', () => { + it("should get the span context of span", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -188,8 +188,8 @@ describe('Span', () => { span.end(); }); - describe('isRecording', () => { - it('should return true when span is not ended', () => { + describe("isRecording", () => { + it("should return true when span is not ended", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -200,7 +200,7 @@ describe('Span', () => { assert.ok(span.isRecording()); span.end(); }); - it('should return false when span is ended', () => { + it("should return false when span is ended", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -213,7 +213,7 @@ describe('Span', () => { }); }); - it('should set an attribute', () => { + it("should set an attribute", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -222,29 +222,29 @@ describe('Span', () => { SpanKind.CLIENT ); - span.setAttribute('string', 'string'); - span.setAttribute('number', 0); - span.setAttribute('bool', true); - span.setAttribute('array', ['str1', 'str2']); - span.setAttribute('array', [1, 2]); - span.setAttribute('array', [true, false]); + span.setAttribute("string", "string"); + span.setAttribute("number", 0); + span.setAttribute("bool", true); + span.setAttribute("array", ["str1", "str2"]); + span.setAttribute("array", [1, 2]); + span.setAttribute("array", [true, false]); //@ts-expect-error invalid attribute type object - span.setAttribute('object', { foo: 'bar' }); + span.setAttribute("object", { foo: "bar" }); //@ts-expect-error invalid attribute inhomogenous array - span.setAttribute('non-homogeneous-array', [0, '']); + span.setAttribute("non-homogeneous-array", [0, ""]); assert.deepStrictEqual(span.attributes, { - string: 'string', + string: "string", number: 0, bool: true, - 'array': ['str1', 'str2'], - 'array': [1, 2], - 'array': [true, false], + "array": ["str1", "str2"], + "array": [1, 2], + "array": [true, false], }); }); - it('should overwrite attributes', () => { + it("should overwrite attributes", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -253,15 +253,15 @@ describe('Span', () => { SpanKind.CLIENT ); - span.setAttribute('overwrite', 'initial value'); - span.setAttribute('overwrite', 'overwritten value'); + span.setAttribute("overwrite", "initial value"); + span.setAttribute("overwrite", "overwritten value"); assert.deepStrictEqual(span.attributes, { - overwrite: 'overwritten value', + overwrite: "overwritten value", }); }); - it('should set attributes', () => { + it("should set attributes", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -271,29 +271,29 @@ describe('Span', () => { ); span.setAttributes({ - string: 'string', + string: "string", number: 0, bool: true, - 'array': ['str1', 'str2'], - 'array': [1, 2], - 'array': [true, false], + "array": ["str1", "str2"], + "array": [1, 2], + "array": [true, false], //@ts-expect-error invalid attribute type object - object: { foo: 'bar' }, + object: { foo: "bar" }, //@ts-expect-error invalid attribute inhomogenous array - 'non-homogeneous-array': [0, ''], + "non-homogeneous-array": [0, ""], }); assert.deepStrictEqual(span.attributes, { - string: 'string', + string: "string", number: 0, bool: true, - 'array': ['str1', 'str2'], - 'array': [1, 2], - 'array': [true, false], + "array": ["str1", "str2"], + "array": [1, 2], + "array": [true, false], }); }); - it('should set an event', () => { + it("should set an event", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -301,35 +301,35 @@ describe('Span', () => { spanContext, SpanKind.CLIENT ); - span.addEvent('sent'); - span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true }); + span.addEvent("sent"); + span.addEvent("rev", { attr1: "value", attr2: 123, attr3: true }); span.end(); }); - it('should set a link', () => { + it("should set a link", () => { const spanContext: SpanContext = { - traceId: 'a3cda95b652f4a1592b449d5929fda1b', - spanId: '5e0c63257de34c92', + traceId: "a3cda95b652f4a1592b449d5929fda1b", + spanId: "5e0c63257de34c92", traceFlags: TraceFlags.SAMPLED, }; const linkContext: LinkContext = { - traceId: 'b3cda95b652f4a1592b449d5929fda1b', - spanId: '6e0c63257de34c92', + traceId: "b3cda95b652f4a1592b449d5929fda1b", + spanId: "6e0c63257de34c92", }; - const attributes = { attr1: 'value', attr2: 123, attr3: true }; + const attributes = { attr1: "value", attr2: 123, attr3: true }; const span = new Span( tracer, ROOT_CONTEXT, name, spanContext, SpanKind.CLIENT, - '12345', + "12345", [{ context: linkContext }, { context: linkContext, attributes }] ); span.end(); }); - it('should drop extra attributes and events', () => { + it("should drop extra attributes and events", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -338,20 +338,20 @@ describe('Span', () => { SpanKind.CLIENT ); for (let i = 0; i < 150; i++) { - span.setAttribute('foo' + i, 'bar' + i); - span.addEvent('sent' + i); + span.setAttribute("foo" + i, "bar" + i); + span.addEvent("sent" + i); } span.end(); assert.strictEqual(span.events.length, 100); assert.strictEqual(Object.keys(span.attributes).length, 100); - assert.strictEqual(span.events[span.events.length - 1].name, 'sent149'); - assert.strictEqual(span.attributes['foo0'], 'bar0'); - assert.strictEqual(span.attributes['foo99'], 'bar99'); - assert.strictEqual(span.attributes['sent100'], undefined); + assert.strictEqual(span.events[span.events.length - 1].name, "sent149"); + assert.strictEqual(span.attributes["foo0"], "bar0"); + assert.strictEqual(span.attributes["foo99"], "bar99"); + assert.strictEqual(span.attributes["sent100"], undefined); }); - it('should set an error status', () => { + it("should set an error status", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -361,23 +361,23 @@ describe('Span', () => { ); span.setStatus({ code: SpanStatusCode.ERROR, - message: 'This is an error', + message: "This is an error", }); span.end(); }); - it('should return ReadableSpan', () => { - const parentId = '5c1c63257de34c67'; + it("should return ReadableSpan", () => { + const parentId = "5c1c63257de34c67"; const span = new Span( tracer, ROOT_CONTEXT, - 'my-span', + "my-span", spanContext, SpanKind.INTERNAL, parentId ); - assert.strictEqual(span.name, 'my-span'); + assert.strictEqual(span.name, "my-span"); assert.strictEqual(span.kind, SpanKind.INTERNAL); assert.strictEqual(span.parentSpanId, parentId); assert.strictEqual(span.spanContext.traceId, spanContext.traceId); @@ -390,20 +390,20 @@ describe('Span', () => { assert.ok(span.instrumentationLibrary); const { name, version } = span.instrumentationLibrary; - assert.strictEqual(name, 'default'); + assert.strictEqual(name, "default"); assert.strictEqual(version, undefined); }); - it('should return ReadableSpan with attributes', () => { + it("should return ReadableSpan with attributes", () => { const span = new Span( tracer, ROOT_CONTEXT, - 'my-span', + "my-span", spanContext, SpanKind.CLIENT ); - span.setAttribute('attr1', 'value1'); - assert.deepStrictEqual(span.attributes, { attr1: 'value1' }); + span.setAttribute("attr1", "value1"); + assert.deepStrictEqual(span.attributes, { attr1: "value1" }); span.setAttributes({ attr2: 123, attr1: false }); assert.deepStrictEqual(span.attributes, { @@ -413,18 +413,18 @@ describe('Span', () => { span.end(); // shouldn't add new attribute - span.setAttribute('attr3', 'value3'); + span.setAttribute("attr3", "value3"); assert.deepStrictEqual(span.attributes, { attr1: false, attr2: 123, }); }); - it('should return ReadableSpan with links', () => { + it("should return ReadableSpan with links", () => { const span = new Span( tracer, ROOT_CONTEXT, - 'my-span', + "my-span", spanContext, SpanKind.CLIENT, undefined, @@ -432,7 +432,7 @@ describe('Span', () => { { context: linkContext }, { context: linkContext, - attributes: { attr1: 'value', attr2: 123, attr3: true }, + attributes: { attr1: "value", attr2: 123, attr3: true }, }, ] ); @@ -442,7 +442,7 @@ describe('Span', () => { context: linkContext, }, { - attributes: { attr1: 'value', attr2: 123, attr3: true }, + attributes: { attr1: "value", attr2: 123, attr3: true }, context: linkContext, }, ]); @@ -450,30 +450,30 @@ describe('Span', () => { span.end(); }); - it('should return ReadableSpan with events', () => { + it("should return ReadableSpan with events", () => { const span = new Span( tracer, ROOT_CONTEXT, - 'my-span', + "my-span", spanContext, SpanKind.CLIENT ); - span.addEvent('sent'); + span.addEvent("sent"); assert.strictEqual(span.events.length, 1); const [event] = span.events; - assert.deepStrictEqual(event.name, 'sent'); + assert.deepStrictEqual(event.name, "sent"); assert.ok(!event.attributes); assert.ok(event.time[0] > 0); - span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true }); + span.addEvent("rev", { attr1: "value", attr2: 123, attr3: true }); assert.strictEqual(span.events.length, 2); const [event1, event2] = span.events; - assert.deepStrictEqual(event1.name, 'sent'); + assert.deepStrictEqual(event1.name, "sent"); assert.ok(!event1.attributes); assert.ok(event1.time[0] > 0); - assert.deepStrictEqual(event2.name, 'rev'); + assert.deepStrictEqual(event2.name, "rev"); assert.deepStrictEqual(event2.attributes, { - attr1: 'value', + attr1: "value", attr2: 123, attr3: true, }); @@ -481,11 +481,11 @@ describe('Span', () => { span.end(); // shouldn't add new event - span.addEvent('sent'); + span.addEvent("sent"); assert.strictEqual(span.events.length, 2); }); - it('should return ReadableSpan with new status', () => { + it("should return ReadableSpan with new status", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -495,21 +495,21 @@ describe('Span', () => { ); span.setStatus({ code: SpanStatusCode.ERROR, - message: 'This is an error', + message: "This is an error", }); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.strictEqual(span.status.message, 'This is an error'); + assert.strictEqual(span.status.message, "This is an error"); span.end(); // shouldn't update status span.setStatus({ code: SpanStatusCode.OK, - message: 'OK', + message: "OK", }); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); }); - it('should only end a span once', () => { + it("should only end a span once", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -523,7 +523,7 @@ describe('Span', () => { assert.deepStrictEqual(span.endTime[0], Math.trunc(endTime / 1000)); }); - it('should update name', () => { + it("should update name", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -531,15 +531,15 @@ describe('Span', () => { spanContext, SpanKind.SERVER ); - span.updateName('foo-span'); + span.updateName("foo-span"); span.end(); // shouldn't update name - span.updateName('bar-span'); - assert.strictEqual(span.name, 'foo-span'); + span.updateName("bar-span"); + assert.strictEqual(span.name, "foo-span"); }); - it('should have ended', () => { + it("should have ended", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -552,8 +552,8 @@ describe('Span', () => { assert.strictEqual(span.ended, true); }); - describe('span processor', () => { - it('should call onStart synchronously when span is started', () => { + describe("span processor", () => { + it("should call onStart synchronously when span is started", () => { let started = false; const processor: SpanProcessor = { onStart: () => { @@ -568,11 +568,11 @@ describe('Span', () => { provider.addSpanProcessor(processor); - provider.getTracer('default').startSpan('test'); + provider.getTracer("default").startSpan("test"); assert.ok(started); }); - it('should call onEnd synchronously when span is ended', () => { + it("should call onEnd synchronously when span is ended", () => { let ended = false; const processor: SpanProcessor = { onStart: () => {}, @@ -587,14 +587,14 @@ describe('Span', () => { provider.addSpanProcessor(processor); - provider.getTracer('default').startSpan('test').end(); + provider.getTracer("default").startSpan("test").end(); assert.ok(ended); }); - it('should call onStart with a writeable span', () => { + it("should call onStart with a writeable span", () => { const processor: SpanProcessor = { - onStart: span => { - span.setAttribute('attr', true); + onStart: (span) => { + span.setAttribute("attr", true); }, forceFlush: () => Promise.resolve(), onEnd() {}, @@ -605,24 +605,24 @@ describe('Span', () => { provider.addSpanProcessor(processor); - const s = provider.getTracer('default').startSpan('test') as Span; + const s = provider.getTracer("default").startSpan("test") as Span; assert.ok(s.attributes.attr); }); }); - describe('recordException', () => { + describe("recordException", () => { const invalidExceptions: any[] = [ 1, null, undefined, - { foo: 'bar' }, - { stack: 'bar' }, - ['a', 'b', 'c'], + { foo: "bar" }, + { stack: "bar" }, + ["a", "b", "c"], ]; - invalidExceptions.forEach(key => { + invalidExceptions.forEach((key) => { describe(`when exception is (${JSON.stringify(key)})`, () => { - it('should NOT record an exception', () => { + it("should NOT record an exception", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -640,9 +640,9 @@ describe('Span', () => { describe('when exception type is "string"', () => { let error: Exception; beforeEach(() => { - error = 'boom'; + error = "boom"; }); - it('should record an exception', () => { + it("should record an exception", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -654,9 +654,9 @@ describe('Span', () => { span.recordException(error); const event = span.events[0]; - assert.strictEqual(event.name, 'exception'); + assert.strictEqual(event.name, "exception"); assert.deepStrictEqual(event.attributes, { - 'exception.message': 'boom', + "exception.message": "boom", }); assert.ok(event.time[0] > 0); }); @@ -664,18 +664,18 @@ describe('Span', () => { const errorsObj = [ { - description: 'code', - obj: { code: 'Error', message: 'boom', stack: 'bar' }, + description: "code", + obj: { code: "Error", message: "boom", stack: "bar" }, }, { - description: 'name', - obj: { name: 'Error', message: 'boom', stack: 'bar' }, + description: "name", + obj: { name: "Error", message: "boom", stack: "bar" }, }, ]; - errorsObj.forEach(errorObj => { + errorsObj.forEach((errorObj) => { describe(`when exception type is an object with ${errorObj.description}`, () => { const error: Exception = errorObj.obj; - it('should record an exception', () => { + it("should record an exception", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -688,24 +688,25 @@ describe('Span', () => { const event = span.events[0]; assert.ok(event.time[0] > 0); - assert.strictEqual(event.name, 'exception'); + assert.strictEqual(event.name, "exception"); assert.ok(event.attributes); - const type = event.attributes[SemanticAttribute.EXCEPTION_TYPE]; - const message = event.attributes[SemanticAttribute.EXCEPTION_MESSAGE]; + const type = event.attributes[SemanticAttributes.EXCEPTION_TYPE]; + const message = + event.attributes[SemanticAttributes.EXCEPTION_MESSAGE]; const stacktrace = String( - event.attributes[SemanticAttribute.EXCEPTION_STACKTRACE] + event.attributes[SemanticAttributes.EXCEPTION_STACKTRACE] ); - assert.strictEqual(type, 'Error'); - assert.strictEqual(message, 'boom'); - assert.strictEqual(stacktrace, 'bar'); + assert.strictEqual(type, "Error"); + assert.strictEqual(message, "boom"); + assert.strictEqual(stacktrace, "bar"); }); }); }); - describe('when time is provided', () => { - it('should record an exception with provided time', () => { + describe("when time is provided", () => { + it("should record an exception with provided time", () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -714,7 +715,7 @@ describe('Span', () => { SpanKind.CLIENT ); assert.strictEqual(span.events.length, 0); - span.recordException('boom', [0, 123]); + span.recordException("boom", [0, 123]); const event = span.events[0]; assert.deepStrictEqual(event.time, [0, 123]); }); diff --git a/packages/opentelemetry-web/src/utils.ts b/packages/opentelemetry-web/src/utils.ts index a9ad053042b..b103d7a8ee1 100644 --- a/packages/opentelemetry-web/src/utils.ts +++ b/packages/opentelemetry-web/src/utils.ts @@ -18,21 +18,21 @@ import { PerformanceEntries, PerformanceResourceTimingInfo, PropagateTraceHeaderCorsUrls, -} from './types'; -import { PerformanceTimingNames as PTN } from './enums/PerformanceTimingNames'; -import * as api from '@opentelemetry/api'; +} from "./types"; +import { PerformanceTimingNames as PTN } from "./enums/PerformanceTimingNames"; +import * as api from "@opentelemetry/api"; import { hrTimeToNanoseconds, timeInputToHrTime, urlMatches, -} from '@opentelemetry/core'; -import { SemanticAttribute } from '@opentelemetry/semantic-conventions'; +} from "@opentelemetry/core"; +import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; // Used to normalize relative URLs let a: HTMLAnchorElement | undefined; const getUrlNormalizingAnchor = () => { if (!a) { - a = document.createElement('a'); + a = document.createElement("a"); } return a; @@ -61,7 +61,7 @@ export function addSpanNetworkEvent( ): api.Span | undefined { if ( hasKey(entries, performanceName) && - typeof entries[performanceName] === 'number' + typeof entries[performanceName] === "number" ) { span.addEvent(performanceName, entries[performanceName]); return span; @@ -90,7 +90,7 @@ export function addSpanNetworkEvents( const contentLength = resource[PTN.ENCODED_BODY_SIZE]; if (contentLength !== undefined) { span.setAttribute( - SemanticAttribute.HTTP_RESPONSE_CONTENT_LENGTH, + SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, contentLength ); } @@ -245,7 +245,7 @@ function filterResourcesForSpan( ) { const startTime = hrTimeToNanoseconds(startTimeHR); const endTime = hrTimeToNanoseconds(endTimeHR); - let filteredResources = resources.filter(resource => { + let filteredResources = resources.filter((resource) => { const resourceStartTime = hrTimeToNanoseconds( timeInputToHrTime(resource[PTN.FETCH_START]) ); @@ -255,7 +255,7 @@ function filterResourcesForSpan( return ( resource.initiatorType.toLowerCase() === - (initiatorType || 'xmlhttprequest') && + (initiatorType || "xmlhttprequest") && resource.name === spanUrl && resourceStartTime >= startTime && resourceEndTime <= endTime @@ -263,7 +263,7 @@ function filterResourcesForSpan( }); if (filteredResources.length > 0) { - filteredResources = filteredResources.filter(resource => { + filteredResources = filteredResources.filter((resource) => { return !ignoredResources.has(resource); }); } @@ -276,7 +276,7 @@ function filterResourcesForSpan( * @param url */ export function parseUrl(url: string): HTMLAnchorElement { - const a = document.createElement('a'); + const a = document.createElement("a"); a.href = url; return a; } @@ -290,13 +290,13 @@ export function parseUrl(url: string): HTMLAnchorElement { // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getElementXPath(target: any, optimised?: boolean) { if (target.nodeType === Node.DOCUMENT_NODE) { - return '/'; + return "/"; } const targetValue = getNodeValue(target, optimised); - if (optimised && targetValue.indexOf('@id') > 0) { + if (optimised && targetValue.indexOf("@id") > 0) { return targetValue; } - let xpath = ''; + let xpath = ""; if (target.parentNode) { xpath += getElementXPath(target.parentNode, false); } @@ -340,9 +340,9 @@ function getNodeIndex(target: HTMLElement): number { function getNodeValue(target: HTMLElement, optimised?: boolean): string { const nodeType = target.nodeType; const index = getNodeIndex(target); - let nodeValue = ''; + let nodeValue = ""; if (nodeType === Node.ELEMENT_NODE) { - const id = target.getAttribute('id'); + const id = target.getAttribute("id"); if (optimised && id) { return `//*[@id="${id}"]`; } @@ -351,11 +351,11 @@ function getNodeValue(target: HTMLElement, optimised?: boolean): string { nodeType === Node.TEXT_NODE || nodeType === Node.CDATA_SECTION_NODE ) { - nodeValue = 'text()'; + nodeValue = "text()"; } else if (nodeType === Node.COMMENT_NODE) { - nodeValue = 'comment()'; + nodeValue = "comment()"; } else { - return ''; + return ""; } // if index is 1 it can be omitted in xpath if (nodeValue && index > 1) { @@ -375,7 +375,7 @@ export function shouldPropagateTraceHeaders( ) { let propagateTraceHeaderUrls = propagateTraceHeaderCorsUrls || []; if ( - typeof propagateTraceHeaderUrls === 'string' || + typeof propagateTraceHeaderUrls === "string" || propagateTraceHeaderUrls instanceof RegExp ) { propagateTraceHeaderUrls = [propagateTraceHeaderUrls]; @@ -385,7 +385,7 @@ export function shouldPropagateTraceHeaders( if (parsedSpanUrl.origin === window.location.origin) { return true; } else { - return propagateTraceHeaderUrls.some(propagateTraceHeaderUrl => + return propagateTraceHeaderUrls.some((propagateTraceHeaderUrl) => urlMatches(spanUrl, propagateTraceHeaderUrl) ); } diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh index c7d5c713059..72eb399de1c 100755 --- a/scripts/semconv/generate.sh +++ b/scripts/semconv/generate.sh @@ -3,7 +3,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ROOT_DIR="${SCRIPT_DIR}/../../" -# freeze the spec version to make SemanticAttributes generation reproducible +# freeze the spec version to make SpanAttributess generation reproducible SPEC_VERSION=v1.1.0 GENERATOR_VERSION=0.2.1 @@ -27,8 +27,8 @@ docker run --rm \ -f /source \ code \ --template /templates/SemanticAttributes.ts.j2 \ - --output /output/SemanticAttribute.ts \ - -Dclass=SemanticAttribute + --output /output/SemanticAttributes.ts \ + -Dclass=SemanticAttributes docker run --rm \ -v ${SCRIPT_DIR}/opentelemetry-specification/semantic_conventions/resource:/source \ @@ -38,8 +38,8 @@ docker run --rm \ -f /source \ code \ --template /templates/SemanticAttributes.ts.j2 \ - --output /output/ResourceAttribute.ts \ - -Dclass=ResourceAttribute + --output /output/ResourceAttributes.ts \ + -Dclass=ResourceAttributes # Run the automatic linting fixing task to ensure it will pass eslint cd "$ROOT_DIR/packages/opentelemetry-semantic-conventions" diff --git a/scripts/semconv/templates/SemanticAttributes.ts.j2 b/scripts/semconv/templates/SemanticAttributes.ts.j2 index aa9081e6a31..fc0a7496fb8 100644 --- a/scripts/semconv/templates/SemanticAttributes.ts.j2 +++ b/scripts/semconv/templates/SemanticAttributes.ts.j2 @@ -27,7 +27,7 @@ // DO NOT EDIT, this is an Auto-generated file from scripts/semconv/templates/{{template}} export const {{class}} = { - {%- for attribute in attributes | unique(attribute="fqn") %} + {%- for attribute in attributes if attribute.is_local and not attribute.ref %} /** * {% filter escape %}{{attribute.brief | to_doc_brief}}.{% endfilter %} @@ -45,33 +45,16 @@ export const {{class}} = { {%- endif %} {{attribute.fqn | to_const_name}}: '{{attribute.fqn}}', {%- endfor %} - - {%- if class == "SemanticAttribute" %} - - // Manually defined and not YET in the YAML - - // HTTP - HTTP_ERROR_NAME: 'http.error_name', - HTTP_ERROR_MESSAGE: 'http.error_message', - HTTP_STATUS_TEXT: 'http.status_text', - - // GRPC - GRPC_KIND: 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD: 'grpc.method', - GRPC_STATUS_CODE: 'grpc.status_code', - GRPC_ERROR_NAME: 'grpc.error_name', - GRPC_ERROR_MESSAGE: 'grpc.error_message', - {%- endif %} } // Enum definitions -{%- for attribute in attributes | unique(attribute="fqn") %} +{%- for attribute in attributes if attribute.is_local %} {%- if attribute.is_enum %} {%- set class_name = attribute.fqn | to_camelcase(True) ~ "Values" %} {%- set type = attribute.attr_type.enum_type %} export enum {{class_name}} { - {%- for member in attribute.attr_type.members %} + {%- for member in attribute.attr_type.members if attribute.is_local and not attribute.ref %} /** {% filter escape %}{{member.brief | to_doc_brief}}.{% endfilter %} */ {{ member.member_id | to_const_name }} = {{ print_value(type, member.value) }}, {%- endfor %} From a8b4115e734a929201af7227e4cd2d5ce9a6330c Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 19:12:16 +0100 Subject: [PATCH 09/14] style: ran `npm run lint:fix` on the code base --- .../src/enums/AttributeNames.ts | 6 +- .../src/fetch.ts | 54 ++-- .../test/fetch.test.ts | 234 +++++++------- .../src/enums.ts | 10 +- .../src/grpc-js/clientUtils.ts | 30 +- .../src/grpc-js/index.ts | 74 ++--- .../src/grpc-js/serverUtils.ts | 44 +-- .../src/grpc/clientUtils.ts | 28 +- .../src/grpc/index.ts | 78 ++--- .../src/grpc/serverUtils.ts | 24 +- .../src/enums.ts | 6 +- .../src/utils.ts | 114 +++---- .../test/functionals/http-enable.test.ts | 284 ++++++++--------- .../test/functionals/https-enable.test.ts | 226 +++++++------- .../test/functionals/utils.test.ts | 206 ++++++------ .../test/integrations/http-enable.test.ts | 170 +++++----- .../test/integrations/https-enable.test.ts | 174 +++++------ .../test/utils/assertSpan.ts | 54 ++-- .../src/enums/AttributeNames.ts | 2 +- .../src/xhr.ts | 84 ++--- .../test/unmocked.test.ts | 24 +- .../test/xhr.test.ts | 294 +++++++++--------- .../src/client/utils.ts | 32 +- .../opentelemetry-plugin-grpc-js/src/enums.ts | 10 +- .../src/server/clientStreamAndUnary.ts | 12 +- .../src/server/patchServer.ts | 58 ++-- .../src/server/serverStreamAndBidi.ts | 16 +- .../opentelemetry-plugin-grpc/src/enums.ts | 10 +- .../opentelemetry-plugin-grpc/src/grpc.ts | 114 +++---- .../opentelemetry-plugin-http/src/enums.ts | 6 +- .../opentelemetry-plugin-http/src/utils.ts | 110 +++---- .../test/functionals/http-enable.test.ts | 272 ++++++++-------- .../test/functionals/utils.test.ts | 204 ++++++------ .../test/integrations/http-enable.test.ts | 162 +++++----- .../test/utils/assertSpan.ts | 54 ++-- .../opentelemetry-plugin-https/src/enums.ts | 2 +- .../test/functionals/https-enable.test.ts | 206 ++++++------ .../test/integrations/https-enable.test.ts | 166 +++++----- .../test/utils/assertSpan.ts | 38 +-- packages/opentelemetry-tracing/src/Span.ts | 34 +- packages/opentelemetry-tracing/src/enums.ts | 2 +- packages/opentelemetry-tracing/src/index.ts | 22 +- .../opentelemetry-tracing/test/Span.test.ts | 280 ++++++++--------- packages/opentelemetry-web/src/utils.ts | 42 +-- 44 files changed, 2036 insertions(+), 2036 deletions(-) diff --git a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts index 9a8c7db1bac..c3ce12fad7f 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/enums/AttributeNames.ts @@ -18,7 +18,7 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - COMPONENT = "component", - HTTP_ERROR_NAME = "http.error_name", - HTTP_STATUS_TEXT = "http.status_text", + COMPONENT = 'component', + HTTP_ERROR_NAME = 'http.error_name', + HTTP_STATUS_TEXT = 'http.status_text', } diff --git a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index e7d319c6502..00eb20e3c13 100644 --- a/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -14,18 +14,18 @@ * limitations under the License. */ -import * as api from "@opentelemetry/api"; +import * as api from '@opentelemetry/api'; import { isWrapped, InstrumentationBase, InstrumentationConfig, -} from "@opentelemetry/instrumentation"; -import * as core from "@opentelemetry/core"; -import * as web from "@opentelemetry/web"; -import { AttributeNames } from "./enums/AttributeNames"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import { FetchError, FetchResponse, SpanData } from "./types"; -import { VERSION } from "./version"; +} from '@opentelemetry/instrumentation'; +import * as core from '@opentelemetry/core'; +import * as web from '@opentelemetry/web'; +import { AttributeNames } from './enums/AttributeNames'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { FetchError, FetchResponse, SpanData } from './types'; +import { VERSION } from './version'; // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer @@ -37,7 +37,7 @@ const OBSERVER_WAIT_TIME_MS = 300; let a: HTMLAnchorElement | undefined; const getUrlNormalizingAnchor = () => { if (!a) { - a = document.createElement("a"); + a = document.createElement('a'); } return a; @@ -69,7 +69,7 @@ export interface FetchInstrumentationConfig extends InstrumentationConfig { export class FetchInstrumentation extends InstrumentationBase< Promise > { - readonly component: string = "fetch"; + readonly component: string = 'fetch'; readonly version: string = VERSION; moduleName = this.component; private _usedResources = new WeakSet(); @@ -77,7 +77,7 @@ export class FetchInstrumentation extends InstrumentationBase< constructor(config: FetchInstrumentationConfig = {}) { super( - "@opentelemetry/instrumentation-fetch", + '@opentelemetry/instrumentation-fetch', VERSION, Object.assign({}, config) ); @@ -99,7 +99,7 @@ export class FetchInstrumentation extends InstrumentationBase< corsPreFlightRequest: PerformanceResourceTiming ): void { const childSpan = this.tracer.startSpan( - "CORS Preflight", + 'CORS Preflight', { startTime: corsPreFlightRequest[web.PerformanceTimingNames.FETCH_START], }, @@ -128,7 +128,7 @@ export class FetchInstrumentation extends InstrumentationBase< span.setAttribute(SemanticAttributes.HTTP_HOST, parsedUrl.host); span.setAttribute( SemanticAttributes.HTTP_SCHEME, - parsedUrl.protocol.replace(":", "") + parsedUrl.protocol.replace(':', '') ); span.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent); } @@ -150,7 +150,7 @@ export class FetchInstrumentation extends InstrumentationBase< if (options instanceof Request) { api.propagation.inject(api.context.active(), options.headers, { - set: (h, k, v) => h.set(k, typeof v === "string" ? v : String(v)), + set: (h, k, v) => h.set(k, typeof v === 'string' ? v : String(v)), }); } else { const headers: Partial> = {}; @@ -182,10 +182,10 @@ export class FetchInstrumentation extends InstrumentationBase< options: Partial = {} ): api.Span | undefined { if (core.isUrlIgnored(url, this._getConfig().ignoreUrls)) { - api.diag.debug("ignoring span as url matches ignored url"); + api.diag.debug('ignoring span as url matches ignored url'); return; } - const method = (options.method || "GET").toUpperCase(); + const method = (options.method || 'GET').toUpperCase(); const spanName = `HTTP ${method}`; return this.tracer.startSpan(spanName, { kind: api.SpanKind.CLIENT, @@ -217,7 +217,7 @@ export class FetchInstrumentation extends InstrumentationBase< // then OBSERVER_WAIT_TIME_MS and observer didn't collect enough // information resources = performance.getEntriesByType( - "resource" + 'resource' ) as PerformanceResourceTiming[]; } const resource = web.getResource( @@ -226,7 +226,7 @@ export class FetchInstrumentation extends InstrumentationBase< endTime, resources, this._usedResources, - "fetch" + 'fetch' ); if (resource.mainRequest) { @@ -363,17 +363,17 @@ export class FetchInstrumentation extends InstrumentationBase< private _prepareSpanData(spanUrl: string): SpanData { const startTime = core.hrTime(); const entries: PerformanceResourceTiming[] = []; - if (typeof window.PerformanceObserver === "undefined") { + if (typeof window.PerformanceObserver === 'undefined') { return { entries, startTime, spanUrl }; } - const observer: PerformanceObserver = new PerformanceObserver((list) => { + const observer: PerformanceObserver = new PerformanceObserver(list => { const perfObsEntries = list.getEntries() as PerformanceResourceTiming[]; const urlNormalizingAnchor = getUrlNormalizingAnchor(); urlNormalizingAnchor.href = spanUrl; - perfObsEntries.forEach((entry) => { + perfObsEntries.forEach(entry => { if ( - entry.initiatorType === "fetch" && + entry.initiatorType === 'fetch' && entry.name === urlNormalizingAnchor.href ) { entries.push(entry); @@ -381,7 +381,7 @@ export class FetchInstrumentation extends InstrumentationBase< }); }); observer.observe({ - entryTypes: ["resource"], + entryTypes: ['resource'], }); return { entries, observer, startTime, spanUrl }; } @@ -391,17 +391,17 @@ export class FetchInstrumentation extends InstrumentationBase< */ enable() { if (isWrapped(window.fetch)) { - this._unwrap(window, "fetch"); - api.diag.debug("removing previous patch for constructor"); + this._unwrap(window, 'fetch'); + api.diag.debug('removing previous patch for constructor'); } - this._wrap(window, "fetch", this._patchConstructor()); + this._wrap(window, 'fetch', this._patchConstructor()); } /** * implements unpatch function */ disable() { - this._unwrap(window, "fetch"); + this._unwrap(window, 'fetch'); this._usedResources = new WeakSet(); } } diff --git a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 08c37370759..622fc30e462 100644 --- a/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as api from "@opentelemetry/api"; -import * as core from "@opentelemetry/core"; +import * as api from '@opentelemetry/api'; +import * as core from '@opentelemetry/core'; import { isWrapped, registerInstrumentations, -} from "@opentelemetry/instrumentation"; +} from '@opentelemetry/instrumentation'; import { B3Propagator, @@ -26,18 +26,18 @@ import { X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_SAMPLED, -} from "@opentelemetry/propagator-b3"; -import { ZoneContextManager } from "@opentelemetry/context-zone"; -import * as tracing from "@opentelemetry/tracing"; +} from '@opentelemetry/propagator-b3'; +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import * as tracing from '@opentelemetry/tracing'; import { PerformanceTimingNames as PTN, WebTracerProvider, -} from "@opentelemetry/web"; -import * as assert from "assert"; -import * as sinon from "sinon"; -import { FetchInstrumentation, FetchInstrumentationConfig } from "../src"; -import { AttributeNames } from "../src/enums/AttributeNames"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/web'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { FetchInstrumentation, FetchInstrumentationConfig } from '../src'; +import { AttributeNames } from '../src/enums/AttributeNames'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -49,11 +49,11 @@ class DummySpanExporter implements tracing.SpanExporter { const getData = (url: string, method?: string) => fetch(url, { - method: method || "GET", + method: method || 'GET', headers: { - foo: "bar", - Accept: "application/json", - "Content-Type": "application/json", + foo: 'bar', + Accept: 'application/json', + 'Content-Type': 'application/json', }, }); @@ -65,8 +65,8 @@ const defaultResource = { domainLookupStart: 11, encodedBodySize: 0, fetchStart: 10.1, - initiatorType: "fetch", - nextHopProtocol: "", + initiatorType: 'fetch', + nextHopProtocol: '', redirectEnd: 0, redirectStart: 0, requestStart: 16, @@ -76,8 +76,8 @@ const defaultResource = { transferSize: 0, workerStart: 0, duration: 0, - entryType: "", - name: "", + entryType: '', + name: '', startTime: 0, }; @@ -92,7 +92,7 @@ function createResource(resource = {}): PerformanceResourceTiming { function createMainResource(resource = {}): PerformanceResourceTiming { const mainResource: any = createResource(resource); Object.keys(mainResource).forEach((key: string) => { - if (typeof mainResource[key] === "number") { + if (typeof mainResource[key] === 'number') { mainResource[key] = mainResource[key] + 30; } }); @@ -103,7 +103,7 @@ function createFakePerformanceObs(url: string) { class FakePerfObs implements PerformanceObserver { constructor(private readonly cb: PerformanceObserverCallback) {} observe() { - const absoluteUrl = url.startsWith("http") ? url : location.origin + url; + const absoluteUrl = url.startsWith('http') ? url : location.origin + url; const resources: PerformanceObserverEntryList = { getEntries(): PerformanceEntryList { return [ @@ -129,7 +129,7 @@ function createFakePerformanceObs(url: string) { return FakePerfObs; } -describe("fetch", () => { +describe('fetch', () => { let contextManager: ZoneContextManager; let lastResponse: any | undefined; let webTracerWithZone: api.Tracer; @@ -141,8 +141,8 @@ describe("fetch", () => { let fakeNow = 0; let fetchInstrumentation: FetchInstrumentation; - const url = "http://localhost:8090/get"; - const badUrl = "http://foo.bar.com/get"; + const url = 'http://localhost:8090/get'; + const badUrl = 'http://foo.bar.com/get'; const clearData = () => { sinon.restore(); @@ -159,8 +159,8 @@ describe("fetch", () => { ) => { sinon.useFakeTimers(); - sinon.stub(core.otperformance, "timeOrigin").value(0); - sinon.stub(core.otperformance, "now").callsFake(() => fakeNow); + sinon.stub(core.otperformance, 'timeOrigin').value(0); + sinon.stub(core.otperformance, 'now').callsFake(() => fakeNow); function fakeFetch(input: RequestInfo | Request, init: RequestInit = {}) { return new Promise((resolve, reject) => { @@ -170,23 +170,23 @@ describe("fetch", () => { }; response.headers = Object.assign({}, init.headers); - if (init.method === "DELETE") { + if (init.method === 'DELETE') { response.status = 405; - response.statusText = "OK"; - resolve(new window.Response("foo", response)); + response.statusText = 'OK'; + resolve(new window.Response('foo', response)); } else if (input === url) { response.status = 200; - response.statusText = "OK"; + response.statusText = 'OK'; resolve(new window.Response(JSON.stringify(response), response)); } else { response.status = 404; - response.statusText = "Bad request"; + response.statusText = 'Bad request'; reject(new window.Response(JSON.stringify(response), response)); } }); } - sinon.stub(window, "fetch").callsFake(fakeFetch as any); + sinon.stub(window, 'fetch').callsFake(fakeFetch as any); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -199,18 +199,18 @@ describe("fetch", () => { ); if (disablePerfObserver) { - sinon.stub(window, "PerformanceObserver").value(undefined); + sinon.stub(window, 'PerformanceObserver').value(undefined); } else { sinon - .stub(window, "PerformanceObserver") + .stub(window, 'PerformanceObserver') .value(createFakePerformanceObs(fileUrl)); } if (disableGetEntries) { - sinon.stub(performance, "getEntriesByType").value(undefined); + sinon.stub(performance, 'getEntriesByType').value(undefined); } else { - const spyEntries = sinon.stub(performance, "getEntriesByType"); - spyEntries.withArgs("resource").returns(resources); + const spyEntries = sinon.stub(performance, 'getEntriesByType'); + spyEntries.withArgs('resource').returns(resources); } fetchInstrumentation = new FetchInstrumentation(config); @@ -219,26 +219,26 @@ describe("fetch", () => { tracerProvider: webTracerProviderWithZone, instrumentations: [fetchInstrumentation], }); - webTracerWithZone = webTracerProviderWithZone.getTracer("fetch-test"); + webTracerWithZone = webTracerProviderWithZone.getTracer('fetch-test'); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, "export"); - clearResourceTimingsSpy = sinon.stub(performance, "clearResourceTimings"); + exportSpy = sinon.stub(dummySpanExporter, 'export'); + clearResourceTimingsSpy = sinon.stub(performance, 'clearResourceTimings'); webTracerProviderWithZone.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - rootSpan = webTracerWithZone.startSpan("root"); + rootSpan = webTracerWithZone.startSpan('root'); api.context.with(api.setSpan(api.context.active(), rootSpan), () => { fakeNow = 0; getData(fileUrl, method).then( - (response) => { + response => { // this is a bit tricky as the only way to get all request headers from // fetch is to use json() response.json().then( - (json) => { + json => { lastResponse = json; const headers: { [key: string]: string } = {}; - Object.keys(lastResponse.headers).forEach((key) => { + Object.keys(lastResponse.headers).forEach(key => { headers[key.toLowerCase()] = lastResponse.headers[key]; }); lastResponse.headers = headers; @@ -282,8 +282,8 @@ describe("fetch", () => { ); }); - describe("when request is successful", () => { - beforeEach((done) => { + describe('when request is successful', () => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = [url]; prepareData(done, url, { propagateTraceHeaderCorsUrls }); }); @@ -292,49 +292,49 @@ describe("fetch", () => { clearData(); }); - it("should wrap methods", () => { + it('should wrap methods', () => { assert.ok(isWrapped(window.fetch)); fetchInstrumentation.enable(); assert.ok(isWrapped(window.fetch)); }); - it("should unwrap methods", () => { + it('should unwrap methods', () => { assert.ok(isWrapped(window.fetch)); fetchInstrumentation.disable(); assert.ok(!isWrapped(window.fetch)); }); - it("should create a span with correct root span", () => { + it('should create a span with correct root span', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - "parent span is not root span" + 'parent span is not root span' ); }); - it("span should have correct name", () => { + it('span should have correct name', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.name, "HTTP GET", "span has wrong name"); + assert.strictEqual(span.name, 'HTTP GET', 'span has wrong name'); }); - it("span should have correct kind", () => { + it('span should have correct kind', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.kind, api.SpanKind.CLIENT, "span has wrong kind"); + assert.strictEqual(span.kind, api.SpanKind.CLIENT, 'span has wrong kind'); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.ok( - attributes[keys[0]] !== "", + attributes[keys[0]] !== '', `attributes ${AttributeNames.COMPONENT} is not defined` ); assert.strictEqual( attributes[keys[1]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -348,19 +348,19 @@ describe("fetch", () => { `attributes ${SemanticAttributes.HTTP_STATUS_CODE} is wrong` ); assert.ok( - attributes[keys[4]] === "OK" || attributes[keys[4]] === "", + attributes[keys[4]] === 'OK' || attributes[keys[4]] === '', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.ok( - (attributes[keys[5]] as string).indexOf("localhost") === 0, + (attributes[keys[5]] as string).indexOf('localhost') === 0, `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== "", + attributes[keys[7]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); assert.ok( @@ -368,13 +368,13 @@ describe("fetch", () => { `attributes ${SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH} is <= 0` ); - assert.strictEqual(keys.length, 9, "number of attributes is wrong"); + assert.strictEqual(keys.length, 9, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[0].name, @@ -423,38 +423,38 @@ describe("fetch", () => { ); }); - it("should create a span for preflight request", () => { + it('should create a span for preflight request', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const parentSpan: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, parentSpan.spanContext.spanId, - "parent span is not root span" + 'parent span is not root span' ); }); - it("preflight request span should have correct name", () => { + it('preflight request span should have correct name', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.name, - "CORS Preflight", - "preflight request span has wrong name" + 'CORS Preflight', + 'preflight request span has wrong name' ); }); - it("preflight request span should have correct kind", () => { + it('preflight request span should have correct kind', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.kind, api.SpanKind.INTERNAL, - "span has wrong kind" + 'span has wrong kind' ); }); - it("preflight request span should have correct events", () => { + it('preflight request span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[0].name, @@ -503,7 +503,7 @@ describe("fetch", () => { ); }); - it("should set trace headers", () => { + it('should set trace headers', () => { const span: api.Span = exportSpy.args[1][0][0]; assert.strictEqual( lastResponse.headers[X_B3_TRACE_ID], @@ -522,26 +522,26 @@ describe("fetch", () => { ); }); - it("should set trace headers with a request object", () => { - const r = new Request("url"); + it('should set trace headers with a request object', () => { + const r = new Request('url'); window.fetch(r); - assert.ok(typeof r.headers.get(X_B3_TRACE_ID) === "string"); + assert.ok(typeof r.headers.get(X_B3_TRACE_ID) === 'string'); }); - it("should NOT clear the resources", () => { + it('should NOT clear the resources', () => { assert.strictEqual( clearResourceTimingsSpy.args.length, 0, - "resources have been cleared" + 'resources have been cleared' ); }); - describe("when propagateTraceHeaderCorsUrls does NOT MATCH", () => { - beforeEach((done) => { + describe('when propagateTraceHeaderCorsUrls does NOT MATCH', () => { + beforeEach(done => { clearData(); prepareData(done, url, {}); }); - it("should NOT set trace headers", () => { + it('should NOT set trace headers', () => { assert.strictEqual( lastResponse.headers[X_B3_TRACE_ID], undefined, @@ -561,8 +561,8 @@ describe("fetch", () => { }); }); - describe("when url is ignored", () => { - beforeEach((done) => { + describe('when url is ignored', () => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { propagateTraceHeaderCorsUrls, @@ -572,13 +572,13 @@ describe("fetch", () => { afterEach(() => { clearData(); }); - it("should NOT create any span", () => { + it('should NOT create any span', () => { assert.strictEqual(exportSpy.args.length, 0, "span shouldn't b exported"); }); }); - describe("when clearTimingResources is TRUE", () => { - beforeEach((done) => { + describe('when clearTimingResources is TRUE', () => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { propagateTraceHeaderCorsUrls, @@ -588,7 +588,7 @@ describe("fetch", () => { afterEach(() => { clearData(); }); - it("should clear the resources", () => { + it('should clear the resources', () => { assert.strictEqual( clearResourceTimingsSpy.args.length, 1, @@ -597,45 +597,45 @@ describe("fetch", () => { }); }); - describe("when request is NOT successful (wrong url)", () => { - beforeEach((done) => { + describe('when request is NOT successful (wrong url)', () => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = badUrl; prepareData(done, badUrl, { propagateTraceHeaderCorsUrls }); }); afterEach(() => { clearData(); }); - it("should create a span with correct root span", () => { + it('should create a span with correct root span', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - "parent span is not root span" + 'parent span is not root span' ); }); }); - describe("when request is NOT successful (405)", () => { - beforeEach((done) => { + describe('when request is NOT successful (405)', () => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = url; - prepareData(done, url, { propagateTraceHeaderCorsUrls }, "DELETE"); + prepareData(done, url, { propagateTraceHeaderCorsUrls }, 'DELETE'); }); afterEach(() => { clearData(); }); - it("should create a span with correct root span", () => { + it('should create a span with correct root span', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - "parent span is not root span" + 'parent span is not root span' ); }); }); - describe("when PerformanceObserver is used by default", () => { - beforeEach((done) => { + describe('when PerformanceObserver is used by default', () => { + beforeEach(done => { // All above tests test it already but just in case // lets explicitly turn getEntriesByType off so we can be sure // that the perf entries come from the observer. @@ -644,7 +644,7 @@ describe("fetch", () => { afterEach(() => { clearData(); }); - it("should create both spans with network events", () => { + it('should create both spans with network events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -654,7 +654,7 @@ describe("fetch", () => { `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[6].name, @@ -664,14 +664,14 @@ describe("fetch", () => { }); }); - describe("when fetching with relative url", () => { - beforeEach((done) => { - prepareData(done, "/get", {}, undefined, false, true); + describe('when fetching with relative url', () => { + beforeEach(done => { + prepareData(done, '/get', {}, undefined, false, true); }); afterEach(() => { clearData(); }); - it("should create spans with network info", () => { + it('should create spans with network info', () => { // no prefetch span because mock observer uses location.origin as url when relative // and prefetch span finding compares url origins const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; @@ -683,7 +683,7 @@ describe("fetch", () => { `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[6].name, PTN.REQUEST_START, @@ -692,8 +692,8 @@ describe("fetch", () => { }); }); - describe("when PerformanceObserver is undefined", () => { - beforeEach((done) => { + describe('when PerformanceObserver is undefined', () => { + beforeEach(done => { prepareData(done, url, {}, undefined, true, false); }); @@ -701,7 +701,7 @@ describe("fetch", () => { clearData(); }); - it("should fallback to getEntries", () => { + it('should fallback to getEntries', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -710,7 +710,7 @@ describe("fetch", () => { 2, `Wrong number of spans: ${exportSpy.args.length}` ); - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[6].name, PTN.REQUEST_START, @@ -719,14 +719,14 @@ describe("fetch", () => { }); }); - describe("when PerformanceObserver and performance.getEntriesByType are undefined", () => { - beforeEach((done) => { + describe('when PerformanceObserver and performance.getEntriesByType are undefined', () => { + beforeEach(done => { prepareData(done, url, {}, undefined, true, true); }); afterEach(() => { clearData(); }); - it("should still capture fetch with basic attributes", () => { + it('should still capture fetch with basic attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; const attributes = span.attributes; @@ -739,11 +739,11 @@ describe("fetch", () => { ); assert.strictEqual( exportSpy.args[0][0][0].name, - "HTTP GET", - "wrong span captured" + 'HTTP GET', + 'wrong span captured' ); - assert.strictEqual(events.length, 0, "Should not have any events"); + assert.strictEqual(events.length, 0, 'Should not have any events'); // should still have basic attributes assert.strictEqual( diff --git a/packages/opentelemetry-instrumentation-grpc/src/enums.ts b/packages/opentelemetry-instrumentation-grpc/src/enums.ts index e573bbbaaa5..f292c19c98a 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/enums.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/enums.ts @@ -18,9 +18,9 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - GRPC_KIND = "grpc.kind", // SERVER or CLIENT - GRPC_METHOD = "grpc.method", - GRPC_STATUS_CODE = "grpc.status_code", - GRPC_ERROR_NAME = "grpc.error_name", - GRPC_ERROR_MESSAGE = "grpc.error_message", + GRPC_KIND = 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD = 'grpc.method', + GRPC_STATUS_CODE = 'grpc.status_code', + GRPC_ERROR_NAME = 'grpc.error_name', + GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts index 2f27b84c793..2c9c6a745a9 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { GrpcJsInstrumentation } from "./"; -import type { GrpcClientFunc, SendUnaryDataCallback } from "./types"; +import { GrpcJsInstrumentation } from './'; +import type { GrpcClientFunc, SendUnaryDataCallback } from './types'; import { SpanKind, Span, @@ -23,16 +23,16 @@ import { SpanStatus, propagation, context, -} from "@opentelemetry/api"; -import type * as grpcJs from "@grpc/grpc-js"; +} from '@opentelemetry/api'; +import type * as grpcJs from '@grpc/grpc-js'; import { _grpcStatusCodeToSpanStatus, _grpcStatusCodeToOpenTelemetryStatusCode, _methodIsIgnored, -} from "../utils"; -import { CALL_SPAN_ENDED } from "./serverUtils"; -import { EventEmitter } from "events"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { CALL_SPAN_ENDED } from './serverUtils'; +import { EventEmitter } from 'events'; +import { AttributeNames } from '../enums'; /** * Parse a package method list and return a list of methods to patch @@ -115,8 +115,8 @@ export function makeGrpcClientRemoteCall( return (span: Span) => { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = args.findIndex((arg) => { - return typeof arg === "function"; + const callbackFuncIndex = args.findIndex(arg => { + return typeof arg === 'function'; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -146,7 +146,7 @@ export function makeGrpcClientRemoteCall( } }; context.bind(call); - call.on("error", (err: grpcJs.ServiceError) => { + call.on('error', (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -164,7 +164,7 @@ export function makeGrpcClientRemoteCall( endSpan(); }); - call.on("status", (status: SpanStatus) => { + call.on('status', (status: SpanStatus) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -197,9 +197,9 @@ export function getMetadata( let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { return ( arg && - typeof arg === "object" && - (arg as grpcJs.Metadata)["internalRepr"] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === "function" + typeof arg === 'object' && + (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 + typeof (arg as grpcJs.Metadata).getMap === 'function' ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index dd3e58f7d80..bec965b22eb 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import type * as grpcJs from "@grpc/grpc-js"; +import type * as grpcJs from '@grpc/grpc-js'; import { InstrumentationNodeModuleDefinition, isWrapped, -} from "@opentelemetry/instrumentation"; +} from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationConfig, -} from "@opentelemetry/instrumentation"; -import { GrpcInstrumentationConfig } from "../types"; +} from '@opentelemetry/instrumentation'; +import { GrpcInstrumentationConfig } from '../types'; import { ServerCallWithMeta, SendUnaryDataCallback, @@ -32,7 +32,7 @@ import { MakeClientConstructorFunction, PackageDefinition, GrpcClientFunc, -} from "./types"; +} from './types'; import { context, SpanOptions, @@ -41,19 +41,19 @@ import { ROOT_CONTEXT, setSpan, diag, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { shouldNotTraceServerCall, handleServerFunction, handleUntracedServerFunction, -} from "./serverUtils"; +} from './serverUtils'; import { getMethodsToWrap, makeGrpcClientRemoteCall, getMetadata, -} from "./clientUtils"; -import { EventEmitter } from "events"; -import { AttributeNames } from "../enums"; +} from './clientUtils'; +import { EventEmitter } from 'events'; +import { AttributeNames } from '../enums'; export class GrpcJsInstrumentation extends InstrumentationBase { constructor( @@ -73,42 +73,42 @@ export class GrpcJsInstrumentation extends InstrumentationBase { init() { return [ new InstrumentationNodeModuleDefinition( - "@grpc/grpc-js", - ["1.*"], + '@grpc/grpc-js', + ['1.*'], (moduleExports, version) => { diag.debug(`Applying patch for @grpc/grpc-js@${version}`); if (isWrapped(moduleExports.Server.prototype.register)) { - this._unwrap(moduleExports.Server.prototype, "register"); + this._unwrap(moduleExports.Server.prototype, 'register'); } // Patch Server methods this._wrap( moduleExports.Server.prototype, - "register", + 'register', this._patchServer() as any ); // Patch Client methods if (isWrapped(moduleExports.makeGenericClientConstructor)) { - this._unwrap(moduleExports, "makeGenericClientConstructor"); + this._unwrap(moduleExports, 'makeGenericClientConstructor'); } this._wrap( moduleExports, - "makeGenericClientConstructor", + 'makeGenericClientConstructor', this._patchClient(moduleExports) ); if (isWrapped(moduleExports.makeClientConstructor)) { - this._unwrap(moduleExports, "makeClientConstructor"); + this._unwrap(moduleExports, 'makeClientConstructor'); } this._wrap( moduleExports, - "makeClientConstructor", + 'makeClientConstructor', this._patchClient(moduleExports) ); if (isWrapped(moduleExports.loadPackageDefinition)) { - this._unwrap(moduleExports, "loadPackageDefinition"); + this._unwrap(moduleExports, 'loadPackageDefinition'); } this._wrap( moduleExports, - "loadPackageDefinition", + 'loadPackageDefinition', this._patchLoadPackageDefinition(moduleExports) ); return moduleExports; @@ -117,10 +117,10 @@ export class GrpcJsInstrumentation extends InstrumentationBase { if (moduleExports === undefined) return; diag.debug(`Removing patch for @grpc/grpc-js@${version}`); - this._unwrap(moduleExports.Server.prototype, "register"); - this._unwrap(moduleExports, "makeClientConstructor"); - this._unwrap(moduleExports, "makeGenericClientConstructor"); - this._unwrap(moduleExports, "loadPackageDefinition"); + this._unwrap(moduleExports.Server.prototype, 'register'); + this._unwrap(moduleExports, 'makeClientConstructor'); + this._unwrap(moduleExports, 'makeGenericClientConstructor'); + this._unwrap(moduleExports, 'loadPackageDefinition'); } ), ]; @@ -136,7 +136,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { const instrumentation = this; return (originalRegister: ServerRegisterFunction) => { const config = this._config; - diag.debug("patched gRPC server"); + diag.debug('patched gRPC server'); return function register( this: grpcJs.Server, name: string, @@ -153,11 +153,11 @@ export class GrpcJsInstrumentation extends InstrumentationBase { deserialize, type ); - const handlerSet = this["handlers"].get(name); + const handlerSet = this['handlers'].get(name); instrumentation._wrap( handlerSet, - "func", + 'func', (originalFunc: HandleCall) => { return function func( this: typeof handlerSet, @@ -181,17 +181,17 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ); } - const spanName = `grpc.${name.replace("/", "")}`; + const spanName = `grpc.${name.replace('/', '')}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug("patch func: %s", JSON.stringify(spanOptions)); + diag.debug('patch func: %s', JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (carrier, key) => carrier.get(key).map(String), - keys: (carrier) => Object.keys(carrier.getMap()), + keys: carrier => Object.keys(carrier.getMap()), }), () => { const span = instrumentation.tracer @@ -231,7 +231,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ) => MakeClientConstructorFunction { const instrumentation = this; return (original: MakeClientConstructorFunction) => { - diag.debug("patching client"); + diag.debug('patching client'); return function makeClientConstructor( this: typeof grpcJs.Client, methods: grpcJs.ServiceDefinition, @@ -255,7 +255,7 @@ export class GrpcJsInstrumentation extends InstrumentationBase { */ private _patchLoadPackageDefinition(grpcClient: typeof grpcJs) { const instrumentation = this; - diag.debug("patching loadPackageDefinition"); + diag.debug('patching loadPackageDefinition'); return (original: typeof grpcJs.loadPackageDefinition) => { return function patchedLoadPackageDefinition( this: null, @@ -279,9 +279,9 @@ export class GrpcJsInstrumentation extends InstrumentationBase { ): (original: GrpcClientFunc) => () => EventEmitter { const instrumentation = this; return (original: GrpcClientFunc) => { - diag.debug("patch all client methods"); + diag.debug('patch all client methods'); return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace("/", "")}`; + const name = `grpc.${original.path.replace('/', '')}`; const args = [...arguments]; const metadata = getMetadata.call( instrumentation, @@ -309,14 +309,14 @@ export class GrpcJsInstrumentation extends InstrumentationBase { grpcClient: typeof grpcJs, result: grpcJs.GrpcObject ): void { - Object.values(result).forEach((service) => { - if (typeof service === "function") { + Object.values(result).forEach(service => { + if (typeof service === 'function') { this._massWrap( service.prototype, getMethodsToWrap.call(this, service, service.service), this._getPatchedClientMethods.call(this, grpcClient) ); - } else if (typeof service.format !== "string") { + } else if (typeof service.format !== 'string') { // GrpcObject this._patchLoadedPackage.call( this, diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts index 2aa72919113..5de7978b806 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts @@ -20,22 +20,22 @@ * error event should be processed. */ -import { context, Span, SpanStatusCode } from "@opentelemetry/api"; -import type * as grpcJs from "@grpc/grpc-js"; +import { context, Span, SpanStatusCode } from '@opentelemetry/api'; +import type * as grpcJs from '@grpc/grpc-js'; import type { ServerCallWithMeta, SendUnaryDataCallback, GrpcEmitter, HandleCall, -} from "./types"; +} from './types'; import { _grpcStatusCodeToOpenTelemetryStatusCode, _methodIsIgnored, -} from "../utils"; -import { IgnoreMatcher } from "../types"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { IgnoreMatcher } from '../types'; +import { AttributeNames } from '../enums'; -export const CALL_SPAN_ENDED = Symbol("opentelemetry call span ended"); +export const CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); /** * Handle patching for serverStream and Bidi type server handlers @@ -56,7 +56,7 @@ function serverStreamAndBidiHandler( }; context.bind(call); - call.on("finish", () => { + call.on('finish', () => { // @grpc/js does not expose a way to check if this call also emitted an error, // e.g. call.status.code !== 0 if (call[CALL_SPAN_ENDED]) { @@ -77,7 +77,7 @@ function serverStreamAndBidiHandler( endSpan(); }); - call.on("error", (err: grpcJs.ServiceError) => { + call.on('error', (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -155,9 +155,9 @@ export function handleServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case "unary": - case "clientStream": - case "client_stream": + case 'unary': + case 'clientStream': + case 'client_stream': return clientStreamAndUnaryHandler( span, call, @@ -166,9 +166,9 @@ export function handleServerFunction( | grpcJs.handleUnaryCall | grpcJs.ClientReadableStream ); - case "serverStream": - case "server_stream": - case "bidi": + case 'serverStream': + case 'server_stream': + case 'bidi': return serverStreamAndBidiHandler( span, call, @@ -192,13 +192,13 @@ export function handleUntracedServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case "unary": - case "clientStream": - case "client_stream": + case 'unary': + case 'clientStream': + case 'client_stream': return (originalFunc as Function).call({}, call, callback); - case "serverStream": - case "server_stream": - case "bidi": + case 'serverStream': + case 'server_stream': + case 'bidi': return (originalFunc as Function).call({}, call); default: break; @@ -213,7 +213,7 @@ export function shouldNotTraceServerCall( methodName: string, ignoreGrpcMethods?: IgnoreMatcher[] ): boolean { - const parsedName = methodName.split("/"); + const parsedName = methodName.split('/'); return _methodIsIgnored( parsedName[parsedName.length - 1] || methodName, ignoreGrpcMethods diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts index 2aad1b653e8..503ab2c1362 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/clientUtils.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import type * as grpcTypes from "grpc"; -import type * as events from "events"; -import { SendUnaryDataCallback, GrpcClientFunc } from "./types"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +import type * as grpcTypes from 'grpc'; +import type * as events from 'events'; +import { SendUnaryDataCallback, GrpcClientFunc } from './types'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { context, Span, @@ -25,13 +25,13 @@ import { SpanKind, SpanStatus, propagation, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { _grpcStatusCodeToSpanStatus, _grpcStatusCodeToOpenTelemetryStatusCode, findIndex, -} from "../utils"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { AttributeNames } from '../enums'; /** * This method handles the client remote call @@ -86,8 +86,8 @@ export const makeGrpcClientRemoteCall = function ( // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, (arg) => { - return typeof arg === "function"; + const callbackFuncIndex = findIndex(args, arg => { + return typeof arg === 'function'; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -98,7 +98,7 @@ export const makeGrpcClientRemoteCall = function ( } } - span.addEvent("sent"); + span.addEvent('sent'); span.setAttributes({ [AttributeNames.GRPC_METHOD]: original.path, [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, @@ -120,7 +120,7 @@ export const makeGrpcClientRemoteCall = function ( }; context.bind(call); ((call as unknown) as events.EventEmitter).on( - "error", + 'error', (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), @@ -135,7 +135,7 @@ export const makeGrpcClientRemoteCall = function ( ); ((call as unknown) as events.EventEmitter).on( - "status", + 'status', (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( @@ -164,9 +164,9 @@ export const getMetadata = function ( let metadataIndex = findIndex(args, (arg: any) => { return ( arg && - typeof arg === "object" && + typeof arg === 'object' && arg._internal_repr && - typeof arg.getMap === "function" + typeof arg.getMap === 'function' ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index 32fd4300ddc..25bf00bb2c1 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -14,21 +14,21 @@ * limitations under the License. */ -import type * as grpcTypes from "grpc"; +import type * as grpcTypes from 'grpc'; import { InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, InstrumentationBase, InstrumentationConfig, isWrapped, -} from "@opentelemetry/instrumentation"; +} from '@opentelemetry/instrumentation'; import { GrpcInternalClientTypes, ServerCallWithMeta, SendUnaryDataCallback, GrpcClientFunc, -} from "./types"; -import { GrpcInstrumentationConfig } from "../types"; +} from './types'; +import { GrpcInstrumentationConfig } from '../types'; import { context, propagation, @@ -36,15 +36,15 @@ import { SpanKind, setSpan, diag, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { clientStreamAndUnaryHandler, shouldNotTraceServerCall, serverStreamAndBidiHandler, -} from "./serverUtils"; -import { makeGrpcClientRemoteCall, getMetadata } from "./clientUtils"; -import { _methodIsIgnored } from "../utils"; -import { AttributeNames } from "../enums"; +} from './serverUtils'; +import { makeGrpcClientRemoteCall, getMetadata } from './clientUtils'; +import { _methodIsIgnored } from '../utils'; +import { AttributeNames } from '../enums'; /** * Holding reference to grpc module here to access constant of grpc modules @@ -72,26 +72,26 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< init() { return [ new InstrumentationNodeModuleDefinition( - "grpc", - ["1.*"], + 'grpc', + ['1.*'], (moduleExports, version) => { diag.debug(`Applying patch for grpc@${version}`); if (isWrapped(moduleExports.Server.prototype.register)) { - this._unwrap(moduleExports.Server.prototype, "register"); + this._unwrap(moduleExports.Server.prototype, 'register'); } grpcClient = moduleExports; this._wrap( moduleExports.Server.prototype, - "register", + 'register', this._patchServer(moduleExports) as any ); // Wrap the externally exported client constructor if (isWrapped(moduleExports.makeGenericClientConstructor)) { - this._unwrap(moduleExports, "makeGenericClientConstructor"); + this._unwrap(moduleExports, 'makeGenericClientConstructor'); } this._wrap( moduleExports, - "makeGenericClientConstructor", + 'makeGenericClientConstructor', this._patchClient(moduleExports) ); return moduleExports; @@ -100,7 +100,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< if (moduleExports === undefined) return; diag.debug(`Removing patch for grpc@${version}`); - this._unwrap(moduleExports.Server.prototype, "register"); + this._unwrap(moduleExports.Server.prototype, 'register'); }, this._getInternalPatchs() ), @@ -114,11 +114,11 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< ) => { diag.debug(`Applying internal patch for grpc@${version}`); if (isWrapped(moduleExports.makeClientConstructor)) { - this._unwrap(moduleExports, "makeClientConstructor"); + this._unwrap(moduleExports, 'makeClientConstructor'); } this._wrap( moduleExports, - "makeClientConstructor", + 'makeClientConstructor', this._patchClient(grpcClient) ); return moduleExports; @@ -129,18 +129,18 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< ) => { if (moduleExports === undefined) return; diag.debug(`Removing internal patch for grpc@${version}`); - this._unwrap(moduleExports, "makeClientConstructor"); + this._unwrap(moduleExports, 'makeClientConstructor'); }; return [ new InstrumentationNodeModuleFile( - "grpc/src/node/src/client.js", - ["0.13 - 1.6"], + 'grpc/src/node/src/client.js', + ['0.13 - 1.6'], onPatch, onUnPatch ), new InstrumentationNodeModuleFile( - "grpc/src/client.js", - ["^1.7"], + 'grpc/src/client.js', + ['^1.7'], onPatch, onUnPatch ), @@ -150,7 +150,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _patchServer(grpcModule: typeof grpcTypes) { const instrumentation = this; return (originalRegister: typeof grpcTypes.Server.prototype.register) => { - diag.debug("patched gRPC server"); + diag.debug('patched gRPC server'); return function register( this: grpcTypes.Server & { handlers: any }, @@ -165,7 +165,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< instrumentation._wrap( handlerSet, - "func", + 'func', (originalFunc: grpcTypes.handleCall) => { return function func( this: typeof handlerSet, @@ -175,31 +175,31 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< const self = this; if (shouldNotTraceServerCall.call(instrumentation, call, name)) { switch (type) { - case "unary": - case "client_stream": + case 'unary': + case 'client_stream': return (originalFunc as Function).call( self, call, callback ); - case "server_stream": - case "bidi": + case 'server_stream': + case 'bidi': return (originalFunc as Function).call(self, call); default: return originalResult; } } - const spanName = `grpc.${name.replace("/", "")}`; + const spanName = `grpc.${name.replace('/', '')}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug("patch func: %s", JSON.stringify(spanOptions)); + diag.debug('patch func: %s', JSON.stringify(spanOptions)); context.with( propagation.extract(context.active(), call.metadata, { get: (metadata, key) => metadata.get(key).map(String), - keys: (metadata) => Object.keys(metadata.getMap()), + keys: metadata => Object.keys(metadata.getMap()), }), () => { const span = instrumentation.tracer @@ -210,8 +210,8 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< context.with(setSpan(context.active(), span), () => { switch (type) { - case "unary": - case "client_stream": + case 'unary': + case 'client_stream': return clientStreamAndUnaryHandler( grpcModule, span, @@ -220,8 +220,8 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< originalFunc, self ); - case "server_stream": - case "bidi": + case 'server_stream': + case 'bidi': return serverStreamAndBidiHandler( span, call, @@ -246,7 +246,7 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _patchClient(grpcClient: typeof grpcTypes) { const instrumentation = this; return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug("patching client"); + diag.debug('patching client'); return function makeClientConstructor( this: typeof grpcTypes.Client, methods: { [key: string]: { originalName?: string } }, @@ -291,9 +291,9 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< private _getPatchedClientMethods(grpcClient: typeof grpcTypes) { const instrumentation = this; return (original: GrpcClientFunc) => { - diag.debug("patch all client methods"); + diag.debug('patch all client methods'); return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace("/", "")}`; + const name = `grpc.${original.path.replace('/', '')}`; const args = Array.prototype.slice.call(arguments); const metadata = getMetadata(grpcClient, original, args); const span = instrumentation.tracer.startSpan(name, { diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts index 4ea51bdf293..84a644a15f9 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts @@ -14,16 +14,16 @@ * limitations under the License. */ -import type * as grpcTypes from "grpc"; -import { SendUnaryDataCallback, ServerCallWithMeta } from "./types"; -import { GrpcNativeInstrumentation } from "./"; -import { context, Span, SpanStatusCode } from "@opentelemetry/api"; +import type * as grpcTypes from 'grpc'; +import { SendUnaryDataCallback, ServerCallWithMeta } from './types'; +import { GrpcNativeInstrumentation } from './'; +import { context, Span, SpanStatusCode } from '@opentelemetry/api'; import { _grpcStatusCodeToOpenTelemetryStatusCode, _grpcStatusCodeToSpanStatus, _methodIsIgnored, -} from "../utils"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { AttributeNames } from '../enums'; export const clientStreamAndUnaryHandler = function ( grpcClient: typeof grpcTypes, @@ -60,7 +60,7 @@ export const clientStreamAndUnaryHandler = function ( grpcClient.status.OK.toString() ); } - span.addEvent("received"); + span.addEvent('received'); // end the span span.end(); @@ -86,7 +86,7 @@ export const serverStreamAndBidiHandler = function ( }; context.bind(call); - call.on("finish", () => { + call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( AttributeNames.GRPC_STATUS_CODE, @@ -95,17 +95,17 @@ export const serverStreamAndBidiHandler = function ( // if there is an error, span will be ended on error event, otherwise end it here if (call.status.code === 0) { - span.addEvent("finished"); + span.addEvent('finished'); endSpan(); } }); - call.on("error", (err: grpcTypes.ServiceError) => { + call.on('error', (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.addEvent("finished with error"); + span.addEvent('finished with error'); span.setAttributes({ [AttributeNames.GRPC_ERROR_NAME]: err.name, [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, @@ -124,7 +124,7 @@ export const shouldNotTraceServerCall = function ( call: ServerCallWithMeta, name: string ): boolean { - const parsedName = name.split("/"); + const parsedName = name.split('/'); return _methodIsIgnored( parsedName[parsedName.length - 1] || name, this._config.ignoreGrpcMethods diff --git a/packages/opentelemetry-instrumentation-http/src/enums.ts b/packages/opentelemetry-instrumentation-http/src/enums.ts index 31c18afc460..f9b8be3c8ea 100644 --- a/packages/opentelemetry-instrumentation-http/src/enums.ts +++ b/packages/opentelemetry-instrumentation-http/src/enums.ts @@ -18,7 +18,7 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - HTTP_ERROR_NAME = "http.error_name", - HTTP_ERROR_MESSAGE = "http.error_message", - HTTP_STATUS_TEXT = "http.status_text", + HTTP_ERROR_NAME = 'http.error_name', + HTTP_ERROR_MESSAGE = 'http.error_message', + HTTP_STATUS_TEXT = 'http.status_text', } diff --git a/packages/opentelemetry-instrumentation-http/src/utils.ts b/packages/opentelemetry-instrumentation-http/src/utils.ts index 3d302d3aa78..4802ecca218 100644 --- a/packages/opentelemetry-instrumentation-http/src/utils.ts +++ b/packages/opentelemetry-instrumentation-http/src/utils.ts @@ -18,11 +18,11 @@ import { SpanStatusCode, Span, SpanStatus, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/semantic-conventions'; import { ClientRequest, IncomingHttpHeaders, @@ -30,11 +30,11 @@ import { OutgoingHttpHeaders, RequestOptions, ServerResponse, -} from "http"; -import { Socket } from "net"; -import * as url from "url"; -import { AttributeNames } from "./enums"; -import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; +} from 'http'; +import { Socket } from 'net'; +import * as url from 'url'; +import { AttributeNames } from './enums'; +import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; /** * Get an absolute url @@ -42,22 +42,22 @@ import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; export const getAbsoluteUrl = ( requestUrl: ParsedRequestOptions | null, headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = "http:" + fallbackProtocol = 'http:' ): string => { const reqUrlObject = requestUrl || {}; const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || "").toString(); - const path = reqUrlObject.path || "/"; + const port = (reqUrlObject.port || '').toString(); + const path = reqUrlObject.path || '/'; let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || "localhost"; + reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; // if there is no port in host and there is a port // it should be displayed if it's not 80 and 443 (default ports) if ( - (host as string).indexOf(":") === -1 && + (host as string).indexOf(':') === -1 && port && - port !== "80" && - port !== "443" + port !== '80' && + port !== '443' ) { host += `:${port}`; } @@ -69,7 +69,7 @@ export const getAbsoluteUrl = ( */ export const parseResponseStatus = ( statusCode: number -): Omit => { +): Omit => { // 1xx, 2xx, 3xx are OK if (statusCode >= 100 && statusCode < 400) { return { code: SpanStatusCode.OK }; @@ -89,7 +89,7 @@ export const hasExpectHeader = (options: RequestOptions): boolean => { } const keys = Object.keys(options.headers); - return !!keys.find((key) => key.toLowerCase() === "expect"); + return !!keys.find(key => key.toLowerCase() === 'expect'); }; /** @@ -101,14 +101,14 @@ export const satisfiesPattern = ( constant: string, pattern: IgnoreMatcher ): boolean => { - if (typeof pattern === "string") { + if (typeof pattern === 'string') { return pattern === constant; } else if (pattern instanceof RegExp) { return pattern.test(constant); - } else if (typeof pattern === "function") { + } else if (typeof pattern === 'function') { return pattern(constant); } else { - throw new TypeError("Pattern is in unsupported datatype"); + throw new TypeError('Pattern is in unsupported datatype'); } }; @@ -227,7 +227,7 @@ export const setResponseContentLengthAttribute = ( function getContentLength( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): number | null { - const contentLengthHeader = headers["content-length"]; + const contentLengthHeader = headers['content-length']; if (contentLengthHeader === undefined) return null; const contentLength = parseInt(contentLengthHeader as string, 10); @@ -239,9 +239,9 @@ function getContentLength( export const isCompressed = ( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): boolean => { - const encoding = headers["content-encoding"]; + const encoding = headers['content-encoding']; - return !!encoding && encoding !== "identity"; + return !!encoding && encoding !== 'identity'; }; /** @@ -254,13 +254,13 @@ export const getRequestInfo = ( options: url.URL | RequestOptions | string, extraOptions?: RequestOptions ) => { - let pathname = "/"; - let origin = ""; + let pathname = '/'; + let origin = ''; let optionsParsed: RequestOptions; - if (typeof options === "string") { + if (typeof options === 'string') { optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || "/"; - origin = `${optionsParsed.protocol || "http:"}//${optionsParsed.host}`; + pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; + origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; if (extraOptions !== undefined) { Object.assign(optionsParsed, extraOptions); } @@ -268,12 +268,12 @@ export const getRequestInfo = ( optionsParsed = { protocol: options.protocol, hostname: - typeof options.hostname === "string" && options.hostname.startsWith("[") + typeof options.hostname === 'string' && options.hostname.startsWith('[') ? options.hostname.slice(1, -1) : options.hostname, - path: `${options.pathname || ""}${options.search || ""}`, + path: `${options.pathname || ''}${options.search || ''}`, }; - if (options.port !== "") { + if (options.port !== '') { optionsParsed.port = Number(options.port); } if (options.username || options.password) { @@ -286,14 +286,14 @@ export const getRequestInfo = ( } } else { optionsParsed = Object.assign( - { protocol: options.host ? "http:" : undefined }, + { protocol: options.host ? 'http:' : undefined }, options ); pathname = (options as url.URL).pathname; if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || "/"; + pathname = url.parse(optionsParsed.path).pathname || '/'; } - origin = `${optionsParsed.protocol || "http:"}//${ + origin = `${optionsParsed.protocol || 'http:'}//${ optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` }`; } @@ -307,7 +307,7 @@ export const getRequestInfo = ( // ensure upperCase for consistency const method = optionsParsed.method ? optionsParsed.method.toUpperCase() - : "GET"; + : 'GET'; return { origin, pathname, method, optionsParsed }; }; @@ -322,7 +322,7 @@ export const isValidOptionsType = (options: unknown): boolean => { } const type = typeof options; - return type === "string" || (type === "object" && !Array.isArray(options)); + return type === 'string' || (type === 'object' && !Array.isArray(options)); }; /** @@ -337,12 +337,12 @@ export const getOutgoingRequestAttributes = ( const host = requestOptions.host; const hostname = requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || - "localhost"; + host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || + 'localhost'; const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : "GET"; + const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; const headers = requestOptions.headers || {}; - const userAgent = headers["user-agent"]; + const userAgent = headers['user-agent']; const attributes: SpanAttributes = { [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestOptions, @@ -350,7 +350,7 @@ export const getOutgoingRequestAttributes = ( `${options.component}:` ), [SemanticAttributes.HTTP_METHOD]: method, - [SemanticAttributes.HTTP_TARGET]: requestOptions.path || "/", + [SemanticAttributes.HTTP_TARGET]: requestOptions.path || '/', [SemanticAttributes.NET_PEER_NAME]: hostname, }; @@ -368,7 +368,7 @@ export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { attributes[SemanticAttributes.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== "QUIC") { + if (kind.toUpperCase() !== 'QUIC') { attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; @@ -398,7 +398,7 @@ export const getOutgoingRequestAttributesOnResponse = ( if (statusCode) { attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; attributes[AttributeNames.HTTP_STATUS_TEXT] = ( - statusMessage || "" + statusMessage || '' ).toUpperCase(); } @@ -416,16 +416,16 @@ export const getIncomingRequestAttributes = ( options: { component: string; serverName?: string } ): SpanAttributes => { const headers = request.headers; - const userAgent = headers["user-agent"]; - const ips = headers["x-forwarded-for"]; - const method = request.method || "GET"; + const userAgent = headers['user-agent']; + const ips = headers['x-forwarded-for']; + const method = request.method || 'GET'; const httpVersion = request.httpVersion; const requestUrl = request.url ? url.parse(request.url) : null; const host = requestUrl?.host || headers.host; const hostname = requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || - "localhost"; + host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || + 'localhost'; const serverName = options.serverName; const attributes: SpanAttributes = { [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( @@ -438,17 +438,17 @@ export const getIncomingRequestAttributes = ( [SemanticAttributes.HTTP_METHOD]: method, }; - if (typeof ips === "string") { - attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(",")[0]; + if (typeof ips === 'string') { + attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(',')[0]; } - if (typeof serverName === "string") { + if (typeof serverName === 'string') { attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || "/"; - attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || "/"; + attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; } if (userAgent !== undefined) { @@ -478,11 +478,11 @@ export const getIncomingRequestAttributesOnResponse = ( }; const route = Array.isArray(__ot_middlewares) ? __ot_middlewares - .filter((path) => path !== "/") - .map((path) => { - return path[0] === "/" ? path : "/" + path; + .filter(path => path !== '/') + .map(path => { + return path[0] === '/' ? path : '/' + path; }) - .join("") + .join('') : undefined; const attributes: SpanAttributes = { @@ -491,7 +491,7 @@ export const getIncomingRequestAttributesOnResponse = ( [SemanticAttributes.NET_PEER_IP]: remoteAddress, [SemanticAttributes.NET_PEER_PORT]: remotePort, [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, - [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || "").toUpperCase(), + [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), }; if (route !== undefined) { diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts index fa5fa73019f..2361b26756c 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/http-enable.test.ts @@ -21,44 +21,44 @@ import { SpanKind, getSpan, setSpan, -} from "@opentelemetry/api"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/api'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; +} from '@opentelemetry/tracing'; import { NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as nock from "nock"; -import * as path from "path"; -import { HttpInstrumentation } from "../../src/http"; -import { HttpInstrumentationConfig } from "../../src/types"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { httpRequest } from "../utils/httpRequest"; -import { ContextManager } from "@opentelemetry/api"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import type { ClientRequest, IncomingMessage, ServerResponse } from "http"; -import { isWrapped } from "@opentelemetry/instrumentation"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as nock from 'nock'; +import * as path from 'path'; +import { HttpInstrumentation } from '../../src/http'; +import { HttpInstrumentationConfig } from '../../src/types'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpRequest } from '../utils/httpRequest'; +import { ContextManager } from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import type { ClientRequest, IncomingMessage, ServerResponse } from 'http'; +import { isWrapped } from '@opentelemetry/instrumentation'; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from "http"; +import * as http from 'http'; const applyCustomAttributesOnSpanErrorMessage = - "bad applyCustomAttributesOnSpan function"; + 'bad applyCustomAttributesOnSpan function'; let server: http.Server; const serverPort = 22346; -const protocol = "http"; -const hostname = "localhost"; -const pathname = "/test"; -const serverName = "my.server.name"; +const protocol = 'http'; +const hostname = 'localhost'; +const pathname = '/test'; +const serverName = 'my.server.name'; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); @@ -79,24 +79,24 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; export const requestHookFunction = ( span: ISpan, request: ClientRequest | IncomingMessage ): void => { - span.setAttribute("custom request hook attribute", "request"); + span.setAttribute('custom request hook attribute', 'request'); }; export const responseHookFunction = ( span: ISpan, response: IncomingMessage | ServerResponse ): void => { - span.setAttribute("custom response hook attribute", "response"); + span.setAttribute('custom response hook attribute', 'response'); }; -describe("HttpInstrumentation", () => { +describe('HttpInstrumentation', () => { let contextManager: ContextManager; before(() => { @@ -116,8 +116,8 @@ describe("HttpInstrumentation", () => { context.disable(); }); - describe("enable()", () => { - describe("with bad instrumentation options", () => { + describe('enable()', () => { + describe('with bad instrumentation options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -126,12 +126,12 @@ describe("HttpInstrumentation", () => { const config: HttpInstrumentationConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error("bad ignoreIncomingPaths function"); + throw new Error('bad ignoreIncomingPaths function'); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error("bad ignoreOutgoingUrls function"); + throw new Error('bad ignoreOutgoingUrls function'); }, ], applyCustomAttributesOnSpan: () => { @@ -141,7 +141,7 @@ describe("HttpInstrumentation", () => { instrumentation.setConfig(config); instrumentation.enable(); server = http.createServer((request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort); @@ -152,7 +152,7 @@ describe("HttpInstrumentation", () => { instrumentation.disable(); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -165,7 +165,7 @@ describe("HttpInstrumentation", () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); @@ -182,7 +182,7 @@ describe("HttpInstrumentation", () => { }); }); - describe("with good instrumentation options", () => { + describe('with good instrumentation options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -190,14 +190,14 @@ describe("HttpInstrumentation", () => { before(() => { instrumentation.setConfig({ ignoreIncomingPaths: [ - "/ignored/string", + '/ignored/string', /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], applyCustomAttributesOnSpan: customAttributeFunction, requestHook: requestHookFunction, @@ -206,10 +206,10 @@ describe("HttpInstrumentation", () => { }); instrumentation.enable(); server = http.createServer((request, response) => { - if (request.url?.includes("/ignored")) { - provider.getTracer("test").startSpan("some-span").end(); + if (request.url?.includes('/ignored')) { + provider.getTracer('test').startSpan('some-span').end(); } - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort); @@ -224,13 +224,13 @@ describe("HttpInstrumentation", () => { assert.strictEqual(isWrapped(http.Server.prototype.emit), true); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - "x-forwarded-for": ", , ", - "user-agent": "chrome", + 'x-forwarded-for': ', , ', + 'user-agent': 'chrome', }, } ); @@ -243,14 +243,14 @@ describe("HttpInstrumentation", () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', serverName, }; assert.strictEqual(spans.length, 2); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - "" + '' ); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], @@ -266,7 +266,7 @@ describe("HttpInstrumentation", () => { ].forEach(({ span, kind }) => { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], - "1.1" + '1.1' ); assert.strictEqual( span.attributes[SemanticAttributes.NET_TRANSPORT], @@ -292,7 +292,7 @@ describe("HttpInstrumentation", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/1"; + const testPath = '/outgoing/rootSpan/1'; doNock( hostname, @@ -316,22 +316,22 @@ describe("HttpInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assertSpan(reqSpan, SpanKind.CLIENT, validations); }); } - it("should create a child span for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 200, "Ok"); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + it('should create a child span for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 200, 'Ok'); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -342,16 +342,16 @@ describe("HttpInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -366,15 +366,15 @@ describe("HttpInstrumentation", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/childs/1"; + const testPath = '/outgoing/rootSpan/childs/1'; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -385,16 +385,16 @@ describe("HttpInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -408,17 +408,17 @@ describe("HttpInstrumentation", () => { }); } - it("should create multiple child spans for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs"; + it('should create multiple child spans for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs'; const num = 5; - doNock(hostname, testPath, 200, "Ok", num); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + doNock(hostname, testPath, 200, 'Ok', num); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, "HTTP GET"); + assert.strictEqual(spans[i].name, 'HTTP GET'); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -431,7 +431,7 @@ describe("HttpInstrumentation", () => { }); }); - for (const ignored of ["string", "function", "regexp"]) { + for (const ignored of ['string', 'function', 'regexp']) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -443,7 +443,7 @@ describe("HttpInstrumentation", () => { }); } - for (const arg of ["string", {}, new Date()]) { + for (const arg of ['string', {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -452,14 +452,14 @@ describe("HttpInstrumentation", () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith("Nock: No match for request")); + assert.ok(error.message.startsWith('Nock: No match for request')); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, ""]) { + for (const arg of [true, 1, false, 0, '']) { it(`should not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -470,7 +470,7 @@ describe("HttpInstrumentation", () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize("/node_modules/nock/lib/intercept.js") + path.normalize('/node_modules/nock/lib/intercept.js') ) > 0 ); } @@ -482,26 +482,26 @@ describe("HttpInstrumentation", () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - http.request({ protocol: "telnet" }); + http.request({ protocol: 'telnet' }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -522,7 +522,7 @@ describe("HttpInstrumentation", () => { nock.cleanAll(); nock.enableNetConnect(); try { - http.request({ protocol: "telnet" }); + http.request({ protocol: 'telnet' }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -530,19 +530,19 @@ describe("HttpInstrumentation", () => { } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -559,28 +559,28 @@ describe("HttpInstrumentation", () => { } }); - it("should have 1 ended span when request is aborted", async () => { + it('should have 1 ended span when request is aborted', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .socketDelay(50) - .reply(200, ""); + .reply(200, ''); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject("timeout"); + reject('timeout'); }); return req.end(); }); @@ -597,9 +597,9 @@ describe("HttpInstrumentation", () => { } }); - it("should have 1 ended span when request is aborted after receiving response", async () => { + it('should have 1 ended span when request is aborted after receiving response', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .delay({ body: 50, }) @@ -609,12 +609,12 @@ describe("HttpInstrumentation", () => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { req.abort(); data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } @@ -635,11 +635,11 @@ describe("HttpInstrumentation", () => { } }); - it("should have 1 ended span when request doesn't listening response", (done) => { + it("should have 1 ended span when request doesn't listening response", done => { nock.cleanAll(); nock.enableNetConnect(); const req = http.request(`${protocol}://${hostname}/`); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -649,13 +649,13 @@ describe("HttpInstrumentation", () => { req.end(); }); - it("should have 1 ended span when response is listened by using req.on('response')", (done) => { + it("should have 1 ended span when response is listened by using req.on('response')", done => { const host = `${protocol}://${hostname}`; - nock(host).get("/").reply(404); + nock(host).get('/').reply(404); const req = http.request(`${host}/`); - req.on("response", (response) => { - response.on("data", () => {}); - response.on("end", () => { + req.on('response', response => { + response.on('data', () => {}); + response.on('end', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -671,7 +671,7 @@ describe("HttpInstrumentation", () => { req.end(); }); - it("custom attributes should show up on client and server spans", async () => { + it('custom attributes should show up on client and server spans', async () => { await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -679,42 +679,42 @@ describe("HttpInstrumentation", () => { const [incomingSpan, outgoingSpan] = spans; assert.strictEqual( - incomingSpan.attributes["custom request hook attribute"], - "request" + incomingSpan.attributes['custom request hook attribute'], + 'request' ); assert.strictEqual( - incomingSpan.attributes["custom response hook attribute"], - "response" + incomingSpan.attributes['custom response hook attribute'], + 'response' ); assert.strictEqual( - incomingSpan.attributes["span kind"], + incomingSpan.attributes['span kind'], SpanKind.CLIENT ); assert.strictEqual( - outgoingSpan.attributes["custom request hook attribute"], - "request" + outgoingSpan.attributes['custom request hook attribute'], + 'request' ); assert.strictEqual( - outgoingSpan.attributes["custom response hook attribute"], - "response" + outgoingSpan.attributes['custom response hook attribute'], + 'response' ); assert.strictEqual( - outgoingSpan.attributes["span kind"], + outgoingSpan.attributes['span kind'], SpanKind.CLIENT ); }); - it("should not set span as active in context for outgoing request", (done) => { + it('should not set span as active in context for outgoing request', done => { assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, (res) => { + http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { assert.deepStrictEqual(getSpan(context.active()), undefined); - res.on("data", () => { + res.on('data', () => { assert.deepStrictEqual(getSpan(context.active()), undefined); }); - res.on("end", () => { + res.on('end', () => { assert.deepStrictEqual(getSpan(context.active()), undefined); done(); }); @@ -722,13 +722,13 @@ describe("HttpInstrumentation", () => { }); }); - describe("with require parent span", () => { - beforeEach((done) => { + describe('with require parent span', () => { + beforeEach(done => { memoryExporter.reset(); instrumentation.setConfig({}); instrumentation.enable(); server = http.createServer((request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort, done); }); @@ -738,14 +738,14 @@ describe("HttpInstrumentation", () => { instrumentation.disable(); }); - it("should not trace without parent with options enabled (both client & server)", async () => { + it('should not trace without parent with options enabled (both client & server)', async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = "/test/test"; + const testPath = '/test/test'; await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -753,13 +753,13 @@ describe("HttpInstrumentation", () => { assert.strictEqual(spans.length, 0); }); - it("should not trace without parent with options enabled (client only)", async () => { + it('should not trace without parent with options enabled (client only)', async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = "/test/test"; + const testPath = '/test/test'; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -772,18 +772,18 @@ describe("HttpInstrumentation", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every((span) => span.kind === SpanKind.SERVER), + spans.every(span => span.kind === SpanKind.SERVER), true ); }); - it("should not trace without parent with options enabled (server only)", async () => { + it('should not trace without parent with options enabled (server only)', async () => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, }); instrumentation.enable(); - const testPath = "/test/test"; + const testPath = '/test/test'; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -796,27 +796,27 @@ describe("HttpInstrumentation", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every((span) => span.kind === SpanKind.CLIENT), + spans.every(span => span.kind === SpanKind.CLIENT), true ); }); - it("should trace with parent with both requireParent options enabled", (done) => { + it('should trace with parent with both requireParent options enabled', done => { instrumentation.disable(); instrumentation.setConfig({ requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }); instrumentation.enable(); - const testPath = "/test/test"; - const tracer = provider.getTracer("default"); - const span = tracer.startSpan("parentSpan", { + const testPath = '/test/test'; + const tracer = provider.getTracer('default'); + const span = tracer.startSpan('parentSpan', { kind: SpanKind.INTERNAL, }); context.with(setSpan(context.active(), span), () => { httpRequest .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then((result) => { + .then(result => { span.end(); assert( result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== @@ -829,11 +829,11 @@ describe("HttpInstrumentation", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2); assert.strictEqual( - spans.filter((span) => span.kind === SpanKind.CLIENT).length, + spans.filter(span => span.kind === SpanKind.CLIENT).length, 1 ); assert.strictEqual( - spans.filter((span) => span.kind === SpanKind.INTERNAL).length, + spans.filter(span => span.kind === SpanKind.INTERNAL).length, 1 ); return done(); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts index b27298007a1..fe473f408f8 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/https-enable.test.ts @@ -21,49 +21,49 @@ import { Span as ISpan, SpanKind, setSpan, -} from "@opentelemetry/api"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { ContextManager } from "@opentelemetry/api"; +} from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { ContextManager } from '@opentelemetry/api'; import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; +} from '@opentelemetry/tracing'; import { NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as fs from "fs"; -import * as semver from "semver"; -import * as nock from "nock"; -import * as path from "path"; -import { HttpInstrumentation } from "../../src/http"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { isWrapped } from "@opentelemetry/instrumentation"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as semver from 'semver'; +import * as nock from 'nock'; +import * as path from 'path'; +import { HttpInstrumentation } from '../../src/http'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { isWrapped } from '@opentelemetry/instrumentation'; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from "http"; -import * as https from "https"; -import { httpsRequest } from "../utils/httpsRequest"; +import * as http from 'http'; +import * as https from 'https'; +import { httpsRequest } from '../utils/httpsRequest'; const applyCustomAttributesOnSpanErrorMessage = - "bad applyCustomAttributesOnSpan function"; + 'bad applyCustomAttributesOnSpan function'; let server: https.Server; const serverPort = 32345; -const protocol = "https"; -const hostname = "localhost"; -const serverName = "my.server.name"; -const pathname = "/test"; +const protocol = 'https'; +const hostname = 'localhost'; +const serverName = 'my.server.name'; +const pathname = '/test'; const memoryExporter = new InMemorySpanExporter(); const provider = new BasicTracerProvider(); instrumentation.setTracerProvider(provider); -const tracer = provider.getTracer("test-https"); +const tracer = provider.getTracer('test-https'); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); function doNock( @@ -81,10 +81,10 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpsInstrumentation", () => { +describe('HttpsInstrumentation', () => { let contextManager: ContextManager; beforeEach(() => { @@ -99,8 +99,8 @@ describe("HttpsInstrumentation", () => { propagation.disable(); }); - describe("enable()", () => { - describe("with bad instrumentation options", () => { + describe('enable()', () => { + describe('with bad instrumentation options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -109,12 +109,12 @@ describe("HttpsInstrumentation", () => { instrumentation.setConfig({ ignoreIncomingPaths: [ (url: string) => { - throw new Error("bad ignoreIncomingPaths function"); + throw new Error('bad ignoreIncomingPaths function'); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error("bad ignoreOutgoingUrls function"); + throw new Error('bad ignoreOutgoingUrls function'); }, ], applyCustomAttributesOnSpan: () => { @@ -124,11 +124,11 @@ describe("HttpsInstrumentation", () => { instrumentation.enable(); server = https.createServer( { - key: fs.readFileSync("test/fixtures/server-key.pem"), - cert: fs.readFileSync("test/fixtures/server-cert.pem"), + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), }, (request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); } ); @@ -140,7 +140,7 @@ describe("HttpsInstrumentation", () => { instrumentation.disable(); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -153,7 +153,7 @@ describe("HttpsInstrumentation", () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); @@ -169,7 +169,7 @@ describe("HttpsInstrumentation", () => { ); }); }); - describe("with good instrumentation options", () => { + describe('with good instrumentation options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -177,14 +177,14 @@ describe("HttpsInstrumentation", () => { before(() => { instrumentation.setConfig({ ignoreIncomingPaths: [ - "/ignored/string", + '/ignored/string', /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], applyCustomAttributesOnSpan: customAttributeFunction, serverName, @@ -192,14 +192,14 @@ describe("HttpsInstrumentation", () => { instrumentation.enable(); server = https.createServer( { - key: fs.readFileSync("test/fixtures/server-key.pem"), - cert: fs.readFileSync("test/fixtures/server-cert.pem"), + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), }, (request, response) => { - if (request.url?.includes("/ignored")) { - tracer.startSpan("some-span").end(); + if (request.url?.includes('/ignored')) { + tracer.startSpan('some-span').end(); } - response.end("Test Server Response"); + response.end('Test Server Response'); } ); @@ -215,13 +215,13 @@ describe("HttpsInstrumentation", () => { assert.strictEqual(isWrapped(https.Server.prototype.emit), true); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - "x-forwarded-for": ", , ", - "user-agent": "chrome", + 'x-forwarded-for': ', , ', + 'user-agent': 'chrome', }, } ); @@ -234,14 +234,14 @@ describe("HttpsInstrumentation", () => { pathname, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', serverName, }; assert.strictEqual(spans.length, 2); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - "" + '' ); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], @@ -258,7 +258,7 @@ describe("HttpsInstrumentation", () => { ].forEach(({ span, kind }) => { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], - "1.1" + '1.1' ); assert.strictEqual( span.attributes[SemanticAttributes.NET_TRANSPORT], @@ -272,7 +272,7 @@ describe("HttpsInstrumentation", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/1"; + const testPath = '/outgoing/rootSpan/1'; doNock( hostname, @@ -296,21 +296,21 @@ describe("HttpsInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assertSpan(reqSpan, SpanKind.CLIENT, validations); }); } - it("should create a child span for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 200, "Ok"); - const name = "TestRootSpan"; + it('should create a child span for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 200, 'Ok'); + const name = 'TestRootSpan'; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -322,16 +322,16 @@ describe("HttpsInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTPS GET"); + assert.strictEqual(reqSpan.name, 'HTTPS GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -346,14 +346,14 @@ describe("HttpsInstrumentation", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/childs/1"; + const testPath = '/outgoing/rootSpan/childs/1'; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = "TestRootSpan"; + const name = 'TestRootSpan'; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -365,16 +365,16 @@ describe("HttpsInstrumentation", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTPS GET"); + assert.strictEqual(reqSpan.name, 'HTTPS GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -388,17 +388,17 @@ describe("HttpsInstrumentation", () => { }); } - it("should create multiple child spans for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs"; + it('should create multiple child spans for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs'; const num = 5; - doNock(hostname, testPath, 200, "Ok", num); - const name = "TestRootSpan"; + doNock(hostname, testPath, 200, 'Ok', num); + const name = 'TestRootSpan'; const span = tracer.startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpsRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, "HTTPS GET"); + assert.strictEqual(spans[i].name, 'HTTPS GET'); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -411,7 +411,7 @@ describe("HttpsInstrumentation", () => { }); }); - for (const ignored of ["string", "function", "regexp"]) { + for (const ignored of ['string', 'function', 'regexp']) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -423,7 +423,7 @@ describe("HttpsInstrumentation", () => { }); } - for (const arg of ["string", {}, new Date()]) { + for (const arg of ['string', {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -432,14 +432,14 @@ describe("HttpsInstrumentation", () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith("Nock: No match for request")); + assert.ok(error.message.startsWith('Nock: No match for request')); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, ""]) { + for (const arg of [true, 1, false, 0, '']) { it(`should not throw exception in https instrumentation when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -450,7 +450,7 @@ describe("HttpsInstrumentation", () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize("/node_modules/nock/lib/intercept.js") + path.normalize('/node_modules/nock/lib/intercept.js') ) > 0 ); } @@ -462,26 +462,26 @@ describe("HttpsInstrumentation", () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - https.request({ protocol: "telnet" }); + https.request({ protocol: 'telnet' }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -502,7 +502,7 @@ describe("HttpsInstrumentation", () => { nock.cleanAll(); nock.enableNetConnect(); try { - https.request({ protocol: "telnet" }); + https.request({ protocol: 'telnet' }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -513,26 +513,26 @@ describe("HttpsInstrumentation", () => { */ assert.strictEqual( spans.length, - semver.gt(process.version, "9.0.0") ? 1 : 2 + semver.gt(process.version, '9.0.0') ? 1 : 2 ); } }); it('should have 2 ended spans when provided "options" are an object without a constructor', async () => { // Related issue: https://github.com/open-telemetry/opentelemetry-js/issues/2008 - const testPath = "/outgoing/test"; + const testPath = '/outgoing/test'; const options = Object.create(null); options.hostname = hostname; options.port = serverPort; options.path = pathname; - options.method = "GET"; + options.method = 'GET'; - doNock(hostname, testPath, 200, "Ok"); + doNock(hostname, testPath, 200, 'Ok'); const promiseRequest = new Promise((resolve, _reject) => { const req = https.request(options, (resp: http.IncomingMessage) => { - resp.on("data", () => {}); - resp.on("end", () => { + resp.on('data', () => {}); + resp.on('end', () => { resolve({}); }); }); @@ -544,19 +544,19 @@ describe("HttpsInstrumentation", () => { assert.strictEqual(spans.length, 2); }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -573,28 +573,28 @@ describe("HttpsInstrumentation", () => { } }); - it("should have 1 ended span when request is aborted", async () => { + it('should have 1 ended span when request is aborted', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .socketDelay(50) - .reply(200, ""); + .reply(200, ''); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject("timeout"); + reject('timeout'); }); return req.end(); }); @@ -611,9 +611,9 @@ describe("HttpsInstrumentation", () => { } }); - it("should have 1 ended span when request is aborted after receiving response", async () => { + it('should have 1 ended span when request is aborted after receiving response', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .delay({ body: 50, }) @@ -623,12 +623,12 @@ describe("HttpsInstrumentation", () => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { req.abort(); data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } @@ -649,13 +649,13 @@ describe("HttpsInstrumentation", () => { } }); - it("should have 1 ended span when response is listened by using req.on('response')", (done) => { + it("should have 1 ended span when response is listened by using req.on('response')", done => { const host = `${protocol}://${hostname}`; - nock(host).get("/").reply(404); + nock(host).get('/').reply(404); const req = https.request(`${host}/`); - req.on("response", (response) => { - response.on("data", () => {}); - response.on("end", () => { + req.on('response', response => { + response.on('data', () => {}); + response.on('end', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); diff --git a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts index e3e7ab24ca7..2e5fdcce13e 100644 --- a/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/functionals/utils.test.ts @@ -19,56 +19,56 @@ import { ROOT_CONTEXT, SpanKind, TraceFlags, -} from "@opentelemetry/api"; -import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import { IncomingMessage, ServerResponse } from "http"; -import { Socket } from "net"; -import * as sinon from "sinon"; -import * as url from "url"; -import { IgnoreMatcher } from "../../src/types"; -import * as utils from "../../src/utils"; -import { AttributeNames } from "../../src/enums"; - -describe("Utility", () => { - describe("parseResponseStatus()", () => { - it("should return ERROR code by default", () => { +} from '@opentelemetry/api'; +import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import { IncomingMessage, ServerResponse } from 'http'; +import { Socket } from 'net'; +import * as sinon from 'sinon'; +import * as url from 'url'; +import { IgnoreMatcher } from '../../src/types'; +import * as utils from '../../src/utils'; +import { AttributeNames } from '../../src/enums'; + +describe('Utility', () => { + describe('parseResponseStatus()', () => { + it('should return ERROR code by default', () => { const status = utils.parseResponseStatus( (undefined as unknown) as number ); assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); }); - it("should return OK for Success HTTP status code", () => { + it('should return OK for Success HTTP status code', () => { for (let index = 100; index < 400; index++) { const status = utils.parseResponseStatus(index); assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); } }); - it("should not return OK for Bad HTTP status code", () => { + it('should not return OK for Bad HTTP status code', () => { for (let index = 400; index <= 600; index++) { const status = utils.parseResponseStatus(index); assert.notStrictEqual(status.code, SpanStatusCode.OK); } }); }); - describe("hasExpectHeader()", () => { - it("should throw if no option", () => { + describe('hasExpectHeader()', () => { + it('should throw if no option', () => { try { - utils.hasExpectHeader("" as http.RequestOptions); + utils.hasExpectHeader('' as http.RequestOptions); assert.fail(); } catch (ignore) {} }); - it("should not throw if no headers", () => { + it('should not throw if no headers', () => { const result = utils.hasExpectHeader({} as http.RequestOptions); assert.strictEqual(result, false); }); - it("should return true on Expect (no case sensitive)", () => { + it('should return true on Expect (no case sensitive)', () => { for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { const result = utils.hasExpectHeader({ headers, @@ -78,9 +78,9 @@ describe("Utility", () => { }); }); - describe("getRequestInfo()", () => { - it("should get options object", () => { - const webUrl = "http://u:p@google.fr/aPath?qu=ry"; + describe('getRequestInfo()', () => { + it('should get options object', () => { + const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; const urlParsed = url.parse(webUrl); const urlParsedWithoutPathname = { ...urlParsed, @@ -94,64 +94,64 @@ describe("Utility", () => { whatWgUrl, ]) { const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, "google.fr"); - assert.strictEqual(result.optionsParsed.protocol, "http:"); - assert.strictEqual(result.optionsParsed.path, "/aPath?qu=ry"); - assert.strictEqual(result.pathname, "/aPath"); - assert.strictEqual(result.origin, "http://google.fr"); + assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); + assert.strictEqual(result.optionsParsed.protocol, 'http:'); + assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); + assert.strictEqual(result.pathname, '/aPath'); + assert.strictEqual(result.origin, 'http://google.fr'); } }); }); - describe("satisfiesPattern()", () => { - it("string pattern", () => { - const answer1 = utils.satisfiesPattern("/test/1", "/test/1"); + describe('satisfiesPattern()', () => { + it('string pattern', () => { + const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern("/test/1", "/test/11"); + const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); assert.strictEqual(answer2, false); }); - it("regex pattern", () => { - const answer1 = utils.satisfiesPattern("/TeSt/1", /\/test/i); + it('regex pattern', () => { + const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern("/2/tEst/1", /\/test/); + const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); assert.strictEqual(answer2, false); }); - it("should throw if type is unknown", () => { + it('should throw if type is unknown', () => { try { - utils.satisfiesPattern("/TeSt/1", (true as unknown) as IgnoreMatcher); + utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); assert.fail(); } catch (error) { assert.strictEqual(error instanceof TypeError, true); } }); - it("function pattern", () => { + it('function pattern', () => { const answer1 = utils.satisfiesPattern( - "/test/home", - (url: string) => url === "/test/home" + '/test/home', + (url: string) => url === '/test/home' ); assert.strictEqual(answer1, true); const answer2 = utils.satisfiesPattern( - "/test/home", - (url: string) => url !== "/test/home" + '/test/home', + (url: string) => url !== '/test/home' ); assert.strictEqual(answer2, false); }); }); - describe("isIgnored()", () => { + describe('isIgnored()', () => { beforeEach(() => { - sinon.spy(utils, "satisfiesPattern"); + sinon.spy(utils, 'satisfiesPattern'); }); afterEach(() => { sinon.restore(); }); - it("should call isSatisfyPattern, n match", () => { - const answer1 = utils.isIgnored("/test/1", ["/test/11"]); + it('should call isSatisfyPattern, n match', () => { + const answer1 = utils.isIgnored('/test/1', ['/test/11']); assert.strictEqual(answer1, false); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, @@ -159,24 +159,24 @@ describe("Utility", () => { ); }); - it("should call isSatisfyPattern, match for function", () => { - const answer1 = utils.isIgnored("/test/1", [ - (url) => url.endsWith("/test/1"), + it('should call isSatisfyPattern, match for function', () => { + const answer1 = utils.isIgnored('/test/1', [ + url => url.endsWith('/test/1'), ]); assert.strictEqual(answer1, true); }); - it("should not re-throw when function throws an exception", () => { + it('should not re-throw when function throws an exception', () => { const onException = (e: Error) => { // Do nothing }; for (const callback of [undefined, onException]) { assert.doesNotThrow(() => utils.isIgnored( - "/test/1", + '/test/1', [ () => { - throw new Error("test"); + throw new Error('test'); }, ], callback @@ -185,14 +185,14 @@ describe("Utility", () => { } }); - it("should call onException when function throws an exception", () => { + it('should call onException when function throws an exception', () => { const onException = sinon.spy(); assert.doesNotThrow(() => utils.isIgnored( - "/test/1", + '/test/1', [ () => { - throw new Error("test"); + throw new Error('test'); }, ], onException @@ -201,58 +201,58 @@ describe("Utility", () => { assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); }); - it("should not call isSatisfyPattern", () => { - utils.isIgnored("/test/1", []); + it('should not call isSatisfyPattern', () => { + utils.isIgnored('/test/1', []); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, 0 ); }); - it("should return false on empty list", () => { - const answer1 = utils.isIgnored("/test/1", []); + it('should return false on empty list', () => { + const answer1 = utils.isIgnored('/test/1', []); assert.strictEqual(answer1, false); }); - it("should not throw and return false when list is undefined", () => { - const answer2 = utils.isIgnored("/test/1", undefined); + it('should not throw and return false when list is undefined', () => { + const answer2 = utils.isIgnored('/test/1', undefined); assert.strictEqual(answer2, false); }); }); - describe("getAbsoluteUrl()", () => { - it("should return absolute url with localhost", () => { - const path = "/test/1"; + describe('getAbsoluteUrl()', () => { + it('should return absolute url with localhost', () => { + const path = '/test/1'; const result = utils.getAbsoluteUrl(url.parse(path), {}); assert.strictEqual(result, `http://localhost${path}`); }); - it("should return absolute url", () => { - const absUrl = "http://www.google/test/1?query=1"; + it('should return absolute url', () => { + const absUrl = 'http://www.google/test/1?query=1'; const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); assert.strictEqual(result, absUrl); }); - it("should return default url", () => { + it('should return default url', () => { const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, "http://localhost/"); + assert.strictEqual(result, 'http://localhost/'); }); it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { const result = utils.getAbsoluteUrl( - { path: "/helloworld", port: 8080 }, + { path: '/helloworld', port: 8080 }, {} ); - assert.strictEqual(result, "http://localhost:8080/helloworld"); + assert.strictEqual(result, 'http://localhost:8080/helloworld'); }); }); - describe("setSpanWithError()", () => { - it("should have error attributes", () => { - const errorMessage = "test error"; + describe('setSpanWithError()', () => { + it('should have error attributes', () => { + const errorMessage = 'test error'; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider().getTracer("default"), + new BasicTracerProvider().getTracer('default'), ROOT_CONTEXT, - "test", - { spanId: "", traceId: "", traceFlags: TraceFlags.SAMPLED }, + 'test', + { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, SpanKind.INTERNAL ); /* tslint:disable-next-line:no-any */ @@ -267,15 +267,15 @@ describe("Utility", () => { }); }); - describe("isValidOptionsType()", () => { - ["", false, true, 1, 0, []].forEach((options) => { + describe('isValidOptionsType()', () => { + ['', false, true, 1, 0, []].forEach(options => { it(`should return false with the following value: ${JSON.stringify( options )}`, () => { assert.strictEqual(utils.isValidOptionsType(options), false); }); }); - for (const options of ["url", url.parse("http://url.com"), {}]) { + for (const options of ['url', url.parse('http://url.com'), {}]) { it(`should return true with the following value: ${JSON.stringify( options )}`, () => { @@ -284,20 +284,20 @@ describe("Utility", () => { } }); - describe("getIncomingRequestAttributesOnResponse()", () => { - it("should correctly parse the middleware stack if present", () => { + describe('getIncomingRequestAttributesOnResponse()', () => { + it('should correctly parse the middleware stack if present', () => { const request = { - __ot_middlewares: ["/test", "/toto", "/"], + __ot_middlewares: ['/test', '/toto', '/'], socket: {}, } as IncomingMessage & { __ot_middlewares?: string[] }; const attributes = utils.getIncomingRequestAttributesOnResponse(request, { socket: {}, } as ServerResponse & { socket: Socket }); - assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], "/test/toto"); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], '/test/toto'); }); - it("should succesfully process without middleware stack", () => { + it('should succesfully process without middleware stack', () => { const request = { socket: {}, } as IncomingMessage; @@ -330,13 +330,13 @@ describe("Utility", () => { } } - describe("setRequestContentLengthAttributes()", () => { - it("should set request content-length uncompressed attribute with no content-encoding header", () => { + describe('setRequestContentLengthAttributes()', () => { + it('should set request content-length uncompressed attribute with no content-encoding header', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", + 'content-length': '1200', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -351,8 +351,8 @@ describe("Utility", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", - "content-encoding": "identity", + 'content-length': '1200', + 'content-encoding': 'identity', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -367,8 +367,8 @@ describe("Utility", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", - "content-encoding": "gzip", + 'content-length': '1200', + 'content-encoding': 'gzip', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -380,14 +380,14 @@ describe("Utility", () => { }); }); - describe("setResponseContentLengthAttributes()", () => { - it("should set response content-length uncompressed attribute with no content-encoding header", () => { + describe('setResponseContentLengthAttributes()', () => { + it('should set response content-length uncompressed attribute with no content-encoding header', () => { const attributes: SpanAttributes = {}; const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", + 'content-length': '1200', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -404,8 +404,8 @@ describe("Utility", () => { const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", - "content-encoding": "identity", + 'content-length': '1200', + 'content-encoding': 'identity', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -423,8 +423,8 @@ describe("Utility", () => { const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", - "content-encoding": "gzip", + 'content-length': '1200', + 'content-encoding': 'gzip', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -436,12 +436,12 @@ describe("Utility", () => { ); }); - it("should set no attributes with no content-length header", () => { + it('should set no attributes with no content-length header', () => { const attributes: SpanAttributes = {}; const message = {} as IncomingMessage; message.headers = { - "content-encoding": "gzip", + 'content-encoding': 'gzip', }; utils.setResponseContentLengthAttribute(message, attributes); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts index 300919547cb..75d21dd7318 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/http-enable.test.ts @@ -14,51 +14,51 @@ * limitations under the License. */ -import { SpanKind, Span, context, propagation } from "@opentelemetry/api"; +import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { HttpFlavorValues, NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as url from "url"; -import { HttpInstrumentation } from "../../src/http"; -import { assertSpan } from "../utils/assertSpan"; -import * as utils from "../utils/utils"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as url from 'url'; +import { HttpInstrumentation } from '../../src/http'; +import { assertSpan } from '../utils/assertSpan'; +import * as utils from '../utils/utils'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +} from '@opentelemetry/tracing'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as http from "http"; -import { httpRequest } from "../utils/httpRequest"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { Socket } from "net"; -import { sendRequestTwice } from "../utils/rawRequest"; +import * as http from 'http'; +import { httpRequest } from '../utils/httpRequest'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { Socket } from 'net'; +import { sendRequestTwice } from '../utils/rawRequest'; -const protocol = "http"; +const protocol = 'http'; const serverPort = 32345; -const hostname = "localhost"; +const hostname = 'localhost'; const memoryExporter = new InMemorySpanExporter(); const customAttributeFunction = (span: Span): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpInstrumentation Integration tests", () => { +describe('HttpInstrumentation Integration tests', () => { let mockServerPort = 0; let mockServer: http.Server; const sockets: Array = []; - before((done) => { + before(done => { mockServer = http.createServer((req, res) => { res.statusCode = 200; - res.setHeader("content-type", "application/json"); + res.setHeader('content-type', 'application/json'); res.write( JSON.stringify({ success: true, @@ -70,17 +70,17 @@ describe("HttpInstrumentation Integration tests", () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error("unexpected addr null")); + done(new Error('unexpected addr null')); return; } - if (typeof addr === "string") { + if (typeof addr === 'string') { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error("Could not get port")); + done(new Error('Could not get port')); return; } mockServerPort = addr.port; @@ -88,8 +88,8 @@ describe("HttpInstrumentation Integration tests", () => { }); }); - after((done) => { - sockets.forEach((s) => s.destroy()); + after(done => { + sockets.forEach(s => s.destroy()); mockServer.close(done); }); @@ -106,7 +106,7 @@ describe("HttpInstrumentation Integration tests", () => { context.disable(); propagation.disable(); }); - describe("enable()", () => { + describe('enable()', () => { before(function (done) { // mandatory if (process.env.CI) { @@ -114,7 +114,7 @@ describe("HttpInstrumentation Integration tests", () => { return; } - utils.checkInternet((isConnected) => { + utils.checkInternet(isConnected => { if (!isConnected) { this.skip(); // don't disturb people @@ -134,7 +134,7 @@ describe("HttpInstrumentation Integration tests", () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ]; instrumentation.setConfig({ ignoreIncomingPaths: ignoreConfig, @@ -148,7 +148,7 @@ describe("HttpInstrumentation Integration tests", () => { instrumentation.disable(); }); - it("should create a rootSpan for GET requests and add propagation headers", async () => { + it('should create a rootSpan for GET requests and add propagation headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -157,25 +157,25 @@ describe("HttpInstrumentation Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { + it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -184,52 +184,52 @@ describe("HttpInstrumentation Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { + it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { "x-foo": "foo" }, + headers: { 'x-foo': 'foo' }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(result.reqHeaders["x-foo"], "foo"); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 @@ -241,62 +241,62 @@ describe("HttpInstrumentation Integration tests", () => { assertSpan(span, SpanKind.CLIENT, validations); }); - it("custom attributes should show up on client spans", async () => { + it('custom attributes should show up on client spans', async () => { const result = await httpRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a span for GET requests and add propagation headers with Expect headers", async () => { + it('should create a span for GET requests and add propagation headers with Expect headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: "100-continue" } }, + { headers: { Expect: '100-continue' } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 200, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "http", + component: 'http', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: "100-continue", "user-agent": "http-plugin-test" }, - { "user-agent": "http-plugin-test" }, + { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, + { 'user-agent': 'http-plugin-test' }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, (done) => { + )}`, done => { let validations: { hostname: string; httpStatusCode: number; @@ -305,7 +305,7 @@ describe("HttpInstrumentation Integration tests", () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ""; + let data = ''; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -317,15 +317,15 @@ describe("HttpInstrumentation Integration tests", () => { req: http.IncomingMessage; }; - resp.on("data", (chunk) => { + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 301, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -337,12 +337,12 @@ describe("HttpInstrumentation Integration tests", () => { } ); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); @@ -351,13 +351,13 @@ describe("HttpInstrumentation Integration tests", () => { }); } - it("should work for multiple active requests in keep-alive mode", async () => { + it('should work for multiple active requests in keep-alive mode', async () => { await sendRequestTwice(hostname, mockServerPort); const spans = memoryExporter.getFinishedSpans(); const span = spans.find((s: any) => s.kind === SpanKind.SERVER); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); }); }); }); diff --git a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts index 4a257c71fdd..729aa1fe912 100644 --- a/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-instrumentation-http/test/integrations/https-enable.test.ts @@ -14,62 +14,62 @@ * limitations under the License. */ -import { SpanKind, Span, context, propagation } from "@opentelemetry/api"; +import { SpanKind, Span, context, propagation } from '@opentelemetry/api'; import { HttpFlavorValues, NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as fs from "fs"; -import * as path from "path"; -import { Socket } from "net"; -import { assertSpan } from "../utils/assertSpan"; -import * as url from "url"; -import * as utils from "../utils/utils"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as fs from 'fs'; +import * as path from 'path'; +import { Socket } from 'net'; +import { assertSpan } from '../utils/assertSpan'; +import * as url from 'url'; +import * as utils from '../utils/utils'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { HttpInstrumentation } from "../../src"; +} from '@opentelemetry/tracing'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { HttpInstrumentation } from '../../src'; const instrumentation = new HttpInstrumentation(); instrumentation.enable(); instrumentation.disable(); -import * as https from "https"; -import { httpsRequest } from "../utils/httpsRequest"; -import { DummyPropagation } from "../utils/DummyPropagation"; +import * as https from 'https'; +import { httpsRequest } from '../utils/httpsRequest'; +import { DummyPropagation } from '../utils/DummyPropagation'; -const protocol = "https"; +const protocol = 'https'; const serverPort = 42345; -const hostname = "localhost"; +const hostname = 'localhost'; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpsInstrumentation Integration tests", () => { +describe('HttpsInstrumentation Integration tests', () => { let mockServerPort = 0; let mockServer: https.Server; const sockets: Array = []; - before((done) => { + before(done => { mockServer = https.createServer( { key: fs.readFileSync( - path.join(__dirname, "..", "fixtures", "server-key.pem") + path.join(__dirname, '..', 'fixtures', 'server-key.pem') ), cert: fs.readFileSync( - path.join(__dirname, "..", "fixtures", "server-cert.pem") + path.join(__dirname, '..', 'fixtures', 'server-cert.pem') ), }, (req, res) => { res.statusCode = 200; - res.setHeader("content-type", "application/json"); + res.setHeader('content-type', 'application/json'); res.write( JSON.stringify({ success: true, @@ -82,17 +82,17 @@ describe("HttpsInstrumentation Integration tests", () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error("unexpected addr null")); + done(new Error('unexpected addr null')); return; } - if (typeof addr === "string") { + if (typeof addr === 'string') { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error("Could not get port")); + done(new Error('Could not get port')); return; } mockServerPort = addr.port; @@ -100,8 +100,8 @@ describe("HttpsInstrumentation Integration tests", () => { }); }); - after((done) => { - sockets.forEach((s) => s.destroy()); + after(done => { + sockets.forEach(s => s.destroy()); mockServer.close(done); }); @@ -114,7 +114,7 @@ describe("HttpsInstrumentation Integration tests", () => { context.disable(); }); - describe("enable()", () => { + describe('enable()', () => { before(function (done) { // mandatory if (process.env.CI) { @@ -122,7 +122,7 @@ describe("HttpsInstrumentation Integration tests", () => { return; } - utils.checkInternet((isConnected) => { + utils.checkInternet(isConnected => { if (!isConnected) { this.skip(); // don't disturb people @@ -141,7 +141,7 @@ describe("HttpsInstrumentation Integration tests", () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ]; propagation.setGlobalPropagator(new DummyPropagation()); instrumentation.setConfig({ @@ -157,7 +157,7 @@ describe("HttpsInstrumentation Integration tests", () => { propagation.disable(); }); - it("should create a rootSpan for GET requests and add propagation headers", async () => { + it('should create a rootSpan for GET requests and add propagation headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -166,25 +166,25 @@ describe("HttpsInstrumentation Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(span.name, 'HTTPS GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { + it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -193,52 +193,52 @@ describe("HttpsInstrumentation Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(span.name, 'HTTPS GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { + it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpsRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { "x-foo": "foo" }, + headers: { 'x-foo': 'foo' }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); - assert.strictEqual(result.reqHeaders["x-foo"], "foo"); + assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 @@ -250,62 +250,62 @@ describe("HttpsInstrumentation Integration tests", () => { assertSpan(span, SpanKind.CLIENT, validations); }); - it("custom attributes should show up on client spans", async () => { + it('custom attributes should show up on client spans', async () => { const result = await httpsRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); - assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); + assert.strictEqual(span.name, 'HTTPS GET'); + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a span for GET requests and add propagation headers with Expect headers", async () => { + it('should create a span for GET requests and add propagation headers with Expect headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: "100-continue" } }, + { headers: { Expect: '100-continue' } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpsRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 200, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, - component: "https", + component: 'https', }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(span.name, 'HTTPS GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: "100-continue", "user-agent": "http-plugin-test" }, - { "user-agent": "http-plugin-test" }, + { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, + { 'user-agent': 'http-plugin-test' }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, (done) => { + )}`, done => { let validations: { hostname: string; httpStatusCode: number; @@ -314,7 +314,7 @@ describe("HttpsInstrumentation Integration tests", () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ""; + let data = ''; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -326,15 +326,15 @@ describe("HttpsInstrumentation Integration tests", () => { req: http.IncomingMessage; }; - resp.on("data", (chunk) => { + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 301, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -346,12 +346,12 @@ describe("HttpsInstrumentation Integration tests", () => { } ); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTPS GET"); + assert.strictEqual(span.name, 'HTTPS GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); diff --git a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts index d549f88bbc8..ff0229c1356 100644 --- a/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-instrumentation-http/test/utils/assertSpan.ts @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanKind, SpanStatus } from "@opentelemetry/api"; -import { hrTimeToNanoseconds } from "@opentelemetry/core"; -import { ReadableSpan } from "@opentelemetry/tracing"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as utils from "../../src/utils"; -import { DummyPropagation } from "./DummyPropagation"; -import { AttributeNames } from "../../src/enums"; +import { SpanKind, SpanStatus } from '@opentelemetry/api'; +import { hrTimeToNanoseconds } from '@opentelemetry/core'; +import { ReadableSpan } from '@opentelemetry/tracing'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as utils from '../../src/utils'; +import { DummyPropagation } from './DummyPropagation'; +import { AttributeNames } from '../../src/enums'; export const assertSpan = ( span: ReadableSpan, @@ -72,11 +72,11 @@ export const assertSpan = ( utils.parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, "must be finished"); - assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); + assert.ok(span.endTime, 'must be finished'); + assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders["user-agent"]; + const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_USER_AGENT], @@ -85,12 +85,12 @@ export const assertSpan = ( } } if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders["content-length"]) { - const contentLength = Number(validations.resHeaders["content-length"]); + if (validations.resHeaders['content-length']) { + const contentLength = Number(validations.resHeaders['content-length']); if ( - validations.resHeaders["content-encoding"] && - validations.resHeaders["content-encoding"] !== "identity" + validations.resHeaders['content-encoding'] && + validations.resHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], @@ -108,30 +108,30 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - "must be consistent (PEER_NAME and hostname)" + 'must be consistent (PEER_NAME and hostname)' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_IP], - "must have PEER_IP" + 'must have PEER_IP' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_PORT], - "must have PEER_PORT" + 'must have PEER_PORT' ); assert.ok( (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - "must be consistent" + 'must be consistent' ); } if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders["content-length"]) { - const contentLength = validations.reqHeaders["content-length"]; + if (validations.reqHeaders && validations.reqHeaders['content-length']) { + const contentLength = validations.reqHeaders['content-length']; if ( - validations.reqHeaders["content-encoding"] && - validations.reqHeaders["content-encoding"] !== "identity" + validations.reqHeaders['content-encoding'] && + validations.reqHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], @@ -150,15 +150,15 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - " must have serverName attribute" + ' must have serverName attribute' ); assert.ok( span.attributes[SemanticAttributes.NET_HOST_PORT], - "must have HOST_PORT" + 'must have HOST_PORT' ); assert.ok( span.attributes[SemanticAttributes.NET_HOST_IP], - "must have HOST_IP" + 'must have HOST_IP' ); } assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts index 4b6005df653..dd1fce77bed 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/enums/AttributeNames.ts @@ -18,5 +18,5 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - HTTP_STATUS_TEXT = "http.status_text", + HTTP_STATUS_TEXT = 'http.status_text', } diff --git a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts index 29dafdee5c4..85d9f3f547e 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts @@ -14,30 +14,30 @@ * limitations under the License. */ -import * as api from "@opentelemetry/api"; +import * as api from '@opentelemetry/api'; import { isWrapped, InstrumentationBase, InstrumentationConfig, -} from "@opentelemetry/instrumentation"; -import { hrTime, isUrlIgnored, otperformance } from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/instrumentation'; +import { hrTime, isUrlIgnored, otperformance } from '@opentelemetry/core'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addSpanNetworkEvents, getResource, parseUrl, PerformanceTimingNames as PTN, shouldPropagateTraceHeaders, -} from "@opentelemetry/web"; -import { EventNames } from "./enums/EventNames"; +} from '@opentelemetry/web'; +import { EventNames } from './enums/EventNames'; import { OpenFunction, PropagateTraceHeaderCorsUrls, SendFunction, XhrMem, -} from "./types"; -import { VERSION } from "./version"; -import { AttributeNames } from "./enums/AttributeNames"; +} from './types'; +import { VERSION } from './version'; +import { AttributeNames } from './enums/AttributeNames'; // how long to wait for observer to collect information about resources // this is needed as event "load" is called before observer @@ -70,7 +70,7 @@ export interface XMLHttpRequestInstrumentationConfig * This class represents a XMLHttpRequest plugin for auto instrumentation */ export class XMLHttpRequestInstrumentation extends InstrumentationBase { - readonly component: string = "xml-http-request"; + readonly component: string = 'xml-http-request'; readonly version: string = VERSION; moduleName = this.component; @@ -82,7 +82,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { + Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, String(headers[key])); }); } @@ -127,7 +127,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { - const childSpan = this.tracer.startSpan("CORS Preflight", { + const childSpan = this.tracer.startSpan('CORS Preflight', { startTime: corsPreFlightRequest[PTN.FETCH_START], }); addSpanNetworkEvents(childSpan, corsPreFlightRequest); @@ -143,7 +143,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { + observer: new PerformanceObserver(list => { const entries = list.getEntries() as PerformanceResourceTiming[]; - entries.forEach((entry) => { + entries.forEach(entry => { if ( - entry.initiatorType === "xmlhttprequest" && + entry.initiatorType === 'xmlhttprequest' && entry.name === spanUrl ) { if (xhrMem.createdResources) { @@ -199,7 +199,7 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase { unregister(this); @@ -476,30 +476,30 @@ export class XMLHttpRequestInstrumentation extends InstrumentationBase(); diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts index bf925b3e64e..95815ce50a0 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/unmocked.test.ts @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Span } from "@opentelemetry/api"; -import { registerInstrumentations } from "@opentelemetry/instrumentation"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import { ReadableSpan, SpanProcessor } from "@opentelemetry/tracing"; -import { WebTracerProvider } from "@opentelemetry/web"; -import { XMLHttpRequestInstrumentation } from "../src"; -import assert = require("assert"); +import { Span } from '@opentelemetry/api'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { ReadableSpan, SpanProcessor } from '@opentelemetry/tracing'; +import { WebTracerProvider } from '@opentelemetry/web'; +import { XMLHttpRequestInstrumentation } from '../src'; +import assert = require('assert'); class TestSpanProcessor implements SpanProcessor { spans: ReadableSpan[] = []; @@ -37,7 +37,7 @@ class TestSpanProcessor implements SpanProcessor { } } -describe("unmocked xhr", () => { +describe('unmocked xhr', () => { let testSpans: TestSpanProcessor; let provider: WebTracerProvider; beforeEach(() => { @@ -53,12 +53,12 @@ describe("unmocked xhr", () => { // nop }); - it("should find resource with a relative url", (done) => { + it('should find resource with a relative url', done => { const xhr = new XMLHttpRequest(); let path = location.pathname; - path = path.substring(path.lastIndexOf("/") + 1); - xhr.open("GET", path); - xhr.addEventListener("loadend", () => { + path = path.substring(path.lastIndexOf('/') + 1); + xhr.open('GET', path); + xhr.addEventListener('loadend', () => { setTimeout(() => { assert.strictEqual(testSpans.spans.length, 1); const span = testSpans.spans[0]; diff --git a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts index 886ccff336c..10e8d24a8ea 100644 --- a/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-instrumentation-xml-http-request/test/xhr.test.ts @@ -13,29 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as api from "@opentelemetry/api"; -import { otperformance as performance, isWrapped } from "@opentelemetry/core"; -import { registerInstrumentations } from "@opentelemetry/instrumentation"; +import * as api from '@opentelemetry/api'; +import { otperformance as performance, isWrapped } from '@opentelemetry/core'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { B3Propagator, B3InjectEncoding, X_B3_SAMPLED, X_B3_SPAN_ID, X_B3_TRACE_ID, -} from "@opentelemetry/propagator-b3"; -import { ZoneContextManager } from "@opentelemetry/context-zone"; -import * as tracing from "@opentelemetry/tracing"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/propagator-b3'; +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import * as tracing from '@opentelemetry/tracing'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { PerformanceTimingNames as PTN, WebTracerProvider, parseUrl, -} from "@opentelemetry/web"; -import * as assert from "assert"; -import * as sinon from "sinon"; -import { EventNames } from "../src/enums/EventNames"; -import { XMLHttpRequestInstrumentation } from "../src/xhr"; -import { AttributeNames } from "../src/enums/AttributeNames"; +} from '@opentelemetry/web'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { EventNames } from '../src/enums/EventNames'; +import { XMLHttpRequestInstrumentation } from '../src/xhr'; +import { AttributeNames } from '../src/enums/AttributeNames'; class DummySpanExporter implements tracing.SpanExporter { export(spans: any) {} @@ -60,7 +60,7 @@ const getData = ( } req.timeout = XHR_TIMEOUT; - req.open("GET", url, async); + req.open('GET', url, async); req.onload = function () { resolve(); }; @@ -90,8 +90,8 @@ function createResource(resource = {}): PerformanceResourceTiming { domainLookupStart: 11, encodedBodySize: 0, fetchStart: 10.1, - initiatorType: "xmlhttprequest", - nextHopProtocol: "", + initiatorType: 'xmlhttprequest', + nextHopProtocol: '', redirectEnd: 0, redirectStart: 0, requestStart: 16, @@ -101,8 +101,8 @@ function createResource(resource = {}): PerformanceResourceTiming { transferSize: 0, workerStart: 0, duration: 0, - entryType: "", - name: "", + entryType: '', + name: '', startTime: 0, }; return Object.assign( @@ -115,16 +115,16 @@ function createResource(resource = {}): PerformanceResourceTiming { function createMainResource(resource = {}): PerformanceResourceTiming { const mainResource: any = createResource(resource); Object.keys(mainResource).forEach((key: string) => { - if (typeof mainResource[key] === "number") { + if (typeof mainResource[key] === 'number') { mainResource[key] = mainResource[key] + 30; } }); return mainResource; } -describe("xhr", () => { +describe('xhr', () => { const asyncTests = [{ async: true }, { async: false }]; - asyncTests.forEach((test) => { + asyncTests.forEach(test => { const testAsync = test.async; describe(`when async='${testAsync}'`, () => { let requests: any[] = []; @@ -151,7 +151,7 @@ describe("xhr", () => { api.propagation.disable(); }); - describe("when request is successful", () => { + describe('when request is successful', () => { let webTracerWithZone: api.Tracer; let webTracerProviderWithZone: WebTracerProvider; let dummySpanExporter: DummySpanExporter; @@ -159,7 +159,7 @@ describe("xhr", () => { let clearResourceTimingsSpy: any; let rootSpan: api.Span; let spyEntries: any; - const url = "http://localhost:8090/xml-http-request.js"; + const url = 'http://localhost:8090/xml-http-request.js'; let fakeNow = 0; let xmlHttpRequestInstrumentation: XMLHttpRequestInstrumentation; @@ -175,8 +175,8 @@ describe("xhr", () => { }; sinon.useFakeTimers(); - sinon.stub(performance, "timeOrigin").value(0); - sinon.stub(performance, "now").callsFake(() => fakeNow); + sinon.stub(performance, 'timeOrigin').value(0); + sinon.stub(performance, 'now').callsFake(() => fakeNow); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -190,9 +190,9 @@ describe("xhr", () => { spyEntries = sinon.stub( (performance as unknown) as Performance, - "getEntriesByType" + 'getEntriesByType' ); - spyEntries.withArgs("resource").returns(resources); + spyEntries.withArgs('resource').returns(resources); xmlHttpRequestInstrumentation = new XMLHttpRequestInstrumentation( config ); @@ -201,18 +201,18 @@ describe("xhr", () => { instrumentations: [xmlHttpRequestInstrumentation], tracerProvider: webTracerProviderWithZone, }); - webTracerWithZone = webTracerProviderWithZone.getTracer("xhr-test"); + webTracerWithZone = webTracerProviderWithZone.getTracer('xhr-test'); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, "export"); + exportSpy = sinon.stub(dummySpanExporter, 'export'); clearResourceTimingsSpy = sinon.stub( (performance as unknown) as Performance, - "clearResourceTimings" + 'clearResourceTimings' ); webTracerProviderWithZone.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - rootSpan = webTracerWithZone.startSpan("root"); + rootSpan = webTracerWithZone.startSpan('root'); api.context.with(api.setSpan(api.context.active(), rootSpan), () => { getData( new XMLHttpRequest(), @@ -226,17 +226,17 @@ describe("xhr", () => { sinon.clock.tick(1000); done(); }); - assert.strictEqual(requests.length, 1, "request not called"); + assert.strictEqual(requests.length, 1, 'request not called'); requests[0].respond( 200, - { "Content-Type": "application/json" }, + { 'Content-Type': 'application/json' }, '{"foo":"bar"}' ); }); }; - beforeEach((done) => { + beforeEach(done => { const propagateTraceHeaderCorsUrls = [window.location.origin]; prepareData(done, url, { propagateTraceHeaderCorsUrls }); }); @@ -245,51 +245,51 @@ describe("xhr", () => { clearData(); }); - it("should patch to wrap XML HTTP Requests when enabled", () => { + it('should patch to wrap XML HTTP Requests when enabled', () => { const xhttp = new XMLHttpRequest(); assert.ok(isWrapped(xhttp.send)); xmlHttpRequestInstrumentation.enable(); assert.ok(isWrapped(xhttp.send)); }); - it("should unpatch to unwrap XML HTTP Requests when disabled", () => { + it('should unpatch to unwrap XML HTTP Requests when disabled', () => { const xhttp = new XMLHttpRequest(); assert.ok(isWrapped(xhttp.send)); xmlHttpRequestInstrumentation.disable(); assert.ok(!isWrapped(xhttp.send)); }); - it("should create a span with correct root span", () => { + it('should create a span with correct root span', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, rootSpan.context().spanId, - "parent span is not root span" + 'parent span is not root span' ); }); - it("span should have correct name", () => { + it('span should have correct name', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; - assert.strictEqual(span.name, "HTTP GET", "span has wrong name"); + assert.strictEqual(span.name, 'HTTP GET', 'span has wrong name'); }); - it("span should have correct kind", () => { + it('span should have correct kind', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.kind, api.SpanKind.CLIENT, - "span has wrong kind" + 'span has wrong kind' ); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -299,7 +299,7 @@ describe("xhr", () => { ); assert.ok( (attributes[keys[2]] as number) > 0, - "attributes ${SemanticAttributess.HTTP_RESPONSE_CONTENT_SIZE} <= 0" + 'attributes ${SemanticAttributess.HTTP_RESPONSE_CONTENT_SIZE} <= 0' ); assert.strictEqual( attributes[keys[3]], @@ -308,7 +308,7 @@ describe("xhr", () => { ); assert.strictEqual( attributes[keys[4]], - "OK", + 'OK', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( @@ -317,18 +317,18 @@ describe("xhr", () => { `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== "", + attributes[keys[7]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 8, "number of attributes is wrong"); + assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[1][0][0]; const events = span.events; @@ -393,41 +393,41 @@ describe("xhr", () => { `event ${EventNames.EVENT_LOAD} is not defined` ); - assert.strictEqual(events.length, 12, "number of events is wrong"); + assert.strictEqual(events.length, 12, 'number of events is wrong'); }); - it("should create a span for preflight request", () => { + it('should create a span for preflight request', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const parentSpan: tracing.ReadableSpan = exportSpy.args[1][0][0]; assert.strictEqual( span.parentSpanId, parentSpan.spanContext.spanId, - "parent span is not root span" + 'parent span is not root span' ); }); - it("preflight request span should have correct name", () => { + it('preflight request span should have correct name', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.name, - "CORS Preflight", - "preflight request span has wrong name" + 'CORS Preflight', + 'preflight request span has wrong name' ); }); - it("preflight request span should have correct kind", () => { + it('preflight request span should have correct kind', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; assert.strictEqual( span.kind, api.SpanKind.INTERNAL, - "span has wrong kind" + 'span has wrong kind' ); }); - it("preflight request span should have correct events", () => { + it('preflight request span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; - assert.strictEqual(events.length, 9, "number of events is wrong"); + assert.strictEqual(events.length, 9, 'number of events is wrong'); assert.strictEqual( events[0].name, @@ -476,24 +476,24 @@ describe("xhr", () => { ); }); - it("should NOT clear the resources", () => { + it('should NOT clear the resources', () => { assert.ok( clearResourceTimingsSpy.notCalled, - "resources have been cleared" + 'resources have been cleared' ); }); - describe("AND origin match with window.location", () => { - beforeEach((done) => { + describe('AND origin match with window.location', () => { + beforeEach(done => { clearData(); // this won't generate a preflight span const propagateTraceHeaderCorsUrls = [url]; - prepareData(done, window.location.origin + "/xml-http-request.js", { + prepareData(done, window.location.origin + '/xml-http-request.js', { propagateTraceHeaderCorsUrls, }); }); - it("should set trace headers", () => { + it('should set trace headers', () => { const span: api.Span = exportSpy.args[0][0][0]; assert.strictEqual( requests[0].requestHeaders[X_B3_TRACE_ID], @@ -514,18 +514,18 @@ describe("xhr", () => { }); describe( - "AND origin does NOT match window.location but match with" + - " propagateTraceHeaderCorsUrls", + 'AND origin does NOT match window.location but match with' + + ' propagateTraceHeaderCorsUrls', () => { - beforeEach((done) => { + beforeEach(done => { clearData(); prepareData( done, - "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json", + 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json', { propagateTraceHeaderCorsUrls: /raw\.githubusercontent\.com/ } ); }); - it("should set trace headers", () => { + it('should set trace headers', () => { // span at exportSpy.args[0][0][0] is the preflight span const span: api.Span = exportSpy.args[1][0][0]; assert.strictEqual( @@ -547,17 +547,17 @@ describe("xhr", () => { } ); describe( - "AND origin does NOT match window.location And does NOT match" + - " with propagateTraceHeaderCorsUrls", + 'AND origin does NOT match window.location And does NOT match' + + ' with propagateTraceHeaderCorsUrls', () => { - beforeEach((done) => { + beforeEach(done => { clearData(); prepareData( done, - "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json" + 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json' ); }); - it("should NOT set trace headers", () => { + it('should NOT set trace headers', () => { assert.strictEqual( requests[0].requestHeaders[X_B3_TRACE_ID], undefined, @@ -577,8 +577,8 @@ describe("xhr", () => { } ); - describe("when url is ignored", () => { - beforeEach((done) => { + describe('when url is ignored', () => { + beforeEach(done => { clearData(); const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { @@ -587,13 +587,13 @@ describe("xhr", () => { }); }); - it("should NOT create any span", () => { + it('should NOT create any span', () => { assert.ok(exportSpy.notCalled, "span shouldn't be exported"); }); }); - describe("when clearTimingResources is set", () => { - beforeEach((done) => { + describe('when clearTimingResources is set', () => { + beforeEach(done => { clearData(); const propagateTraceHeaderCorsUrls = url; prepareData(done, url, { @@ -602,7 +602,7 @@ describe("xhr", () => { }); }); - it("should clear the resources", () => { + it('should clear the resources', () => { assert.ok( clearResourceTimingsSpy.calledOnce, "resources haven't been cleared" @@ -610,11 +610,11 @@ describe("xhr", () => { }); }); - describe("when reusing the same XML Http request", () => { - const firstUrl = "http://localhost:8090/get"; - const secondUrl = "http://localhost:8099/get"; + describe('when reusing the same XML Http request', () => { + const firstUrl = 'http://localhost:8090/get'; + const secondUrl = 'http://localhost:8099/get'; - beforeEach((done) => { + beforeEach(done => { requests = []; const resources: PerformanceResourceTiming[] = []; resources.push( @@ -662,19 +662,19 @@ describe("xhr", () => { assert.strictEqual( requests.length, 1, - "first request not called" + 'first request not called' ); requests[0].respond( 200, - { "Content-Type": "application/json" }, + { 'Content-Type': 'application/json' }, '{"foo":"bar"}' ); } ); }); - it("should clear previous span information", () => { + it('should clear previous span information', () => { const span: tracing.ReadableSpan = exportSpy.args[2][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); @@ -688,7 +688,7 @@ describe("xhr", () => { }); }); - describe("when request is NOT successful", () => { + describe('when request is NOT successful', () => { let webTracerWithZoneProvider: WebTracerProvider; let webTracerWithZone: api.Tracer; let dummySpanExporter: DummySpanExporter; @@ -696,7 +696,7 @@ describe("xhr", () => { let rootSpan: api.Span; let spyEntries: any; const url = - "https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json"; + 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json'; let fakeNow = 0; beforeEach(() => { @@ -707,8 +707,8 @@ describe("xhr", () => { sinon.useFakeTimers(); - sinon.stub(performance, "timeOrigin").value(0); - sinon.stub(performance, "now").callsFake(() => fakeNow); + sinon.stub(performance, 'timeOrigin').value(0); + sinon.stub(performance, 'now').callsFake(() => fakeNow); const resources: PerformanceResourceTiming[] = []; resources.push( @@ -719,9 +719,9 @@ describe("xhr", () => { spyEntries = sinon.stub( (performance as unknown) as Performance, - "getEntriesByType" + 'getEntriesByType' ); - spyEntries.withArgs("resource").returns(resources); + spyEntries.withArgs('resource').returns(resources); webTracerWithZoneProvider = new WebTracerProvider(); @@ -731,21 +731,21 @@ describe("xhr", () => { }); dummySpanExporter = new DummySpanExporter(); - exportSpy = sinon.stub(dummySpanExporter, "export"); + exportSpy = sinon.stub(dummySpanExporter, 'export'); webTracerWithZoneProvider.addSpanProcessor( new tracing.SimpleSpanProcessor(dummySpanExporter) ); - webTracerWithZone = webTracerWithZoneProvider.getTracer("xhr-test"); + webTracerWithZone = webTracerWithZoneProvider.getTracer('xhr-test'); - rootSpan = webTracerWithZone.startSpan("root"); + rootSpan = webTracerWithZone.startSpan('root'); }); afterEach(() => { clearData(); }); - describe("when request loads and receives an error code", () => { - beforeEach((done) => { + describe('when request loads and receives an error code', () => { + beforeEach(done => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -761,23 +761,23 @@ describe("xhr", () => { sinon.clock.tick(1000); done(); }); - assert.strictEqual(requests.length, 1, "request not called"); + assert.strictEqual(requests.length, 1, 'request not called'); requests[0].respond( 400, - { "Content-Type": "text/plain" }, - "Bad Request" + { 'Content-Type': 'text/plain' }, + 'Bad Request' ); } ); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -797,27 +797,27 @@ describe("xhr", () => { ); assert.strictEqual( attributes[keys[4]], - "Bad Request", + 'Bad Request', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[5]], - "raw.githubusercontent.com", + 'raw.githubusercontent.com', `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[6]] === "http" || attributes[keys[6]] === "https", + attributes[keys[6]] === 'http' || attributes[keys[6]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[7]] !== "", + attributes[keys[7]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 8, "number of attributes is wrong"); + assert.strictEqual(keys.length, 8, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -882,12 +882,12 @@ describe("xhr", () => { `event ${EventNames.EVENT_ERROR} is not defined` ); - assert.strictEqual(events.length, 12, "number of events is wrong"); + assert.strictEqual(events.length, 12, 'number of events is wrong'); }); }); - describe("when request encounters a network error", () => { - beforeEach((done) => { + describe('when request encounters a network error', () => { + beforeEach(done => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -899,20 +899,20 @@ describe("xhr", () => { } ); - assert.strictEqual(requests.length, 1, "request not called"); + assert.strictEqual(requests.length, 1, 'request not called'); requests[0].error(); } ); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -927,27 +927,27 @@ describe("xhr", () => { ); assert.strictEqual( attributes[keys[3]], - "", + '', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - "raw.githubusercontent.com", + 'raw.githubusercontent.com', `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== "", + attributes[keys[6]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, "number of attributes is wrong"); + assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -967,11 +967,11 @@ describe("xhr", () => { `event ${EventNames.EVENT_ERROR} is not defined` ); - assert.strictEqual(events.length, 3, "number of events is wrong"); + assert.strictEqual(events.length, 3, 'number of events is wrong'); }); }); - describe("when request is aborted", () => { + describe('when request is aborted', () => { before(function () { // Can only abort Async requests if (!testAsync) { @@ -979,7 +979,7 @@ describe("xhr", () => { } }); - beforeEach((done) => { + beforeEach(done => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -991,20 +991,20 @@ describe("xhr", () => { } ); - assert.strictEqual(requests.length, 1, "request not called"); + assert.strictEqual(requests.length, 1, 'request not called'); requests[0].abort(); } ); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -1019,27 +1019,27 @@ describe("xhr", () => { ); assert.strictEqual( attributes[keys[3]], - "", + '', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - "raw.githubusercontent.com", + 'raw.githubusercontent.com', `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== "", + attributes[keys[6]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, "number of attributes is wrong"); + assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -1059,11 +1059,11 @@ describe("xhr", () => { `event ${EventNames.EVENT_ABORT} is not defined` ); - assert.strictEqual(events.length, 3, "number of events is wrong"); + assert.strictEqual(events.length, 3, 'number of events is wrong'); }); }); - describe("when request times out", () => { + describe('when request times out', () => { before(function () { // Can only set timeout for Async requests if (!testAsync) { @@ -1071,7 +1071,7 @@ describe("xhr", () => { } }); - beforeEach((done) => { + beforeEach(done => { api.context.with( api.setSpan(api.context.active(), rootSpan), () => { @@ -1091,14 +1091,14 @@ describe("xhr", () => { ); }); - it("span should have correct attributes", () => { + it('span should have correct attributes', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const attributes = span.attributes; const keys = Object.keys(attributes); assert.strictEqual( attributes[keys[0]], - "GET", + 'GET', `attributes ${SemanticAttributes.HTTP_METHOD} is wrong` ); assert.strictEqual( @@ -1113,27 +1113,27 @@ describe("xhr", () => { ); assert.strictEqual( attributes[keys[3]], - "", + '', `attributes ${AttributeNames.HTTP_STATUS_TEXT} is wrong` ); assert.strictEqual( attributes[keys[4]], - "raw.githubusercontent.com", + 'raw.githubusercontent.com', `attributes ${SemanticAttributes.HTTP_HOST} is wrong` ); assert.ok( - attributes[keys[5]] === "http" || attributes[keys[5]] === "https", + attributes[keys[5]] === 'http' || attributes[keys[5]] === 'https', `attributes ${SemanticAttributes.HTTP_SCHEME} is wrong` ); assert.ok( - attributes[keys[6]] !== "", + attributes[keys[6]] !== '', `attributes ${SemanticAttributes.HTTP_USER_AGENT} is not defined` ); - assert.strictEqual(keys.length, 7, "number of attributes is wrong"); + assert.strictEqual(keys.length, 7, 'number of attributes is wrong'); }); - it("span should have correct events", () => { + it('span should have correct events', () => { const span: tracing.ReadableSpan = exportSpy.args[0][0][0]; const events = span.events; @@ -1153,7 +1153,7 @@ describe("xhr", () => { `event ${EventNames.EVENT_TIMEOUT} is not defined` ); - assert.strictEqual(events.length, 3, "number of events is wrong"); + assert.strictEqual(events.length, 3, 'number of events is wrong'); }); }); }); diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts index bd7d1c3b2c0..ff4e19ebccf 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { GrpcJsPlugin } from "../grpcJs"; -import type { GrpcClientFunc, SendUnaryDataCallback } from "../types"; +import { GrpcJsPlugin } from '../grpcJs'; +import type { GrpcClientFunc, SendUnaryDataCallback } from '../types'; import { SpanKind, Span, @@ -25,16 +25,16 @@ import { context, setSpan, diag, -} from "@opentelemetry/api"; -import type * as grpcJs from "@grpc/grpc-js"; +} from '@opentelemetry/api'; +import type * as grpcJs from '@grpc/grpc-js'; import { grpcStatusCodeToSpanStatus, grpcStatusCodeToOpenTelemetryStatusCode, CALL_SPAN_ENDED, methodIsIgnored, -} from "../utils"; -import { EventEmitter } from "events"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { EventEmitter } from 'events'; +import { AttributeNames } from '../enums'; /** * Parse a package method list and return a list of methods to patch @@ -74,9 +74,9 @@ export function getPatchedClientMethods( ): (original: GrpcClientFunc) => () => EventEmitter { const plugin = this; return (original: GrpcClientFunc) => { - diag.debug("patch all client methods"); + diag.debug('patch all client methods'); return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace("/", "")}`; + const name = `grpc.${original.path.replace('/', '')}`; const args = [...arguments]; const metadata = getMetadata.call(plugin, original, args); const span = plugin.tracer.startSpan(name, { @@ -140,8 +140,8 @@ export function makeGrpcClientRemoteCall( return (span: Span) => { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = args.findIndex((arg) => { - return typeof arg === "function"; + const callbackFuncIndex = args.findIndex(arg => { + return typeof arg === 'function'; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -171,7 +171,7 @@ export function makeGrpcClientRemoteCall( } }; context.bind(call); - call.on("error", (err: grpcJs.ServiceError) => { + call.on('error', (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -189,7 +189,7 @@ export function makeGrpcClientRemoteCall( endSpan(); }); - call.on("status", (status: SpanStatus) => { + call.on('status', (status: SpanStatus) => { if (call[CALL_SPAN_ENDED]) { return; } @@ -221,9 +221,9 @@ function getMetadata( let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { return ( arg && - typeof arg === "object" && - (arg as grpcJs.Metadata)["internalRepr"] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === "function" + typeof arg === 'object' && + (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 + typeof (arg as grpcJs.Metadata).getMap === 'function' ); }); if (metadataIndex === -1) { diff --git a/packages/opentelemetry-plugin-grpc-js/src/enums.ts b/packages/opentelemetry-plugin-grpc-js/src/enums.ts index e573bbbaaa5..f292c19c98a 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/enums.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/enums.ts @@ -18,9 +18,9 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - GRPC_KIND = "grpc.kind", // SERVER or CLIENT - GRPC_METHOD = "grpc.method", - GRPC_STATUS_CODE = "grpc.status_code", - GRPC_ERROR_NAME = "grpc.error_name", - GRPC_ERROR_MESSAGE = "grpc.error_message", + GRPC_KIND = 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD = 'grpc.method', + GRPC_STATUS_CODE = 'grpc.status_code', + GRPC_ERROR_NAME = 'grpc.error_name', + GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts index 5ba38fe2c2d..8edc034e221 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { context, Span, SpanStatusCode } from "@opentelemetry/api"; -import type { ServerCallWithMeta, SendUnaryDataCallback } from "../types"; -import { grpcStatusCodeToOpenTelemetryStatusCode } from "../utils"; -import type { GrpcJsPlugin } from "../grpcJs"; -import type * as grpcJs from "@grpc/grpc-js"; -import { AttributeNames } from "../enums"; +import { context, Span, SpanStatusCode } from '@opentelemetry/api'; +import type { ServerCallWithMeta, SendUnaryDataCallback } from '../types'; +import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; +import type { GrpcJsPlugin } from '../grpcJs'; +import type * as grpcJs from '@grpc/grpc-js'; +import { AttributeNames } from '../enums'; /** * Handle patching for clientStream and unary type server handlers diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts index f9423038a9e..8f8b0aca725 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import type * as grpcJs from "@grpc/grpc-js"; -import type { HandleCall } from "@grpc/grpc-js/build/src/server-call"; -import { GrpcJsPlugin } from "../grpcJs"; -import * as shimmer from "shimmer"; +import type * as grpcJs from '@grpc/grpc-js'; +import type { HandleCall } from '@grpc/grpc-js/build/src/server-call'; +import { GrpcJsPlugin } from '../grpcJs'; +import * as shimmer from 'shimmer'; import { ServerCallWithMeta, SendUnaryDataCallback, IgnoreMatcher, -} from "../types"; +} from '../types'; import { context, SpanOptions, @@ -32,11 +32,11 @@ import { ROOT_CONTEXT, setSpan, diag, -} from "@opentelemetry/api"; -import { clientStreamAndUnaryHandler } from "./clientStreamAndUnary"; -import { serverStreamAndBidiHandler } from "./serverStreamAndBidi"; -import { methodIsIgnored } from "../utils"; -import { AttributeNames } from "../enums"; +} from '@opentelemetry/api'; +import { clientStreamAndUnaryHandler } from './clientStreamAndUnary'; +import { serverStreamAndBidiHandler } from './serverStreamAndBidi'; +import { methodIsIgnored } from '../utils'; +import { AttributeNames } from '../enums'; type ServerRegisterFunction = typeof grpcJs.Server.prototype.register; @@ -51,7 +51,7 @@ export function patchServer( const plugin = this; const config = this._config; - diag.debug("patched gRPC server"); + diag.debug('patched gRPC server'); return function register( this: grpcJs.Server, name: string, @@ -68,11 +68,11 @@ export function patchServer( deserialize, type ); - const handlerSet = this["handlers"].get(name); + const handlerSet = this['handlers'].get(name); shimmer.wrap( handlerSet, - "func", + 'func', (originalFunc: HandleCall) => { return function func( this: typeof handlerSet, @@ -96,17 +96,17 @@ export function patchServer( ); } - const spanName = `grpc.${name.replace("/", "")}`; + const spanName = `grpc.${name.replace('/', '')}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug("patch func: %s", JSON.stringify(spanOptions)); + diag.debug('patch func: %s', JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (carrier, key) => carrier.get(key).map(String), - keys: (carrier) => Object.keys(carrier.getMap()), + keys: carrier => Object.keys(carrier.getMap()), }), () => { const span = plugin.tracer @@ -144,7 +144,7 @@ function shouldNotTraceServerCall( methodName: string, ignoreGrpcMethods?: IgnoreMatcher[] ): boolean { - const parsedName = methodName.split("/"); + const parsedName = methodName.split('/'); return methodIsIgnored( parsedName[parsedName.length - 1] || methodName, ignoreGrpcMethods @@ -165,9 +165,9 @@ function handleServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case "unary": - case "clientStream": - case "client_stream": + case 'unary': + case 'clientStream': + case 'client_stream': return clientStreamAndUnaryHandler( plugin, span, @@ -177,9 +177,9 @@ function handleServerFunction( | grpcJs.handleUnaryCall | grpcJs.ClientReadableStream ); - case "serverStream": - case "server_stream": - case "bidi": + case 'serverStream': + case 'server_stream': + case 'bidi': return serverStreamAndBidiHandler( plugin, span, @@ -204,13 +204,13 @@ function handleUntracedServerFunction( callback: SendUnaryDataCallback ): void { switch (type) { - case "unary": - case "clientStream": - case "client_stream": + case 'unary': + case 'clientStream': + case 'client_stream': return (originalFunc as Function).call({}, call, callback); - case "serverStream": - case "server_stream": - case "bidi": + case 'serverStream': + case 'server_stream': + case 'bidi': return (originalFunc as Function).call({}, call); default: break; diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts index 268ffbde4ae..a1963ec59af 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { context, Span, SpanStatusCode } from "@opentelemetry/api"; -import type * as grpcJs from "@grpc/grpc-js"; -import type { GrpcJsPlugin } from "../grpcJs"; -import { GrpcEmitter } from "../types"; +import { context, Span, SpanStatusCode } from '@opentelemetry/api'; +import type * as grpcJs from '@grpc/grpc-js'; +import type { GrpcJsPlugin } from '../grpcJs'; +import { GrpcEmitter } from '../types'; import { CALL_SPAN_ENDED, grpcStatusCodeToOpenTelemetryStatusCode, -} from "../utils"; -import { AttributeNames } from "../enums"; +} from '../utils'; +import { AttributeNames } from '../enums'; /** * Handle patching for serverStream and Bidi type server handlers @@ -44,7 +44,7 @@ export function serverStreamAndBidiHandler( }; context.bind(call); - call.on("finish", () => { + call.on('finish', () => { // @grpc/js does not expose a way to check if this call also emitted an error, // e.g. call.status.code !== 0 if (call[CALL_SPAN_ENDED]) { @@ -65,7 +65,7 @@ export function serverStreamAndBidiHandler( endSpan(); }); - call.on("error", (err: grpcJs.ServiceError) => { + call.on('error', (err: grpcJs.ServiceError) => { if (call[CALL_SPAN_ENDED]) { return; } diff --git a/packages/opentelemetry-plugin-grpc/src/enums.ts b/packages/opentelemetry-plugin-grpc/src/enums.ts index e573bbbaaa5..f292c19c98a 100644 --- a/packages/opentelemetry-plugin-grpc/src/enums.ts +++ b/packages/opentelemetry-plugin-grpc/src/enums.ts @@ -18,9 +18,9 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - GRPC_KIND = "grpc.kind", // SERVER or CLIENT - GRPC_METHOD = "grpc.method", - GRPC_STATUS_CODE = "grpc.status_code", - GRPC_ERROR_NAME = "grpc.error_name", - GRPC_ERROR_MESSAGE = "grpc.error_message", + GRPC_KIND = 'grpc.kind', // SERVER or CLIENT + GRPC_METHOD = 'grpc.method', + GRPC_STATUS_CODE = 'grpc.status_code', + GRPC_ERROR_NAME = 'grpc.error_name', + GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 6d7476dd8a3..556a612a5a2 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -25,12 +25,12 @@ import { ROOT_CONTEXT, setSpan, diag, -} from "@opentelemetry/api"; -import { BasePlugin } from "@opentelemetry/core"; -import * as events from "events"; -import * as grpcTypes from "grpc"; -import * as path from "path"; -import * as shimmer from "shimmer"; +} from '@opentelemetry/api'; +import { BasePlugin } from '@opentelemetry/core'; +import * as events from 'events'; +import * as grpcTypes from 'grpc'; +import * as path from 'path'; +import * as shimmer from 'shimmer'; import { grpc, GrpcClientFunc, @@ -39,45 +39,45 @@ import { ModuleExportsMapping, SendUnaryDataCallback, ServerCallWithMeta, -} from "./types"; +} from './types'; import { findIndex, _grpcStatusCodeToOpenTelemetryStatusCode, _grpcStatusCodeToSpanStatus, _methodIsIgnored, -} from "./utils"; -import { VERSION } from "./version"; -import { AttributeNames } from "./enums"; +} from './utils'; +import { VERSION } from './version'; +import { AttributeNames } from './enums'; /** The metadata key under which span context is stored as a binary value. */ -export const GRPC_TRACE_KEY = "grpc-trace-bin"; +export const GRPC_TRACE_KEY = 'grpc-trace-bin'; let grpcClientModule: GrpcInternalClientTypes; export class GrpcPlugin extends BasePlugin { - static readonly component = "grpc"; - readonly supportedVersions = ["1.*"]; + static readonly component = 'grpc'; + readonly supportedVersions = ['1.*']; protected _config!: GrpcPluginOptions; constructor(readonly moduleName: string, readonly version: string) { - super("@opentelemetry/plugin-grpc", VERSION); + super('@opentelemetry/plugin-grpc', VERSION); this._config = {}; } protected readonly _internalFilesList: ModuleExportsMapping = { - "0.13 - 1.6": { client: "src/node/src/client.js" }, - "^1.7": { client: "src/client.js" }, + '0.13 - 1.6': { client: 'src/node/src/client.js' }, + '^1.7': { client: 'src/client.js' }, }; protected readonly _basedir = basedir; protected patch(): typeof grpcTypes { - diag.debug("applying patch to %s@%s", this.moduleName, this.version); + diag.debug('applying patch to %s@%s', this.moduleName, this.version); if (this._moduleExports.Server) { shimmer.wrap( this._moduleExports.Server.prototype, - "register", + 'register', this._patchServer() as any ); } @@ -85,19 +85,19 @@ export class GrpcPlugin extends BasePlugin { // Wrap the externally exported client constructor shimmer.wrap( this._moduleExports, - "makeGenericClientConstructor", + 'makeGenericClientConstructor', this._patchClient() ); - if (this._internalFilesExports["client"]) { + if (this._internalFilesExports['client']) { grpcClientModule = this._internalFilesExports[ - "client" + 'client' ] as GrpcInternalClientTypes; // Wrap the internally used client constructor shimmer.wrap( grpcClientModule, - "makeClientConstructor", + 'makeClientConstructor', this._patchClient() ); } @@ -105,16 +105,16 @@ export class GrpcPlugin extends BasePlugin { return this._moduleExports; } protected unpatch(): void { - diag.debug("removing patch to %s@%s", this.moduleName, this.version); + diag.debug('removing patch to %s@%s', this.moduleName, this.version); if (this._moduleExports.Server) { - shimmer.unwrap(this._moduleExports.Server.prototype, "register"); + shimmer.unwrap(this._moduleExports.Server.prototype, 'register'); } - shimmer.unwrap(this._moduleExports, "makeGenericClientConstructor"); + shimmer.unwrap(this._moduleExports, 'makeGenericClientConstructor'); if (grpcClientModule) { - shimmer.unwrap(grpcClientModule, "makeClientConstructor"); + shimmer.unwrap(grpcClientModule, 'makeClientConstructor'); } } @@ -127,7 +127,7 @@ export class GrpcPlugin extends BasePlugin { private _patchServer() { return (originalRegister: typeof grpcTypes.Server.prototype.register) => { const plugin = this; - diag.debug("patched gRPC server"); + diag.debug('patched gRPC server'); return function register( this: grpcTypes.Server & { handlers: any }, @@ -142,7 +142,7 @@ export class GrpcPlugin extends BasePlugin { shimmer.wrap( handlerSet, - "func", + 'func', (originalFunc: grpcTypes.handleCall) => { return function func( this: typeof handlerSet, @@ -152,31 +152,31 @@ export class GrpcPlugin extends BasePlugin { const self = this; if (plugin._shouldNotTraceServerCall(call, name)) { switch (type) { - case "unary": - case "client_stream": + case 'unary': + case 'client_stream': return (originalFunc as Function).call( self, call, callback ); - case "server_stream": - case "bidi": + case 'server_stream': + case 'bidi': return (originalFunc as Function).call(self, call); default: return originalResult; } } - const spanName = `grpc.${name.replace("/", "")}`; + const spanName = `grpc.${name.replace('/', '')}`; const spanOptions: SpanOptions = { kind: SpanKind.SERVER, }; - diag.debug("patch func: %s", JSON.stringify(spanOptions)); + diag.debug('patch func: %s', JSON.stringify(spanOptions)); context.with( propagation.extract(ROOT_CONTEXT, call.metadata, { get: (metadata, key) => metadata.get(key).map(String), - keys: (metadata) => Object.keys(metadata.getMap()), + keys: metadata => Object.keys(metadata.getMap()), }), () => { const span = plugin._tracer @@ -187,8 +187,8 @@ export class GrpcPlugin extends BasePlugin { context.with(setSpan(context.active(), span), () => { switch (type) { - case "unary": - case "client_stream": + case 'unary': + case 'client_stream': return plugin._clientStreamAndUnaryHandler( plugin, span, @@ -197,8 +197,8 @@ export class GrpcPlugin extends BasePlugin { originalFunc, self ); - case "server_stream": - case "bidi": + case 'server_stream': + case 'bidi': return plugin._serverStreamAndBidiHandler( plugin, span, @@ -228,7 +228,7 @@ export class GrpcPlugin extends BasePlugin { call: ServerCallWithMeta, name: string ): boolean { - const parsedName = name.split("/"); + const parsedName = name.split('/'); return _methodIsIgnored( parsedName[parsedName.length - 1] || name, this._config.ignoreGrpcMethods @@ -273,7 +273,7 @@ export class GrpcPlugin extends BasePlugin { plugin._moduleExports.status.OK.toString() ); } - span.addEvent("received"); + span.addEvent('received'); // end the span span.end(); @@ -300,7 +300,7 @@ export class GrpcPlugin extends BasePlugin { }; context.bind(call); - call.on("finish", () => { + call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( AttributeNames.GRPC_STATUS_CODE, @@ -309,17 +309,17 @@ export class GrpcPlugin extends BasePlugin { // if there is an error, span will be ended on error event, otherwise end it here if (call.status.code === 0) { - span.addEvent("finished"); + span.addEvent('finished'); endSpan(); } }); - call.on("error", (err: grpcTypes.ServiceError) => { + call.on('error', (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.addEvent("finished with error"); + span.addEvent('finished with error'); span.setAttributes({ [AttributeNames.GRPC_ERROR_NAME]: err.name, [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, @@ -333,7 +333,7 @@ export class GrpcPlugin extends BasePlugin { private _patchClient() { const plugin = this; return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug("patching client"); + diag.debug('patching client'); return function makeClientConstructor( this: typeof grpcTypes.Client, methods: { [key: string]: { originalName?: string } }, @@ -378,9 +378,9 @@ export class GrpcPlugin extends BasePlugin { private _getPatchedClientMethods() { const plugin = this; return (original: GrpcClientFunc) => { - diag.debug("patch all client methods"); + diag.debug('patch all client methods'); return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace("/", "")}`; + const name = `grpc.${original.path.replace('/', '')}`; const args = Array.prototype.slice.call(arguments); const metadata = plugin._getMetadata(original, args); const span = plugin._tracer.startSpan(name, { @@ -452,8 +452,8 @@ export class GrpcPlugin extends BasePlugin { // if unary or clientStream if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, (arg) => { - return typeof arg === "function"; + const callbackFuncIndex = findIndex(args, arg => { + return typeof arg === 'function'; }); if (callbackFuncIndex !== -1) { args[callbackFuncIndex] = patchedCallback( @@ -464,7 +464,7 @@ export class GrpcPlugin extends BasePlugin { } } - span.addEvent("sent"); + span.addEvent('sent'); span.setAttributes({ [AttributeNames.GRPC_METHOD]: original.path, [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, @@ -486,7 +486,7 @@ export class GrpcPlugin extends BasePlugin { }; context.bind(call); ((call as unknown) as events.EventEmitter).on( - "error", + 'error', (err: grpcTypes.ServiceError) => { span.setStatus({ code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), @@ -501,7 +501,7 @@ export class GrpcPlugin extends BasePlugin { ); ((call as unknown) as events.EventEmitter).on( - "status", + 'status', (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( @@ -529,9 +529,9 @@ export class GrpcPlugin extends BasePlugin { let metadataIndex = findIndex(args, (arg: any) => { return ( arg && - typeof arg === "object" && + typeof arg === 'object' && arg._internal_repr && - typeof arg.getMap === "function" + typeof arg.getMap === 'function' ); }); if (metadataIndex === -1) { @@ -560,6 +560,6 @@ export class GrpcPlugin extends BasePlugin { } } -const basedir = path.dirname(require.resolve("grpc")); -const version = require(path.join(basedir, "package.json")).version; +const basedir = path.dirname(require.resolve('grpc')); +const version = require(path.join(basedir, 'package.json')).version; export const plugin = new GrpcPlugin(GrpcPlugin.component, version); diff --git a/packages/opentelemetry-plugin-http/src/enums.ts b/packages/opentelemetry-plugin-http/src/enums.ts index 31c18afc460..f9b8be3c8ea 100644 --- a/packages/opentelemetry-plugin-http/src/enums.ts +++ b/packages/opentelemetry-plugin-http/src/enums.ts @@ -18,7 +18,7 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - HTTP_ERROR_NAME = "http.error_name", - HTTP_ERROR_MESSAGE = "http.error_message", - HTTP_STATUS_TEXT = "http.status_text", + HTTP_ERROR_NAME = 'http.error_name', + HTTP_ERROR_MESSAGE = 'http.error_message', + HTTP_STATUS_TEXT = 'http.status_text', } diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index da524b7d179..fe2cc687044 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -18,11 +18,11 @@ import { SpanStatusCode, Span, SpanStatus, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/semantic-conventions'; import { ClientRequest, IncomingHttpHeaders, @@ -30,10 +30,10 @@ import { OutgoingHttpHeaders, RequestOptions, ServerResponse, -} from "http"; -import * as url from "url"; -import { AttributeNames } from "./enums"; -import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; +} from 'http'; +import * as url from 'url'; +import { AttributeNames } from './enums'; +import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; /** * Get an absolute url @@ -41,22 +41,22 @@ import { Err, IgnoreMatcher, ParsedRequestOptions } from "./types"; export const getAbsoluteUrl = ( requestUrl: ParsedRequestOptions | null, headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = "http:" + fallbackProtocol = 'http:' ): string => { const reqUrlObject = requestUrl || {}; const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || "").toString(); - const path = reqUrlObject.path || "/"; + const port = (reqUrlObject.port || '').toString(); + const path = reqUrlObject.path || '/'; let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || "localhost"; + reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; // if there is no port in host and there is a port // it should be displayed if it's not 80 and 443 (default ports) if ( - (host as string).indexOf(":") === -1 && + (host as string).indexOf(':') === -1 && port && - port !== "80" && - port !== "443" + port !== '80' && + port !== '443' ) { host += `:${port}`; } @@ -68,7 +68,7 @@ export const getAbsoluteUrl = ( */ export const parseResponseStatus = ( statusCode: number -): Omit => { +): Omit => { // 1xx, 2xx, 3xx are OK if (statusCode >= 100 && statusCode < 400) { return { code: SpanStatusCode.OK }; @@ -88,7 +88,7 @@ export const hasExpectHeader = (options: RequestOptions): boolean => { } const keys = Object.keys(options.headers); - return !!keys.find((key) => key.toLowerCase() === "expect"); + return !!keys.find(key => key.toLowerCase() === 'expect'); }; /** @@ -100,14 +100,14 @@ export const satisfiesPattern = ( constant: string, pattern: IgnoreMatcher ): boolean => { - if (typeof pattern === "string") { + if (typeof pattern === 'string') { return pattern === constant; } else if (pattern instanceof RegExp) { return pattern.test(constant); - } else if (typeof pattern === "function") { + } else if (typeof pattern === 'function') { return pattern(constant); } else { - throw new TypeError("Pattern is in unsupported datatype"); + throw new TypeError('Pattern is in unsupported datatype'); } }; @@ -226,7 +226,7 @@ export const setResponseContentLengthAttribute = ( function getContentLength( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): number | null { - const contentLengthHeader = headers["content-length"]; + const contentLengthHeader = headers['content-length']; if (contentLengthHeader === undefined) return null; const contentLength = parseInt(contentLengthHeader as string, 10); @@ -238,9 +238,9 @@ function getContentLength( export const isCompressed = ( headers: OutgoingHttpHeaders | IncomingHttpHeaders ): boolean => { - const encoding = headers["content-encoding"]; + const encoding = headers['content-encoding']; - return !!encoding && encoding !== "identity"; + return !!encoding && encoding !== 'identity'; }; /** @@ -253,13 +253,13 @@ export const getRequestInfo = ( options: url.URL | RequestOptions | string, extraOptions?: RequestOptions ) => { - let pathname = "/"; - let origin = ""; + let pathname = '/'; + let origin = ''; let optionsParsed: RequestOptions; - if (typeof options === "string") { + if (typeof options === 'string') { optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || "/"; - origin = `${optionsParsed.protocol || "http:"}//${optionsParsed.host}`; + pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; + origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; if (extraOptions !== undefined) { Object.assign(optionsParsed, extraOptions); } @@ -267,12 +267,12 @@ export const getRequestInfo = ( optionsParsed = { protocol: options.protocol, hostname: - typeof options.hostname === "string" && options.hostname.startsWith("[") + typeof options.hostname === 'string' && options.hostname.startsWith('[') ? options.hostname.slice(1, -1) : options.hostname, - path: `${options.pathname || ""}${options.search || ""}`, + path: `${options.pathname || ''}${options.search || ''}`, }; - if (options.port !== "") { + if (options.port !== '') { optionsParsed.port = Number(options.port); } if (options.username || options.password) { @@ -287,9 +287,9 @@ export const getRequestInfo = ( optionsParsed = Object.assign({}, options); pathname = (options as url.URL).pathname; if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || "/"; + pathname = url.parse(optionsParsed.path).pathname || '/'; } - origin = `${optionsParsed.protocol || "http:"}//${ + origin = `${optionsParsed.protocol || 'http:'}//${ optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` }`; } @@ -303,7 +303,7 @@ export const getRequestInfo = ( // ensure upperCase for consistency const method = optionsParsed.method ? optionsParsed.method.toUpperCase() - : "GET"; + : 'GET'; return { origin, pathname, method, optionsParsed }; }; @@ -318,7 +318,7 @@ export const isValidOptionsType = (options: unknown): boolean => { } const type = typeof options; - return type === "string" || (type === "object" && !Array.isArray(options)); + return type === 'string' || (type === 'object' && !Array.isArray(options)); }; /** @@ -333,12 +333,12 @@ export const getOutgoingRequestAttributes = ( const host = requestOptions.host; const hostname = requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || - "localhost"; + host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || + 'localhost'; const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : "GET"; + const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; const headers = requestOptions.headers || {}; - const userAgent = headers["user-agent"]; + const userAgent = headers['user-agent']; const attributes: SpanAttributes = { [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( requestOptions, @@ -346,7 +346,7 @@ export const getOutgoingRequestAttributes = ( `${options.component}:` ), [SemanticAttributes.HTTP_METHOD]: method, - [SemanticAttributes.HTTP_TARGET]: requestOptions.path || "/", + [SemanticAttributes.HTTP_TARGET]: requestOptions.path || '/', [SemanticAttributes.NET_PEER_NAME]: hostname, }; @@ -364,7 +364,7 @@ export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { const attributes: SpanAttributes = {}; if (kind) { attributes[SemanticAttributes.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== "QUIC") { + if (kind.toUpperCase() !== 'QUIC') { attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; } else { attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; @@ -396,7 +396,7 @@ export const getOutgoingRequestAttributesOnResponse = ( if (statusCode) { attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; attributes[AttributeNames.HTTP_STATUS_TEXT] = ( - statusMessage || "" + statusMessage || '' ).toUpperCase(); } @@ -414,16 +414,16 @@ export const getIncomingRequestAttributes = ( options: { component: string; serverName?: string } ): SpanAttributes => { const headers = request.headers; - const userAgent = headers["user-agent"]; - const ips = headers["x-forwarded-for"]; - const method = request.method || "GET"; + const userAgent = headers['user-agent']; + const ips = headers['x-forwarded-for']; + const method = request.method || 'GET'; const httpVersion = request.httpVersion; const requestUrl = request.url ? url.parse(request.url) : null; const host = requestUrl?.host || headers.host; const hostname = requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, "$1") || - "localhost"; + host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || + 'localhost'; const serverName = options.serverName; const attributes: SpanAttributes = { [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( @@ -436,17 +436,17 @@ export const getIncomingRequestAttributes = ( [SemanticAttributes.HTTP_METHOD]: method, }; - if (typeof ips === "string") { - attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(",")[0]; + if (typeof ips === 'string') { + attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(',')[0]; } - if (typeof serverName === "string") { + if (typeof serverName === 'string') { attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; } if (requestUrl) { - attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || "/"; - attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || "/"; + attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || '/'; + attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; } if (userAgent !== undefined) { @@ -477,11 +477,11 @@ export const getIncomingRequestAttributesOnResponse = ( }; const route = Array.isArray(__ot_middlewares) ? __ot_middlewares - .filter((path) => path !== "/") - .map((path) => { - return path[0] === "/" ? path : "/" + path; + .filter(path => path !== '/') + .map(path => { + return path[0] === '/' ? path : '/' + path; }) - .join("") + .join('') : undefined; const attributes: SpanAttributes = { @@ -490,7 +490,7 @@ export const getIncomingRequestAttributesOnResponse = ( [SemanticAttributes.NET_PEER_IP]: remoteAddress, [SemanticAttributes.NET_PEER_PORT]: remotePort, [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, - [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || "").toUpperCase(), + [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), }; if (route !== undefined) { diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index c3bcc25327a..7299f60b9a0 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -21,38 +21,38 @@ import { SpanKind, getSpan, setSpan, -} from "@opentelemetry/api"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/api'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; +} from '@opentelemetry/tracing'; import { NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as nock from "nock"; -import * as path from "path"; -import { HttpPlugin, plugin } from "../../src/http"; -import { Http, HttpPluginConfig } from "../../src/types"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { httpRequest } from "../utils/httpRequest"; -import { ContextManager } from "@opentelemetry/api"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { ClientRequest, IncomingMessage, ServerResponse } from "http"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as nock from 'nock'; +import * as path from 'path'; +import { HttpPlugin, plugin } from '../../src/http'; +import { Http, HttpPluginConfig } from '../../src/types'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpRequest } from '../utils/httpRequest'; +import { ContextManager } from '@opentelemetry/api'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { ClientRequest, IncomingMessage, ServerResponse } from 'http'; const applyCustomAttributesOnSpanErrorMessage = - "bad applyCustomAttributesOnSpan function"; + 'bad applyCustomAttributesOnSpan function'; let server: http.Server; const serverPort = 22345; -const protocol = "http"; -const hostname = "localhost"; -const pathname = "/test"; -const serverName = "my.server.name"; +const protocol = 'http'; +const hostname = 'localhost'; +const pathname = '/test'; +const serverName = 'my.server.name'; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); @@ -73,24 +73,24 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; export const requestHookFunction = ( span: ISpan, request: ClientRequest | IncomingMessage ): void => { - span.setAttribute("custom request hook attribute", "request"); + span.setAttribute('custom request hook attribute', 'request'); }; export const responseHookFunction = ( span: ISpan, response: IncomingMessage | ServerResponse ): void => { - span.setAttribute("custom response hook attribute", "response"); + span.setAttribute('custom response hook attribute', 'response'); }; -describe("HttpPlugin", () => { +describe('HttpPlugin', () => { let contextManager: ContextManager; beforeEach(() => { @@ -102,11 +102,11 @@ describe("HttpPlugin", () => { context.disable(); }); - it("should return a plugin", () => { + it('should return a plugin', () => { assert.ok(plugin instanceof HttpPlugin); }); - it("should match version", () => { + it('should match version', () => { assert.strictEqual(process.versions.node, plugin.version); }); @@ -114,8 +114,8 @@ describe("HttpPlugin", () => { assert.strictEqual(protocol, plugin.moduleName); }); - describe("enable()", () => { - describe("with bad plugin options", () => { + describe('enable()', () => { + describe('with bad plugin options', () => { let pluginWithBadOptions: HttpPlugin; beforeEach(() => { memoryExporter.reset(); @@ -125,12 +125,12 @@ describe("HttpPlugin", () => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error("bad ignoreIncomingPaths function"); + throw new Error('bad ignoreIncomingPaths function'); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error("bad ignoreOutgoingUrls function"); + throw new Error('bad ignoreOutgoingUrls function'); }, ], applyCustomAttributesOnSpan: () => { @@ -143,7 +143,7 @@ describe("HttpPlugin", () => { ); pluginWithBadOptions.enable(http, provider, config); server = http.createServer((request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort); @@ -154,7 +154,7 @@ describe("HttpPlugin", () => { pluginWithBadOptions.disable(); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -183,7 +183,7 @@ describe("HttpPlugin", () => { ); }); }); - describe("with good plugin options", () => { + describe('with good plugin options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -191,14 +191,14 @@ describe("HttpPlugin", () => { before(() => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ - "/ignored/string", + '/ignored/string', /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], applyCustomAttributesOnSpan: customAttributeFunction, requestHook: requestHookFunction, @@ -207,10 +207,10 @@ describe("HttpPlugin", () => { }; plugin.enable(http, provider, config); server = http.createServer((request, response) => { - if (request.url?.includes("/ignored")) { - provider.getTracer("test").startSpan("some-span").end(); + if (request.url?.includes('/ignored')) { + provider.getTracer('test').startSpan('some-span').end(); } - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort); @@ -233,13 +233,13 @@ describe("HttpPlugin", () => { assert.strictEqual(Object.keys(httpNotPatched).length, 0); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - "x-forwarded-for": ", , ", - "user-agent": "chrome", + 'x-forwarded-for': ', , ', + 'user-agent': 'chrome', }, } ); @@ -259,7 +259,7 @@ describe("HttpPlugin", () => { assert.strictEqual(spans.length, 2); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - "" + '' ); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], @@ -275,7 +275,7 @@ describe("HttpPlugin", () => { ].forEach(({ span, kind }) => { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], - "1.1" + '1.1' ); assert.strictEqual( span.attributes[SemanticAttributes.NET_TRANSPORT], @@ -301,7 +301,7 @@ describe("HttpPlugin", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/1"; + const testPath = '/outgoing/rootSpan/1'; doNock( hostname, @@ -325,7 +325,7 @@ describe("HttpPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, @@ -336,11 +336,11 @@ describe("HttpPlugin", () => { }); } - it("should create a child span for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 200, "Ok"); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + it('should create a child span for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 200, 'Ok'); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -351,16 +351,16 @@ describe("HttpPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -375,15 +375,15 @@ describe("HttpPlugin", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/childs/1"; + const testPath = '/outgoing/rootSpan/childs/1'; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpRequest.get( `${protocol}://${hostname}${testPath}` @@ -394,16 +394,16 @@ describe("HttpPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -417,17 +417,17 @@ describe("HttpPlugin", () => { }); } - it("should create multiple child spans for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs"; + it('should create multiple child spans for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs'; const num = 5; - doNock(hostname, testPath, 200, "Ok", num); - const name = "TestRootSpan"; - const span = provider.getTracer("default").startSpan(name); + doNock(hostname, testPath, 200, 'Ok', num); + const name = 'TestRootSpan'; + const span = provider.getTracer('default').startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, "HTTP GET"); + assert.strictEqual(spans[i].name, 'HTTP GET'); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -440,7 +440,7 @@ describe("HttpPlugin", () => { }); }); - for (const ignored of ["string", "function", "regexp"]) { + for (const ignored of ['string', 'function', 'regexp']) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -452,7 +452,7 @@ describe("HttpPlugin", () => { }); } - for (const arg of ["string", {}, new Date()]) { + for (const arg of ['string', {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -461,14 +461,14 @@ describe("HttpPlugin", () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith("Nock: No match for request")); + assert.ok(error.message.startsWith('Nock: No match for request')); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, ""]) { + for (const arg of [true, 1, false, 0, '']) { it(`should not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -479,7 +479,7 @@ describe("HttpPlugin", () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize("/node_modules/nock/lib/intercept.js") + path.normalize('/node_modules/nock/lib/intercept.js') ) > 0 ); } @@ -491,26 +491,26 @@ describe("HttpPlugin", () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - http.request({ protocol: "telnet" }); + http.request({ protocol: 'telnet' }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -531,7 +531,7 @@ describe("HttpPlugin", () => { nock.cleanAll(); nock.enableNetConnect(); try { - http.request({ protocol: "telnet" }); + http.request({ protocol: 'telnet' }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -539,19 +539,19 @@ describe("HttpPlugin", () => { } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -568,28 +568,28 @@ describe("HttpPlugin", () => { } }); - it("should have 1 ended span when request is aborted", async () => { + it('should have 1 ended span when request is aborted', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .socketDelay(50) - .reply(200, ""); + .reply(200, ''); const promiseRequest = new Promise((resolve, reject) => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject("timeout"); + reject('timeout'); }); return req.end(); }); @@ -606,9 +606,9 @@ describe("HttpPlugin", () => { } }); - it("should have 1 ended span when request is aborted after receiving response", async () => { + it('should have 1 ended span when request is aborted after receiving response', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .delay({ body: 50, }) @@ -618,12 +618,12 @@ describe("HttpPlugin", () => { const req = http.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { req.abort(); data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } @@ -644,11 +644,11 @@ describe("HttpPlugin", () => { } }); - it("should have 1 ended span when request doesn't listening response", (done) => { + it("should have 1 ended span when request doesn't listening response", done => { nock.cleanAll(); nock.enableNetConnect(); const req = http.request(`${protocol}://${hostname}/`); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -658,13 +658,13 @@ describe("HttpPlugin", () => { req.end(); }); - it("should have 1 ended span when response is listened by using req.on('response')", (done) => { + it("should have 1 ended span when response is listened by using req.on('response')", done => { const host = `${protocol}://${hostname}`; - nock(host).get("/").reply(404); + nock(host).get('/').reply(404); const req = http.request(`${host}/`); - req.on("response", (response) => { - response.on("data", () => {}); - response.on("end", () => { + req.on('response', response => { + response.on('data', () => {}); + response.on('end', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); @@ -680,7 +680,7 @@ describe("HttpPlugin", () => { req.end(); }); - it("custom attributes should show up on client and server spans", async () => { + it('custom attributes should show up on client and server spans', async () => { await httpRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -688,47 +688,47 @@ describe("HttpPlugin", () => { const [incomingSpan, outgoingSpan] = spans; assert.strictEqual( - incomingSpan.attributes["custom request hook attribute"], - "request" + incomingSpan.attributes['custom request hook attribute'], + 'request' ); assert.strictEqual( - incomingSpan.attributes["custom response hook attribute"], - "response" + incomingSpan.attributes['custom response hook attribute'], + 'response' ); assert.strictEqual( - incomingSpan.attributes["span kind"], + incomingSpan.attributes['span kind'], SpanKind.CLIENT ); assert.strictEqual( - outgoingSpan.attributes["custom request hook attribute"], - "request" + outgoingSpan.attributes['custom request hook attribute'], + 'request' ); assert.strictEqual( - outgoingSpan.attributes["custom response hook attribute"], - "response" + outgoingSpan.attributes['custom response hook attribute'], + 'response' ); assert.strictEqual( - outgoingSpan.attributes["span kind"], + outgoingSpan.attributes['span kind'], SpanKind.CLIENT ); }); - it("should not set span as active in context for outgoing request", (done) => { + it('should not set span as active in context for outgoing request', done => { assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, (res) => { + http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { assert.deepStrictEqual(getSpan(context.active()), undefined); done(); }); }); }); - describe("with require parent span", () => { - beforeEach((done) => { + describe('with require parent span', () => { + beforeEach(done => { memoryExporter.reset(); plugin.enable(http, provider, {}); server = http.createServer((request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); }); server.listen(serverPort, done); }); @@ -738,14 +738,14 @@ describe("HttpPlugin", () => { plugin.disable(); }); - it("should not trace without parent with options enabled (both client & server)", async () => { + it('should not trace without parent with options enabled (both client & server)', async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = "/test/test"; + const testPath = '/test/test'; await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -753,13 +753,13 @@ describe("HttpPlugin", () => { assert.strictEqual(spans.length, 0); }); - it("should not trace without parent with options enabled (client only)", async () => { + it('should not trace without parent with options enabled (client only)', async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = "/test/test"; + const testPath = '/test/test'; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -772,18 +772,18 @@ describe("HttpPlugin", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every((span) => span.kind === SpanKind.SERVER), + spans.every(span => span.kind === SpanKind.SERVER), true ); }); - it("should not trace without parent with options enabled (server only)", async () => { + it('should not trace without parent with options enabled (server only)', async () => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, }; plugin.enable(http, provider, config); - const testPath = "/test/test"; + const testPath = '/test/test'; const result = await httpRequest.get( `${protocol}://${hostname}:${serverPort}${testPath}` ); @@ -796,27 +796,27 @@ describe("HttpPlugin", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); assert.strictEqual( - spans.every((span) => span.kind === SpanKind.CLIENT), + spans.every(span => span.kind === SpanKind.CLIENT), true ); }); - it("should trace with parent with both requireParent options enabled", (done) => { + it('should trace with parent with both requireParent options enabled', done => { plugin.disable(); const config: HttpPluginConfig = { requireParentforIncomingSpans: true, requireParentforOutgoingSpans: true, }; plugin.enable(http, provider, config); - const testPath = "/test/test"; - const tracer = provider.getTracer("default"); - const span = tracer.startSpan("parentSpan", { + const testPath = '/test/test'; + const tracer = provider.getTracer('default'); + const span = tracer.startSpan('parentSpan', { kind: SpanKind.INTERNAL, }); context.with(setSpan(context.active(), span), () => { httpRequest .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then((result) => { + .then(result => { span.end(); assert( result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== @@ -829,11 +829,11 @@ describe("HttpPlugin", () => { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 2); assert.strictEqual( - spans.filter((span) => span.kind === SpanKind.CLIENT).length, + spans.filter(span => span.kind === SpanKind.CLIENT).length, 1 ); assert.strictEqual( - spans.filter((span) => span.kind === SpanKind.INTERNAL).length, + spans.filter(span => span.kind === SpanKind.INTERNAL).length, 1 ); return done(); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index 008907bf7a4..258ce2e7f55 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -19,55 +19,55 @@ import { ROOT_CONTEXT, SpanKind, TraceFlags, -} from "@opentelemetry/api"; -import { BasicTracerProvider, Span } from "@opentelemetry/tracing"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import { IncomingMessage, ServerResponse } from "http"; -import * as sinon from "sinon"; -import * as url from "url"; -import { IgnoreMatcher } from "../../src/types"; -import * as utils from "../../src/utils"; -import { AttributeNames } from "../../src/enums"; - -describe("Utility", () => { - describe("parseResponseStatus()", () => { - it("should return ERROR code by default", () => { +} from '@opentelemetry/api'; +import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import { IncomingMessage, ServerResponse } from 'http'; +import * as sinon from 'sinon'; +import * as url from 'url'; +import { IgnoreMatcher } from '../../src/types'; +import * as utils from '../../src/utils'; +import { AttributeNames } from '../../src/enums'; + +describe('Utility', () => { + describe('parseResponseStatus()', () => { + it('should return ERROR code by default', () => { const status = utils.parseResponseStatus( (undefined as unknown) as number ); assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); }); - it("should return OK for Success HTTP status code", () => { + it('should return OK for Success HTTP status code', () => { for (let index = 100; index < 400; index++) { const status = utils.parseResponseStatus(index); assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); } }); - it("should not return OK for Bad HTTP status code", () => { + it('should not return OK for Bad HTTP status code', () => { for (let index = 400; index <= 600; index++) { const status = utils.parseResponseStatus(index); assert.notStrictEqual(status.code, SpanStatusCode.OK); } }); }); - describe("hasExpectHeader()", () => { - it("should throw if no option", () => { + describe('hasExpectHeader()', () => { + it('should throw if no option', () => { try { - utils.hasExpectHeader("" as http.RequestOptions); + utils.hasExpectHeader('' as http.RequestOptions); assert.fail(); } catch (ignore) {} }); - it("should not throw if no headers", () => { + it('should not throw if no headers', () => { const result = utils.hasExpectHeader({} as http.RequestOptions); assert.strictEqual(result, false); }); - it("should return true on Expect (no case sensitive)", () => { + it('should return true on Expect (no case sensitive)', () => { for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { const result = utils.hasExpectHeader({ headers, @@ -77,9 +77,9 @@ describe("Utility", () => { }); }); - describe("getRequestInfo()", () => { - it("should get options object", () => { - const webUrl = "http://u:p@google.fr/aPath?qu=ry"; + describe('getRequestInfo()', () => { + it('should get options object', () => { + const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; const urlParsed = url.parse(webUrl); const urlParsedWithoutPathname = { ...urlParsed, @@ -93,64 +93,64 @@ describe("Utility", () => { whatWgUrl, ]) { const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, "google.fr"); - assert.strictEqual(result.optionsParsed.protocol, "http:"); - assert.strictEqual(result.optionsParsed.path, "/aPath?qu=ry"); - assert.strictEqual(result.pathname, "/aPath"); - assert.strictEqual(result.origin, "http://google.fr"); + assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); + assert.strictEqual(result.optionsParsed.protocol, 'http:'); + assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); + assert.strictEqual(result.pathname, '/aPath'); + assert.strictEqual(result.origin, 'http://google.fr'); } }); }); - describe("satisfiesPattern()", () => { - it("string pattern", () => { - const answer1 = utils.satisfiesPattern("/test/1", "/test/1"); + describe('satisfiesPattern()', () => { + it('string pattern', () => { + const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern("/test/1", "/test/11"); + const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); assert.strictEqual(answer2, false); }); - it("regex pattern", () => { - const answer1 = utils.satisfiesPattern("/TeSt/1", /\/test/i); + it('regex pattern', () => { + const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern("/2/tEst/1", /\/test/); + const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); assert.strictEqual(answer2, false); }); - it("should throw if type is unknown", () => { + it('should throw if type is unknown', () => { try { - utils.satisfiesPattern("/TeSt/1", (true as unknown) as IgnoreMatcher); + utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); assert.fail(); } catch (error) { assert.strictEqual(error instanceof TypeError, true); } }); - it("function pattern", () => { + it('function pattern', () => { const answer1 = utils.satisfiesPattern( - "/test/home", - (url: string) => url === "/test/home" + '/test/home', + (url: string) => url === '/test/home' ); assert.strictEqual(answer1, true); const answer2 = utils.satisfiesPattern( - "/test/home", - (url: string) => url !== "/test/home" + '/test/home', + (url: string) => url !== '/test/home' ); assert.strictEqual(answer2, false); }); }); - describe("isIgnored()", () => { + describe('isIgnored()', () => { beforeEach(() => { - sinon.spy(utils, "satisfiesPattern"); + sinon.spy(utils, 'satisfiesPattern'); }); afterEach(() => { sinon.restore(); }); - it("should call isSatisfyPattern, n match", () => { - const answer1 = utils.isIgnored("/test/1", ["/test/11"]); + it('should call isSatisfyPattern, n match', () => { + const answer1 = utils.isIgnored('/test/1', ['/test/11']); assert.strictEqual(answer1, false); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, @@ -158,24 +158,24 @@ describe("Utility", () => { ); }); - it("should call isSatisfyPattern, match for function", () => { - const answer1 = utils.isIgnored("/test/1", [ - (url) => url.endsWith("/test/1"), + it('should call isSatisfyPattern, match for function', () => { + const answer1 = utils.isIgnored('/test/1', [ + url => url.endsWith('/test/1'), ]); assert.strictEqual(answer1, true); }); - it("should not re-throw when function throws an exception", () => { + it('should not re-throw when function throws an exception', () => { const onException = (e: Error) => { // Do Nothing }; for (const callback of [undefined, onException]) { assert.doesNotThrow(() => utils.isIgnored( - "/test/1", + '/test/1', [ () => { - throw new Error("test"); + throw new Error('test'); }, ], callback @@ -184,14 +184,14 @@ describe("Utility", () => { } }); - it("should call onException when function throws an exception", () => { + it('should call onException when function throws an exception', () => { const onException = sinon.spy(); assert.doesNotThrow(() => utils.isIgnored( - "/test/1", + '/test/1', [ () => { - throw new Error("test"); + throw new Error('test'); }, ], onException @@ -200,58 +200,58 @@ describe("Utility", () => { assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); }); - it("should not call isSatisfyPattern", () => { - utils.isIgnored("/test/1", []); + it('should not call isSatisfyPattern', () => { + utils.isIgnored('/test/1', []); assert.strictEqual( (utils.satisfiesPattern as sinon.SinonSpy).callCount, 0 ); }); - it("should return false on empty list", () => { - const answer1 = utils.isIgnored("/test/1", []); + it('should return false on empty list', () => { + const answer1 = utils.isIgnored('/test/1', []); assert.strictEqual(answer1, false); }); - it("should not throw and return false when list is undefined", () => { - const answer2 = utils.isIgnored("/test/1", undefined); + it('should not throw and return false when list is undefined', () => { + const answer2 = utils.isIgnored('/test/1', undefined); assert.strictEqual(answer2, false); }); }); - describe("getAbsoluteUrl()", () => { - it("should return absolute url with localhost", () => { - const path = "/test/1"; + describe('getAbsoluteUrl()', () => { + it('should return absolute url with localhost', () => { + const path = '/test/1'; const result = utils.getAbsoluteUrl(url.parse(path), {}); assert.strictEqual(result, `http://localhost${path}`); }); - it("should return absolute url", () => { - const absUrl = "http://www.google/test/1?query=1"; + it('should return absolute url', () => { + const absUrl = 'http://www.google/test/1?query=1'; const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); assert.strictEqual(result, absUrl); }); - it("should return default url", () => { + it('should return default url', () => { const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, "http://localhost/"); + assert.strictEqual(result, 'http://localhost/'); }); it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { const result = utils.getAbsoluteUrl( - { path: "/helloworld", port: 8080 }, + { path: '/helloworld', port: 8080 }, {} ); - assert.strictEqual(result, "http://localhost:8080/helloworld"); + assert.strictEqual(result, 'http://localhost:8080/helloworld'); }); }); - describe("setSpanWithError()", () => { - it("should have error attributes", () => { - const errorMessage = "test error"; + describe('setSpanWithError()', () => { + it('should have error attributes', () => { + const errorMessage = 'test error'; for (const obj of [undefined, { statusCode: 400 }]) { const span = new Span( - new BasicTracerProvider().getTracer("default"), + new BasicTracerProvider().getTracer('default'), ROOT_CONTEXT, - "test", - { spanId: "", traceId: "", traceFlags: TraceFlags.SAMPLED }, + 'test', + { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, SpanKind.INTERNAL ); /* tslint:disable-next-line:no-any */ @@ -266,15 +266,15 @@ describe("Utility", () => { }); }); - describe("isValidOptionsType()", () => { - ["", false, true, 1, 0, []].forEach((options) => { + describe('isValidOptionsType()', () => { + ['', false, true, 1, 0, []].forEach(options => { it(`should return false with the following value: ${JSON.stringify( options )}`, () => { assert.strictEqual(utils.isValidOptionsType(options), false); }); }); - for (const options of ["url", url.parse("http://url.com"), {}]) { + for (const options of ['url', url.parse('http://url.com'), {}]) { it(`should return true with the following value: ${JSON.stringify( options )}`, () => { @@ -283,10 +283,10 @@ describe("Utility", () => { } }); - describe("getIncomingRequestAttributesOnResponse()", () => { - it("should correctly parse the middleware stack if present", () => { + describe('getIncomingRequestAttributesOnResponse()', () => { + it('should correctly parse the middleware stack if present', () => { const request = { - __ot_middlewares: ["/test", "/toto", "/"], + __ot_middlewares: ['/test', '/toto', '/'], socket: {}, } as IncomingMessage & { __ot_middlewares?: string[] }; const response = {} as ServerResponse; @@ -294,10 +294,10 @@ describe("Utility", () => { request, response ); - assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], "/test/toto"); + assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], '/test/toto'); }); - it("should succesfully process without middleware stack", () => { + it('should succesfully process without middleware stack', () => { const request = { socket: {} } as IncomingMessage; const response = {} as ServerResponse; const attributes = utils.getIncomingRequestAttributesOnResponse( @@ -331,13 +331,13 @@ describe("Utility", () => { } } - describe("setRequestContentLengthAttributes()", () => { - it("should set request content-length uncompressed attribute with no content-encoding header", () => { + describe('setRequestContentLengthAttributes()', () => { + it('should set request content-length uncompressed attribute with no content-encoding header', () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", + 'content-length': '1200', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -352,8 +352,8 @@ describe("Utility", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", - "content-encoding": "identity", + 'content-length': '1200', + 'content-encoding': 'identity', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -368,8 +368,8 @@ describe("Utility", () => { const attributes: SpanAttributes = {}; const request = {} as IncomingMessage; request.headers = { - "content-length": "1200", - "content-encoding": "gzip", + 'content-length': '1200', + 'content-encoding': 'gzip', }; utils.setRequestContentLengthAttribute(request, attributes); @@ -381,14 +381,14 @@ describe("Utility", () => { }); }); - describe("setResponseContentLengthAttributes()", () => { - it("should set response content-length uncompressed attribute with no content-encoding header", () => { + describe('setResponseContentLengthAttributes()', () => { + it('should set response content-length uncompressed attribute with no content-encoding header', () => { const attributes: SpanAttributes = {}; const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", + 'content-length': '1200', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -405,8 +405,8 @@ describe("Utility", () => { const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", - "content-encoding": "identity", + 'content-length': '1200', + 'content-encoding': 'identity', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -424,8 +424,8 @@ describe("Utility", () => { const response = {} as IncomingMessage; response.headers = { - "content-length": "1200", - "content-encoding": "gzip", + 'content-length': '1200', + 'content-encoding': 'gzip', }; utils.setResponseContentLengthAttribute(response, attributes); @@ -437,12 +437,12 @@ describe("Utility", () => { ); }); - it("should set no attributes with no content-length header", () => { + it('should set no attributes with no content-length header', () => { const attributes: SpanAttributes = {}; const message = {} as IncomingMessage; message.headers = { - "content-encoding": "gzip", + 'content-encoding': 'gzip', }; utils.setResponseContentLengthAttribute(message, attributes); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 6e4805362b2..ad6825497cc 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -14,46 +14,46 @@ * limitations under the License. */ -import { SpanKind, Span, context } from "@opentelemetry/api"; +import { SpanKind, Span, context } from '@opentelemetry/api'; import { HttpFlavorValues, NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as url from "url"; -import { plugin } from "../../src/http"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { httpRequest } from "../utils/httpRequest"; -import * as utils from "../utils/utils"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as url from 'url'; +import { plugin } from '../../src/http'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpRequest } from '../utils/httpRequest'; +import * as utils from '../utils/utils'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; -import { HttpPluginConfig } from "../../src/types"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { Socket } from "net"; -import { sendRequestTwice } from "../utils/rawRequest"; -const protocol = "http"; +} from '@opentelemetry/tracing'; +import { HttpPluginConfig } from '../../src/types'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { Socket } from 'net'; +import { sendRequestTwice } from '../utils/rawRequest'; +const protocol = 'http'; const serverPort = 32345; -const hostname = "localhost"; +const hostname = 'localhost'; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpPlugin Integration tests", () => { +describe('HttpPlugin Integration tests', () => { let mockServerPort = 0; let mockServer: http.Server; const sockets: Array = []; - before((done) => { + before(done => { mockServer = http.createServer((req, res) => { res.statusCode = 200; - res.setHeader("content-type", "application/json"); + res.setHeader('content-type', 'application/json'); res.write( JSON.stringify({ success: true, @@ -65,17 +65,17 @@ describe("HttpPlugin Integration tests", () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error("unexpected addr null")); + done(new Error('unexpected addr null')); return; } - if (typeof addr === "string") { + if (typeof addr === 'string') { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error("Could not get port")); + done(new Error('Could not get port')); return; } mockServerPort = addr.port; @@ -83,8 +83,8 @@ describe("HttpPlugin Integration tests", () => { }); }); - after((done) => { - sockets.forEach((s) => s.destroy()); + after(done => { + sockets.forEach(s => s.destroy()); mockServer.close(done); }); @@ -96,7 +96,7 @@ describe("HttpPlugin Integration tests", () => { afterEach(() => { context.disable(); }); - describe("enable()", () => { + describe('enable()', () => { before(function (done) { // mandatory if (process.env.CI) { @@ -104,7 +104,7 @@ describe("HttpPlugin Integration tests", () => { return; } - utils.checkInternet((isConnected) => { + utils.checkInternet(isConnected => { if (!isConnected) { this.skip(); // don't disturb people @@ -123,7 +123,7 @@ describe("HttpPlugin Integration tests", () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ]; const config: HttpPluginConfig = { ignoreIncomingPaths: ignoreConfig, @@ -140,7 +140,7 @@ describe("HttpPlugin Integration tests", () => { plugin.disable(); }); - it("should create a rootSpan for GET requests and add propagation headers", async () => { + it('should create a rootSpan for GET requests and add propagation headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -149,25 +149,25 @@ describe("HttpPlugin Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { + it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -176,52 +176,52 @@ describe("HttpPlugin Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { + it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { "x-foo": "foo" }, + headers: { 'x-foo': 'foo' }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(result.reqHeaders["x-foo"], "foo"); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 @@ -233,62 +233,62 @@ describe("HttpPlugin Integration tests", () => { assertSpan(span, SpanKind.CLIENT, validations); }); - it("custom attributes should show up on client spans", async () => { + it('custom attributes should show up on client spans', async () => { const result = await httpRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a span for GET requests and add propagation headers with Expect headers", async () => { + it('should create a span for GET requests and add propagation headers with Expect headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: "100-continue" } }, + { headers: { Expect: '100-continue' } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 200, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: "100-continue", "user-agent": "http-plugin-test" }, - { "user-agent": "http-plugin-test" }, + { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, + { 'user-agent': 'http-plugin-test' }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, (done) => { + )}`, done => { let validations: { hostname: string; httpStatusCode: number; @@ -297,7 +297,7 @@ describe("HttpPlugin Integration tests", () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ""; + let data = ''; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -309,15 +309,15 @@ describe("HttpPlugin Integration tests", () => { req: http.IncomingMessage; }; - resp.on("data", (chunk) => { + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 301, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -329,12 +329,12 @@ describe("HttpPlugin Integration tests", () => { } ); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); @@ -343,13 +343,13 @@ describe("HttpPlugin Integration tests", () => { }); } - it("should work for multiple active requests in keep-alive mode", async () => { + it('should work for multiple active requests in keep-alive mode', async () => { await sendRequestTwice(hostname, mockServerPort); const spans = memoryExporter.getFinishedSpans(); const span = spans.find((s: any) => s.kind === SpanKind.SERVER); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); }); }); }); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts index 134bef5c626..b629dc7c0f1 100644 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SpanKind, SpanStatus } from "@opentelemetry/api"; -import { hrTimeToNanoseconds } from "@opentelemetry/core"; -import { ReadableSpan } from "@opentelemetry/tracing"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as utils from "../../src/utils"; -import { DummyPropagation } from "./DummyPropagation"; -import { AttributeNames } from "../../src/enums"; +import { SpanKind, SpanStatus } from '@opentelemetry/api'; +import { hrTimeToNanoseconds } from '@opentelemetry/core'; +import { ReadableSpan } from '@opentelemetry/tracing'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as utils from '../../src/utils'; +import { DummyPropagation } from './DummyPropagation'; +import { AttributeNames } from '../../src/enums'; export const assertSpan = ( span: ReadableSpan, @@ -69,11 +69,11 @@ export const assertSpan = ( utils.parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, "must be finished"); - assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); + assert.ok(span.endTime, 'must be finished'); + assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders["user-agent"]; + const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_USER_AGENT], @@ -83,12 +83,12 @@ export const assertSpan = ( } if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders["content-length"]) { - const contentLength = Number(validations.resHeaders["content-length"]); + if (validations.resHeaders['content-length']) { + const contentLength = Number(validations.resHeaders['content-length']); if ( - validations.resHeaders["content-encoding"] && - validations.resHeaders["content-encoding"] !== "identity" + validations.resHeaders['content-encoding'] && + validations.resHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], @@ -107,30 +107,30 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - "must be consistent (PEER_NAME and hostname)" + 'must be consistent (PEER_NAME and hostname)' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_IP], - "must have PEER_IP" + 'must have PEER_IP' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_PORT], - "must have PEER_PORT" + 'must have PEER_PORT' ); assert.ok( (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - "must be consistent" + 'must be consistent' ); } if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders["content-length"]) { - const contentLength = validations.reqHeaders["content-length"]; + if (validations.reqHeaders && validations.reqHeaders['content-length']) { + const contentLength = validations.reqHeaders['content-length']; if ( - validations.reqHeaders["content-encoding"] && - validations.reqHeaders["content-encoding"] !== "identity" + validations.reqHeaders['content-encoding'] && + validations.reqHeaders['content-encoding'] !== 'identity' ) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], @@ -150,15 +150,15 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - " must have serverName attribute" + ' must have serverName attribute' ); assert.ok( span.attributes[SemanticAttributes.NET_HOST_PORT], - "must have HOST_PORT" + 'must have HOST_PORT' ); assert.ok( span.attributes[SemanticAttributes.NET_HOST_IP], - "must have HOST_IP" + 'must have HOST_IP' ); } assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); diff --git a/packages/opentelemetry-plugin-https/src/enums.ts b/packages/opentelemetry-plugin-https/src/enums.ts index b0fca38cc80..d906510ac9d 100644 --- a/packages/opentelemetry-plugin-https/src/enums.ts +++ b/packages/opentelemetry-plugin-https/src/enums.ts @@ -18,5 +18,5 @@ * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md */ export enum AttributeNames { - HTTP_ERROR_MESSAGE = "http.error_message", + HTTP_ERROR_MESSAGE = 'http.error_message', } diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts index 6e20460cccf..3291d909a3e 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -21,43 +21,43 @@ import { Span as ISpan, SpanKind, setSpan, -} from "@opentelemetry/api"; -import { NodeTracerProvider } from "@opentelemetry/node"; -import { Http, HttpPluginConfig } from "@opentelemetry/plugin-http"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { ContextManager } from "@opentelemetry/api"; +} from '@opentelemetry/api'; +import { NodeTracerProvider } from '@opentelemetry/node'; +import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { ContextManager } from '@opentelemetry/api'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; +} from '@opentelemetry/tracing'; import { HttpFlavorValues, NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as fs from "fs"; -import * as http from "http"; -import * as https from "https"; -import * as nock from "nock"; -import * as path from "path"; -import { HttpsPlugin, plugin } from "../../src/https"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { httpsRequest } from "../utils/httpsRequest"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as http from 'http'; +import * as https from 'https'; +import * as nock from 'nock'; +import * as path from 'path'; +import { HttpsPlugin, plugin } from '../../src/https'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpsRequest } from '../utils/httpsRequest'; const applyCustomAttributesOnSpanErrorMessage = - "bad applyCustomAttributesOnSpan function"; + 'bad applyCustomAttributesOnSpan function'; let server: https.Server; const serverPort = 32345; -const protocol = "https"; -const hostname = "localhost"; -const serverName = "my.server.name"; -const pathname = "/test"; +const protocol = 'https'; +const hostname = 'localhost'; +const serverName = 'my.server.name'; +const pathname = '/test'; const memoryExporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider(); -const tracer = provider.getTracer("test-https"); +const tracer = provider.getTracer('test-https'); provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); propagation.setGlobalPropagator(new DummyPropagation()); @@ -76,10 +76,10 @@ function doNock( } export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpsPlugin", () => { +describe('HttpsPlugin', () => { let contextManager: ContextManager; beforeEach(() => { @@ -92,11 +92,11 @@ describe("HttpsPlugin", () => { context.disable(); }); - it("should return a plugin", () => { + it('should return a plugin', () => { assert.ok(plugin instanceof HttpsPlugin); }); - it("should match version", () => { + it('should match version', () => { assert.strictEqual(process.versions.node, plugin.version); }); @@ -104,8 +104,8 @@ describe("HttpsPlugin", () => { assert.strictEqual(protocol, plugin.moduleName); }); - describe("enable()", () => { - describe("with bad plugin options", () => { + describe('enable()', () => { + describe('with bad plugin options', () => { let pluginWithBadOptions: HttpsPlugin; beforeEach(() => { memoryExporter.reset(); @@ -115,12 +115,12 @@ describe("HttpsPlugin", () => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ (url: string) => { - throw new Error("bad ignoreIncomingPaths function"); + throw new Error('bad ignoreIncomingPaths function'); }, ], ignoreOutgoingUrls: [ (url: string) => { - throw new Error("bad ignoreOutgoingUrls function"); + throw new Error('bad ignoreOutgoingUrls function'); }, ], applyCustomAttributesOnSpan: () => { @@ -135,11 +135,11 @@ describe("HttpsPlugin", () => { ); server = https.createServer( { - key: fs.readFileSync("test/fixtures/server-key.pem"), - cert: fs.readFileSync("test/fixtures/server-cert.pem"), + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), }, (request, response) => { - response.end("Test Server Response"); + response.end('Test Server Response'); } ); @@ -151,7 +151,7 @@ describe("HttpsPlugin", () => { pluginWithBadOptions.disable(); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}` ); @@ -180,7 +180,7 @@ describe("HttpsPlugin", () => { ); }); }); - describe("with good plugin options", () => { + describe('with good plugin options', () => { beforeEach(() => { memoryExporter.reset(); }); @@ -188,14 +188,14 @@ describe("HttpsPlugin", () => { before(() => { const config: HttpPluginConfig = { ignoreIncomingPaths: [ - "/ignored/string", + '/ignored/string', /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], ignoreOutgoingUrls: [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ], applyCustomAttributesOnSpan: customAttributeFunction, serverName, @@ -203,14 +203,14 @@ describe("HttpsPlugin", () => { plugin.enable((https as unknown) as Http, provider, config); server = https.createServer( { - key: fs.readFileSync("test/fixtures/server-key.pem"), - cert: fs.readFileSync("test/fixtures/server-cert.pem"), + key: fs.readFileSync('test/fixtures/server-key.pem'), + cert: fs.readFileSync('test/fixtures/server-cert.pem'), }, (request, response) => { - if (request.url?.includes("/ignored")) { - tracer.startSpan("some-span").end(); + if (request.url?.includes('/ignored')) { + tracer.startSpan('some-span').end(); } - response.end("Test Server Response"); + response.end('Test Server Response'); } ); @@ -235,13 +235,13 @@ describe("HttpsPlugin", () => { assert.strictEqual(Object.keys(httpsNotPatched).length, 0); }); - it("should generate valid spans (client side and server side)", async () => { + it('should generate valid spans (client side and server side)', async () => { const result = await httpsRequest.get( `${protocol}://${hostname}:${serverPort}${pathname}`, { headers: { - "x-forwarded-for": ", , ", - "user-agent": "chrome", + 'x-forwarded-for': ', , ', + 'user-agent': 'chrome', }, } ); @@ -261,7 +261,7 @@ describe("HttpsPlugin", () => { assert.strictEqual(spans.length, 2); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - "" + '' ); assert.strictEqual( incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], @@ -292,7 +292,7 @@ describe("HttpsPlugin", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/1"; + const testPath = '/outgoing/rootSpan/1'; doNock( hostname, @@ -316,7 +316,7 @@ describe("HttpsPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, @@ -327,10 +327,10 @@ describe("HttpsPlugin", () => { }); } - it("should create a child span for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 200, "Ok"); - const name = "TestRootSpan"; + it('should create a child span for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 200, 'Ok'); + const name = 'TestRootSpan'; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -342,16 +342,16 @@ describe("HttpsPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -366,14 +366,14 @@ describe("HttpsPlugin", () => { for (let i = 0; i < httpErrorCodes.length; i++) { it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = "/outgoing/rootSpan/childs/1"; + const testPath = '/outgoing/rootSpan/childs/1'; doNock( hostname, testPath, httpErrorCodes[i], httpErrorCodes[i].toString() ); - const name = "TestRootSpan"; + const name = 'TestRootSpan'; const span = tracer.startSpan(name); return context.with(setSpan(context.active(), span), async () => { const result = await httpsRequest.get( @@ -385,16 +385,16 @@ describe("HttpsPlugin", () => { const validations = { hostname, httpStatusCode: result.statusCode!, - httpMethod: "GET", + httpMethod: 'GET', pathname: testPath, resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; - assert.ok(localSpan.name.indexOf("TestRootSpan") >= 0); + assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, "HTTP GET"); + assert.strictEqual(reqSpan.name, 'HTTP GET'); assert.strictEqual( localSpan.spanContext.traceId, reqSpan.spanContext.traceId @@ -408,17 +408,17 @@ describe("HttpsPlugin", () => { }); } - it("should create multiple child spans for GET requests", async () => { - const testPath = "/outgoing/rootSpan/childs"; + it('should create multiple child spans for GET requests', async () => { + const testPath = '/outgoing/rootSpan/childs'; const num = 5; - doNock(hostname, testPath, 200, "Ok", num); - const name = "TestRootSpan"; + doNock(hostname, testPath, 200, 'Ok', num); + const name = 'TestRootSpan'; const span = tracer.startSpan(name); await context.with(setSpan(context.active(), span), async () => { for (let i = 0; i < num; i++) { await httpsRequest.get(`${protocol}://${hostname}${testPath}`); const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, "HTTP GET"); + assert.strictEqual(spans[i].name, 'HTTP GET'); assert.strictEqual( span.context().traceId, spans[i].spanContext.traceId @@ -431,7 +431,7 @@ describe("HttpsPlugin", () => { }); }); - for (const ignored of ["string", "function", "regexp"]) { + for (const ignored of ['string', 'function', 'regexp']) { it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { const testPath = `/ignored/${ignored}`; @@ -443,7 +443,7 @@ describe("HttpsPlugin", () => { }); } - for (const arg of ["string", {}, new Date()]) { + for (const arg of ['string', {}, new Date()]) { it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -452,14 +452,14 @@ describe("HttpsPlugin", () => { } catch (error) { // request has been made // nock throw - assert.ok(error.message.startsWith("Nock: No match for request")); + assert.ok(error.message.startsWith('Nock: No match for request')); } const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); }); } - for (const arg of [true, 1, false, 0, ""]) { + for (const arg of [true, 1, false, 0, '']) { it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify( arg )}`, async () => { @@ -470,7 +470,7 @@ describe("HttpsPlugin", () => { // nock throw assert.ok( error.stack.indexOf( - path.normalize("/node_modules/nock/lib/intercept.js") + path.normalize('/node_modules/nock/lib/intercept.js') ) > 0 ); } @@ -482,26 +482,26 @@ describe("HttpsPlugin", () => { it('should have 1 ended span when request throw on bad "options" object', () => { try { - https.request({ protocol: "telnet" }); + https.request({ protocol: 'telnet' }); } catch (error) { const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 1); } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -522,7 +522,7 @@ describe("HttpsPlugin", () => { nock.cleanAll(); nock.enableNetConnect(); try { - https.request({ protocol: "telnet" }); + https.request({ protocol: 'telnet' }); assert.fail(); } catch (error) { const spans = memoryExporter.getFinishedSpans(); @@ -530,19 +530,19 @@ describe("HttpsPlugin", () => { } }); - it("should have 1 ended span when response.end throw an exception", async () => { - const testPath = "/outgoing/rootSpan/childs/1"; - doNock(hostname, testPath, 400, "Not Ok"); + it('should have 1 ended span when response.end throw an exception', async () => { + const testPath = '/outgoing/rootSpan/childs/1'; + doNock(hostname, testPath, 400, 'Not Ok'); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://${hostname}${testPath}`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { reject(new Error(data)); }); } @@ -559,28 +559,28 @@ describe("HttpsPlugin", () => { } }); - it("should have 1 ended span when request is aborted", async () => { + it('should have 1 ended span when request is aborted', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .socketDelay(50) - .reply(200, ""); + .reply(200, ''); const promiseRequest = new Promise((resolve, reject) => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } ); req.setTimeout(10, () => { req.abort(); - reject("timeout"); + reject('timeout'); }); return req.end(); }); @@ -597,9 +597,9 @@ describe("HttpsPlugin", () => { } }); - it("should have 1 ended span when request is aborted after receiving response", async () => { + it('should have 1 ended span when request is aborted after receiving response', async () => { nock(`${protocol}://my.server.com`) - .get("/") + .get('/') .delay({ body: 50, }) @@ -609,12 +609,12 @@ describe("HttpsPlugin", () => { const req = https.request( `${protocol}://my.server.com`, (resp: http.IncomingMessage) => { - let data = ""; - resp.on("data", (chunk) => { + let data = ''; + resp.on('data', chunk => { req.abort(); data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { resolve(data); }); } @@ -635,13 +635,13 @@ describe("HttpsPlugin", () => { } }); - it("should have 1 ended span when response is listened by using req.on('response')", (done) => { + it("should have 1 ended span when response is listened by using req.on('response')", done => { const host = `${protocol}://${hostname}`; - nock(host).get("/").reply(404); + nock(host).get('/').reply(404); const req = https.request(`${host}/`); - req.on("response", (response) => { - response.on("data", () => {}); - response.on("end", () => { + req.on('response', response => { + response.on('data', () => {}); + response.on('end', () => { const spans = memoryExporter.getFinishedSpans(); const [span] = spans; assert.strictEqual(spans.length, 1); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts index 7f805bdd67b..58896a9b8a2 100644 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -14,58 +14,58 @@ * limitations under the License. */ -import { HttpPluginConfig, Http } from "@opentelemetry/plugin-http"; -import { SpanKind, Span, context } from "@opentelemetry/api"; +import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; +import { SpanKind, Span, context } from '@opentelemetry/api'; import { HttpFlavorValues, NetTransportValues, SemanticAttributes, -} from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import * as fs from "fs"; -import * as path from "path"; -import * as https from "https"; -import { plugin } from "../../src/https"; -import { assertSpan } from "../utils/assertSpan"; -import { DummyPropagation } from "../utils/DummyPropagation"; -import { httpsRequest } from "../utils/httpsRequest"; -import * as url from "url"; -import * as utils from "../utils/utils"; -import { NodeTracerProvider } from "@opentelemetry/node"; +} from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as https from 'https'; +import { plugin } from '../../src/https'; +import { assertSpan } from '../utils/assertSpan'; +import { DummyPropagation } from '../utils/DummyPropagation'; +import { httpsRequest } from '../utils/httpsRequest'; +import * as url from 'url'; +import * as utils from '../utils/utils'; +import { NodeTracerProvider } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, -} from "@opentelemetry/tracing"; -import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; -import { Socket } from "net"; +} from '@opentelemetry/tracing'; +import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { Socket } from 'net'; -const protocol = "https"; +const protocol = 'https'; const serverPort = 42345; -const hostname = "localhost"; +const hostname = 'localhost'; const memoryExporter = new InMemorySpanExporter(); export const customAttributeFunction = (span: Span): void => { - span.setAttribute("span kind", SpanKind.CLIENT); + span.setAttribute('span kind', SpanKind.CLIENT); }; -describe("HttpsPlugin Integration tests", () => { +describe('HttpsPlugin Integration tests', () => { let mockServerPort = 0; let mockServer: https.Server; const sockets: Array = []; - before((done) => { + before(done => { mockServer = https.createServer( { key: fs.readFileSync( - path.join(__dirname, "..", "fixtures", "server-key.pem") + path.join(__dirname, '..', 'fixtures', 'server-key.pem') ), cert: fs.readFileSync( - path.join(__dirname, "..", "fixtures", "server-cert.pem") + path.join(__dirname, '..', 'fixtures', 'server-cert.pem') ), }, (req, res) => { res.statusCode = 200; - res.setHeader("content-type", "application/json"); + res.setHeader('content-type', 'application/json'); res.write( JSON.stringify({ success: true, @@ -78,17 +78,17 @@ describe("HttpsPlugin Integration tests", () => { mockServer.listen(0, () => { const addr = mockServer.address(); if (addr == null) { - done(new Error("unexpected addr null")); + done(new Error('unexpected addr null')); return; } - if (typeof addr === "string") { + if (typeof addr === 'string') { done(new Error(`unexpected addr ${addr}`)); return; } if (addr.port <= 0) { - done(new Error("Could not get port")); + done(new Error('Could not get port')); return; } mockServerPort = addr.port; @@ -96,8 +96,8 @@ describe("HttpsPlugin Integration tests", () => { }); }); - after((done) => { - sockets.forEach((s) => s.destroy()); + after(done => { + sockets.forEach(s => s.destroy()); mockServer.close(done); }); @@ -110,7 +110,7 @@ describe("HttpsPlugin Integration tests", () => { context.disable(); }); - describe("enable()", () => { + describe('enable()', () => { before(function (done) { // mandatory if (process.env.CI) { @@ -118,7 +118,7 @@ describe("HttpsPlugin Integration tests", () => { return; } - utils.checkInternet((isConnected) => { + utils.checkInternet(isConnected => { if (!isConnected) { this.skip(); // don't disturb people @@ -136,7 +136,7 @@ describe("HttpsPlugin Integration tests", () => { const ignoreConfig = [ `${protocol}://${hostname}:${serverPort}/ignored/string`, /\/ignored\/regexp$/i, - (url: string) => url.endsWith("/ignored/function"), + (url: string) => url.endsWith('/ignored/function'), ]; const config: HttpPluginConfig = { ignoreIncomingPaths: ignoreConfig, @@ -153,7 +153,7 @@ describe("HttpsPlugin Integration tests", () => { plugin.disable(); }); - it("should create a rootSpan for GET requests and add propagation headers", async () => { + it('should create a rootSpan for GET requests and add propagation headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -162,25 +162,25 @@ describe("HttpsPlugin Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a rootSpan for GET requests and add propagation headers if URL is used", async () => { + it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); @@ -189,52 +189,52 @@ describe("HttpsPlugin Integration tests", () => { ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a valid rootSpan with propagation headers for GET requests if URL and options are used", async () => { + it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const result = await httpsRequest.get( new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), { - headers: { "x-foo": "foo" }, + headers: { 'x-foo': 'foo' }, } ); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", - path: "/?query=test", + httpMethod: 'GET', + pathname: '/', + path: '/?query=test', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(result.reqHeaders["x-foo"], "foo"); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); assert.strictEqual( span.attributes[SemanticAttributes.HTTP_FLAVOR], HttpFlavorValues.HTTP_1_1 @@ -246,62 +246,62 @@ describe("HttpsPlugin Integration tests", () => { assertSpan(span, SpanKind.CLIENT, validations); }); - it("custom attributes should show up on client spans", async () => { + it('custom attributes should show up on client spans', async () => { const result = await httpsRequest.get( `${protocol}://localhost:${mockServerPort}/` ); const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: result.statusCode!, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); - assert.strictEqual(span.attributes["span kind"], SpanKind.CLIENT); + assert.strictEqual(span.name, 'HTTP GET'); + assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); assertSpan(span, SpanKind.CLIENT, validations); }); - it("should create a span for GET requests and add propagation headers with Expect headers", async () => { + it('should create a span for GET requests and add propagation headers with Expect headers', async () => { let spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = Object.assign( - { headers: { Expect: "100-continue" } }, + { headers: { Expect: '100-continue' } }, url.parse(`${protocol}://localhost:${mockServerPort}/`) ); const result = await httpsRequest.get(options); spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); const validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 200, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: result.resHeaders, reqHeaders: result.reqHeaders, component: plugin.component, }; assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assertSpan(span, SpanKind.CLIENT, validations); }); for (const headers of [ - { Expect: "100-continue", "user-agent": "http-plugin-test" }, - { "user-agent": "http-plugin-test" }, + { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, + { 'user-agent': 'http-plugin-test' }, ]) { it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( headers - )}`, (done) => { + )}`, done => { let validations: { hostname: string; httpStatusCode: number; @@ -310,7 +310,7 @@ describe("HttpsPlugin Integration tests", () => { reqHeaders: http.OutgoingHttpHeaders; resHeaders: http.IncomingHttpHeaders; }; - let data = ""; + let data = ''; const spans = memoryExporter.getFinishedSpans(); assert.strictEqual(spans.length, 0); const options = { headers }; @@ -322,15 +322,15 @@ describe("HttpsPlugin Integration tests", () => { req: http.IncomingMessage; }; - resp.on("data", (chunk) => { + resp.on('data', chunk => { data += chunk; }); - resp.on("end", () => { + resp.on('end', () => { validations = { - hostname: "localhost", + hostname: 'localhost', httpStatusCode: 301, - httpMethod: "GET", - pathname: "/", + httpMethod: 'GET', + pathname: '/', resHeaders: resp.headers, /* tslint:disable:no-any */ reqHeaders: (res.req as any).getHeaders @@ -342,12 +342,12 @@ describe("HttpsPlugin Integration tests", () => { } ); - req.on("close", () => { + req.on('close', () => { const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s) => s.kind === SpanKind.CLIENT); + const span = spans.find(s => s.kind === SpanKind.CLIENT); assert.ok(span); assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, "HTTP GET"); + assert.strictEqual(span.name, 'HTTP GET'); assert.ok(data); assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts index f4e7e95683a..37b8e09f479 100644 --- a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts +++ b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import { SpanKind } from "@opentelemetry/api"; -import { hrTimeToNanoseconds } from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import * as http from "http"; -import { DummyPropagation } from "./DummyPropagation"; -import { ReadableSpan } from "@opentelemetry/tracing"; -import { parseResponseStatus } from "@opentelemetry/plugin-http"; -import { AttributeNames } from "../../src/enums"; +import { SpanKind } from '@opentelemetry/api'; +import { hrTimeToNanoseconds } from '@opentelemetry/core'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import * as http from 'http'; +import { DummyPropagation } from './DummyPropagation'; +import { ReadableSpan } from '@opentelemetry/tracing'; +import { parseResponseStatus } from '@opentelemetry/plugin-http'; +import { AttributeNames } from '../../src/enums'; export const assertSpan = ( span: ReadableSpan, @@ -67,11 +67,11 @@ export const assertSpan = ( parseResponseStatus(validations.httpStatusCode) ); - assert.ok(span.endTime, "must be finished"); - assert.ok(hrTimeToNanoseconds(span.duration), "must have positive duration"); + assert.ok(span.endTime, 'must be finished'); + assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); if (validations.reqHeaders) { - const userAgent = validations.reqHeaders["user-agent"]; + const userAgent = validations.reqHeaders['user-agent']; if (userAgent) { assert.strictEqual( span.attributes[SemanticAttributes.HTTP_USER_AGENT], @@ -83,21 +83,21 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.NET_PEER_NAME], validations.hostname, - "must be consistent (PEER_NAME and hostname)" + 'must be consistent (PEER_NAME and hostname)' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_IP], - "must have PEER_IP" + 'must have PEER_IP' ); assert.ok( span.attributes[SemanticAttributes.NET_PEER_PORT], - "must have PEER_PORT" + 'must have PEER_PORT' ); assert.ok( (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( span.attributes[SemanticAttributes.NET_PEER_NAME] as string ) > -1, - "must be consistent" + 'must be consistent' ); } if (span.kind === SpanKind.SERVER) { @@ -105,16 +105,16 @@ export const assertSpan = ( assert.strictEqual( span.attributes[SemanticAttributes.HTTP_SERVER_NAME], validations.serverName, - " must have serverName attribute" + ' must have serverName attribute' ); } assert.ok( span.attributes[SemanticAttributes.NET_HOST_PORT], - "must have HOST_PORT" + 'must have HOST_PORT' ); assert.ok( span.attributes[SemanticAttributes.NET_HOST_IP], - "must have HOST_IP" + 'must have HOST_IP' ); assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); } else if (validations.reqHeaders) { diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index 09cf81bb8d7..58360b0792f 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as api from "@opentelemetry/api"; +import * as api from '@opentelemetry/api'; import { isAttributeValue, hrTime, @@ -22,15 +22,15 @@ import { InstrumentationLibrary, isTimeInput, timeInputToHrTime, -} from "@opentelemetry/core"; -import { Resource } from "@opentelemetry/resources"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import { ReadableSpan } from "./export/ReadableSpan"; -import { Tracer } from "./Tracer"; -import { SpanProcessor } from "./SpanProcessor"; -import { TraceParams } from "./types"; -import { SpanAttributeValue, Context } from "@opentelemetry/api"; -import { ExceptionEventName } from "./enums"; +} from '@opentelemetry/core'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { ReadableSpan } from './export/ReadableSpan'; +import { Tracer } from './Tracer'; +import { SpanProcessor } from './SpanProcessor'; +import { TraceParams } from './types'; +import { SpanAttributeValue, Context } from '@opentelemetry/api'; +import { ExceptionEventName } from './enums'; /** * This class represents a span. @@ -129,16 +129,16 @@ export class Span implements api.Span, ReadableSpan { ): this { if (this._isSpanEnded()) return this; if (this.events.length >= this._traceParams.numberOfEventsPerSpan!) { - api.diag.warn("Dropping extra events."); + api.diag.warn('Dropping extra events.'); this.events.shift(); } if (isTimeInput(attributesOrStartTime)) { - if (typeof startTime === "undefined") { + if (typeof startTime === 'undefined') { startTime = attributesOrStartTime as api.TimeInput; } attributesOrStartTime = undefined; } - if (typeof startTime === "undefined") { + if (typeof startTime === 'undefined') { startTime = hrTime(); } this.events.push({ @@ -163,7 +163,7 @@ export class Span implements api.Span, ReadableSpan { end(endTime: api.TimeInput = hrTime()): void { if (this._isSpanEnded()) { - api.diag.error("You can only call end() on a span once."); + api.diag.error('You can only call end() on a span once.'); return; } this._ended = true; @@ -172,7 +172,7 @@ export class Span implements api.Span, ReadableSpan { this._duration = hrTimeDuration(this.startTime, this.endTime); if (this._duration[0] < 0) { api.diag.warn( - "Inconsistent start and end time, startTime > endTime", + 'Inconsistent start and end time, startTime > endTime', this.startTime, this.endTime ); @@ -187,7 +187,7 @@ export class Span implements api.Span, ReadableSpan { recordException(exception: api.Exception, time: api.TimeInput = hrTime()) { const attributes: api.SpanAttributes = {}; - if (typeof exception === "string") { + if (typeof exception === 'string') { attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception; } else if (exception) { if (exception.code) { @@ -225,7 +225,7 @@ export class Span implements api.Span, ReadableSpan { private _isSpanEnded(): boolean { if (this._ended) { api.diag.warn( - "Can not execute the operation on ended Span {traceId: %s, spanId: %s}", + 'Can not execute the operation on ended Span {traceId: %s, spanId: %s}', this.spanContext.traceId, this.spanContext.spanId ); diff --git a/packages/opentelemetry-tracing/src/enums.ts b/packages/opentelemetry-tracing/src/enums.ts index 1b766da3d65..af2909dc68e 100644 --- a/packages/opentelemetry-tracing/src/enums.ts +++ b/packages/opentelemetry-tracing/src/enums.ts @@ -15,4 +15,4 @@ */ // Event name definitions -export const ExceptionEventName = "exception"; +export const ExceptionEventName = 'exception'; diff --git a/packages/opentelemetry-tracing/src/index.ts b/packages/opentelemetry-tracing/src/index.ts index 6ae68127539..720241a3107 100644 --- a/packages/opentelemetry-tracing/src/index.ts +++ b/packages/opentelemetry-tracing/src/index.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -export * from "./Tracer"; -export * from "./BasicTracerProvider"; -export * from "./export/ConsoleSpanExporter"; -export * from "./export/BatchSpanProcessor"; -export * from "./export/InMemorySpanExporter"; -export * from "./export/ReadableSpan"; -export * from "./export/SimpleSpanProcessor"; -export * from "./export/SpanExporter"; -export * from "./Span"; -export * from "./SpanProcessor"; -export * from "./types"; +export * from './Tracer'; +export * from './BasicTracerProvider'; +export * from './export/ConsoleSpanExporter'; +export * from './export/BatchSpanProcessor'; +export * from './export/InMemorySpanExporter'; +export * from './export/ReadableSpan'; +export * from './export/SimpleSpanProcessor'; +export * from './export/SpanExporter'; +export * from './Span'; +export * from './SpanProcessor'; +export * from './types'; diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 8fca0c58daf..76f63bcd7cc 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -22,38 +22,38 @@ import { SpanContext, SpanKind, TraceFlags, -} from "@opentelemetry/api"; +} from '@opentelemetry/api'; import { hrTime, hrTimeDuration, hrTimeToMilliseconds, hrTimeToNanoseconds, -} from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; -import * as assert from "assert"; -import { BasicTracerProvider, Span, SpanProcessor } from "../src"; +} from '@opentelemetry/core'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import * as assert from 'assert'; +import { BasicTracerProvider, Span, SpanProcessor } from '../src'; const performanceTimeOrigin = hrTime(); -describe("Span", () => { +describe('Span', () => { const tracer = new BasicTracerProvider({ traceParams: { numberOfAttributesPerSpan: 100, numberOfEventsPerSpan: 100, }, - }).getTracer("default"); - const name = "span1"; + }).getTracer('default'); + const name = 'span1'; const spanContext: SpanContext = { - traceId: "d4cda95b652f4a1592b449d5929fda1b", - spanId: "6e0c63257de34c92", + traceId: 'd4cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', traceFlags: TraceFlags.SAMPLED, }; const linkContext: LinkContext = { - traceId: "e4cda95b652f4a1592b449d5929fda1b", - spanId: "7e0c63257de34c92", + traceId: 'e4cda95b652f4a1592b449d5929fda1b', + spanId: '7e0c63257de34c92', }; - it("should create a Span instance", () => { + it('should create a Span instance', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -65,7 +65,7 @@ describe("Span", () => { span.end(); }); - it("should have valid startTime", () => { + it('should have valid startTime', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -79,7 +79,7 @@ describe("Span", () => { ); }); - it("should have valid endTime", () => { + it('should have valid endTime', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -90,17 +90,17 @@ describe("Span", () => { span.end(); assert.ok( hrTimeToNanoseconds(span.endTime) >= hrTimeToNanoseconds(span.startTime), - "end time must be bigger or equal start time" + 'end time must be bigger or equal start time' ); assert.ok( hrTimeToMilliseconds(span.endTime) > hrTimeToMilliseconds(performanceTimeOrigin), - "end time must be bigger than time origin" + 'end time must be bigger than time origin' ); }); - it("should have a duration", () => { + it('should have a duration', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -112,7 +112,7 @@ describe("Span", () => { assert.ok(hrTimeToNanoseconds(span.duration) >= 0); }); - it("should have valid event.time", () => { + it('should have valid event.time', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -120,14 +120,14 @@ describe("Span", () => { spanContext, SpanKind.SERVER ); - span.addEvent("my-event"); + span.addEvent('my-event'); assert.ok( hrTimeToMilliseconds(span.events[0].time) > hrTimeToMilliseconds(performanceTimeOrigin) ); }); - it("should have an entered time for event", () => { + it('should have an entered time for event', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -142,14 +142,14 @@ describe("Span", () => { const spanStartTime = hrTimeToMilliseconds(span.startTime); const eventTime = spanStartTime + timeMS; - span.addEvent("my-event", undefined, eventTime); + span.addEvent('my-event', undefined, eventTime); const diff = hrTimeDuration(span.startTime, span.events[0].time); assert.strictEqual(hrTimeToMilliseconds(diff), 123); }); describe('when 2nd param is "TimeInput" type', () => { - it("should have an entered time for event - ", () => { + it('should have an entered time for event - ', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -164,14 +164,14 @@ describe("Span", () => { const spanStartTime = hrTimeToMilliseconds(span.startTime); const eventTime = spanStartTime + timeMS; - span.addEvent("my-event", eventTime); + span.addEvent('my-event', eventTime); const diff = hrTimeDuration(span.startTime, span.events[0].time); assert.strictEqual(hrTimeToMilliseconds(diff), 123); }); }); - it("should get the span context of span", () => { + it('should get the span context of span', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -188,8 +188,8 @@ describe("Span", () => { span.end(); }); - describe("isRecording", () => { - it("should return true when span is not ended", () => { + describe('isRecording', () => { + it('should return true when span is not ended', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -200,7 +200,7 @@ describe("Span", () => { assert.ok(span.isRecording()); span.end(); }); - it("should return false when span is ended", () => { + it('should return false when span is ended', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -213,7 +213,7 @@ describe("Span", () => { }); }); - it("should set an attribute", () => { + it('should set an attribute', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -222,29 +222,29 @@ describe("Span", () => { SpanKind.CLIENT ); - span.setAttribute("string", "string"); - span.setAttribute("number", 0); - span.setAttribute("bool", true); - span.setAttribute("array", ["str1", "str2"]); - span.setAttribute("array", [1, 2]); - span.setAttribute("array", [true, false]); + span.setAttribute('string', 'string'); + span.setAttribute('number', 0); + span.setAttribute('bool', true); + span.setAttribute('array', ['str1', 'str2']); + span.setAttribute('array', [1, 2]); + span.setAttribute('array', [true, false]); //@ts-expect-error invalid attribute type object - span.setAttribute("object", { foo: "bar" }); + span.setAttribute('object', { foo: 'bar' }); //@ts-expect-error invalid attribute inhomogenous array - span.setAttribute("non-homogeneous-array", [0, ""]); + span.setAttribute('non-homogeneous-array', [0, '']); assert.deepStrictEqual(span.attributes, { - string: "string", + string: 'string', number: 0, bool: true, - "array": ["str1", "str2"], - "array": [1, 2], - "array": [true, false], + 'array': ['str1', 'str2'], + 'array': [1, 2], + 'array': [true, false], }); }); - it("should overwrite attributes", () => { + it('should overwrite attributes', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -253,15 +253,15 @@ describe("Span", () => { SpanKind.CLIENT ); - span.setAttribute("overwrite", "initial value"); - span.setAttribute("overwrite", "overwritten value"); + span.setAttribute('overwrite', 'initial value'); + span.setAttribute('overwrite', 'overwritten value'); assert.deepStrictEqual(span.attributes, { - overwrite: "overwritten value", + overwrite: 'overwritten value', }); }); - it("should set attributes", () => { + it('should set attributes', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -271,29 +271,29 @@ describe("Span", () => { ); span.setAttributes({ - string: "string", + string: 'string', number: 0, bool: true, - "array": ["str1", "str2"], - "array": [1, 2], - "array": [true, false], + 'array': ['str1', 'str2'], + 'array': [1, 2], + 'array': [true, false], //@ts-expect-error invalid attribute type object - object: { foo: "bar" }, + object: { foo: 'bar' }, //@ts-expect-error invalid attribute inhomogenous array - "non-homogeneous-array": [0, ""], + 'non-homogeneous-array': [0, ''], }); assert.deepStrictEqual(span.attributes, { - string: "string", + string: 'string', number: 0, bool: true, - "array": ["str1", "str2"], - "array": [1, 2], - "array": [true, false], + 'array': ['str1', 'str2'], + 'array': [1, 2], + 'array': [true, false], }); }); - it("should set an event", () => { + it('should set an event', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -301,35 +301,35 @@ describe("Span", () => { spanContext, SpanKind.CLIENT ); - span.addEvent("sent"); - span.addEvent("rev", { attr1: "value", attr2: 123, attr3: true }); + span.addEvent('sent'); + span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true }); span.end(); }); - it("should set a link", () => { + it('should set a link', () => { const spanContext: SpanContext = { - traceId: "a3cda95b652f4a1592b449d5929fda1b", - spanId: "5e0c63257de34c92", + traceId: 'a3cda95b652f4a1592b449d5929fda1b', + spanId: '5e0c63257de34c92', traceFlags: TraceFlags.SAMPLED, }; const linkContext: LinkContext = { - traceId: "b3cda95b652f4a1592b449d5929fda1b", - spanId: "6e0c63257de34c92", + traceId: 'b3cda95b652f4a1592b449d5929fda1b', + spanId: '6e0c63257de34c92', }; - const attributes = { attr1: "value", attr2: 123, attr3: true }; + const attributes = { attr1: 'value', attr2: 123, attr3: true }; const span = new Span( tracer, ROOT_CONTEXT, name, spanContext, SpanKind.CLIENT, - "12345", + '12345', [{ context: linkContext }, { context: linkContext, attributes }] ); span.end(); }); - it("should drop extra attributes and events", () => { + it('should drop extra attributes and events', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -338,20 +338,20 @@ describe("Span", () => { SpanKind.CLIENT ); for (let i = 0; i < 150; i++) { - span.setAttribute("foo" + i, "bar" + i); - span.addEvent("sent" + i); + span.setAttribute('foo' + i, 'bar' + i); + span.addEvent('sent' + i); } span.end(); assert.strictEqual(span.events.length, 100); assert.strictEqual(Object.keys(span.attributes).length, 100); - assert.strictEqual(span.events[span.events.length - 1].name, "sent149"); - assert.strictEqual(span.attributes["foo0"], "bar0"); - assert.strictEqual(span.attributes["foo99"], "bar99"); - assert.strictEqual(span.attributes["sent100"], undefined); + assert.strictEqual(span.events[span.events.length - 1].name, 'sent149'); + assert.strictEqual(span.attributes['foo0'], 'bar0'); + assert.strictEqual(span.attributes['foo99'], 'bar99'); + assert.strictEqual(span.attributes['sent100'], undefined); }); - it("should set an error status", () => { + it('should set an error status', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -361,23 +361,23 @@ describe("Span", () => { ); span.setStatus({ code: SpanStatusCode.ERROR, - message: "This is an error", + message: 'This is an error', }); span.end(); }); - it("should return ReadableSpan", () => { - const parentId = "5c1c63257de34c67"; + it('should return ReadableSpan', () => { + const parentId = '5c1c63257de34c67'; const span = new Span( tracer, ROOT_CONTEXT, - "my-span", + 'my-span', spanContext, SpanKind.INTERNAL, parentId ); - assert.strictEqual(span.name, "my-span"); + assert.strictEqual(span.name, 'my-span'); assert.strictEqual(span.kind, SpanKind.INTERNAL); assert.strictEqual(span.parentSpanId, parentId); assert.strictEqual(span.spanContext.traceId, spanContext.traceId); @@ -390,20 +390,20 @@ describe("Span", () => { assert.ok(span.instrumentationLibrary); const { name, version } = span.instrumentationLibrary; - assert.strictEqual(name, "default"); + assert.strictEqual(name, 'default'); assert.strictEqual(version, undefined); }); - it("should return ReadableSpan with attributes", () => { + it('should return ReadableSpan with attributes', () => { const span = new Span( tracer, ROOT_CONTEXT, - "my-span", + 'my-span', spanContext, SpanKind.CLIENT ); - span.setAttribute("attr1", "value1"); - assert.deepStrictEqual(span.attributes, { attr1: "value1" }); + span.setAttribute('attr1', 'value1'); + assert.deepStrictEqual(span.attributes, { attr1: 'value1' }); span.setAttributes({ attr2: 123, attr1: false }); assert.deepStrictEqual(span.attributes, { @@ -413,18 +413,18 @@ describe("Span", () => { span.end(); // shouldn't add new attribute - span.setAttribute("attr3", "value3"); + span.setAttribute('attr3', 'value3'); assert.deepStrictEqual(span.attributes, { attr1: false, attr2: 123, }); }); - it("should return ReadableSpan with links", () => { + it('should return ReadableSpan with links', () => { const span = new Span( tracer, ROOT_CONTEXT, - "my-span", + 'my-span', spanContext, SpanKind.CLIENT, undefined, @@ -432,7 +432,7 @@ describe("Span", () => { { context: linkContext }, { context: linkContext, - attributes: { attr1: "value", attr2: 123, attr3: true }, + attributes: { attr1: 'value', attr2: 123, attr3: true }, }, ] ); @@ -442,7 +442,7 @@ describe("Span", () => { context: linkContext, }, { - attributes: { attr1: "value", attr2: 123, attr3: true }, + attributes: { attr1: 'value', attr2: 123, attr3: true }, context: linkContext, }, ]); @@ -450,30 +450,30 @@ describe("Span", () => { span.end(); }); - it("should return ReadableSpan with events", () => { + it('should return ReadableSpan with events', () => { const span = new Span( tracer, ROOT_CONTEXT, - "my-span", + 'my-span', spanContext, SpanKind.CLIENT ); - span.addEvent("sent"); + span.addEvent('sent'); assert.strictEqual(span.events.length, 1); const [event] = span.events; - assert.deepStrictEqual(event.name, "sent"); + assert.deepStrictEqual(event.name, 'sent'); assert.ok(!event.attributes); assert.ok(event.time[0] > 0); - span.addEvent("rev", { attr1: "value", attr2: 123, attr3: true }); + span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true }); assert.strictEqual(span.events.length, 2); const [event1, event2] = span.events; - assert.deepStrictEqual(event1.name, "sent"); + assert.deepStrictEqual(event1.name, 'sent'); assert.ok(!event1.attributes); assert.ok(event1.time[0] > 0); - assert.deepStrictEqual(event2.name, "rev"); + assert.deepStrictEqual(event2.name, 'rev'); assert.deepStrictEqual(event2.attributes, { - attr1: "value", + attr1: 'value', attr2: 123, attr3: true, }); @@ -481,11 +481,11 @@ describe("Span", () => { span.end(); // shouldn't add new event - span.addEvent("sent"); + span.addEvent('sent'); assert.strictEqual(span.events.length, 2); }); - it("should return ReadableSpan with new status", () => { + it('should return ReadableSpan with new status', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -495,21 +495,21 @@ describe("Span", () => { ); span.setStatus({ code: SpanStatusCode.ERROR, - message: "This is an error", + message: 'This is an error', }); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.strictEqual(span.status.message, "This is an error"); + assert.strictEqual(span.status.message, 'This is an error'); span.end(); // shouldn't update status span.setStatus({ code: SpanStatusCode.OK, - message: "OK", + message: 'OK', }); assert.strictEqual(span.status.code, SpanStatusCode.ERROR); }); - it("should only end a span once", () => { + it('should only end a span once', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -523,7 +523,7 @@ describe("Span", () => { assert.deepStrictEqual(span.endTime[0], Math.trunc(endTime / 1000)); }); - it("should update name", () => { + it('should update name', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -531,15 +531,15 @@ describe("Span", () => { spanContext, SpanKind.SERVER ); - span.updateName("foo-span"); + span.updateName('foo-span'); span.end(); // shouldn't update name - span.updateName("bar-span"); - assert.strictEqual(span.name, "foo-span"); + span.updateName('bar-span'); + assert.strictEqual(span.name, 'foo-span'); }); - it("should have ended", () => { + it('should have ended', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -552,8 +552,8 @@ describe("Span", () => { assert.strictEqual(span.ended, true); }); - describe("span processor", () => { - it("should call onStart synchronously when span is started", () => { + describe('span processor', () => { + it('should call onStart synchronously when span is started', () => { let started = false; const processor: SpanProcessor = { onStart: () => { @@ -568,11 +568,11 @@ describe("Span", () => { provider.addSpanProcessor(processor); - provider.getTracer("default").startSpan("test"); + provider.getTracer('default').startSpan('test'); assert.ok(started); }); - it("should call onEnd synchronously when span is ended", () => { + it('should call onEnd synchronously when span is ended', () => { let ended = false; const processor: SpanProcessor = { onStart: () => {}, @@ -587,14 +587,14 @@ describe("Span", () => { provider.addSpanProcessor(processor); - provider.getTracer("default").startSpan("test").end(); + provider.getTracer('default').startSpan('test').end(); assert.ok(ended); }); - it("should call onStart with a writeable span", () => { + it('should call onStart with a writeable span', () => { const processor: SpanProcessor = { - onStart: (span) => { - span.setAttribute("attr", true); + onStart: span => { + span.setAttribute('attr', true); }, forceFlush: () => Promise.resolve(), onEnd() {}, @@ -605,24 +605,24 @@ describe("Span", () => { provider.addSpanProcessor(processor); - const s = provider.getTracer("default").startSpan("test") as Span; + const s = provider.getTracer('default').startSpan('test') as Span; assert.ok(s.attributes.attr); }); }); - describe("recordException", () => { + describe('recordException', () => { const invalidExceptions: any[] = [ 1, null, undefined, - { foo: "bar" }, - { stack: "bar" }, - ["a", "b", "c"], + { foo: 'bar' }, + { stack: 'bar' }, + ['a', 'b', 'c'], ]; - invalidExceptions.forEach((key) => { + invalidExceptions.forEach(key => { describe(`when exception is (${JSON.stringify(key)})`, () => { - it("should NOT record an exception", () => { + it('should NOT record an exception', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -640,9 +640,9 @@ describe("Span", () => { describe('when exception type is "string"', () => { let error: Exception; beforeEach(() => { - error = "boom"; + error = 'boom'; }); - it("should record an exception", () => { + it('should record an exception', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -654,9 +654,9 @@ describe("Span", () => { span.recordException(error); const event = span.events[0]; - assert.strictEqual(event.name, "exception"); + assert.strictEqual(event.name, 'exception'); assert.deepStrictEqual(event.attributes, { - "exception.message": "boom", + 'exception.message': 'boom', }); assert.ok(event.time[0] > 0); }); @@ -664,18 +664,18 @@ describe("Span", () => { const errorsObj = [ { - description: "code", - obj: { code: "Error", message: "boom", stack: "bar" }, + description: 'code', + obj: { code: 'Error', message: 'boom', stack: 'bar' }, }, { - description: "name", - obj: { name: "Error", message: "boom", stack: "bar" }, + description: 'name', + obj: { name: 'Error', message: 'boom', stack: 'bar' }, }, ]; - errorsObj.forEach((errorObj) => { + errorsObj.forEach(errorObj => { describe(`when exception type is an object with ${errorObj.description}`, () => { const error: Exception = errorObj.obj; - it("should record an exception", () => { + it('should record an exception', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -688,7 +688,7 @@ describe("Span", () => { const event = span.events[0]; assert.ok(event.time[0] > 0); - assert.strictEqual(event.name, "exception"); + assert.strictEqual(event.name, 'exception'); assert.ok(event.attributes); @@ -698,15 +698,15 @@ describe("Span", () => { const stacktrace = String( event.attributes[SemanticAttributes.EXCEPTION_STACKTRACE] ); - assert.strictEqual(type, "Error"); - assert.strictEqual(message, "boom"); - assert.strictEqual(stacktrace, "bar"); + assert.strictEqual(type, 'Error'); + assert.strictEqual(message, 'boom'); + assert.strictEqual(stacktrace, 'bar'); }); }); }); - describe("when time is provided", () => { - it("should record an exception with provided time", () => { + describe('when time is provided', () => { + it('should record an exception with provided time', () => { const span = new Span( tracer, ROOT_CONTEXT, @@ -715,7 +715,7 @@ describe("Span", () => { SpanKind.CLIENT ); assert.strictEqual(span.events.length, 0); - span.recordException("boom", [0, 123]); + span.recordException('boom', [0, 123]); const event = span.events[0]; assert.deepStrictEqual(event.time, [0, 123]); }); diff --git a/packages/opentelemetry-web/src/utils.ts b/packages/opentelemetry-web/src/utils.ts index b103d7a8ee1..af9d15c9d38 100644 --- a/packages/opentelemetry-web/src/utils.ts +++ b/packages/opentelemetry-web/src/utils.ts @@ -18,21 +18,21 @@ import { PerformanceEntries, PerformanceResourceTimingInfo, PropagateTraceHeaderCorsUrls, -} from "./types"; -import { PerformanceTimingNames as PTN } from "./enums/PerformanceTimingNames"; -import * as api from "@opentelemetry/api"; +} from './types'; +import { PerformanceTimingNames as PTN } from './enums/PerformanceTimingNames'; +import * as api from '@opentelemetry/api'; import { hrTimeToNanoseconds, timeInputToHrTime, urlMatches, -} from "@opentelemetry/core"; -import { SemanticAttributes } from "@opentelemetry/semantic-conventions"; +} from '@opentelemetry/core'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; // Used to normalize relative URLs let a: HTMLAnchorElement | undefined; const getUrlNormalizingAnchor = () => { if (!a) { - a = document.createElement("a"); + a = document.createElement('a'); } return a; @@ -61,7 +61,7 @@ export function addSpanNetworkEvent( ): api.Span | undefined { if ( hasKey(entries, performanceName) && - typeof entries[performanceName] === "number" + typeof entries[performanceName] === 'number' ) { span.addEvent(performanceName, entries[performanceName]); return span; @@ -245,7 +245,7 @@ function filterResourcesForSpan( ) { const startTime = hrTimeToNanoseconds(startTimeHR); const endTime = hrTimeToNanoseconds(endTimeHR); - let filteredResources = resources.filter((resource) => { + let filteredResources = resources.filter(resource => { const resourceStartTime = hrTimeToNanoseconds( timeInputToHrTime(resource[PTN.FETCH_START]) ); @@ -255,7 +255,7 @@ function filterResourcesForSpan( return ( resource.initiatorType.toLowerCase() === - (initiatorType || "xmlhttprequest") && + (initiatorType || 'xmlhttprequest') && resource.name === spanUrl && resourceStartTime >= startTime && resourceEndTime <= endTime @@ -263,7 +263,7 @@ function filterResourcesForSpan( }); if (filteredResources.length > 0) { - filteredResources = filteredResources.filter((resource) => { + filteredResources = filteredResources.filter(resource => { return !ignoredResources.has(resource); }); } @@ -276,7 +276,7 @@ function filterResourcesForSpan( * @param url */ export function parseUrl(url: string): HTMLAnchorElement { - const a = document.createElement("a"); + const a = document.createElement('a'); a.href = url; return a; } @@ -290,13 +290,13 @@ export function parseUrl(url: string): HTMLAnchorElement { // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getElementXPath(target: any, optimised?: boolean) { if (target.nodeType === Node.DOCUMENT_NODE) { - return "/"; + return '/'; } const targetValue = getNodeValue(target, optimised); - if (optimised && targetValue.indexOf("@id") > 0) { + if (optimised && targetValue.indexOf('@id') > 0) { return targetValue; } - let xpath = ""; + let xpath = ''; if (target.parentNode) { xpath += getElementXPath(target.parentNode, false); } @@ -340,9 +340,9 @@ function getNodeIndex(target: HTMLElement): number { function getNodeValue(target: HTMLElement, optimised?: boolean): string { const nodeType = target.nodeType; const index = getNodeIndex(target); - let nodeValue = ""; + let nodeValue = ''; if (nodeType === Node.ELEMENT_NODE) { - const id = target.getAttribute("id"); + const id = target.getAttribute('id'); if (optimised && id) { return `//*[@id="${id}"]`; } @@ -351,11 +351,11 @@ function getNodeValue(target: HTMLElement, optimised?: boolean): string { nodeType === Node.TEXT_NODE || nodeType === Node.CDATA_SECTION_NODE ) { - nodeValue = "text()"; + nodeValue = 'text()'; } else if (nodeType === Node.COMMENT_NODE) { - nodeValue = "comment()"; + nodeValue = 'comment()'; } else { - return ""; + return ''; } // if index is 1 it can be omitted in xpath if (nodeValue && index > 1) { @@ -375,7 +375,7 @@ export function shouldPropagateTraceHeaders( ) { let propagateTraceHeaderUrls = propagateTraceHeaderCorsUrls || []; if ( - typeof propagateTraceHeaderUrls === "string" || + typeof propagateTraceHeaderUrls === 'string' || propagateTraceHeaderUrls instanceof RegExp ) { propagateTraceHeaderUrls = [propagateTraceHeaderUrls]; @@ -385,7 +385,7 @@ export function shouldPropagateTraceHeaders( if (parsedSpanUrl.origin === window.location.origin) { return true; } else { - return propagateTraceHeaderUrls.some((propagateTraceHeaderUrl) => + return propagateTraceHeaderUrls.some(propagateTraceHeaderUrl => urlMatches(spanUrl, propagateTraceHeaderUrl) ); } From c2fb34af25a9e896d62ba8289be49907ba918d5a Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 20:04:48 +0100 Subject: [PATCH 10/14] fix: use `RPC_GRPC_STATUS_CODE` from `semantic-convention` that a custom one --- .../opentelemetry-instrumentation-grpc/src/enums.ts | 1 - .../src/grpc-js/clientUtils.ts | 5 +++-- .../src/grpc-js/serverUtils.ts | 10 +++++++--- .../src/grpc/serverUtils.ts | 10 +++++++--- .../src/client/utils.ts | 5 +++-- packages/opentelemetry-plugin-grpc-js/src/enums.ts | 1 - .../src/server/clientStreamAndUnary.ts | 8 ++++++-- .../src/server/serverStreamAndBidi.ts | 3 ++- packages/opentelemetry-plugin-grpc/src/enums.ts | 1 - packages/opentelemetry-plugin-grpc/src/grpc.ts | 13 +++++++------ 10 files changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/opentelemetry-instrumentation-grpc/src/enums.ts b/packages/opentelemetry-instrumentation-grpc/src/enums.ts index f292c19c98a..9dfd3e0923e 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/enums.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/enums.ts @@ -20,7 +20,6 @@ export enum AttributeNames { GRPC_KIND = 'grpc.kind', // SERVER or CLIENT GRPC_METHOD = 'grpc.method', - GRPC_STATUS_CODE = 'grpc.status_code', GRPC_ERROR_NAME = 'grpc.error_name', GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts index 2c9c6a745a9..a53daa3ee07 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/clientUtils.ts @@ -33,6 +33,7 @@ import { import { CALL_SPAN_ENDED } from './serverUtils'; import { EventEmitter } from 'events'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Parse a package method list and return a list of methods to patch @@ -90,7 +91,7 @@ export function makeGrpcClientRemoteCall( if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString() ); } @@ -101,7 +102,7 @@ export function makeGrpcClientRemoteCall( } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.UNSET.toString() ); } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts index 5de7978b806..ab953af8c6e 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc-js/serverUtils.ts @@ -34,6 +34,7 @@ import { } from '../utils'; import { IgnoreMatcher } from '../types'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const CALL_SPAN_ENDED = Symbol('opentelemetry call span ended'); @@ -70,7 +71,7 @@ function serverStreamAndBidiHandler( code: SpanStatusCode.UNSET, }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); @@ -121,7 +122,10 @@ function clientStreamAndUnaryHandler( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ [AttributeNames.GRPC_ERROR_NAME]: err.name, @@ -130,7 +134,7 @@ function clientStreamAndUnaryHandler( } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts index 84a644a15f9..9d6673c09fd 100644 --- a/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts +++ b/packages/opentelemetry-instrumentation-grpc/src/grpc/serverUtils.ts @@ -24,6 +24,7 @@ import { _methodIsIgnored, } from '../utils'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const clientStreamAndUnaryHandler = function ( grpcClient: typeof grpcTypes, @@ -47,7 +48,10 @@ export const clientStreamAndUnaryHandler = function ( code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ [AttributeNames.GRPC_ERROR_NAME]: err.name, @@ -56,7 +60,7 @@ export const clientStreamAndUnaryHandler = function ( } else { span.setStatus({ code: SpanStatusCode.UNSET }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, grpcClient.status.OK.toString() ); } @@ -89,7 +93,7 @@ export const serverStreamAndBidiHandler = function ( call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, call.status.code.toString() ); diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts index ff4e19ebccf..542ff455101 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts @@ -35,6 +35,7 @@ import { } from '../utils'; import { EventEmitter } from 'events'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Parse a package method list and return a list of methods to patch @@ -115,7 +116,7 @@ export function makeGrpcClientRemoteCall( if (err.code) { span.setStatus(grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString() ); } @@ -126,7 +127,7 @@ export function makeGrpcClientRemoteCall( } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-plugin-grpc-js/src/enums.ts b/packages/opentelemetry-plugin-grpc-js/src/enums.ts index f292c19c98a..9dfd3e0923e 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/enums.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/enums.ts @@ -20,7 +20,6 @@ export enum AttributeNames { GRPC_KIND = 'grpc.kind', // SERVER or CLIENT GRPC_METHOD = 'grpc.method', - GRPC_STATUS_CODE = 'grpc.status_code', GRPC_ERROR_NAME = 'grpc.error_name', GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts index 8edc034e221..dd18886f2c1 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts @@ -20,6 +20,7 @@ import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; import type { GrpcJsPlugin } from '../grpcJs'; import type * as grpcJs from '@grpc/grpc-js'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Handle patching for clientStream and unary type server handlers @@ -43,7 +44,10 @@ export function clientStreamAndUnaryHandler( code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), message: err.message, }); - span.setAttribute(AttributeNames.GRPC_STATUS_CODE, err.code.toString()); + span.setAttribute( + SemanticAttributes.RPC_GRPC_STATUS_CODE, + err.code.toString() + ); } span.setAttributes({ [AttributeNames.GRPC_ERROR_NAME]: err.name, @@ -52,7 +56,7 @@ export function clientStreamAndUnaryHandler( } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); } diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts index a1963ec59af..50122e0c505 100644 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts @@ -23,6 +23,7 @@ import { grpcStatusCodeToOpenTelemetryStatusCode, } from '../utils'; import { AttributeNames } from '../enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** * Handle patching for serverStream and Bidi type server handlers @@ -58,7 +59,7 @@ export function serverStreamAndBidiHandler( code: SpanStatusCode.OK, }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, SpanStatusCode.OK.toString() ); diff --git a/packages/opentelemetry-plugin-grpc/src/enums.ts b/packages/opentelemetry-plugin-grpc/src/enums.ts index f292c19c98a..9dfd3e0923e 100644 --- a/packages/opentelemetry-plugin-grpc/src/enums.ts +++ b/packages/opentelemetry-plugin-grpc/src/enums.ts @@ -20,7 +20,6 @@ export enum AttributeNames { GRPC_KIND = 'grpc.kind', // SERVER or CLIENT GRPC_METHOD = 'grpc.method', - GRPC_STATUS_CODE = 'grpc.status_code', GRPC_ERROR_NAME = 'grpc.error_name', GRPC_ERROR_MESSAGE = 'grpc.error_message', } diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 556a612a5a2..e1df72749f6 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -48,6 +48,7 @@ import { } from './utils'; import { VERSION } from './version'; import { AttributeNames } from './enums'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /** The metadata key under which span context is stored as a binary value. */ export const GRPC_TRACE_KEY = 'grpc-trace-bin'; @@ -258,7 +259,7 @@ export class GrpcPlugin extends BasePlugin { message: err.message, }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString() ); } @@ -269,7 +270,7 @@ export class GrpcPlugin extends BasePlugin { } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } @@ -303,7 +304,7 @@ export class GrpcPlugin extends BasePlugin { call.on('finish', () => { span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, call.status.code.toString() ); @@ -423,7 +424,7 @@ export class GrpcPlugin extends BasePlugin { if (err.code) { span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, err.code.toString() ); } @@ -434,7 +435,7 @@ export class GrpcPlugin extends BasePlugin { } else { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, plugin._moduleExports.status.OK.toString() ); } @@ -505,7 +506,7 @@ export class GrpcPlugin extends BasePlugin { (status: SpanStatus) => { span.setStatus({ code: SpanStatusCode.OK }); span.setAttribute( - AttributeNames.GRPC_STATUS_CODE, + SemanticAttributes.RPC_GRPC_STATUS_CODE, status.code.toString() ); endSpan(); From 4b16cff8f52c56da8d741febc44a9f876424b274 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 8 Apr 2021 20:12:59 +0100 Subject: [PATCH 11/14] test: fix Span test to ensure attribute value is a string --- packages/opentelemetry-tracing/src/Span.ts | 4 +++- packages/opentelemetry-tracing/test/Span.test.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-tracing/src/Span.ts b/packages/opentelemetry-tracing/src/Span.ts index 58360b0792f..bfaf77b20b9 100644 --- a/packages/opentelemetry-tracing/src/Span.ts +++ b/packages/opentelemetry-tracing/src/Span.ts @@ -191,7 +191,9 @@ export class Span implements api.Span, ReadableSpan { attributes[SemanticAttributes.EXCEPTION_MESSAGE] = exception; } else if (exception) { if (exception.code) { - attributes[SemanticAttributes.EXCEPTION_TYPE] = exception.code; + attributes[ + SemanticAttributes.EXCEPTION_TYPE + ] = exception.code.toString(); } else if (exception.name) { attributes[SemanticAttributes.EXCEPTION_TYPE] = exception.name; } diff --git a/packages/opentelemetry-tracing/test/Span.test.ts b/packages/opentelemetry-tracing/test/Span.test.ts index 76f63bcd7cc..23dcacd639c 100644 --- a/packages/opentelemetry-tracing/test/Span.test.ts +++ b/packages/opentelemetry-tracing/test/Span.test.ts @@ -734,7 +734,7 @@ describe('Span', () => { span.recordException({ code: 12 }); const event = span.events[0]; assert.deepStrictEqual(event.attributes, { - [ExceptionAttribute.TYPE]: '12', + [SemanticAttributes.EXCEPTION_TYPE]: '12', }); }); }); From 8eda3478eb26fe6fcbbd467b3b718c51868259bf Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Fri, 9 Apr 2021 17:01:49 +0100 Subject: [PATCH 12/14] chore: attempt to correct fix failed merge --- .../src/client/utils.ts | 255 ------ .../opentelemetry-plugin-grpc-js/src/enums.ts | 25 - .../src/server/clientStreamAndUnary.ts | 70 -- .../src/server/patchServer.ts | 218 ----- .../src/server/serverStreamAndBidi.ts | 90 -- .../opentelemetry-plugin-grpc/src/enums.ts | 25 - .../opentelemetry-plugin-grpc/src/grpc.ts | 566 ------------ .../opentelemetry-plugin-http/src/enums.ts | 24 - .../opentelemetry-plugin-http/src/utils.ts | 500 ----------- .../test/functionals/http-enable.test.ts | 846 ------------------ .../test/functionals/utils.test.ts | 452 ---------- .../test/integrations/http-enable.test.ts | 355 -------- .../test/utils/assertSpan.ts | 169 ---- .../opentelemetry-plugin-https/src/enums.ts | 22 - .../test/functionals/https-enable.test.ts | 661 -------------- .../test/integrations/https-enable.test.ts | 359 -------- .../test/utils/assertSpan.ts | 124 --- 17 files changed, 4761 deletions(-) delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/client/utils.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/enums.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts delete mode 100644 packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/enums.ts delete mode 100644 packages/opentelemetry-plugin-grpc/src/grpc.ts delete mode 100644 packages/opentelemetry-plugin-http/src/enums.ts delete mode 100644 packages/opentelemetry-plugin-http/src/utils.ts delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/functionals/utils.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-http/test/utils/assertSpan.ts delete mode 100644 packages/opentelemetry-plugin-https/src/enums.ts delete mode 100644 packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts delete mode 100644 packages/opentelemetry-plugin-https/test/utils/assertSpan.ts diff --git a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts b/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts deleted file mode 100644 index 542ff455101..00000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/client/utils.ts +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { GrpcJsPlugin } from '../grpcJs'; -import type { GrpcClientFunc, SendUnaryDataCallback } from '../types'; -import { - SpanKind, - Span, - SpanStatusCode, - SpanStatus, - propagation, - context, - setSpan, - diag, -} from '@opentelemetry/api'; -import type * as grpcJs from '@grpc/grpc-js'; -import { - grpcStatusCodeToSpanStatus, - grpcStatusCodeToOpenTelemetryStatusCode, - CALL_SPAN_ENDED, - methodIsIgnored, -} from '../utils'; -import { EventEmitter } from 'events'; -import { AttributeNames } from '../enums'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; - -/** - * Parse a package method list and return a list of methods to patch - * with both possible casings e.g. "TestMethod" & "testMethod" - */ -export function getMethodsToWrap( - this: GrpcJsPlugin, - client: typeof grpcJs.Client, - methods: { [key: string]: { originalName?: string } } -): string[] { - const methodList: string[] = []; - - // For a method defined in .proto as "UnaryMethod" - Object.entries(methods).forEach(([name, { originalName }]) => { - if (!methodIsIgnored(name, this._config.ignoreGrpcMethods)) { - methodList.push(name); // adds camel case method name: "unaryMethod" - if ( - originalName && - // eslint-disable-next-line no-prototype-builtins - client.prototype.hasOwnProperty(originalName) && - name !== originalName // do not add duplicates - ) { - // adds original method name: "UnaryMethod", - methodList.push(originalName); - } - } - }); - - return methodList; -} - -/** - * Parse initial client call properties and start a span to trace its execution - */ -export function getPatchedClientMethods( - this: GrpcJsPlugin -): (original: GrpcClientFunc) => () => EventEmitter { - const plugin = this; - return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); - return function clientMethodTrace(this: grpcJs.Client) { - const name = `grpc.${original.path.replace('/', '')}`; - const args = [...arguments]; - const metadata = getMetadata.call(plugin, original, args); - const span = plugin.tracer.startSpan(name, { - kind: SpanKind.CLIENT, - }); - return context.with(setSpan(context.active(), span), () => - makeGrpcClientRemoteCall(original, args, metadata, this)(span) - ); - }; - }; -} - -/** - * Execute grpc client call. Apply completitionspan properties and end the - * span on callback or receiving an emitted event. - */ -export function makeGrpcClientRemoteCall( - original: GrpcClientFunc, - args: unknown[], - metadata: grpcJs.Metadata, - self: grpcJs.Client -): (span: Span) => EventEmitter { - /** - * Patches a callback so that the current span for this trace is also ended - * when the callback is invoked. - */ - function patchedCallback( - span: Span, - callback: SendUnaryDataCallback - ) { - const wrappedFn: SendUnaryDataCallback = ( - err: grpcJs.ServiceError | null, - res - ) => { - if (err) { - if (err.code) { - span.setStatus(grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - err.code.toString() - ); - } - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - } - - span.end(); - callback(err, res); - }; - return context.bind(wrappedFn); - } - - return (span: Span) => { - // if unary or clientStream - if (!original.responseStream) { - const callbackFuncIndex = args.findIndex(arg => { - return typeof arg === 'function'; - }); - if (callbackFuncIndex !== -1) { - args[callbackFuncIndex] = patchedCallback( - span, - args[callbackFuncIndex] as SendUnaryDataCallback - ); - } - } - - span.setAttributes({ - [AttributeNames.GRPC_METHOD]: original.path, - [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, - }); - - setSpanContext(metadata); - const call = original.apply(self, args); - - // if server stream or bidi - if (original.responseStream) { - // Both error and status events can be emitted - // the first one emitted set spanEnded to true - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - span.end(); - spanEnded = true; - } - }; - context.bind(call); - call.on('error', (err: grpcJs.ServiceError) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - - endSpan(); - }); - - call.on('status', (status: SpanStatus) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - call[CALL_SPAN_ENDED] = true; - - span.setStatus(grpcStatusCodeToSpanStatus(status.code)); - - endSpan(); - }); - } - return call; - }; -} - -/** - * Returns the metadata argument from user provided arguments (`args`) - */ -function getMetadata( - this: GrpcJsPlugin, - original: GrpcClientFunc, - args: Array -): grpcJs.Metadata { - let metadata: grpcJs.Metadata; - - // This finds an instance of Metadata among the arguments. - // A possible issue that could occur is if the 'options' parameter from - // the user contains an '_internal_repr' as well as a 'getMap' function, - // but this is an extremely rare case. - let metadataIndex = args.findIndex((arg: unknown | grpcJs.Metadata) => { - return ( - arg && - typeof arg === 'object' && - (arg as grpcJs.Metadata)['internalRepr'] && // changed from _internal_repr in grpc --> @grpc/grpc-js https://github.com/grpc/grpc-node/blob/95289edcaf36979cccf12797cc27335da8d01f03/packages/grpc-js/src/metadata.ts#L88 - typeof (arg as grpcJs.Metadata).getMap === 'function' - ); - }); - if (metadataIndex === -1) { - metadata = new this._moduleExports.Metadata(); - if (!original.requestStream) { - // unary or server stream - metadataIndex = 1; - } else { - // client stream or bidi - metadataIndex = 0; - } - args.splice(metadataIndex, 0, metadata); - } else { - metadata = args[metadataIndex] as grpcJs.Metadata; - } - return metadata; -} - -/** - * Inject opentelemetry trace context into `metadata` for use by another - * grpc receiver - * @param metadata - */ -export function setSpanContext(metadata: grpcJs.Metadata): void { - propagation.inject(context.active(), metadata, { - set: (metadata, k, v) => metadata.set(k, v as grpcJs.MetadataValue), - }); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/enums.ts b/packages/opentelemetry-plugin-grpc-js/src/enums.ts deleted file mode 100644 index 9dfd3e0923e..00000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/enums.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md - */ -export enum AttributeNames { - GRPC_KIND = 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD = 'grpc.method', - GRPC_ERROR_NAME = 'grpc.error_name', - GRPC_ERROR_MESSAGE = 'grpc.error_message', -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts b/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts deleted file mode 100644 index dd18886f2c1..00000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/clientStreamAndUnary.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import type { ServerCallWithMeta, SendUnaryDataCallback } from '../types'; -import { grpcStatusCodeToOpenTelemetryStatusCode } from '../utils'; -import type { GrpcJsPlugin } from '../grpcJs'; -import type * as grpcJs from '@grpc/grpc-js'; -import { AttributeNames } from '../enums'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; - -/** - * Handle patching for clientStream and unary type server handlers - */ -export function clientStreamAndUnaryHandler( - plugin: GrpcJsPlugin, - span: Span, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback, - original: - | grpcJs.handleUnaryCall - | grpcJs.ClientReadableStream -): void { - const patchedCallback: SendUnaryDataCallback = ( - err: grpcJs.ServiceError | null, - value?: ResponseType - ) => { - if (err) { - if (err.code) { - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - err.code.toString() - ); - } - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - } - - span.end(); - return callback(err, value); - }; - - context.bind(call); - return (original as Function).call({}, call, patchedCallback); -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts b/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts deleted file mode 100644 index 8f8b0aca725..00000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/patchServer.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import type * as grpcJs from '@grpc/grpc-js'; -import type { HandleCall } from '@grpc/grpc-js/build/src/server-call'; -import { GrpcJsPlugin } from '../grpcJs'; -import * as shimmer from 'shimmer'; -import { - ServerCallWithMeta, - SendUnaryDataCallback, - IgnoreMatcher, -} from '../types'; -import { - context, - SpanOptions, - SpanKind, - propagation, - Span, - ROOT_CONTEXT, - setSpan, - diag, -} from '@opentelemetry/api'; -import { clientStreamAndUnaryHandler } from './clientStreamAndUnary'; -import { serverStreamAndBidiHandler } from './serverStreamAndBidi'; -import { methodIsIgnored } from '../utils'; -import { AttributeNames } from '../enums'; - -type ServerRegisterFunction = typeof grpcJs.Server.prototype.register; - -/** - * Patch for grpc.Server.prototype.register(...) function. Provides auto-instrumentation for - * client_stream, server_stream, bidi, unary server handler calls. - */ -export function patchServer( - this: GrpcJsPlugin -): (originalRegister: ServerRegisterFunction) => ServerRegisterFunction { - return (originalRegister: ServerRegisterFunction) => { - const plugin = this; - const config = this._config; - - diag.debug('patched gRPC server'); - return function register( - this: grpcJs.Server, - name: string, - handler: HandleCall, - serialize: grpcJs.serialize, - deserialize: grpcJs.deserialize, - type: string - ): boolean { - const originalRegisterResult = originalRegister.call( - this, - name, - handler, - serialize, - deserialize, - type - ); - const handlerSet = this['handlers'].get(name); - - shimmer.wrap( - handlerSet, - 'func', - (originalFunc: HandleCall) => { - return function func( - this: typeof handlerSet, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback - ) { - const self = this; - - if ( - shouldNotTraceServerCall( - call.metadata, - name, - config.ignoreGrpcMethods - ) - ) { - return handleUntracedServerFunction( - type, - originalFunc, - call, - callback - ); - } - - const spanName = `grpc.${name.replace('/', '')}`; - const spanOptions: SpanOptions = { - kind: SpanKind.SERVER, - }; - - diag.debug('patch func: %s', JSON.stringify(spanOptions)); - - context.with( - propagation.extract(ROOT_CONTEXT, call.metadata, { - get: (carrier, key) => carrier.get(key).map(String), - keys: carrier => Object.keys(carrier.getMap()), - }), - () => { - const span = plugin.tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [AttributeNames.GRPC_KIND]: spanOptions.kind, - }); - - context.with(setSpan(context.active(), span), () => { - handleServerFunction.call( - self, - plugin, - span, - type, - originalFunc, - call, - callback - ); - }); - } - ); - }; - } - ); - return originalRegisterResult; - } as typeof grpcJs.Server.prototype.register; - }; -} - -/** - * Returns true if the server call should not be traced. - */ -function shouldNotTraceServerCall( - metadata: grpcJs.Metadata, - methodName: string, - ignoreGrpcMethods?: IgnoreMatcher[] -): boolean { - const parsedName = methodName.split('/'); - return methodIsIgnored( - parsedName[parsedName.length - 1] || methodName, - ignoreGrpcMethods - ); -} - -/** - * Patch callback or EventEmitter provided by `originalFunc` and set appropriate `span` - * properties based on its result. - */ -function handleServerFunction( - this: unknown, - plugin: GrpcJsPlugin, - span: Span, - type: string, - originalFunc: HandleCall, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback -): void { - switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': - return clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc as - | grpcJs.handleUnaryCall - | grpcJs.ClientReadableStream - ); - case 'serverStream': - case 'server_stream': - case 'bidi': - return serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc as - | grpcJs.handleBidiStreamingCall - | grpcJs.handleServerStreamingCall - ); - default: - break; - } -} - -/** - * Does not patch any callbacks or EventEmitters to omit tracing on requests - * that should not be traced. - */ -function handleUntracedServerFunction( - type: string, - originalFunc: HandleCall, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback -): void { - switch (type) { - case 'unary': - case 'clientStream': - case 'client_stream': - return (originalFunc as Function).call({}, call, callback); - case 'serverStream': - case 'server_stream': - case 'bidi': - return (originalFunc as Function).call({}, call); - default: - break; - } -} diff --git a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts b/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts deleted file mode 100644 index 50122e0c505..00000000000 --- a/packages/opentelemetry-plugin-grpc-js/src/server/serverStreamAndBidi.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { context, Span, SpanStatusCode } from '@opentelemetry/api'; -import type * as grpcJs from '@grpc/grpc-js'; -import type { GrpcJsPlugin } from '../grpcJs'; -import { GrpcEmitter } from '../types'; -import { - CALL_SPAN_ENDED, - grpcStatusCodeToOpenTelemetryStatusCode, -} from '../utils'; -import { AttributeNames } from '../enums'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; - -/** - * Handle patching for serverStream and Bidi type server handlers - */ -export function serverStreamAndBidiHandler( - plugin: GrpcJsPlugin, - span: Span, - call: GrpcEmitter, - original: - | grpcJs.handleBidiStreamingCall - | grpcJs.handleServerStreamingCall -): void { - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - spanEnded = true; - span.end(); - } - }; - - context.bind(call); - call.on('finish', () => { - // @grpc/js does not expose a way to check if this call also emitted an error, - // e.g. call.status.code !== 0 - if (call[CALL_SPAN_ENDED]) { - return; - } - - // Set the "grpc call had an error" flag - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: SpanStatusCode.OK, - }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - SpanStatusCode.OK.toString() - ); - - endSpan(); - }); - - call.on('error', (err: grpcJs.ServiceError) => { - if (call[CALL_SPAN_ENDED]) { - return; - } - - // Set the "grpc call had an error" flag - call[CALL_SPAN_ENDED] = true; - - span.setStatus({ - code: grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - }); - - // Types of parameters 'call' and 'call' are incompatible. - return (original as Function).call({}, call); -} diff --git a/packages/opentelemetry-plugin-grpc/src/enums.ts b/packages/opentelemetry-plugin-grpc/src/enums.ts deleted file mode 100644 index 9dfd3e0923e..00000000000 --- a/packages/opentelemetry-plugin-grpc/src/enums.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md - */ -export enum AttributeNames { - GRPC_KIND = 'grpc.kind', // SERVER or CLIENT - GRPC_METHOD = 'grpc.method', - GRPC_ERROR_NAME = 'grpc.error_name', - GRPC_ERROR_MESSAGE = 'grpc.error_message', -} diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts deleted file mode 100644 index e1df72749f6..00000000000 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { - SpanStatusCode, - context, - propagation, - Span, - SpanKind, - SpanOptions, - SpanStatus, - ROOT_CONTEXT, - setSpan, - diag, -} from '@opentelemetry/api'; -import { BasePlugin } from '@opentelemetry/core'; -import * as events from 'events'; -import * as grpcTypes from 'grpc'; -import * as path from 'path'; -import * as shimmer from 'shimmer'; -import { - grpc, - GrpcClientFunc, - GrpcInternalClientTypes, - GrpcPluginOptions, - ModuleExportsMapping, - SendUnaryDataCallback, - ServerCallWithMeta, -} from './types'; -import { - findIndex, - _grpcStatusCodeToOpenTelemetryStatusCode, - _grpcStatusCodeToSpanStatus, - _methodIsIgnored, -} from './utils'; -import { VERSION } from './version'; -import { AttributeNames } from './enums'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; - -/** The metadata key under which span context is stored as a binary value. */ -export const GRPC_TRACE_KEY = 'grpc-trace-bin'; - -let grpcClientModule: GrpcInternalClientTypes; - -export class GrpcPlugin extends BasePlugin { - static readonly component = 'grpc'; - readonly supportedVersions = ['1.*']; - - protected _config!: GrpcPluginOptions; - - constructor(readonly moduleName: string, readonly version: string) { - super('@opentelemetry/plugin-grpc', VERSION); - this._config = {}; - } - - protected readonly _internalFilesList: ModuleExportsMapping = { - '0.13 - 1.6': { client: 'src/node/src/client.js' }, - '^1.7': { client: 'src/client.js' }, - }; - protected readonly _basedir = basedir; - - protected patch(): typeof grpcTypes { - diag.debug('applying patch to %s@%s', this.moduleName, this.version); - - if (this._moduleExports.Server) { - shimmer.wrap( - this._moduleExports.Server.prototype, - 'register', - this._patchServer() as any - ); - } - - // Wrap the externally exported client constructor - shimmer.wrap( - this._moduleExports, - 'makeGenericClientConstructor', - this._patchClient() - ); - - if (this._internalFilesExports['client']) { - grpcClientModule = this._internalFilesExports[ - 'client' - ] as GrpcInternalClientTypes; - - // Wrap the internally used client constructor - shimmer.wrap( - grpcClientModule, - 'makeClientConstructor', - this._patchClient() - ); - } - - return this._moduleExports; - } - protected unpatch(): void { - diag.debug('removing patch to %s@%s', this.moduleName, this.version); - - if (this._moduleExports.Server) { - shimmer.unwrap(this._moduleExports.Server.prototype, 'register'); - } - - shimmer.unwrap(this._moduleExports, 'makeGenericClientConstructor'); - - if (grpcClientModule) { - shimmer.unwrap(grpcClientModule, 'makeClientConstructor'); - } - } - - private _setSpanContext(metadata: grpcTypes.Metadata): void { - propagation.inject(context.active(), metadata, { - set: (metadata, k, v) => metadata.set(k, v as grpcTypes.MetadataValue), - }); - } - - private _patchServer() { - return (originalRegister: typeof grpcTypes.Server.prototype.register) => { - const plugin = this; - diag.debug('patched gRPC server'); - - return function register( - this: grpcTypes.Server & { handlers: any }, - name: string, - handler: grpcTypes.handleCall, - serialize: grpcTypes.serialize, - deserialize: grpcTypes.deserialize, - type: string - ) { - const originalResult = originalRegister.apply(this, arguments as any); - const handlerSet = this.handlers[name]; - - shimmer.wrap( - handlerSet, - 'func', - (originalFunc: grpcTypes.handleCall) => { - return function func( - this: typeof handlerSet, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback - ) { - const self = this; - if (plugin._shouldNotTraceServerCall(call, name)) { - switch (type) { - case 'unary': - case 'client_stream': - return (originalFunc as Function).call( - self, - call, - callback - ); - case 'server_stream': - case 'bidi': - return (originalFunc as Function).call(self, call); - default: - return originalResult; - } - } - const spanName = `grpc.${name.replace('/', '')}`; - const spanOptions: SpanOptions = { - kind: SpanKind.SERVER, - }; - - diag.debug('patch func: %s', JSON.stringify(spanOptions)); - - context.with( - propagation.extract(ROOT_CONTEXT, call.metadata, { - get: (metadata, key) => metadata.get(key).map(String), - keys: metadata => Object.keys(metadata.getMap()), - }), - () => { - const span = plugin._tracer - .startSpan(spanName, spanOptions) - .setAttributes({ - [AttributeNames.GRPC_KIND]: spanOptions.kind, - }); - - context.with(setSpan(context.active(), span), () => { - switch (type) { - case 'unary': - case 'client_stream': - return plugin._clientStreamAndUnaryHandler( - plugin, - span, - call, - callback, - originalFunc, - self - ); - case 'server_stream': - case 'bidi': - return plugin._serverStreamAndBidiHandler( - plugin, - span, - call, - originalFunc, - self - ); - default: - break; - } - }); - } - ); - }; - } - ); - - return originalResult; - }; - }; - } - - /** - * Returns true if the server call should not be traced. - */ - private _shouldNotTraceServerCall( - call: ServerCallWithMeta, - name: string - ): boolean { - const parsedName = name.split('/'); - return _methodIsIgnored( - parsedName[parsedName.length - 1] || name, - this._config.ignoreGrpcMethods - ); - } - - private _clientStreamAndUnaryHandler( - plugin: GrpcPlugin, - span: Span, - call: ServerCallWithMeta, - callback: SendUnaryDataCallback, - original: - | grpcTypes.handleCall - | grpcTypes.ClientReadableStream, - self: {} - ) { - function patchedCallback( - err: grpcTypes.ServiceError, - value: any, - trailer: grpcTypes.Metadata, - flags: grpcTypes.writeFlags - ) { - if (err) { - if (err.code) { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - err.code.toString() - ); - } - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - plugin._moduleExports.status.OK.toString() - ); - } - span.addEvent('received'); - - // end the span - span.end(); - return callback(err, value, trailer, flags); - } - - context.bind(call); - return (original as Function).call(self, call, patchedCallback); - } - - private _serverStreamAndBidiHandler( - plugin: GrpcPlugin, - span: Span, - call: ServerCallWithMeta, - original: grpcTypes.handleCall, - self: {} - ) { - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - spanEnded = true; - span.end(); - } - }; - - context.bind(call); - call.on('finish', () => { - span.setStatus(_grpcStatusCodeToSpanStatus(call.status.code)); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - call.status.code.toString() - ); - - // if there is an error, span will be ended on error event, otherwise end it here - if (call.status.code === 0) { - span.addEvent('finished'); - endSpan(); - } - }); - - call.on('error', (err: grpcTypes.ServiceError) => { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.addEvent('finished with error'); - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - }); - - return (original as any).call(self, call); - } - - private _patchClient() { - const plugin = this; - return (original: typeof grpcTypes.makeGenericClientConstructor): never => { - diag.debug('patching client'); - return function makeClientConstructor( - this: typeof grpcTypes.Client, - methods: { [key: string]: { originalName?: string } }, - _serviceName: string, - _options: grpcTypes.GenericClientOptions - ) { - const client = original.apply(this, arguments as any); - shimmer.massWrap( - client.prototype as never, - plugin._getMethodsToWrap(client, methods) as never[], - plugin._getPatchedClientMethods() as any - ); - return client; - } as never; - }; - } - - private _getMethodsToWrap( - client: typeof grpcTypes.Client, - methods: { [key: string]: { originalName?: string } } - ): string[] { - const methodList: string[] = []; - - // For a method defined in .proto as "UnaryMethod" - Object.entries(methods).forEach(([name, { originalName }]) => { - if (!_methodIsIgnored(name, this._config.ignoreGrpcMethods)) { - methodList.push(name); // adds camel case method name: "unaryMethod" - if ( - originalName && - // eslint-disable-next-line no-prototype-builtins - client.prototype.hasOwnProperty(originalName) && - name !== originalName // do not add duplicates - ) { - // adds original method name: "UnaryMethod", - methodList.push(originalName); - } - } - }); - return methodList; - } - - private _getPatchedClientMethods() { - const plugin = this; - return (original: GrpcClientFunc) => { - diag.debug('patch all client methods'); - return function clientMethodTrace(this: grpcTypes.Client) { - const name = `grpc.${original.path.replace('/', '')}`; - const args = Array.prototype.slice.call(arguments); - const metadata = plugin._getMetadata(original, args); - const span = plugin._tracer.startSpan(name, { - kind: SpanKind.CLIENT, - }); - return context.with(setSpan(context.active(), span), () => - plugin._makeGrpcClientRemoteCall( - original, - args, - metadata, - this, - plugin - )(span) - ); - }; - }; - } - - /** - * This method handles the client remote call - */ - private _makeGrpcClientRemoteCall( - original: GrpcClientFunc, - args: any[], - metadata: grpcTypes.Metadata, - self: grpcTypes.Client, - plugin: GrpcPlugin - ) { - /** - * Patches a callback so that the current span for this trace is also ended - * when the callback is invoked. - */ - function patchedCallback( - span: Span, - callback: SendUnaryDataCallback, - _metadata: grpcTypes.Metadata - ) { - const wrappedFn = (err: grpcTypes.ServiceError, res: any) => { - if (err) { - if (err.code) { - span.setStatus(_grpcStatusCodeToSpanStatus(err.code)); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - err.code.toString() - ); - } - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - } else { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - plugin._moduleExports.status.OK.toString() - ); - } - - span.end(); - callback(err, res); - }; - return context.bind(wrappedFn); - } - - return (span: Span) => { - if (!span) { - return original.apply(self, args); - } - - // if unary or clientStream - if (!original.responseStream) { - const callbackFuncIndex = findIndex(args, arg => { - return typeof arg === 'function'; - }); - if (callbackFuncIndex !== -1) { - args[callbackFuncIndex] = patchedCallback( - span, - args[callbackFuncIndex], - metadata - ); - } - } - - span.addEvent('sent'); - span.setAttributes({ - [AttributeNames.GRPC_METHOD]: original.path, - [AttributeNames.GRPC_KIND]: SpanKind.CLIENT, - }); - - this._setSpanContext(metadata); - const call = original.apply(self, args); - - // if server stream or bidi - if (original.responseStream) { - // Both error and status events can be emitted - // the first one emitted set spanEnded to true - let spanEnded = false; - const endSpan = () => { - if (!spanEnded) { - span.end(); - spanEnded = true; - } - }; - context.bind(call); - ((call as unknown) as events.EventEmitter).on( - 'error', - (err: grpcTypes.ServiceError) => { - span.setStatus({ - code: _grpcStatusCodeToOpenTelemetryStatusCode(err.code), - message: err.message, - }); - span.setAttributes({ - [AttributeNames.GRPC_ERROR_NAME]: err.name, - [AttributeNames.GRPC_ERROR_MESSAGE]: err.message, - }); - endSpan(); - } - ); - - ((call as unknown) as events.EventEmitter).on( - 'status', - (status: SpanStatus) => { - span.setStatus({ code: SpanStatusCode.OK }); - span.setAttribute( - SemanticAttributes.RPC_GRPC_STATUS_CODE, - status.code.toString() - ); - endSpan(); - } - ); - } - return call; - }; - } - - private _getMetadata( - original: GrpcClientFunc, - args: any[] - ): grpcTypes.Metadata { - let metadata: grpcTypes.Metadata; - - // This finds an instance of Metadata among the arguments. - // A possible issue that could occur is if the 'options' parameter from - // the user contains an '_internal_repr' as well as a 'getMap' function, - // but this is an extremely rare case. - let metadataIndex = findIndex(args, (arg: any) => { - return ( - arg && - typeof arg === 'object' && - arg._internal_repr && - typeof arg.getMap === 'function' - ); - }); - if (metadataIndex === -1) { - metadata = new this._moduleExports.Metadata(); - if (!original.requestStream) { - // unary or server stream - if (args.length === 0) { - // No argument (for the gRPC call) was provided, so we will have to - // provide one, since metadata cannot be the first argument. - // The internal representation of argument defaults to undefined - // in its non-presence. - // Note that we can't pass null instead of undefined because the - // serializer within gRPC doesn't accept it. - args.push(undefined); - } - metadataIndex = 1; - } else { - // client stream or bidi - metadataIndex = 0; - } - args.splice(metadataIndex, 0, metadata); - } else { - metadata = args[metadataIndex]; - } - return metadata; - } -} - -const basedir = path.dirname(require.resolve('grpc')); -const version = require(path.join(basedir, 'package.json')).version; -export const plugin = new GrpcPlugin(GrpcPlugin.component, version); diff --git a/packages/opentelemetry-plugin-http/src/enums.ts b/packages/opentelemetry-plugin-http/src/enums.ts deleted file mode 100644 index f9b8be3c8ea..00000000000 --- a/packages/opentelemetry-plugin-http/src/enums.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md - */ -export enum AttributeNames { - HTTP_ERROR_NAME = 'http.error_name', - HTTP_ERROR_MESSAGE = 'http.error_message', - HTTP_STATUS_TEXT = 'http.status_text', -} diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts deleted file mode 100644 index fe2cc687044..00000000000 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -import { - SpanAttributes, - SpanStatusCode, - Span, - SpanStatus, -} from '@opentelemetry/api'; -import { - NetTransportValues, - SemanticAttributes, -} from '@opentelemetry/semantic-conventions'; -import { - ClientRequest, - IncomingHttpHeaders, - IncomingMessage, - OutgoingHttpHeaders, - RequestOptions, - ServerResponse, -} from 'http'; -import * as url from 'url'; -import { AttributeNames } from './enums'; -import { Err, IgnoreMatcher, ParsedRequestOptions } from './types'; - -/** - * Get an absolute url - */ -export const getAbsoluteUrl = ( - requestUrl: ParsedRequestOptions | null, - headers: IncomingHttpHeaders | OutgoingHttpHeaders, - fallbackProtocol = 'http:' -): string => { - const reqUrlObject = requestUrl || {}; - const protocol = reqUrlObject.protocol || fallbackProtocol; - const port = (reqUrlObject.port || '').toString(); - const path = reqUrlObject.path || '/'; - let host = - reqUrlObject.host || reqUrlObject.hostname || headers.host || 'localhost'; - - // if there is no port in host and there is a port - // it should be displayed if it's not 80 and 443 (default ports) - if ( - (host as string).indexOf(':') === -1 && - port && - port !== '80' && - port !== '443' - ) { - host += `:${port}`; - } - - return `${protocol}//${host}${path}`; -}; -/** - * Parse status code from HTTP response. [More details](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#status) - */ -export const parseResponseStatus = ( - statusCode: number -): Omit => { - // 1xx, 2xx, 3xx are OK - if (statusCode >= 100 && statusCode < 400) { - return { code: SpanStatusCode.OK }; - } - - // All other codes are error - return { code: SpanStatusCode.ERROR }; -}; - -/** - * Returns whether the Expect header is on the given options object. - * @param options Options for http.request. - */ -export const hasExpectHeader = (options: RequestOptions): boolean => { - if (!options.headers) { - return false; - } - - const keys = Object.keys(options.headers); - return !!keys.find(key => key.toLowerCase() === 'expect'); -}; - -/** - * Check whether the given obj match pattern - * @param constant e.g URL of request - * @param pattern Match pattern - */ -export const satisfiesPattern = ( - constant: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern === constant; - } else if (pattern instanceof RegExp) { - return pattern.test(constant); - } else if (typeof pattern === 'function') { - return pattern(constant); - } else { - throw new TypeError('Pattern is in unsupported datatype'); - } -}; - -/** - * Check whether the given request is ignored by configuration - * It will not re-throw exceptions from `list` provided by the client - * @param constant e.g URL of request - * @param [list] List of ignore patterns - * @param [onException] callback for doing something when an exception has - * occurred - */ -export const isIgnored = ( - constant: string, - list?: IgnoreMatcher[], - onException?: (error: Error) => void -): boolean => { - if (!list) { - // No ignored urls - trace everything - return false; - } - // Try/catch outside the loop for failing fast - try { - for (const pattern of list) { - if (satisfiesPattern(constant, pattern)) { - return true; - } - } - } catch (e) { - if (onException) { - onException(e); - } - } - - return false; -}; - -/** - * Sets the span with the error passed in params - * @param {Span} span the span that need to be set - * @param {Error} error error that will be set to span - * @param {(IncomingMessage | ClientRequest)} [obj] used for enriching the status by checking the statusCode. - */ -export const setSpanWithError = ( - span: Span, - error: Err, - obj?: IncomingMessage | ClientRequest -) => { - const message = error.message; - - span.setAttributes({ - [AttributeNames.HTTP_ERROR_NAME]: error.name, - [AttributeNames.HTTP_ERROR_MESSAGE]: message, - }); - - if (!obj) { - span.setStatus({ code: SpanStatusCode.ERROR, message }); - return; - } - - let status: SpanStatus; - if ((obj as IncomingMessage).statusCode) { - status = parseResponseStatus((obj as IncomingMessage).statusCode!); - } else if ((obj as ClientRequest).aborted) { - status = { code: SpanStatusCode.ERROR }; - } else { - status = { code: SpanStatusCode.ERROR }; - } - - status.message = message; - - span.setStatus(status); -}; - -/** - * Adds attributes for request content-length and content-encoding HTTP headers - * @param { IncomingMessage } Request object whose headers will be analyzed - * @param { SpanAttributes } SpanAttributes object to be modified - */ -export const setRequestContentLengthAttribute = ( - request: IncomingMessage, - attributes: SpanAttributes -) => { - const length = getContentLength(request.headers); - if (length === null) return; - - if (isCompressed(request.headers)) { - attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH] = length; - } else { - attributes[ - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED - ] = length; - } -}; - -/** - * Adds attributes for response content-length and content-encoding HTTP headers - * @param { IncomingMessage } Response object whose headers will be analyzed - * @param { SpanAttributes } SpanAttributes object to be modified - */ -export const setResponseContentLengthAttribute = ( - response: IncomingMessage, - attributes: SpanAttributes -) => { - const length = getContentLength(response.headers); - if (length === null) return; - - if (isCompressed(response.headers)) { - attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH] = length; - } else { - attributes[ - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED - ] = length; - } -}; - -function getContentLength( - headers: OutgoingHttpHeaders | IncomingHttpHeaders -): number | null { - const contentLengthHeader = headers['content-length']; - if (contentLengthHeader === undefined) return null; - - const contentLength = parseInt(contentLengthHeader as string, 10); - if (isNaN(contentLength)) return null; - - return contentLength; -} - -export const isCompressed = ( - headers: OutgoingHttpHeaders | IncomingHttpHeaders -): boolean => { - const encoding = headers['content-encoding']; - - return !!encoding && encoding !== 'identity'; -}; - -/** - * Makes sure options is an url object - * return an object with default value and parsed options - * @param options original options for the request - * @param [extraOptions] additional options for the request - */ -export const getRequestInfo = ( - options: url.URL | RequestOptions | string, - extraOptions?: RequestOptions -) => { - let pathname = '/'; - let origin = ''; - let optionsParsed: RequestOptions; - if (typeof options === 'string') { - optionsParsed = url.parse(options); - pathname = (optionsParsed as url.UrlWithStringQuery).pathname || '/'; - origin = `${optionsParsed.protocol || 'http:'}//${optionsParsed.host}`; - if (extraOptions !== undefined) { - Object.assign(optionsParsed, extraOptions); - } - } else if (options instanceof url.URL) { - optionsParsed = { - protocol: options.protocol, - hostname: - typeof options.hostname === 'string' && options.hostname.startsWith('[') - ? options.hostname.slice(1, -1) - : options.hostname, - path: `${options.pathname || ''}${options.search || ''}`, - }; - if (options.port !== '') { - optionsParsed.port = Number(options.port); - } - if (options.username || options.password) { - optionsParsed.auth = `${options.username}:${options.password}`; - } - pathname = options.pathname; - origin = options.origin; - if (extraOptions !== undefined) { - Object.assign(optionsParsed, extraOptions); - } - } else { - optionsParsed = Object.assign({}, options); - pathname = (options as url.URL).pathname; - if (!pathname && optionsParsed.path) { - pathname = url.parse(optionsParsed.path).pathname || '/'; - } - origin = `${optionsParsed.protocol || 'http:'}//${ - optionsParsed.host || `${optionsParsed.hostname}:${optionsParsed.port}` - }`; - } - - if (hasExpectHeader(optionsParsed)) { - optionsParsed.headers = Object.assign({}, optionsParsed.headers); - } else if (!optionsParsed.headers) { - optionsParsed.headers = {}; - } - // some packages return method in lowercase.. - // ensure upperCase for consistency - const method = optionsParsed.method - ? optionsParsed.method.toUpperCase() - : 'GET'; - - return { origin, pathname, method, optionsParsed }; -}; - -/** - * Makes sure options is of type string or object - * @param options for the request - */ -export const isValidOptionsType = (options: unknown): boolean => { - if (!options) { - return false; - } - - const type = typeof options; - return type === 'string' || (type === 'object' && !Array.isArray(options)); -}; - -/** - * Returns outgoing request attributes scoped to the options passed to the request - * @param {ParsedRequestOptions} requestOptions the same options used to make the request - * @param {{ component: string, hostname: string }} options used to pass data needed to create attributes - */ -export const getOutgoingRequestAttributes = ( - requestOptions: ParsedRequestOptions, - options: { component: string; hostname: string } -): SpanAttributes => { - const host = requestOptions.host; - const hostname = - requestOptions.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; - const requestMethod = requestOptions.method; - const method = requestMethod ? requestMethod.toUpperCase() : 'GET'; - const headers = requestOptions.headers || {}; - const userAgent = headers['user-agent']; - const attributes: SpanAttributes = { - [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( - requestOptions, - headers, - `${options.component}:` - ), - [SemanticAttributes.HTTP_METHOD]: method, - [SemanticAttributes.HTTP_TARGET]: requestOptions.path || '/', - [SemanticAttributes.NET_PEER_NAME]: hostname, - }; - - if (userAgent !== undefined) { - attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; - } - return attributes; -}; - -/** - * Returns attributes related to the kind of HTTP protocol used - * @param {string} [kind] Kind of HTTP protocol used: "1.0", "1.1", "2", "SPDY" or "QUIC". - */ -export const getAttributesFromHttpKind = (kind?: string): SpanAttributes => { - const attributes: SpanAttributes = {}; - if (kind) { - attributes[SemanticAttributes.HTTP_FLAVOR] = kind; - if (kind.toUpperCase() !== 'QUIC') { - attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_TCP; - } else { - attributes[SemanticAttributes.NET_TRANSPORT] = NetTransportValues.IP_UDP; - } - } - return attributes; -}; - -/** - * Returns outgoing request attributes scoped to the response data - * @param {IncomingMessage} response the response object - * @param {{ hostname: string }} options used to pass data needed to create attributes - */ -export const getOutgoingRequestAttributesOnResponse = ( - response: IncomingMessage, - options: { hostname: string } -): SpanAttributes => { - const { statusCode, statusMessage, httpVersion, socket } = response; - const { remoteAddress, remotePort } = socket; - - const attributes: SpanAttributes = { - [SemanticAttributes.NET_PEER_IP]: remoteAddress, - [SemanticAttributes.NET_PEER_PORT]: remotePort, - [SemanticAttributes.HTTP_HOST]: `${options.hostname}:${remotePort}`, - }; - - setResponseContentLengthAttribute(response, attributes); - - if (statusCode) { - attributes[SemanticAttributes.HTTP_STATUS_CODE] = statusCode; - attributes[AttributeNames.HTTP_STATUS_TEXT] = ( - statusMessage || '' - ).toUpperCase(); - } - - const httpKindAttributes = getAttributesFromHttpKind(httpVersion); - return Object.assign(attributes, httpKindAttributes); -}; - -/** - * Returns incoming request attributes scoped to the request data - * @param {IncomingMessage} request the request object - * @param {{ component: string, serverName?: string }} options used to pass data needed to create attributes - */ -export const getIncomingRequestAttributes = ( - request: IncomingMessage, - options: { component: string; serverName?: string } -): SpanAttributes => { - const headers = request.headers; - const userAgent = headers['user-agent']; - const ips = headers['x-forwarded-for']; - const method = request.method || 'GET'; - const httpVersion = request.httpVersion; - const requestUrl = request.url ? url.parse(request.url) : null; - const host = requestUrl?.host || headers.host; - const hostname = - requestUrl?.hostname || - host?.replace(/^(.*)(:[0-9]{1,5})/, '$1') || - 'localhost'; - const serverName = options.serverName; - const attributes: SpanAttributes = { - [SemanticAttributes.HTTP_URL]: getAbsoluteUrl( - requestUrl, - headers, - `${options.component}:` - ), - [SemanticAttributes.HTTP_HOST]: host, - [SemanticAttributes.NET_HOST_NAME]: hostname, - [SemanticAttributes.HTTP_METHOD]: method, - }; - - if (typeof ips === 'string') { - attributes[SemanticAttributes.HTTP_CLIENT_IP] = ips.split(',')[0]; - } - - if (typeof serverName === 'string') { - attributes[SemanticAttributes.HTTP_SERVER_NAME] = serverName; - } - - if (requestUrl) { - attributes[SemanticAttributes.HTTP_ROUTE] = requestUrl.pathname || '/'; - attributes[SemanticAttributes.HTTP_TARGET] = requestUrl.pathname || '/'; - } - - if (userAgent !== undefined) { - attributes[SemanticAttributes.HTTP_USER_AGENT] = userAgent; - } - - setRequestContentLengthAttribute(request, attributes); - - const httpKindAttributes = getAttributesFromHttpKind(httpVersion); - return Object.assign(attributes, httpKindAttributes); -}; - -/** - * Returns incoming request attributes scoped to the response data - * @param {(ServerResponse & { socket: Socket; })} response the response object - */ -export const getIncomingRequestAttributesOnResponse = ( - request: IncomingMessage & { __ot_middlewares?: string[] }, - response: ServerResponse -): SpanAttributes => { - // use socket from the request, - // since it may be detached from the response object in keep-alive mode - const { socket } = request; - const { statusCode, statusMessage } = response; - const { localAddress, localPort, remoteAddress, remotePort } = socket; - const { __ot_middlewares } = (request as unknown) as { - [key: string]: unknown; - }; - const route = Array.isArray(__ot_middlewares) - ? __ot_middlewares - .filter(path => path !== '/') - .map(path => { - return path[0] === '/' ? path : '/' + path; - }) - .join('') - : undefined; - - const attributes: SpanAttributes = { - [SemanticAttributes.NET_HOST_IP]: localAddress, - [SemanticAttributes.NET_HOST_PORT]: localPort, - [SemanticAttributes.NET_PEER_IP]: remoteAddress, - [SemanticAttributes.NET_PEER_PORT]: remotePort, - [SemanticAttributes.HTTP_STATUS_CODE]: statusCode, - [AttributeNames.HTTP_STATUS_TEXT]: (statusMessage || '').toUpperCase(), - }; - - if (route !== undefined) { - attributes[SemanticAttributes.HTTP_ROUTE] = route; - } - return attributes; -}; diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts deleted file mode 100644 index 7299f60b9a0..00000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ /dev/null @@ -1,846 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -import { - SpanStatusCode, - context, - propagation, - Span as ISpan, - SpanKind, - getSpan, - setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { - NetTransportValues, - SemanticAttributes, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpPlugin, plugin } from '../../src/http'; -import { Http, HttpPluginConfig } from '../../src/types'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import { ContextManager } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ClientRequest, IncomingMessage, ServerResponse } from 'http'; - -const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; - -let server: http.Server; -const serverPort = 22345; -const protocol = 'http'; -const hostname = 'localhost'; -const pathname = '/test'; -const serverName = 'my.server.name'; -const memoryExporter = new InMemorySpanExporter(); -const provider = new NodeTracerProvider(); -provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); -propagation.setGlobalPropagator(new DummyPropagation()); - -function doNock( - hostname: string, - path: string, - httpCode: number, - respBody: string, - times?: number -) { - const i = times || 1; - nock(`${protocol}://${hostname}`) - .get(path) - .times(i) - .reply(httpCode, respBody); -} - -export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -export const requestHookFunction = ( - span: ISpan, - request: ClientRequest | IncomingMessage -): void => { - span.setAttribute('custom request hook attribute', 'request'); -}; - -export const responseHookFunction = ( - span: ISpan, - response: IncomingMessage | ServerResponse -): void => { - span.setAttribute('custom response hook attribute', 'response'); -}; - -describe('HttpPlugin', () => { - let contextManager: ContextManager; - - beforeEach(() => { - contextManager = new AsyncHooksContextManager().enable(); - context.setGlobalContextManager(contextManager); - }); - - afterEach(() => { - context.disable(); - }); - - it('should return a plugin', () => { - assert.ok(plugin instanceof HttpPlugin); - }); - - it('should match version', () => { - assert.strictEqual(process.versions.node, plugin.version); - }); - - it(`moduleName should be ${protocol}`, () => { - assert.strictEqual(protocol, plugin.moduleName); - }); - - describe('enable()', () => { - describe('with bad plugin options', () => { - let pluginWithBadOptions: HttpPlugin; - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); - }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); - }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); - }, - }; - pluginWithBadOptions = new HttpPlugin( - plugin.component, - process.versions.node - ); - pluginWithBadOptions.enable(http, provider, config); - server = http.createServer((request, response) => { - response.end('Test Server Response'); - }); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - pluginWithBadOptions.disable(); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assertSpan(incomingSpan, SpanKind.SERVER, validations); - assertSpan(outgoingSpan, SpanKind.CLIENT, validations); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], - serverPort - ); - }); - }); - describe('with good plugin options', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - '/ignored/string', - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - requestHook: requestHookFunction, - responseHook: responseHookFunction, - serverName, - }; - plugin.enable(http, provider, config); - server = http.createServer((request, response) => { - if (request.url?.includes('/ignored')) { - provider.getTracer('test').startSpan('some-span').end(); - } - response.end('Test Server Response'); - }); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - plugin.disable(); - }); - - it(`${protocol} module should be patched`, () => { - assert.strictEqual(http.Server.prototype.emit.__wrapped, true); - }); - - it(`should not patch if it's not a ${protocol} module`, () => { - const httpNotPatched = new HttpPlugin( - plugin.component, - process.versions.node - ).enable({} as Http, provider, {}); - assert.strictEqual(Object.keys(httpNotPatched).length, 0); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}`, - { - headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', - }, - } - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - serverName, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - '' - ); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], - serverPort - ); - [ - { span: incomingSpan, kind: SpanKind.SERVER }, - { span: outgoingSpan, kind: SpanKind.CLIENT }, - ].forEach(({ span, kind }) => { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_FLAVOR], - '1.1' - ); - assert.strictEqual( - span.attributes[SemanticAttributes.NET_TRANSPORT], - NetTransportValues.IP_TCP - ); - assertSpan(span, kind, validations); - }); - }); - - const httpErrorCodes = [ - 400, - 401, - 403, - 404, - 429, - 501, - 503, - 504, - 500, - 505, - 597, - ]; - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; - - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - - const isReset = memoryExporter.getFinishedSpans().length === 0; - assert.ok(isReset); - - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - const reqSpan = spans[0]; - - assert.strictEqual(result.data, httpErrorCodes[i].toString()); - assert.strictEqual(spans.length, 1); - - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assertSpan(reqSpan, SpanKind.CLIENT, validations); - }); - } - - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - } - - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; - const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = provider.getTracer('default').startSpan(name); - await context.with(setSpan(context.active(), span), async () => { - for (let i = 0; i < num; i++) { - await httpRequest.get(`${protocol}://${hostname}${testPath}`); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); - assert.strictEqual( - span.context().traceId, - spans[i].spanContext.traceId - ); - } - span.end(); - const spans = memoryExporter.getFinishedSpans(); - // 5 child spans ended + 1 span (root) - assert.strictEqual(spans.length, 6); - }); - }); - - for (const ignored of ['string', 'function', 'regexp']) { - it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { - const testPath = `/ignored/${ignored}`; - - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - } - - for (const arg of ['string', {}, new Date()]) { - it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpRequest.get(arg); - } catch (error) { - // request has been made - // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); - } - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - }); - } - - for (const arg of [true, 1, false, 0, '']) { - it(`should not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpRequest.get(arg as any); - } catch (error) { - // request has been made - // nock throw - assert.ok( - error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') - ) > 0 - ); - } - const spans = memoryExporter.getFinishedSpans(); - // for this arg with don't provide trace. We pass arg to original method (http.get) - assert.strictEqual(spans.length, 0); - }); - } - - it('should have 1 ended span when request throw on bad "options" object', () => { - try { - http.request({ protocol: 'telnet' }); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request throw on bad "options" object', () => { - nock.cleanAll(); - nock.enableNetConnect(); - try { - http.request({ protocol: 'telnet' }); - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request is aborted', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .socketDelay(50) - .reply(200, ''); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - req.setTimeout(10, () => { - req.abort(); - reject('timeout'); - }); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length >= 6); - } - }); - - it('should have 1 ended span when request is aborted after receiving response', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .delay({ - body: 50, - }) - .replyWithFile(200, `${process.cwd()}/package.json`); - - const promiseRequest = new Promise((resolve, reject) => { - const req = http.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - req.abort(); - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length > 7); - } - }); - - it("should have 1 ended span when request doesn't listening response", done => { - nock.cleanAll(); - nock.enableNetConnect(); - const req = http.request(`${protocol}://${hostname}/`); - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - done(); - }); - req.end(); - }); - - it("should have 1 ended span when response is listened by using req.on('response')", done => { - const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); - const req = http.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_STATUS_CODE], - 404 - ); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - done(); - }); - }); - req.end(); - }); - - it('custom attributes should show up on client and server spans', async () => { - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - - assert.strictEqual( - incomingSpan.attributes['custom request hook attribute'], - 'request' - ); - assert.strictEqual( - incomingSpan.attributes['custom response hook attribute'], - 'response' - ); - assert.strictEqual( - incomingSpan.attributes['span kind'], - SpanKind.CLIENT - ); - - assert.strictEqual( - outgoingSpan.attributes['custom request hook attribute'], - 'request' - ); - assert.strictEqual( - outgoingSpan.attributes['custom response hook attribute'], - 'response' - ); - assert.strictEqual( - outgoingSpan.attributes['span kind'], - SpanKind.CLIENT - ); - }); - - it('should not set span as active in context for outgoing request', done => { - assert.deepStrictEqual(getSpan(context.active()), undefined); - http.get(`${protocol}://${hostname}:${serverPort}/test`, res => { - assert.deepStrictEqual(getSpan(context.active()), undefined); - done(); - }); - }); - }); - - describe('with require parent span', () => { - beforeEach(done => { - memoryExporter.reset(); - plugin.enable(http, provider, {}); - server = http.createServer((request, response) => { - response.end('Test Server Response'); - }); - server.listen(serverPort, done); - }); - - afterEach(() => { - server.close(); - plugin.disable(); - }); - - it('should not trace without parent with options enabled (both client & server)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - - it('should not trace without parent with options enabled (client only)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - assert.strictEqual( - spans.every(span => span.kind === SpanKind.SERVER), - true - ); - }); - - it('should not trace without parent with options enabled (server only)', async () => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const result = await httpRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - assert.strictEqual( - spans.every(span => span.kind === SpanKind.CLIENT), - true - ); - }); - - it('should trace with parent with both requireParent options enabled', done => { - plugin.disable(); - const config: HttpPluginConfig = { - requireParentforIncomingSpans: true, - requireParentforOutgoingSpans: true, - }; - plugin.enable(http, provider, config); - const testPath = '/test/test'; - const tracer = provider.getTracer('default'); - const span = tracer.startSpan('parentSpan', { - kind: SpanKind.INTERNAL, - }); - context.with(setSpan(context.active(), span), () => { - httpRequest - .get(`${protocol}://${hostname}:${serverPort}${testPath}`) - .then(result => { - span.end(); - assert( - result.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY] !== - undefined - ); - assert( - result.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY] !== - undefined - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 2); - assert.strictEqual( - spans.filter(span => span.kind === SpanKind.CLIENT).length, - 1 - ); - assert.strictEqual( - spans.filter(span => span.kind === SpanKind.INTERNAL).length, - 1 - ); - return done(); - }) - .catch(done); - }); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts deleted file mode 100644 index 258ce2e7f55..00000000000 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -import { - SpanAttributes, - SpanStatusCode, - ROOT_CONTEXT, - SpanKind, - TraceFlags, -} from '@opentelemetry/api'; -import { BasicTracerProvider, Span } from '@opentelemetry/tracing'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { IncomingMessage, ServerResponse } from 'http'; -import * as sinon from 'sinon'; -import * as url from 'url'; -import { IgnoreMatcher } from '../../src/types'; -import * as utils from '../../src/utils'; -import { AttributeNames } from '../../src/enums'; - -describe('Utility', () => { - describe('parseResponseStatus()', () => { - it('should return ERROR code by default', () => { - const status = utils.parseResponseStatus( - (undefined as unknown) as number - ); - assert.deepStrictEqual(status, { code: SpanStatusCode.ERROR }); - }); - - it('should return OK for Success HTTP status code', () => { - for (let index = 100; index < 400; index++) { - const status = utils.parseResponseStatus(index); - assert.deepStrictEqual(status, { code: SpanStatusCode.OK }); - } - }); - - it('should not return OK for Bad HTTP status code', () => { - for (let index = 400; index <= 600; index++) { - const status = utils.parseResponseStatus(index); - assert.notStrictEqual(status.code, SpanStatusCode.OK); - } - }); - }); - describe('hasExpectHeader()', () => { - it('should throw if no option', () => { - try { - utils.hasExpectHeader('' as http.RequestOptions); - assert.fail(); - } catch (ignore) {} - }); - - it('should not throw if no headers', () => { - const result = utils.hasExpectHeader({} as http.RequestOptions); - assert.strictEqual(result, false); - }); - - it('should return true on Expect (no case sensitive)', () => { - for (const headers of [{ Expect: 1 }, { expect: 1 }, { ExPect: 1 }]) { - const result = utils.hasExpectHeader({ - headers, - } as http.RequestOptions); - assert.strictEqual(result, true); - } - }); - }); - - describe('getRequestInfo()', () => { - it('should get options object', () => { - const webUrl = 'http://u:p@google.fr/aPath?qu=ry'; - const urlParsed = url.parse(webUrl); - const urlParsedWithoutPathname = { - ...urlParsed, - pathname: undefined, - }; - const whatWgUrl = new url.URL(webUrl); - for (const param of [ - webUrl, - urlParsed, - urlParsedWithoutPathname, - whatWgUrl, - ]) { - const result = utils.getRequestInfo(param); - assert.strictEqual(result.optionsParsed.hostname, 'google.fr'); - assert.strictEqual(result.optionsParsed.protocol, 'http:'); - assert.strictEqual(result.optionsParsed.path, '/aPath?qu=ry'); - assert.strictEqual(result.pathname, '/aPath'); - assert.strictEqual(result.origin, 'http://google.fr'); - } - }); - }); - - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); - assert.strictEqual(answer2, false); - }); - - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); - assert.strictEqual(answer2, false); - }); - - it('should throw if type is unknown', () => { - try { - utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); - assert.fail(); - } catch (error) { - assert.strictEqual(error instanceof TypeError, true); - } - }); - - it('function pattern', () => { - const answer1 = utils.satisfiesPattern( - '/test/home', - (url: string) => url === '/test/home' - ); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern( - '/test/home', - (url: string) => url !== '/test/home' - ); - assert.strictEqual(answer2, false); - }); - }); - - describe('isIgnored()', () => { - beforeEach(() => { - sinon.spy(utils, 'satisfiesPattern'); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('/test/1', ['/test/11']); - assert.strictEqual(answer1, false); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 1 - ); - }); - - it('should call isSatisfyPattern, match for function', () => { - const answer1 = utils.isIgnored('/test/1', [ - url => url.endsWith('/test/1'), - ]); - assert.strictEqual(answer1, true); - }); - - it('should not re-throw when function throws an exception', () => { - const onException = (e: Error) => { - // Do Nothing - }; - for (const callback of [undefined, onException]) { - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - () => { - throw new Error('test'); - }, - ], - callback - ) - ); - } - }); - - it('should call onException when function throws an exception', () => { - const onException = sinon.spy(); - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - () => { - throw new Error('test'); - }, - ], - onException - ) - ); - assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); - }); - - it('should not call isSatisfyPattern', () => { - utils.isIgnored('/test/1', []); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 0 - ); - }); - - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('/test/1', []); - assert.strictEqual(answer1, false); - }); - - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('/test/1', undefined); - assert.strictEqual(answer2, false); - }); - }); - - describe('getAbsoluteUrl()', () => { - it('should return absolute url with localhost', () => { - const path = '/test/1'; - const result = utils.getAbsoluteUrl(url.parse(path), {}); - assert.strictEqual(result, `http://localhost${path}`); - }); - it('should return absolute url', () => { - const absUrl = 'http://www.google/test/1?query=1'; - const result = utils.getAbsoluteUrl(url.parse(absUrl), {}); - assert.strictEqual(result, absUrl); - }); - it('should return default url', () => { - const result = utils.getAbsoluteUrl(null, {}); - assert.strictEqual(result, 'http://localhost/'); - }); - it("{ path: '/helloworld', port: 8080 } should return http://localhost:8080/helloworld", () => { - const result = utils.getAbsoluteUrl( - { path: '/helloworld', port: 8080 }, - {} - ); - assert.strictEqual(result, 'http://localhost:8080/helloworld'); - }); - }); - - describe('setSpanWithError()', () => { - it('should have error attributes', () => { - const errorMessage = 'test error'; - for (const obj of [undefined, { statusCode: 400 }]) { - const span = new Span( - new BasicTracerProvider().getTracer('default'), - ROOT_CONTEXT, - 'test', - { spanId: '', traceId: '', traceFlags: TraceFlags.SAMPLED }, - SpanKind.INTERNAL - ); - /* tslint:disable-next-line:no-any */ - utils.setSpanWithError(span, new Error(errorMessage), obj as any); - const attributes = span.attributes; - assert.strictEqual( - attributes[AttributeNames.HTTP_ERROR_MESSAGE], - errorMessage - ); - assert.ok(attributes[AttributeNames.HTTP_ERROR_NAME]); - } - }); - }); - - describe('isValidOptionsType()', () => { - ['', false, true, 1, 0, []].forEach(options => { - it(`should return false with the following value: ${JSON.stringify( - options - )}`, () => { - assert.strictEqual(utils.isValidOptionsType(options), false); - }); - }); - for (const options of ['url', url.parse('http://url.com'), {}]) { - it(`should return true with the following value: ${JSON.stringify( - options - )}`, () => { - assert.strictEqual(utils.isValidOptionsType(options), true); - }); - } - }); - - describe('getIncomingRequestAttributesOnResponse()', () => { - it('should correctly parse the middleware stack if present', () => { - const request = { - __ot_middlewares: ['/test', '/toto', '/'], - socket: {}, - } as IncomingMessage & { __ot_middlewares?: string[] }; - const response = {} as ServerResponse; - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); - assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], '/test/toto'); - }); - - it('should succesfully process without middleware stack', () => { - const request = { socket: {} } as IncomingMessage; - const response = {} as ServerResponse; - const attributes = utils.getIncomingRequestAttributesOnResponse( - request, - response - ); - assert.deepEqual(attributes[SemanticAttributes.HTTP_ROUTE], undefined); - }); - }); - - // Verify the key in the given attributes is set to the given value, - // and that no other HTTP Content Length attributes are set. - function verifyValueInAttributes( - attributes: SpanAttributes, - key: string | undefined, - value: number - ) { - const SemanticAttributess = [ - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, - ]; - - for (const attr of SemanticAttributess) { - if (attr === key) { - assert.strictEqual(attributes[attr], value); - } else { - assert.strictEqual(attributes[attr], undefined); - } - } - } - - describe('setRequestContentLengthAttributes()', () => { - it('should set request content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - - request.headers = { - 'content-length': '1200', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set request content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - request.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set request content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; - const request = {} as IncomingMessage; - request.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', - }; - utils.setRequestContentLengthAttribute(request, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, - 1200 - ); - }); - }); - - describe('setResponseContentLengthAttributes()', () => { - it('should set response content-length uncompressed attribute with no content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - }; - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set response content-length uncompressed attribute with "identity" content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - 'content-encoding': 'identity', - }; - - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED, - 1200 - ); - }); - - it('should set response content-length compressed attribute with "gzip" content-encoding header', () => { - const attributes: SpanAttributes = {}; - - const response = {} as IncomingMessage; - - response.headers = { - 'content-length': '1200', - 'content-encoding': 'gzip', - }; - - utils.setResponseContentLengthAttribute(response, attributes); - - verifyValueInAttributes( - attributes, - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, - 1200 - ); - }); - - it('should set no attributes with no content-length header', () => { - const attributes: SpanAttributes = {}; - const message = {} as IncomingMessage; - - message.headers = { - 'content-encoding': 'gzip', - }; - utils.setResponseContentLengthAttribute(message, attributes); - - verifyValueInAttributes(attributes, undefined, 1200); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts deleted file mode 100644 index ad6825497cc..00000000000 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { SpanKind, Span, context } from '@opentelemetry/api'; -import { - HttpFlavorValues, - NetTransportValues, - SemanticAttributes, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as url from 'url'; -import { plugin } from '../../src/http'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpRequest } from '../utils/httpRequest'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { HttpPluginConfig } from '../../src/types'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; -import { sendRequestTwice } from '../utils/rawRequest'; -const protocol = 'http'; -const serverPort = 32345; -const hostname = 'localhost'; -const memoryExporter = new InMemorySpanExporter(); - -export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpPlugin Integration tests', () => { - let mockServerPort = 0; - let mockServer: http.Server; - const sockets: Array = []; - before(done => { - mockServer = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - }); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - memoryExporter.reset(); - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - describe('enable()', () => { - before(function (done) { - // mandatory - if (process.env.CI) { - done(); - return; - } - - utils.checkInternet(isConnected => { - if (!isConnected) { - this.skip(); - // don't disturb people - } - done(); - }); - }); - - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const ignoreConfig = [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, - }; - try { - plugin.disable(); - } catch (e) {} - plugin.enable(http, provider, config); - }); - - after(() => { - plugin.disable(); - }); - - it('should create a rootSpan for GET requests and add propagation headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - `${protocol}://localhost:${mockServerPort}/?query=test` - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`) - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), - { - headers: { 'x-foo': 'foo' }, - } - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_FLAVOR], - HttpFlavorValues.HTTP_1_1 - ); - assert.strictEqual( - span.attributes[SemanticAttributes.NET_TRANSPORT], - NetTransportValues.IP_TCP - ); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('custom attributes should show up on client spans', async () => { - const result = await httpRequest.get( - `${protocol}://localhost:${mockServerPort}/` - ); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = Object.assign( - { headers: { Expect: '100-continue' } }, - url.parse(`${protocol}://localhost:${mockServerPort}/`) - ); - - const result = await httpRequest.get(options); - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, - ]) { - it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( - headers - )}`, done => { - let validations: { - hostname: string; - httpStatusCode: number; - httpMethod: string; - pathname: string; - reqHeaders: http.OutgoingHttpHeaders; - resHeaders: http.IncomingHttpHeaders; - }; - let data = ''; - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = { headers }; - const req = http.get( - `${protocol}://localhost:${mockServerPort}/`, - options, - (resp: http.IncomingMessage) => { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - validations = { - hostname: 'localhost', - httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', - resHeaders: resp.headers, - /* tslint:disable:no-any */ - reqHeaders: (res.req as any).getHeaders - ? (res.req as any).getHeaders() - : (res.req as any)._headers, - /* tslint:enable:no-any */ - }; - }); - } - ); - - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.ok(data); - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - done(); - }); - }); - } - - it('should work for multiple active requests in keep-alive mode', async () => { - await sendRequestTwice(hostname, mockServerPort); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find((s: any) => s.kind === SpanKind.SERVER); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts deleted file mode 100644 index b629dc7c0f1..00000000000 --- a/packages/opentelemetry-plugin-http/test/utils/assertSpan.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ -import { SpanKind, SpanStatus } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as utils from '../../src/utils'; -import { DummyPropagation } from './DummyPropagation'; -import { AttributeNames } from '../../src/enums'; - -export const assertSpan = ( - span: ReadableSpan, - kind: SpanKind, - validations: { - httpStatusCode: number; - httpMethod: string; - resHeaders: http.IncomingHttpHeaders; - hostname: string; - pathname: string; - reqHeaders?: http.OutgoingHttpHeaders; - path?: string | null; - forceStatus?: SpanStatus; - serverName?: string; - component: string; - } -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); - assert.strictEqual( - span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], - span.status.message - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_METHOD], - validations.httpMethod - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_TARGET], - validations.path || validations.pathname - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_STATUS_CODE], - validations.httpStatusCode - ); - - assert.strictEqual(span.links.length, 0); - assert.strictEqual(span.events.length, 0); - - assert.deepStrictEqual( - span.status, - validations.forceStatus || - utils.parseResponseStatus(validations.httpStatusCode) - ); - - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); - - if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; - if (userAgent) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_USER_AGENT], - userAgent - ); - } - } - - if (span.kind === SpanKind.CLIENT) { - if (validations.resHeaders['content-length']) { - const contentLength = Number(validations.resHeaders['content-length']); - - if ( - validations.resHeaders['content-encoding'] && - validations.resHeaders['content-encoding'] !== 'identity' - ) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH], - contentLength - ); - } else { - assert.strictEqual( - span.attributes[ - SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH_UNCOMPRESSED - ], - contentLength - ); - } - } - - assert.strictEqual( - span.attributes[SemanticAttributes.NET_PEER_NAME], - validations.hostname, - 'must be consistent (PEER_NAME and hostname)' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_PEER_IP], - 'must have PEER_IP' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_PEER_PORT], - 'must have PEER_PORT' - ); - assert.ok( - (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( - span.attributes[SemanticAttributes.NET_PEER_NAME] as string - ) > -1, - 'must be consistent' - ); - } - if (span.kind === SpanKind.SERVER) { - if (validations.reqHeaders && validations.reqHeaders['content-length']) { - const contentLength = validations.reqHeaders['content-length']; - - if ( - validations.reqHeaders['content-encoding'] && - validations.reqHeaders['content-encoding'] !== 'identity' - ) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH], - contentLength - ); - } else { - assert.strictEqual( - span.attributes[ - SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH_UNCOMPRESSED - ], - contentLength - ); - } - } - - if (validations.serverName) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_SERVER_NAME], - validations.serverName, - ' must have serverName attribute' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_HOST_PORT], - 'must have HOST_PORT' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_HOST_IP], - 'must have HOST_IP' - ); - } - assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); - } else if (validations.reqHeaders) { - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - } -}; diff --git a/packages/opentelemetry-plugin-https/src/enums.ts b/packages/opentelemetry-plugin-https/src/enums.ts deleted file mode 100644 index d906510ac9d..00000000000 --- a/packages/opentelemetry-plugin-https/src/enums.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -/** - * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md - */ -export enum AttributeNames { - HTTP_ERROR_MESSAGE = 'http.error_message', -} diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts deleted file mode 100644 index 3291d909a3e..00000000000 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { - SpanStatusCode, - context, - propagation, - Span as ISpan, - SpanKind, - setSpan, -} from '@opentelemetry/api'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { ContextManager } from '@opentelemetry/api'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { - HttpFlavorValues, - NetTransportValues, - SemanticAttributes, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as http from 'http'; -import * as https from 'https'; -import * as nock from 'nock'; -import * as path from 'path'; -import { HttpsPlugin, plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; - -const applyCustomAttributesOnSpanErrorMessage = - 'bad applyCustomAttributesOnSpan function'; - -let server: https.Server; -const serverPort = 32345; -const protocol = 'https'; -const hostname = 'localhost'; -const serverName = 'my.server.name'; -const pathname = '/test'; -const memoryExporter = new InMemorySpanExporter(); -const provider = new NodeTracerProvider(); -const tracer = provider.getTracer('test-https'); -provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); -propagation.setGlobalPropagator(new DummyPropagation()); - -function doNock( - hostname: string, - path: string, - httpCode: number, - respBody: string, - times?: number -) { - const i = times || 1; - nock(`${protocol}://${hostname}`) - .get(path) - .times(i) - .reply(httpCode, respBody); -} - -export const customAttributeFunction = (span: ISpan): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpsPlugin', () => { - let contextManager: ContextManager; - - beforeEach(() => { - contextManager = new AsyncHooksContextManager().enable(); - context.setGlobalContextManager(contextManager); - }); - - afterEach(() => { - contextManager.disable(); - context.disable(); - }); - - it('should return a plugin', () => { - assert.ok(plugin instanceof HttpsPlugin); - }); - - it('should match version', () => { - assert.strictEqual(process.versions.node, plugin.version); - }); - - it(`moduleName should be ${protocol}`, () => { - assert.strictEqual(protocol, plugin.moduleName); - }); - - describe('enable()', () => { - describe('with bad plugin options', () => { - let pluginWithBadOptions: HttpsPlugin; - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); - }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); - }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); - }, - }; - pluginWithBadOptions = new HttpsPlugin(process.versions.node); - pluginWithBadOptions.enable( - (https as unknown) as Http, - provider, - config - ); - server = https.createServer( - { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), - }, - (request, response) => { - response.end('Test Server Response'); - } - ); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - pluginWithBadOptions.disable(); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}` - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assertSpan(incomingSpan, SpanKind.SERVER, validations); - assertSpan(outgoingSpan, SpanKind.CLIENT, validations); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], - serverPort - ); - }); - }); - describe('with good plugin options', () => { - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - '/ignored/string', - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - serverName, - }; - plugin.enable((https as unknown) as Http, provider, config); - server = https.createServer( - { - key: fs.readFileSync('test/fixtures/server-key.pem'), - cert: fs.readFileSync('test/fixtures/server-cert.pem'), - }, - (request, response) => { - if (request.url?.includes('/ignored')) { - tracer.startSpan('some-span').end(); - } - response.end('Test Server Response'); - } - ); - - server.listen(serverPort); - }); - - after(() => { - server.close(); - plugin.disable(); - }); - - it(`${protocol} module should be patched`, () => { - assert.strictEqual(https.Server.prototype.emit.__wrapped, true); - }); - - it(`should not patch if it's not a ${protocol} module`, () => { - const httpsNotPatched = new HttpsPlugin(process.versions.node).enable( - {} as Http, - provider, - {} - ); - assert.strictEqual(Object.keys(httpsNotPatched).length, 0); - }); - - it('should generate valid spans (client side and server side)', async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${pathname}`, - { - headers: { - 'x-forwarded-for': ', , ', - 'user-agent': 'chrome', - }, - } - ); - const spans = memoryExporter.getFinishedSpans(); - const [incomingSpan, outgoingSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: result.method!, - pathname, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - serverName, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.HTTP_CLIENT_IP], - '' - ); - assert.strictEqual( - incomingSpan.attributes[SemanticAttributes.NET_HOST_PORT], - serverPort - ); - assert.strictEqual( - outgoingSpan.attributes[SemanticAttributes.NET_PEER_PORT], - serverPort - ); - - [ - { span: incomingSpan, kind: SpanKind.SERVER }, - { span: outgoingSpan, kind: SpanKind.CLIENT }, - ].forEach(({ span, kind }) => { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_FLAVOR], - HttpFlavorValues.HTTP_1_1 - ); - assert.strictEqual( - span.attributes[SemanticAttributes.NET_TRANSPORT], - NetTransportValues.IP_TCP - ); - assertSpan(span, kind, validations); - }); - }); - - const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505]; - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/1'; - - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - - const isReset = memoryExporter.getFinishedSpans().length === 0; - assert.ok(isReset); - - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - const reqSpan = spans[0]; - - assert.strictEqual(result.data, httpErrorCodes[i].toString()); - assert.strictEqual(spans.length, 1); - - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assertSpan(reqSpan, SpanKind.CLIENT, validations); - }); - } - - it('should create a child span for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 200, 'Ok'); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - - for (let i = 0; i < httpErrorCodes.length; i++) { - it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock( - hostname, - testPath, - httpErrorCodes[i], - httpErrorCodes[i].toString() - ); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - return context.with(setSpan(context.active(), span), async () => { - const result = await httpsRequest.get( - `${protocol}://${hostname}${testPath}` - ); - span.end(); - const spans = memoryExporter.getFinishedSpans(); - const [reqSpan, localSpan] = spans; - const validations = { - hostname, - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: testPath, - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0); - assert.strictEqual(spans.length, 2); - assert.strictEqual(reqSpan.name, 'HTTP GET'); - assert.strictEqual( - localSpan.spanContext.traceId, - reqSpan.spanContext.traceId - ); - assertSpan(reqSpan, SpanKind.CLIENT, validations); - assert.notStrictEqual( - localSpan.spanContext.spanId, - reqSpan.spanContext.spanId - ); - }); - }); - } - - it('should create multiple child spans for GET requests', async () => { - const testPath = '/outgoing/rootSpan/childs'; - const num = 5; - doNock(hostname, testPath, 200, 'Ok', num); - const name = 'TestRootSpan'; - const span = tracer.startSpan(name); - await context.with(setSpan(context.active(), span), async () => { - for (let i = 0; i < num; i++) { - await httpsRequest.get(`${protocol}://${hostname}${testPath}`); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans[i].name, 'HTTP GET'); - assert.strictEqual( - span.context().traceId, - spans[i].spanContext.traceId - ); - } - span.end(); - const spans = memoryExporter.getFinishedSpans(); - // 5 child spans ended + 1 span (root) - assert.strictEqual(spans.length, 6); - }); - }); - - for (const ignored of ['string', 'function', 'regexp']) { - it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => { - const testPath = `/ignored/${ignored}`; - - await httpsRequest.get( - `${protocol}://${hostname}:${serverPort}${testPath}` - ); - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - }); - } - - for (const arg of ['string', {}, new Date()]) { - it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpsRequest.get(arg); - } catch (error) { - // request has been made - // nock throw - assert.ok(error.message.startsWith('Nock: No match for request')); - } - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - }); - } - - for (const arg of [true, 1, false, 0, '']) { - it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify( - arg - )}`, async () => { - try { - await httpsRequest.get(arg as any); - } catch (error) { - // request has been made - // nock throw - assert.ok( - error.stack.indexOf( - path.normalize('/node_modules/nock/lib/intercept.js') - ) > 0 - ); - } - const spans = memoryExporter.getFinishedSpans(); - // for this arg with don't provide trace. We pass arg to original method (https.get) - assert.strictEqual(spans.length, 0); - }); - } - - it('should have 1 ended span when request throw on bad "options" object', () => { - try { - https.request({ protocol: 'telnet' }); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request throw on bad "options" object', () => { - nock.cleanAll(); - nock.enableNetConnect(); - try { - https.request({ protocol: 'telnet' }); - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when response.end throw an exception', async () => { - const testPath = '/outgoing/rootSpan/childs/1'; - doNock(hostname, testPath, 400, 'Not Ok'); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://${hostname}${testPath}`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - reject(new Error(data)); - }); - } - ); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 1); - } - }); - - it('should have 1 ended span when request is aborted', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .socketDelay(50) - .reply(200, ''); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - req.setTimeout(10, () => { - req.abort(); - reject('timeout'); - }); - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length >= 6); - } - }); - - it('should have 1 ended span when request is aborted after receiving response', async () => { - nock(`${protocol}://my.server.com`) - .get('/') - .delay({ - body: 50, - }) - .replyWithFile(200, `${process.cwd()}/package.json`); - - const promiseRequest = new Promise((resolve, reject) => { - const req = https.request( - `${protocol}://my.server.com`, - (resp: http.IncomingMessage) => { - let data = ''; - resp.on('data', chunk => { - req.abort(); - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }); - } - ); - - return req.end(); - }); - - try { - await promiseRequest; - assert.fail(); - } catch (error) { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - assert.ok(Object.keys(span.attributes).length > 7); - } - }); - - it("should have 1 ended span when response is listened by using req.on('response')", done => { - const host = `${protocol}://${hostname}`; - nock(host).get('/').reply(404); - const req = https.request(`${host}/`); - req.on('response', response => { - response.on('data', () => {}); - response.on('end', () => { - const spans = memoryExporter.getFinishedSpans(); - const [span] = spans; - assert.strictEqual(spans.length, 1); - assert.ok(Object.keys(span.attributes).length > 6); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_STATUS_CODE], - 404 - ); - assert.strictEqual(span.status.code, SpanStatusCode.ERROR); - done(); - }); - }); - req.end(); - }); - }); - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts deleted file mode 100644 index 58896a9b8a2..00000000000 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http'; -import { SpanKind, Span, context } from '@opentelemetry/api'; -import { - HttpFlavorValues, - NetTransportValues, - SemanticAttributes, -} from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as https from 'https'; -import { plugin } from '../../src/https'; -import { assertSpan } from '../utils/assertSpan'; -import { DummyPropagation } from '../utils/DummyPropagation'; -import { httpsRequest } from '../utils/httpsRequest'; -import * as url from 'url'; -import * as utils from '../utils/utils'; -import { NodeTracerProvider } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; -import { Socket } from 'net'; - -const protocol = 'https'; -const serverPort = 42345; -const hostname = 'localhost'; -const memoryExporter = new InMemorySpanExporter(); - -export const customAttributeFunction = (span: Span): void => { - span.setAttribute('span kind', SpanKind.CLIENT); -}; - -describe('HttpsPlugin Integration tests', () => { - let mockServerPort = 0; - let mockServer: https.Server; - const sockets: Array = []; - before(done => { - mockServer = https.createServer( - { - key: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-key.pem') - ), - cert: fs.readFileSync( - path.join(__dirname, '..', 'fixtures', 'server-cert.pem') - ), - }, - (req, res) => { - res.statusCode = 200; - res.setHeader('content-type', 'application/json'); - res.write( - JSON.stringify({ - success: true, - }) - ); - res.end(); - } - ); - - mockServer.listen(0, () => { - const addr = mockServer.address(); - if (addr == null) { - done(new Error('unexpected addr null')); - return; - } - - if (typeof addr === 'string') { - done(new Error(`unexpected addr ${addr}`)); - return; - } - - if (addr.port <= 0) { - done(new Error('Could not get port')); - return; - } - mockServerPort = addr.port; - done(); - }); - }); - - after(done => { - sockets.forEach(s => s.destroy()); - mockServer.close(done); - }); - - beforeEach(() => { - memoryExporter.reset(); - context.setGlobalContextManager(new AsyncHooksContextManager().enable()); - }); - - afterEach(() => { - context.disable(); - }); - - describe('enable()', () => { - before(function (done) { - // mandatory - if (process.env.CI) { - done(); - return; - } - - utils.checkInternet(isConnected => { - if (!isConnected) { - this.skip(); - // don't disturb people - } - done(); - }); - }); - const provider = new NodeTracerProvider(); - provider.addSpanProcessor(new SimpleSpanProcessor(memoryExporter)); - beforeEach(() => { - memoryExporter.reset(); - }); - - before(() => { - const ignoreConfig = [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith('/ignored/function'), - ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, - }; - try { - plugin.disable(); - } catch (e) {} - plugin.enable((https as unknown) as Http, provider, config); - }); - - after(() => { - plugin.disable(); - }); - - it('should create a rootSpan for GET requests and add propagation headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - `${protocol}://localhost:${mockServerPort}/?query=test` - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`) - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a valid rootSpan with propagation headers for GET requests if URL and options are used', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - - const result = await httpsRequest.get( - new url.URL(`${protocol}://localhost:${mockServerPort}/?query=test`), - { - headers: { 'x-foo': 'foo' }, - } - ); - - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - path: '/?query=test', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(result.reqHeaders['x-foo'], 'foo'); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_FLAVOR], - HttpFlavorValues.HTTP_1_1 - ); - assert.strictEqual( - span.attributes[SemanticAttributes.NET_TRANSPORT], - NetTransportValues.IP_TCP - ); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('custom attributes should show up on client spans', async () => { - const result = await httpsRequest.get( - `${protocol}://localhost:${mockServerPort}/` - ); - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: result.statusCode!, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.strictEqual(span.attributes['span kind'], SpanKind.CLIENT); - assertSpan(span, SpanKind.CLIENT, validations); - }); - - it('should create a span for GET requests and add propagation headers with Expect headers', async () => { - let spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = Object.assign( - { headers: { Expect: '100-continue' } }, - url.parse(`${protocol}://localhost:${mockServerPort}/`) - ); - - const result = await httpsRequest.get(options); - spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - const validations = { - hostname: 'localhost', - httpStatusCode: 200, - httpMethod: 'GET', - pathname: '/', - resHeaders: result.resHeaders, - reqHeaders: result.reqHeaders, - component: plugin.component, - }; - - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assertSpan(span, SpanKind.CLIENT, validations); - }); - for (const headers of [ - { Expect: '100-continue', 'user-agent': 'http-plugin-test' }, - { 'user-agent': 'http-plugin-test' }, - ]) { - it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify( - headers - )}`, done => { - let validations: { - hostname: string; - httpStatusCode: number; - httpMethod: string; - pathname: string; - reqHeaders: http.OutgoingHttpHeaders; - resHeaders: http.IncomingHttpHeaders; - }; - let data = ''; - const spans = memoryExporter.getFinishedSpans(); - assert.strictEqual(spans.length, 0); - const options = { headers }; - const req = https.get( - `${protocol}://localhost:${mockServerPort}/`, - options, - (resp: http.IncomingMessage) => { - const res = (resp as unknown) as http.IncomingMessage & { - req: http.IncomingMessage; - }; - - resp.on('data', chunk => { - data += chunk; - }); - resp.on('end', () => { - validations = { - hostname: 'localhost', - httpStatusCode: 301, - httpMethod: 'GET', - pathname: '/', - resHeaders: resp.headers, - /* tslint:disable:no-any */ - reqHeaders: (res.req as any).getHeaders - ? (res.req as any).getHeaders() - : (res.req as any)._headers, - /* tslint:enable:no-any */ - }; - }); - } - ); - - req.on('close', () => { - const spans = memoryExporter.getFinishedSpans(); - const span = spans.find(s => s.kind === SpanKind.CLIENT); - assert.ok(span); - assert.strictEqual(spans.length, 2); - assert.strictEqual(span.name, 'HTTP GET'); - assert.ok(data); - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - done(); - }); - }); - } - }); -}); diff --git a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts b/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts deleted file mode 100644 index 37b8e09f479..00000000000 --- a/packages/opentelemetry-plugin-https/test/utils/assertSpan.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * 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. - */ - -import { SpanKind } from '@opentelemetry/api'; -import { hrTimeToNanoseconds } from '@opentelemetry/core'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import * as assert from 'assert'; -import * as http from 'http'; -import { DummyPropagation } from './DummyPropagation'; -import { ReadableSpan } from '@opentelemetry/tracing'; -import { parseResponseStatus } from '@opentelemetry/plugin-http'; -import { AttributeNames } from '../../src/enums'; - -export const assertSpan = ( - span: ReadableSpan, - kind: SpanKind, - validations: { - httpStatusCode: number; - httpMethod: string; - resHeaders: http.IncomingHttpHeaders; - hostname: string; - pathname: string; - reqHeaders?: http.OutgoingHttpHeaders; - path?: string | null; - serverName?: string; - component: string; - } -) => { - assert.strictEqual(span.spanContext.traceId.length, 32); - assert.strictEqual(span.spanContext.spanId.length, 16); - assert.strictEqual(span.kind, kind); - assert.strictEqual(span.name, `HTTP ${validations.httpMethod}`); - assert.strictEqual( - span.attributes[AttributeNames.HTTP_ERROR_MESSAGE], - span.status.message - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_METHOD], - validations.httpMethod - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_TARGET], - validations.path || validations.pathname - ); - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_STATUS_CODE], - validations.httpStatusCode - ); - assert.ok(span.endTime); - assert.strictEqual(span.links.length, 0); - assert.strictEqual(span.events.length, 0); - assert.deepStrictEqual( - span.status, - parseResponseStatus(validations.httpStatusCode) - ); - - assert.ok(span.endTime, 'must be finished'); - assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration'); - - if (validations.reqHeaders) { - const userAgent = validations.reqHeaders['user-agent']; - if (userAgent) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_USER_AGENT], - userAgent - ); - } - } - if (span.kind === SpanKind.CLIENT) { - assert.strictEqual( - span.attributes[SemanticAttributes.NET_PEER_NAME], - validations.hostname, - 'must be consistent (PEER_NAME and hostname)' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_PEER_IP], - 'must have PEER_IP' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_PEER_PORT], - 'must have PEER_PORT' - ); - assert.ok( - (span.attributes[SemanticAttributes.HTTP_URL] as string).indexOf( - span.attributes[SemanticAttributes.NET_PEER_NAME] as string - ) > -1, - 'must be consistent' - ); - } - if (span.kind === SpanKind.SERVER) { - if (validations.serverName) { - assert.strictEqual( - span.attributes[SemanticAttributes.HTTP_SERVER_NAME], - validations.serverName, - ' must have serverName attribute' - ); - } - assert.ok( - span.attributes[SemanticAttributes.NET_HOST_PORT], - 'must have HOST_PORT' - ); - assert.ok( - span.attributes[SemanticAttributes.NET_HOST_IP], - 'must have HOST_IP' - ); - assert.strictEqual(span.parentSpanId, DummyPropagation.SPAN_CONTEXT_KEY); - } else if (validations.reqHeaders) { - assert.ok(validations.reqHeaders[DummyPropagation.TRACE_CONTEXT_KEY]); - assert.ok(validations.reqHeaders[DummyPropagation.SPAN_CONTEXT_KEY]); - } -}; From 5b45d5b24afc740890f12591310efad945cefa3e Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Sat, 10 Apr 2021 02:22:33 +0100 Subject: [PATCH 13/14] fix: remove unwanted `@deprecated`-attribute in semconv template file --- scripts/semconv/templates/SemanticAttributes.ts.j2 | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/semconv/templates/SemanticAttributes.ts.j2 b/scripts/semconv/templates/SemanticAttributes.ts.j2 index fc0a7496fb8..f2ee675302c 100644 --- a/scripts/semconv/templates/SemanticAttributes.ts.j2 +++ b/scripts/semconv/templates/SemanticAttributes.ts.j2 @@ -40,9 +40,6 @@ export const {{class}} = { * @deprecated {{attribute.deprecated | to_doc_brief}}. {%- endif %} */ - {%- if attribute.deprecated %} - @Deprecated - {%- endif %} {{attribute.fqn | to_const_name}}: '{{attribute.fqn}}', {%- endfor %} } From 50ce03126d731ed3002c6d222ee6e44b9180f8f2 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Sat, 10 Apr 2021 02:23:44 +0100 Subject: [PATCH 14/14] fix: don't fetch the whole otel specification repo --- scripts/semconv/generate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/semconv/generate.sh b/scripts/semconv/generate.sh index 72eb399de1c..589537c2699 100755 --- a/scripts/semconv/generate.sh +++ b/scripts/semconv/generate.sh @@ -15,7 +15,7 @@ cd opentelemetry-specification git init git remote add origin https://github.com/open-telemetry/opentelemetry-specification.git -git fetch origin "$SPEC_VERSION" +git fetch origin "$SPEC_VERSION" --depth=1 git reset --hard FETCH_HEAD cd ${SCRIPT_DIR}