diff --git a/README.md b/README.md index 2de28e5..4f68249 100644 --- a/README.md +++ b/README.md @@ -572,7 +572,7 @@ When creating a tracer you need to create two types: 1. Your tracer conforming to `Tracer` 2. A span class conforming to `Span` -> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it. +> The `Span` conforms to the standard rules defined in [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span), so if unsure about usage patterns, you can refer to this specification and examples referring to it. ### Defining, injecting and extracting Baggage diff --git a/Sources/Tracing/Span.swift b/Sources/Tracing/Span.swift index b9297cc..d36fe0c 100644 --- a/Sources/Tracing/Span.swift +++ b/Sources/Tracing/Span.swift @@ -20,7 +20,7 @@ import Dispatch /// /// Creating a `Span` is delegated to a `Tracer` and end users should never create them directly. /// -/// - SeeAlso: For more details refer to the [OpenTelemetry Specification: Span](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#span) which this type is compatible with. +/// - SeeAlso: For more details refer to the [OpenTelemetry Specification: Span](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#span) which this type is compatible with. public protocol Span: AnyObject { /// The read-only `Baggage` of this `Span`, set when starting this `Span`. var baggage: Baggage { get } @@ -600,7 +600,7 @@ public struct SpanStatus { /// A code representing the status of a `Span`. /// - /// - SeeAlso: For the semantics of status codes see [OpenTelemetry Specification: setStatus](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#set-status) + /// - SeeAlso: For the semantics of status codes see [OpenTelemetry Specification: setStatus](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/api.md#set-status) public enum Code { /// The Span has been validated by an Application developer or Operator to have completed successfully. case ok diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+DatabaseSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+DatabaseSemantics.swift new file mode 100644 index 0000000..17b7af9 --- /dev/null +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+DatabaseSemantics.swift @@ -0,0 +1,305 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift Distributed Tracing project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Tracing + +extension SpanAttributeName { + /// - SeeAlso: DatabaseAttributes + public enum Database { + /// - SeeAlso: DatabaseAttributes + public static let system = "db.system" + /// - SeeAlso: DatabaseAttributes + public static let connectionString = "db.connection_string" + /// - SeeAlso: DatabaseAttributes + public static let user = "db.user" + /// - SeeAlso: DatabaseAttributes + public static let name = "db.name" + /// - SeeAlso: DatabaseAttributes + public static let statement = "db.statement" + /// - SeeAlso: DatabaseAttributes + public static let operation = "db.operation" + + /// - SeeAlso: DatabaseAttributes.MSSQLAttributes + public enum MSSQL { + /// - SeeAlso: DatabaseAttributes.MSSQLAttributes + public static let instanceName = "db.mssql.instance_name" + } + + /// - SeeAlso: DatabaseAttributes.CassandraAttributes + public enum Cassandra { + /// - SeeAlso: DatabaseAttributes.CassandraAttributes + public static let keyspace = "db.cassandra.keyspace" + } + + /// - SeeAlso: DatabaseAttributes.HBaseAttributes + public enum HBase { + /// - SeeAlso: DatabaseAttributes.HBaseAttributes + public static let namespace = "db.hbase.namespace" + } + + /// - SeeAlso: DatabaseAttributes.RedisAttributes + public enum Redis { + /// - SeeAlso: DatabaseAttributes.RedisAttributes + public static let databaseIndex = "db.redis.database_index" + } + + /// - SeeAlso: DatabaseAttributes.MongoDBAttributes + public enum MongoDB { + /// - SeeAlso: DatabaseAttributes.MongoDBAttributes + public static let collection = "db.mongodb.collection" + } + + /// - SeeAlso: DatabaseAttributes.SQLAttributes + public enum SQL { + /// - SeeAlso: DatabaseAttributes.SQLAttributes + public static let table = "db.sql.table" + } + } +} + +#if swift(>=5.2) +extension SpanAttributes { + /// Semantic database client call attributes. + public var db: DatabaseAttributes { + get { + .init(attributes: self) + } + set { + self = newValue.attributes + } + } +} + +/// Semantic conventions for database client calls as defined in the OpenTelemetry spec. +/// +/// - SeeAlso: [OpenTelemetry: Database attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/database.md) +@dynamicMemberLookup +public struct DatabaseAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + // MARK: - General + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// An identifier for the database management system (DBMS) product being used. See [OpenTelemetry: Database attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/database.md) for a list of well-known identifiers. + public var system: Key { .init(name: SpanAttributeName.Database.system) } + + /// The connection string used to connect to the database. It is recommended to remove embedded credentials. + public var connectionString: Key { .init(name: SpanAttributeName.Database.connectionString) } + + /// Username for accessing the database. + public var user: Key { .init(name: SpanAttributeName.Database.user) } + + /// If no [tech-specific attribute](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/database.md#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". + public var name: Key { .init(name: SpanAttributeName.Database.name) } + + /// The database statement being executed. + /// + /// - Note: The value may be sanitized to exclude sensitive information. + public var statement: Key { .init(name: SpanAttributeName.Database.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. + public var operation: Key { .init(name: SpanAttributeName.Database.operation) } + } + + // MARK: - MSSQL + + /// Semantic MSSQL client call attributes. + public var mssql: MSSQLAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for MSSQL client calls as defined in the OpenTelemetry spec. + public struct MSSQLAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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). + public var instanceName: Key { .init(name: SpanAttributeName.Database.MSSQL.instanceName) } + } + } + + // MARK: - Cassandra + + /// Semantic Cassandra client call attributes. + public var cassandra: CassandraAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for Cassandra client calls as defined in the OpenTelemetry spec. + public struct CassandraAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// The name of the keyspace being accessed. To be used instead of the generic `db.name` attribute. + public var keyspace: Key { .init(name: SpanAttributeName.Database.Cassandra.keyspace) } + } + } + + // MARK: - HBase + + /// Semantic HBase client call attributes. + public var hbase: HBaseAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for HBase client calls as defined in the OpenTelemetry spec. + public struct HBaseAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// The [HBase namespace](https://hbase.apache.org/book.html#_namespace) being accessed. + /// To be used instead of the generic `db.name` attribute. + public var namespace: Key { .init(name: SpanAttributeName.Database.HBase.namespace) } + } + } + + // MARK: - Redis + + /// Semantic Redis client call attributes. + public var redis: RedisAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for Redis client calls as defined in the OpenTelemetry spec. + public struct RedisAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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. + public var databaseIndex: Key { .init(name: SpanAttributeName.Database.Redis.databaseIndex) } + } + } + + // MARK: - MongoDB + + /// Semantic MongoDB client call attributes. + public var mongodb: MongoDBAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for MongoDB client calls as defined in the OpenTelemetry spec. + public struct MongoDBAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// The collection being accessed within the database stated in `db.name`. + public var collection: Key { .init(name: SpanAttributeName.Database.MongoDB.collection) } + } + } + + // MARK: - SQL + + /// Semantic SQL client call attributes. + public var sql: SQLAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for SQL client calls as defined in the OpenTelemetry spec. + public struct SQLAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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. + public var table: Key { .init(name: SpanAttributeName.Database.SQL.table) } + } + } +} +#endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUser.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUserSemantics.swift similarity index 86% rename from Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUser.swift rename to Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUserSemantics.swift index 9c3d71c..c8e33e8 100644 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUser.swift +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+EndUserSemantics.swift @@ -14,13 +14,13 @@ import Tracing extension SpanAttributeName { - /// - See: EndUserAttributes + /// - SeeAlso: EndUserAttributes public enum EndUser { - /// - See: EndUserAttributes + /// - SeeAlso: EndUserAttributes public static let id = "enduser.id" - /// - See: EndUserAttributes + /// - SeeAlso: EndUserAttributes public static let role = "enduser.role" - /// - See: EndUserAttributes + /// - SeeAlso: EndUserAttributes public static let scope = "enduser.scope" } } @@ -40,7 +40,7 @@ extension SpanAttributes { /// End-user-related semantic conventions as defined in the OpenTelemetry spec. /// -/// - SeeAlso: [OpenTelemetry: General identity attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md#general-identity-attributes) (as of August 2020) +/// - SeeAlso: [OpenTelemetry: General identity attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/span-general.md#general-identity-attributes) @dynamicMemberLookup public struct EndUserAttributes: SpanAttributeNamespace { public var attributes: SpanAttributes diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+ExceptionSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+ExceptionSemantics.swift new file mode 100644 index 0000000..fea71fa --- /dev/null +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+ExceptionSemantics.swift @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift Distributed Tracing project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Tracing + +extension SpanAttributeName { + /// - SeeAlso: ExceptionAttributes + public enum Exception { + /// - SeeAlso: ExceptionAttributes + public static let type = "exception.type" + /// - SeeAlso: ExceptionAttributes + public static let message = "exception.message" + /// - SeeAlso: ExceptionAttributes + public static let stacktrace = "exception.stacktrace" + /// - SeeAlso: ExceptionAttributes + public static let escaped = "exception.escaped" + } +} + +#if swift(>=5.2) +extension SpanAttributes { + /// Semantic exception attributes. + public var exception: ExceptionAttributes { + get { + .init(attributes: self) + } + set { + self = newValue.attributes + } + } +} + +/// Semantic conventions for reporting a single exception associated with a span as defined in the OpenTelemetry spec. +/// +/// - SeeAlso: [OpenTelemetry: Exception attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/exceptions.md) +@dynamicMemberLookup +public struct ExceptionAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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. + public var type: Key { .init(name: SpanAttributeName.Exception.type) } + + /// The exception message. + public var message: Key { .init(name: SpanAttributeName.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. + public var stacktrace: Key { .init(name: SpanAttributeName.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. + public var escaped: Key { .init(name: SpanAttributeName.Exception.escaped) } + } +} +#endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+FaaSSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+FaaSSemantics.swift new file mode 100644 index 0000000..498e9c6 --- /dev/null +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+FaaSSemantics.swift @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift Distributed Tracing project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Tracing + +extension SpanAttributeName { + /// - SeeAlso: FaaSAttributes + public enum FaaS { + /// - SeeAlso: FaaSAttributes + public static let trigger = "faas.trigger" + /// - SeeAlso: FaaSAttributes + public static let execution = "faas.execution" + /// - SeeAlso: FaaSAttributes + public static let time = "faas.time" + /// - SeeAlso: FaaSAttributes + public static let cron = "faas.cron" + /// - SeeAlso: FaaSAttributes + public static let coldstart = "faas.coldstart" + /// - SeeAlso: FaaSAttributes + public static let invokedName = "faas.invoked_name" + /// - SeeAlso: FaaSAttributes + public static let invokedProvider = "faas.invoked_provider" + /// - SeeAlso: FaaSAttributes + public static let invokedRegion = "faas.invoked_region" + + /// - SeeAlso: FaaSAttributes.DocumentAttributes + public enum Document { + /// - SeeAlso: FaaSAttributes.DocumentAttributes + public static let collection = "faas.document.collection" + /// - SeeAlso: FaaSAttributes.DocumentAttributes + public static let operation = "faas.document.operation" + /// - SeeAlso: FaaSAttributes.DocumentAttributes + public static let time = "faas.document.time" + /// - SeeAlso: FaaSAttributes.DocumentAttributes + public static let name = "faas.document.name" + } + } +} + +#if swift(>=5.2) +extension SpanAttributes { + /// Semantic exception attributes. + public var faas: FaaSAttributes { + get { + .init(attributes: self) + } + set { + self = newValue.attributes + } + } +} + +/// Semantic conventions for reporting a single exception associated with a span as defined in the OpenTelemetry spec. +/// +/// - SeeAlso: [OpenTelemetry: FaaS attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/faas.md) +@dynamicMemberLookup +public struct FaaSAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + // MARK: - General + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// Type of the trigger on which the function is executed. See [OpenTelemetry: Function Trigger Type](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/faas.md#function-trigger-type) for possible values. + public var trigger: Key { .init(name: SpanAttributeName.FaaS.trigger) } + + /// The execution ID of the current function execution. + public var execution: Key { .init(name: SpanAttributeName.FaaS.execution) } + + /// 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). + public var time: Key { .init(name: SpanAttributeName.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). + public var cron: Key { .init(name: SpanAttributeName.FaaS.cron) } + + /// A boolean that is true if the serverless function is executed for the first time (aka cold-start). + public var coldstart: Key { .init(name: SpanAttributeName.FaaS.coldstart) } + + /// The name of the invoked function. + public var invokedName: Key { .init(name: SpanAttributeName.FaaS.invokedName) } + + /// The cloud provider of the invoked function. + /// See [OpenTelemetry: Outgoing Invocations](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/faas.md#outgoing-invocations) for possible values. + public var invokedProvider: Key { .init(name: SpanAttributeName.FaaS.invokedProvider) } + + /// The cloud region of the invoked function. + public var invokedRegion: Key { .init(name: SpanAttributeName.FaaS.invokedRegion) } + } + + // MARK: - Document + + /// Semantic conventions for HTTP server spans. + public var document: DocumentAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic Convention for FaaS triggered as a response to some data source operation such as a database or filesystem read/write. + /// + /// - SeeAlso: [OpenTelemetry: Datasource attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/faas.md#datasource) + public struct DocumentAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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. + public var collection: Key { .init(name: SpanAttributeName.FaaS.Document.collection) } + + /// Describes the type of the operation that was performed on the data. See [OpenTelemetry: Operation Type](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/faas.md#datasource) for possible values. + public var operation: Key { .init(name: SpanAttributeName.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). + public var time: Key { .init(name: SpanAttributeName.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. + public var name: Key { .init(name: SpanAttributeName.FaaS.Document.name) } + } + } +} +#endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+GRPCSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+GRPCSemantics.swift deleted file mode 100644 index 82f65d0..0000000 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+GRPCSemantics.swift +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Distributed Tracing open source project -// -// Copyright (c) 2020 Apple Inc. and the Swift Distributed Tracing project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Tracing - -extension SpanAttributeName { - /// - See: GRPCAttributes - public enum GRPC { - /// - See: GRPCAttributes - public static let messageType = "message.type" - /// - See: GRPCAttributes - public static let messageID = "message.id" - /// - See: GRPCAttributes - public static let messageCompressedSize = "message.compressed_size" - /// - See: GRPCAttributes - public static let messageUncompressedSize = "message.uncompressed_size" - } -} - -#if swift(>=5.2) -extension SpanAttributes { - /// Semantic conventions for gRPC spans. - public var gRPC: GRPCAttributes { - get { - .init(attributes: self) - } - set { - self = newValue.attributes - } - } -} - -/// Semantic conventions for gRPC spans as defined in the OpenTelemetry spec. -/// -/// - SeeAlso: [OpenTelemetry: Semantic conventions for gRPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md#grpc) (as of August 2020) -@dynamicMemberLookup -public struct GRPCAttributes: SpanAttributeNamespace { - public var attributes: SpanAttributes - - public init(attributes: SpanAttributes) { - self.attributes = attributes - } - - public struct NestedSpanAttributes: NestedSpanAttributesProtocol { - public init() {} - - /// The type of message, e.g. "SENT" or "RECEIVED". - public var messageType: Key { .init(name: SpanAttributeName.GRPC.messageType) } - - /// The message id calculated as two different counters starting from 1, one for sent messages and one for received messages. - public var messageID: Key { .init(name: SpanAttributeName.GRPC.messageID) } - - /// The compressed message size in bytes. - public var messageCompressedSize: Key { - .init(name: SpanAttributeName.GRPC.messageCompressedSize) - } - - /// The uncompressed message size in bytes. - public var messageUncompressedSize: Key { - .init(name: SpanAttributeName.GRPC.messageUncompressedSize) - } - } -} -#endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+HTTPSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+HTTPSemantics.swift index 14959f6..08b589c 100644 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+HTTPSemantics.swift +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+HTTPSemantics.swift @@ -14,40 +14,42 @@ import Tracing extension SpanAttributeName { - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public enum HTTP { - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let method = "http.method" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let url = "http.url" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let target = "http.target" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let host = "http.host" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let scheme = "http.scheme" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let statusCode = "http.status_code" - /// - See: HTTPAttributes - public static let statusText = "http.status_text" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let flavor = "http.flavor" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let userAgent = "http.user_agent" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let requestContentLength = "http.request_content_length" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let requestContentLengthUncompressed = "http.request_content_length_uncompressed" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let responseContentLength = "http.response_content_length" - /// - See: HTTPAttributes + /// - SeeAlso: HTTPAttributes public static let responseContentLengthUncompressed = "http.response_content_length_uncompressed" - /// - See: HTTPAttributes - public static let serverName = "http.server_name" - /// - See: HTTPAttributes - public static let serverRoute = "http.route" - /// - See: HTTPAttributes - public static let serverClientIP = "http.client_ip" + + /// - SeeAlso: HTTPAttributes.ServerAttributes + public enum Server { + /// - SeeAlso: HTTPAttributes.ServerAttributes + public static let name = "http.server_name" + /// - SeeAlso: HTTPAttributes.ServerAttributes + public static let route = "http.route" + /// - SeeAlso: HTTPAttributes.ServerAttributes + public static let clientIP = "http.client_ip" + } } } @@ -66,7 +68,7 @@ extension SpanAttributes { /// Semantic conventions for HTTP spans as defined in the OpenTelemetry spec. /// -/// - SeeAlso: [OpenTelemetry: Semantic conventions for HTTP spans](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/http.md) (as of August 2020) +/// - SeeAlso: [OpenTelemetry: Semantic conventions for HTTP spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/http.md) @dynamicMemberLookup public struct HTTPAttributes: SpanAttributeNamespace { public var attributes: SpanAttributes @@ -75,6 +77,8 @@ public struct HTTPAttributes: SpanAttributeNamespace { self.attributes = attributes } + // MARK: - General + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { public init() {} @@ -97,10 +101,10 @@ public struct HTTPAttributes: SpanAttributeNamespace { /// HTTP response status code. E.g. 200. public var statusCode: Key { .init(name: SpanAttributeName.HTTP.statusCode) } - /// HTTP reason phrase. E.g. "OK". - public var statusText: Key { .init(name: SpanAttributeName.HTTP.statusText) } - /// Kind of HTTP protocol used: "1.0", "1.1", "2", "SPDY" or "QUIC". + /// + /// - 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. public var flavor: Key { .init(name: SpanAttributeName.HTTP.flavor) } /// Value of the HTTP User-Agent header sent by the client. @@ -129,18 +133,45 @@ public struct HTTPAttributes: SpanAttributeNamespace { public var responseContentLengthUncompressed: Key { .init(name: SpanAttributeName.HTTP.responseContentLengthUncompressed) } + } - /// 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.hostName` should be used instead). - public var serverName: Key { .init(name: SpanAttributeName.HTTP.serverName) } + // MARK: - Server - /// The matched route (path template). E.g. "/users/:userID?". - public var serverRoute: Key { .init(name: SpanAttributeName.HTTP.serverRoute) } + /// Semantic conventions for HTTP server spans. + public var server: ServerAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for HTTP Server spans as defined in the OpenTelemetry spec. + /// + /// - SeeAlso: [OpenTelemetry: Semantic conventions for HTTP Server spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/http.md#http-server) + public struct ServerAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } - /// The IP address of the original client behind all proxies, if known (e.g. from X-Forwarded-For). - /// Note that this is not necessarily the same as `net.peerIP`, which would identify the network-level peer, - /// which may be a proxy. - public var serverClientIP: Key { .init(name: SpanAttributeName.HTTP.serverClientIP) } + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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.hostName` should be used instead). + public var name: Key { .init(name: SpanAttributeName.HTTP.Server.name) } + + /// The matched route (path template). E.g. "/users/:userID?". + public var route: Key { .init(name: SpanAttributeName.HTTP.Server.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. + public var clientIP: Key { .init(name: SpanAttributeName.HTTP.Server.clientIP) } + } } } #endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+MessagingSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+MessagingSemantics.swift new file mode 100644 index 0000000..299e89a --- /dev/null +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+MessagingSemantics.swift @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Distributed Tracing open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift Distributed Tracing project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Tracing + +extension SpanAttributeName { + /// - SeeAlso: MessagingAttributes + public enum Messaging { + /// - SeeAlso: MessagingAttributes + public static let system = "messaging.system" + /// - SeeAlso: MessagingAttributes + public static let destination = "messaging.destination" + /// - SeeAlso: MessagingAttributes + public static let destinationKind = "messaging.destination_kind" + /// - SeeAlso: MessagingAttributes + public static let tempDestination = "messaging.temp_destination" + /// - SeeAlso: MessagingAttributes + public static let `protocol` = "messaging.protocol" + /// - SeeAlso: MessagingAttributes + public static let protocolVersion = "messaging.protocol_version" + /// - SeeAlso: MessagingAttributes + public static let url = "messaging.url" + /// - SeeAlso: MessagingAttributes + public static let messageID = "messaging.message_id" + /// - SeeAlso: MessagingAttributes + public static let conversationID = "messaging.conversation_id" + /// - SeeAlso: MessagingAttributes + public static let messagePayloadSizeBytes = "messaging.message_payload_size_bytes" + /// - SeeAlso: MessagingAttributes + public static let messagePayloadCompressedSizeBytes = "messaging.message_payload_compressed_size_bytes" + /// - SeeAlso: MessagingAttributes + public static let operation = "messaging.operation" + + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public enum Kafka { + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public static let messageKey = "messaging.kafka.message_key" + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public static let consumerGroup = "messaging.kafka.consumer_group" + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public static let clientID = "messaging.kafka.client_id" + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public static let partition = "messaging.kafka.partition" + /// - SeeAlso: MessagingAttributes.KafkaAttributes + public static let tombstone = "messaging.kafka.tombstone" + } + } +} + +#if swift(>=5.2) +extension SpanAttributes { + /// Semantic conventions for messaging system spans. + public var messaging: MessagingAttributes { + get { + .init(attributes: self) + } + set { + self = newValue.attributes + } + } +} + +/// Semantic conventions for messaging system spans as defined in the OpenTelemetry spec. +/// +/// - SeeAlso: [OpenTelemetry: Messaging Attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/messaging.md#messaging-attributes) +@dynamicMemberLookup +public struct MessagingAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + // MARK: - General + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// A string identifying the messaging system. + public var system: Key { .init(name: SpanAttributeName.Messaging.system) } + + /// The message destination name. This might be equal to the span name but is required nevertheless. + public var destination: Key { .init(name: SpanAttributeName.Messaging.destination) } + + /// The kind of message destination. + public var destinationKind: Key { .init(name: SpanAttributeName.Messaging.destinationKind) } + + /// A boolean that is true if the message destination is temporary. + /// + /// - SeeAlso: [OpenTelemetry: Temporary Destinations](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/messaging.md#temporary-destinations) + public var tempDestination: Key { .init(name: SpanAttributeName.Messaging.tempDestination) } + + /// The name of the transport protocol. + public var `protocol`: Key { .init(name: SpanAttributeName.Messaging.protocol) } + + /// The version of the transport protocol. + public var protocolVersion: Key { .init(name: SpanAttributeName.Messaging.protocolVersion) } + + /// The connection string. + public var url: Key { .init(name: SpanAttributeName.Messaging.url) } + + /// A value used by the messaging system as an identifier for the message, represented as a string. + public var messageID: Key { .init(name: SpanAttributeName.Messaging.messageID) } + + /// The conversation ID identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID". + /// + /// - SeeAlso: [OpenTelemetry: Conversations](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/messaging.md#conversations) + public var conversationID: Key { .init(name: SpanAttributeName.Messaging.conversationID) } + + /// 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. + public var messagePayloadSizeBytes: Key { + .init(name: SpanAttributeName.Messaging.messagePayloadSizeBytes) + } + + /// The compressed size of the message payload in bytes. + public var messagePayloadCompressedSizeBytes: Key { + .init(name: SpanAttributeName.Messaging.messagePayloadCompressedSizeBytes) + } + + /// A string identifying the kind of message consumption as defined in the [Operation names](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/messaging.md#operation-names) section. + /// If the operation is "send", this attribute MUST NOT be set, since the operation can be inferred from the span kind in that case. + public var operation: Key { .init(name: SpanAttributeName.Messaging.operation) } + } + + // MARK: - Kafka + + /// Semantic conventions for Kafka spans. + public var kafka: KafkaAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic conventions for Kafka spans as defined in the OpenTelemetry spec. + /// + /// - SeeAlso: [OpenTelemetry: Kafka Attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/messaging.md#apache-kafka) + public struct KafkaAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// 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. + public var messageKey: Key { .init(name: SpanAttributeName.Messaging.Kafka.messageKey) } + + /// Name of the Kafka Consumer Group that is handling the message. Only applies to consumers, not producers. + public var consumerGroup: Key { .init(name: SpanAttributeName.Messaging.Kafka.consumerGroup) } + + /// Client Id for the Consumer or Producer that is handling the message. + public var clientID: Key { .init(name: SpanAttributeName.Messaging.Kafka.clientID) } + + /// Partition the message is sent to. + public var partition: Key { .init(name: SpanAttributeName.Messaging.Kafka.partition) } + + /// A boolean that is true if the message is a tombstone. + public var tombstone: Key { .init(name: SpanAttributeName.Messaging.Kafka.tombstone) } + } + } +} +#endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+NetSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+NetSemantics.swift index cbe77df..902fc84 100644 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+NetSemantics.swift +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+NetSemantics.swift @@ -14,22 +14,30 @@ import Tracing extension SpanAttributeName { - /// - See: NetAttributes + /// - SeeAlso: NetAttributes public enum Net { - /// - See: NetAttributes + /// - SeeAlso: NetAttributes public static let transport = "net.transport" - /// - See: NetAttributes - public static let peerIP = "net.peer.ip" - /// - See: NetAttributes - public static let peerPort = "net.peer.port" - /// - See: NetAttributes - public static let peerName = "net.peer.name" - /// - See: NetAttributes - public static let hostIP = "net.host.ip" - /// - See: NetAttributes - public static let hostPort = "net.host.port" - /// - See: NetAttributes - public static let hostName = "net.host.name" + + /// - SeeAlso: NetAttributes.PeerAttributes + public enum Peer { + /// - SeeAlso: NetAttributes.PeerAttributes + public static let ip = "net.peer.ip" + /// - SeeAlso: NetAttributes.PeerAttributes + public static let port = "net.peer.port" + /// - SeeAlso: NetAttributes.PeerAttributes + public static let name = "net.peer.name" + } + + /// - SeeAlso: NetAttributes.HostAttributes + public enum Host { + /// - SeeAlso: NetAttributes.HostAttributes + public static let ip = "net.host.ip" + /// - SeeAlso: NetAttributes.HostAttributes + public static let port = "net.host.port" + /// - SeeAlso: NetAttributes.HostAttributes + public static let name = "net.host.name" + } } } @@ -46,9 +54,9 @@ extension SpanAttributes { } } -/// Network-related semantic conventions as defined in the OpenTelemetry spec. +/// Network related semantic conventions as defined in the OpenTelemetry spec. /// -/// - SeeAlso: [OpenTelemetry: General semantic attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md) (as of August 2020) +/// - SeeAlso: [OpenTelemetry: General semantic attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/span-general.md#general-network-connection-attributes) @dynamicMemberLookup public struct NetAttributes: SpanAttributeNamespace { public var attributes: SpanAttributes @@ -57,29 +65,81 @@ public struct NetAttributes: SpanAttributeNamespace { self.attributes = attributes } + // MARK: - General + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { public init() {} /// Transport protocol used. public var transport: Key { .init(name: SpanAttributeName.Net.transport) } + } + + // MARK: - Peer + + /// Semantic network peer attributes. + public var peer: PeerAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } - /// Remote address of the peer (dotted decimal for IPv4 or RFC5952 for IPv6). - public var peerIP: Key { .init(name: SpanAttributeName.Net.peerIP) } + /// Semantic network peer attributes. + public struct PeerAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes - /// Remote port number as an integer. E.g., 80. - public var peerPort: Key { .init(name: SpanAttributeName.Net.peerPort) } + public init(attributes: SpanAttributes) { + self.attributes = attributes + } - /// Remote hostname or similar. - public var peerName: Key { .init(name: SpanAttributeName.Net.peerName) } + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} - /// Like `peerIP` but for the host IP. Useful in case of a multi-IP host. - public var hostIP: Key { .init(name: SpanAttributeName.Net.hostIP) } + /// Remote address of the peer (dotted decimal for IPv4 or RFC5952 for IPv6). + public var ip: Key { .init(name: SpanAttributeName.Net.Peer.ip) } - /// Like `peerPort` but for the host port. - public var hostPort: Key { .init(name: SpanAttributeName.Net.hostPort) } + /// Remote port number as an integer. E.g., 80. + public var port: Key { .init(name: SpanAttributeName.Net.Peer.port) } - /// Local hostname or similar. - public var hostName: Key { .init(name: SpanAttributeName.Net.hostName) } + /// Remote hostname or similar. + public var name: Key { .init(name: SpanAttributeName.Net.Peer.name) } + } + } + + // MARK: - Host + + /// Semantic network host attributes. + public var host: HostAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic network host attributes. + public struct HostAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// Like `peer.ip` but for the host IP. Useful in case of a multi-IP host. + public var ip: Key { .init(name: SpanAttributeName.Net.Host.ip) } + + /// Like `peer.port` but for the host port. + public var port: Key { .init(name: SpanAttributeName.Net.Host.port) } + + /// Local hostname or similar. + public var name: Key { .init(name: SpanAttributeName.Net.Host.name) } + } } } #endif diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+PeerSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+PeerSemantics.swift index 4df8b27..9f85808 100644 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+PeerSemantics.swift +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+PeerSemantics.swift @@ -14,9 +14,9 @@ import Tracing extension SpanAttributeName { - /// - See: PeerAttributes + /// - SeeAlso: PeerAttributes public enum Peer { - /// - See: PeerAttributes + /// - SeeAlso: PeerAttributes public static let service = "peer.service" } } @@ -36,7 +36,7 @@ extension SpanAttributes { /// Peer-related semantic conventions as defined in the OpenTelemetry spec. /// -/// - SeeAlso: [OpenTelemetry: General remote service attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/span-general.md#general-remote-service-attributes) (as of August 2020) +/// - SeeAlso: [OpenTelemetry: General remote service attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/span-general.md#general-remote-service-attributes) @dynamicMemberLookup public struct PeerAttributes: SpanAttributeNamespace { public var attributes: SpanAttributes diff --git a/Sources/TracingOpenTelemetrySupport/SpanAttribute+RPCSemantics.swift b/Sources/TracingOpenTelemetrySupport/SpanAttribute+RPCSemantics.swift index a41a17b..3cbc22a 100644 --- a/Sources/TracingOpenTelemetrySupport/SpanAttribute+RPCSemantics.swift +++ b/Sources/TracingOpenTelemetrySupport/SpanAttribute+RPCSemantics.swift @@ -14,14 +14,20 @@ import Tracing extension SpanAttributeName { - /// - See: RPCAttributes + /// - SeeAlso: RPCAttributes public enum RPC { - /// - See: RPCAttributes + /// - SeeAlso: RPCAttributes public static let system = "rpc.system" - /// - See: RPCAttributes + /// - SeeAlso: RPCAttributes public static let service = "rpc.service" - /// - See: RPCAttributes + /// - SeeAlso: RPCAttributes public static let method = "rpc.method" + + /// - SeeAlso: RPCAttributes.GRPCAttributes + public enum GRPC { + /// - SeeAlso: RPCAttributes.GRPCAttributes + public static let statusCode = "rpc.grpc.status_code" + } } } @@ -40,7 +46,7 @@ extension SpanAttributes { /// Semantic conventions for RPC spans as defined in the OpenTelemetry spec. /// -/// - SeeAlso: [OpenTelemetry: Semantic conventions for RPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/b70565d5a8a13d26c91fb692879dc874d22c3ac8/specification/trace/semantic_conventions/rpc.md) (as of August 2020) +/// - SeeAlso: [OpenTelemetry: Semantic conventions for RPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/rpc.md) @dynamicMemberLookup public struct RPCAttributes: SpanAttributeNamespace { public var attributes: SpanAttributes @@ -61,5 +67,33 @@ public struct RPCAttributes: SpanAttributeNamespace { /// The name of the method being called, must be equal to the $method part in the span name. public var method: Key { .init(name: SpanAttributeName.RPC.method) } } + + /// Semantic conventions for gRPC spans. + public var gRPC: GRPCAttributes { + get { + .init(attributes: self.attributes) + } + set { + self.attributes = newValue.attributes + } + } + + /// Semantic concentions for gRPC spans as defined in the OpenTelemetry spec. + /// + /// - SeeAlso: [OpenTelemetry: Semantic conventions for gRPC spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v0.7.0/specification/trace/semantic_conventions/rpc.md#grpc) + public struct GRPCAttributes: SpanAttributeNamespace { + public var attributes: SpanAttributes + + public init(attributes: SpanAttributes) { + self.attributes = attributes + } + + public struct NestedSpanAttributes: NestedSpanAttributesProtocol { + public init() {} + + /// The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request. + public var statusCode: Key { .init(name: SpanAttributeName.RPC.GRPC.statusCode) } + } + } } #endif diff --git a/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests+XCTest.swift b/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests+XCTest.swift index 5d53a6b..83a3726 100644 --- a/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests+XCTest.swift +++ b/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests+XCTest.swift @@ -25,7 +25,7 @@ extension SpanAttributeSemanticsTests { @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (SpanAttributeSemanticsTests) -> () throws -> Void)] { return [ - ("testDynamicMemberLookup", testDynamicMemberLookup), + ("testDynamicMemberLookupForEachNamespace", testDynamicMemberLookupForEachNamespace), ] } } diff --git a/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests.swift b/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests.swift index c68e626..80223eb 100644 --- a/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests.swift +++ b/Tests/TracingOpenTelemetrySupportTests/SpanAttributeSemanticsTests.swift @@ -18,21 +18,72 @@ import TracingOpenTelemetrySupport import XCTest final class SpanAttributeSemanticsTests: XCTestCase { - func testDynamicMemberLookup() { + func testDynamicMemberLookupForEachNamespace() { #if swift(>=5.2) var attributes: SpanAttributes = [:] + attributes.db.system = "postgresql" + XCTAssertEqual(attributes.db.system, "postgresql") + + attributes.db.mssql.instanceName = "test" + XCTAssertEqual(attributes.db.mssql.instanceName, "test") + + attributes.db.cassandra.keyspace = "test" + XCTAssertEqual(attributes.db.cassandra.keyspace, "test") + + attributes.db.hbase.namespace = "test" + XCTAssertEqual(attributes.db.hbase.namespace, "test") + + attributes.db.redis.databaseIndex = 1 + XCTAssertEqual(attributes.db.redis.databaseIndex, 1) + + attributes.db.mongodb.collection = "test" + XCTAssertEqual(attributes.db.mongodb.collection, "test") + + attributes.db.sql.table = "test" + XCTAssertEqual(attributes.db.sql.table, "test") + + attributes.endUser.id = "steve" + XCTAssertEqual(attributes.endUser.id, "steve") + + attributes.exception.type = "SomeError" + XCTAssertEqual(attributes.exception.type, "SomeError") + + attributes.faas.trigger = "datasource" + XCTAssertEqual(attributes.faas.trigger, "datasource") + + attributes.faas.document.collection = "collection" + XCTAssertEqual(attributes.faas.document.collection, "collection") + attributes.http.method = "GET" XCTAssertEqual(attributes.http.method, "GET") - attributes.net.hostPort = 8080 - XCTAssertEqual(attributes.net.hostPort, 8080) + attributes.http.server.route = "/users/:userID?" + XCTAssertEqual(attributes.http.server.route, "/users/:userID?") + + attributes.messaging.system = "kafka" + XCTAssertEqual(attributes.messaging.system, "kafka") + + attributes.messaging.kafka.partition = 2 + XCTAssertEqual(attributes.messaging.kafka.partition, 2) + + attributes.net.transport = "IP.TCP" + XCTAssertEqual(attributes.net.transport, "IP.TCP") + + attributes.net.peer.ip = "127.0.0.1" + XCTAssertEqual(attributes.net.peer.ip, "127.0.0.1") + + attributes.net.host.ip = "127.0.0.1" + XCTAssertEqual(attributes.net.host.ip, "127.0.0.1") attributes.peer.service = "hotrod" XCTAssertEqual(attributes.peer.service, "hotrod") - attributes.endUser.id = "steve" - XCTAssertEqual(attributes.endUser.id, "steve") + attributes.rpc.system = "grpc" + XCTAssertEqual(attributes.rpc.system, "grpc") + + attributes.rpc.gRPC.statusCode = 0 + XCTAssertEqual(attributes.rpc.gRPC.statusCode, 0) #endif } }