From ffa4e8e91143ef4fd3c7190436f5c10ff2d07644 Mon Sep 17 00:00:00 2001 From: Liudmila Molkova Date: Fri, 18 Oct 2024 15:07:22 -0700 Subject: [PATCH] Render events with bodies (#1464) --- docs/azure/events.md | 50 +++--- docs/exceptions/exceptions-spans.md | 11 +- docs/feature-flags/feature-flags-logs.md | 8 +- docs/feature-flags/feature-flags-spans.md | 7 + docs/mobile/events.md | 86 +++++---- docs/rpc/rpc-spans.md | 11 +- model/azure/logs.yaml | 164 ++++++++---------- model/device/events.yaml | 13 +- model/event/common.yaml | 1 + model/exceptions/events.yaml | 4 +- model/feature-flag/events.yaml | 4 +- model/feature-flag/logs.yaml | 4 +- model/feature-flag/registry.yaml | 1 + model/rpc/spans.yaml | 7 +- policies/yaml_schema.rego | 12 ++ policies/yaml_schema_test.rego | 8 + .../registry/markdown/attribute_macros.j2 | 17 +- .../markdown/attribute_namespace.md.j2 | 5 +- .../registry/markdown/attribute_table.j2 | 3 +- .../registry/markdown/body_field_table.j2 | 15 ++ templates/registry/markdown/enum_macros.j2 | 13 ++ templates/registry/markdown/event_macros.j2 | 14 +- .../registry/markdown/examples_macros.j2 | 17 ++ templates/registry/markdown/snippet.md.j2 | 23 ++- 24 files changed, 294 insertions(+), 204 deletions(-) create mode 100644 templates/registry/markdown/body_field_table.j2 create mode 100644 templates/registry/markdown/examples_macros.j2 diff --git a/docs/azure/events.md b/docs/azure/events.md index aba71f17d..4a3241308 100644 --- a/docs/azure/events.md +++ b/docs/azure/events.md @@ -7,8 +7,6 @@ Resource Log events. ## Azure Resource Log -### Attributes - @@ -16,39 +14,43 @@ Resource Log events. +**Status:** ![Experimental](https://img.shields.io/badge/-experimental-blue) + The event name MUST be `az.resource.log`. +Describes Azure Resource Log event, see [Azure Resource Log Top-level Schema](https://learn.microsoft.com/azure/azure-monitor/essentials/resource-logs-schema#top-level-common-schema) for more details. + | Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| | [`az.service_request_id`](/docs/attributes-registry/azure.md) | string | The unique identifier of the service request. It's generated by the Azure service and returned with the response. | `00000000-0000-0000-0000-000000000000` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | | [`cloud.resource_id`](/docs/attributes-registry/cloud.md) | string | The [Fully Qualified Azure Resource ID](https://docs.microsoft.com/rest/api/resources/resources/get-by-id) the log is emitted for. | `arn:aws:lambda:REGION:ACCOUNT_ID:function:my-function`; `//run.googleapis.com/projects/PROJECT_ID/locations/LOCATION_ID/services/SERVICE_ID`; `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| [`event.name`](/docs/attributes-registry/event.md) | string | Identifies the class / type of event. [1] | `browser.mouse.click`; `device.app.lifecycle` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -**[1]:** Event names are subject to the same rules as [attribute names](/docs/general/attribute-naming.md). Notably, event names are namespaced to avoid collisions and provide a clean separation of semantics for events in separate domains like browser, mobile, and kubernetes. +**Body fields:** + +| Body Field | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +| `category` | string | The Azure category of the log entry. | `AuditEvent`; `GatewayLogs`; `ApplicationGatewayAccessLog` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `correlation.id` | string | The correlation ID of the log entry. | `607964b6-41a5-4e24-a5db-db7aab3b9b34` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `duration` | int | The duration of the operations in milliseconds. | `1000` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `identity` | undefined | "A JSON blob that describes the identity of the user or application that performed the operation." [1] | | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `operation.name` | string | The name of the operation. | `SecretGet`; `Microsoft.ApiManagement/GatewayLogs`; `ApplicationGatewayAccess` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `operation.version` | string | The version of the operation. | `1.0` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `properties` | undefined | The properties provided in the Azure Resource Log. | | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `result.description` | string | The description of the result. | `The operation was successful`; `The operation failed` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `result.signature` | string | The substatus of associated with the logged event. | `OK` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `result.type` | string | The status associated with the logged event. | `Succeeded`; `Failed`; `Started` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `tenant.id` | string | The tenant ID of the Active Directory tenant that this event is tied to. | `00000000-0000-0000-0000-000000000000` | `Conditionally Required` [2] | ![Experimental](https://img.shields.io/badge/-experimental-blue) | + +**[1]:** Typically, this field includes the authorization and claims or JWT token from Active Directory. + +> [!Warning] +> This field contains sensitive (PII) information. + +**[2]:** if the event is tied to an Active Directory tenant. -### Body Fields - - -| Body Field | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | -|---|---|---|---|---|---| -| `category` | string | The Azure category of the log entry. | `AuditEvent`, `GatewayLogs`, `ApplicationGatewayAccessLog` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `correlation.id` | string | The correlation ID of the log entry. | `607964b6-41a5-4e24-a5db-db7aab3b9b34` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `duration` | int | The duration of the operations in milliseconds. | `1000` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `identity` | string | A JSON blob that describes the identity of the user or application that performed the operation. | `someone` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `operation.name` | string | The name of the operation. | `SecretGet`, `Microsoft.ApiManagement/GatewayLogs`, `ApplicationGatewayAccess` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `operation.version` | string | The version of the operation. | `1.0` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `properties` | keyvaluelist | The properties provided in the Azure Resource Log. | {
  "statusCode": "Created",
  "serviceRequestId": "50d5cddb-8ca0-47ad-9b80-6cde2207f97c"
}
| `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `result.type` | string | The status associated with the logged event. | `Started`, `In Progress`, `Succeeded`, `Failed`, `Active`, `Resolved` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `result.signature` | string | The substatus of associated with the logged event. | `OK` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `result.description` | string | The description of the result. | `The operation was successful`, `The operation failed` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `tenant.id` | string | The tenant ID of the Active Directory tenant that this event is tied to. | `607964b6-41a5-4e24-a5db-db7aab3b9b34` | `Conditionally Required`: if the event is tied to an Active Directory tenant. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | - - -See [Azure Resource Log definition](/model/azure/logs.yaml) for the details. - [DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status diff --git a/docs/exceptions/exceptions-spans.md b/docs/exceptions/exceptions-spans.md index 4fd632587..df00c1b87 100644 --- a/docs/exceptions/exceptions-spans.md +++ b/docs/exceptions/exceptions-spans.md @@ -12,7 +12,7 @@ exceptions associated with spans. - [Recording an Exception](#recording-an-exception) -- [Attributes](#attributes) +- [Exception event](#exception-event) - [Stacktrace Representation](#stacktrace-representation) @@ -38,10 +38,7 @@ try { } ``` -## Attributes - -The table below indicates which attributes should be added to the `Event` and -their types. +## Exception event @@ -50,8 +47,12 @@ their types. +**Status:** ![Stable](https://img.shields.io/badge/-stable-lightgreen) + The event name MUST be `exception`. +This event describes a single exception. + | Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| | [`exception.message`](/docs/attributes-registry/exception.md) | string | The exception message. | `Division by zero`; `Can't convert 'int' object to str implicitly` | `Conditionally Required` [1] | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | diff --git a/docs/feature-flags/feature-flags-logs.md b/docs/feature-flags/feature-flags-logs.md index f9c3bfa4e..3ca0dff07 100644 --- a/docs/feature-flags/feature-flags-logs.md +++ b/docs/feature-flags/feature-flags-logs.md @@ -21,7 +21,7 @@ section of the trace semantic convention for feature flag evaluations. - [Recording an Evaluation](#recording-an-evaluation) -- [Attributes](#attributes) +- [Evaluation event](#evaluation-event) @@ -32,7 +32,7 @@ Feature flag evaluations SHOULD be recorded as attributes on the operations. Evaluations MAY be recorded on "logs" or "events" depending on the context. -## Attributes +## Evaluation event The table below indicates which attributes should be added to the [LogRecord](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.37.0/specification/logs/data-model.md#log-and-event-record-definition) and their types. @@ -44,8 +44,12 @@ The table below indicates which attributes should be added to the +**Status:** ![Experimental](https://img.shields.io/badge/-experimental-blue) + The event name MUST be `feature_flag`. +This event describes feature flag evaluation representation on a Log Record. + | Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| | [`feature_flag.key`](/docs/attributes-registry/feature-flag.md) | string | The unique identifier of the feature flag. | `logo-color` | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | diff --git a/docs/feature-flags/feature-flags-spans.md b/docs/feature-flags/feature-flags-spans.md index 842045e72..8ca86c38b 100644 --- a/docs/feature-flags/feature-flags-spans.md +++ b/docs/feature-flags/feature-flags-spans.md @@ -18,6 +18,7 @@ To record an evaluation outside of a transaction context, consider - [Motivation](#motivation) - [Overview](#overview) - [Convention](#convention) + - [Evaluation event](#evaluation-event) @@ -41,6 +42,8 @@ It's intended to be vendor neutral and provide flexibility for current and futur A flag evaluation SHOULD be recorded as an Event on the span during which it occurred. +### Evaluation event + @@ -48,8 +51,12 @@ A flag evaluation SHOULD be recorded as an Event on the span during which it occ +**Status:** ![Experimental](https://img.shields.io/badge/-experimental-blue) + The event name MUST be `feature_flag`. +This event describes feature flag evaluation. + | Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| | [`feature_flag.key`](/docs/attributes-registry/feature-flag.md) | string | The unique identifier of the feature flag. | `logo-color` | `Required` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | diff --git a/docs/mobile/events.md b/docs/mobile/events.md index c7d0ce458..d9aa669fc 100644 --- a/docs/mobile/events.md +++ b/docs/mobile/events.md @@ -9,25 +9,16 @@ events on mobile platforms. All mobile events MUST use a namespace of - [Lifecycle instrumentation](#lifecycle-instrumentation) - - [Event details](#event-details) + - [Device app lifecycle event](#device-app-lifecycle-event) ## Lifecycle instrumentation This section defines how to apply semantic conventions when instrumenting -application lifecycle. This event is meant to be used in conjunction with -`os.name` [resource semantic convention](/docs/resource/os.md) to identify the -mobile operating system (e.g. Android, iOS). +application lifecycle. -The following table describes the payload fields that MUST -be used to describe the state of the application at the time of the event. - -The `android.state` and `ios.state` fields are mutually exclusive and MUST -NOT be used together, each field MUST be used with its corresponding -`os.name` [resource semantic convention](/docs/resource/os.md) value. - -### Event details +### Device app lifecycle event @@ -36,45 +27,48 @@ NOT be used together, each field MUST be used with its corresponding +**Status:** ![Experimental](https://img.shields.io/badge/-experimental-blue) + The event name MUST be `device.app.lifecycle`. +This event represents an occurrence of a lifecycle transition on Android or iOS platform. + +The event body fields MUST be used to describe the state of the application at the time of the event. +This event is meant to be used in conjunction with `os.name` [resource semantic convention](/docs/resource/os.md) to identify the mobile operating system (e.g. Android, iOS). +The `android.state` and `ios.state` fields are mutually exclusive and MUST NOT be used together, each field MUST be used with its corresponding `os.name` value. + +**Body fields:** + +| Body Field | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +| `android.state` | enum | This attribute represents the state the application has transitioned into at the occurrence of the event. [1] | `created` | `Conditionally Required` if and only if `os.name` is `android` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `ios.state` | enum | This attribute represents the state the application has transitioned into at the occurrence of the event. [2] | `active` | `Conditionally Required` if and only if `os.name` is `ios` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | + +**[1]:** The Android lifecycle states are defined in [Activity lifecycle callbacks](https://developer.android.com/guide/components/activities/activity-lifecycle#lc), and from which the `OS identifiers` are derived. + +**[2]:** The iOS lifecycle states are defined in the [UIApplicationDelegate documentation](https://developer.apple.com/documentation/uikit/uiapplicationdelegate#1656902), and from which the `OS terminology` column values are derived. + +`android.state` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +|---|---|---| +| `background` | Any time after Activity.onPause() or, if the app has no Activity, Context.stopService() has been called when the app was in the foreground state. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `created` | Any time before Activity.onResume() or, if the app has no Activity, Context.startService() has been called in the app for the first time. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `foreground` | Any time after Activity.onResume() or, if the app has no Activity, Context.startService() has been called when the app was in either the created or background states. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | + +`ios.state` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +|---|---|---| +| `active` | The app has become `active`. Associated with UIKit notification `applicationDidBecomeActive`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `background` | The app is now in the background. This value is associated with UIKit notification `applicationDidEnterBackground`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `foreground` | The app is now in the foreground. This value is associated with UIKit notification `applicationWillEnterForeground`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `inactive` | The app is now `inactive`. Associated with UIKit notification `applicationWillResignActive`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | +| `terminate` | The app is about to terminate. Associated with UIKit notification `applicationWillTerminate`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | + - -| Body Field | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | -|---|---|---|---|---|---| -| `android.state` | string | This attribute represents the state the application has transitioned into at the occurrence of the event. [1] | `created` | `Conditionally Required`: if and only if `os.name` is `android` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `ios.state` | string | This attribute represents the state the application has transitioned into at the occurrence of the event. [2] | `active` | `Conditionally Required`: if and only if `os.name` is `ios` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | - -**[1]:** The Android lifecycle states are defined in [Activity lifecycle callbacks](https://developer.android.com/guide/components/activities/activity-lifecycle#lc), and from which the `OS identifiers` are derived. - -**[2]:** The iOS lifecycle states are defined in the [UIApplicationDelegate documentation](https://developer.apple.com/documentation/uikit/uiapplicationdelegate#1656902), and from which the `OS terminology` column values are derived. - -**Additional attribute requirements:** At least one of the following sets of attributes is required: - -* `ios.state` -* `android.state` - -`android.state` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. - -| Value | Description | Stability | -|---|---|---| -| `created` | Any time before Activity.onResume() or, if the app has no Activity, Context.startService() has been called in the app for the first time. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `background` | Any time after Activity.onPause() or, if the app has no Activity, Context.stopService() has been called when the app was in the foreground state. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `foreground` | Any time after Activity.onResume() or, if the app has no Activity, Context.startService() has been called when the app was in either the created or background states. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | - -`ios.state` MUST be one of the following: - -| Value | Description | Stability | -|---|---|---| -| `active` | The app has become `active`. Associated with UIKit notification `applicationDidBecomeActive`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `inactive` | The app is now `inactive`. Associated with UIKit notification `applicationWillResignActive`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `background` | The app is now in the background. This value is associated with UIKit notification `applicationDidEnterBackground`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `foreground` | The app is now in the foreground. This value is associated with UIKit notification `applicationWillEnterForeground`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | -| `terminate` | The app is about to terminate. Associated with UIKit notification `applicationWillTerminate`. | ![Experimental](https://img.shields.io/badge/-experimental-blue) | - - [DocumentStatus]: https://opentelemetry.io/docs/specs/otel/document-status diff --git a/docs/rpc/rpc-spans.md b/docs/rpc/rpc-spans.md index c4fab776f..bb6b08b36 100644 --- a/docs/rpc/rpc-spans.md +++ b/docs/rpc/rpc-spans.md @@ -19,6 +19,7 @@ This document defines how to describe remote procedure calls - [Client attributes](#client-attributes) - [Server attributes](#server-attributes) - [Events](#events) + - [Message event](#message-event) - [Distinction from HTTP spans](#distinction-from-http-spans) - [Semantic Conventions for specific RPC technologies](#semantic-conventions-for-specific-rpc-technologies) @@ -242,9 +243,7 @@ different processes could be listening on TCP port 12345 and UDP port 12345. ### Events -In the lifetime of an RPC stream, an event for each message sent/received on -client and server spans SHOULD be created. In case of unary calls only one sent -and one received message will be recorded for both client and server spans. +#### Message event @@ -253,8 +252,14 @@ and one received message will be recorded for both client and server spans. +**Status:** ![Experimental](https://img.shields.io/badge/-experimental-blue) + The event name MUST be `rpc.message`. +Describes a message sent or received within the context of an RPC call. + +In the lifetime of an RPC stream, an event for each message sent/received on client and server spans SHOULD be created. In case of unary calls only one sent and one received message will be recorded for both client and server spans. + | Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| | [`rpc.message.compressed_size`](/docs/attributes-registry/rpc.md) | int | Compressed size of the message in bytes. | | `Recommended` | ![Experimental](https://img.shields.io/badge/-experimental-blue) | diff --git a/model/azure/logs.yaml b/model/azure/logs.yaml index da061e5f3..4a5e3fc22 100644 --- a/model/azure/logs.yaml +++ b/model/azure/logs.yaml @@ -12,91 +12,79 @@ groups: - ref: cloud.resource_id brief: The [Fully Qualified Azure Resource ID](https://docs.microsoft.com/rest/api/resources/resources/get-by-id) the log is emitted for. note: "" - - ref: event.name - # Future Note: When the build tools support this definition please uncomment and validate the details - # included here and what has been added to the manual markdown table - # body: - # fields: - # - id: category - # type: string - # stability: experimental - # brief: "The Azure category of the log entry." - # examples: - # - AuditEvent - # - GatewayLogs - # - ApplicationGatewayAccessLog - # - id: correlation.id - # type: string - # stability: experimental - # brief: "The correlation ID of the log entry." - # examples: - # - 607964b6-41a5-4e24-a5db-db7aab3b9b34 - # - id: duration - # type: int - # stability: experimental - # brief: "The duration of the operations in milliseconds." - # examples: - # - 1000 - # - id: identity - # type: string - # stability: experimental - # brief: > - # "A JSON blob that describes the identity of the user or application that performed the operation." - # note: > - # Typically, this field includes the authorization and claims or JWT token from Active Directory. - # > Warning: - # > this field contains sensitive (PII) information." - # requirement_level: opt-in - # examples: - # - "someone" - # - id: operation.name - # type: string - # stability: experimental - # brief: "The name of the operation." - # examples: - # - SecretGet - # - Microsoft.ApiManagement/GatewayLogs - # - ApplicationGatewayAccess - # - id: operation.version - # type: string - # stability: experimental - # brief: "The version of the operation." - # examples: - # - "1.0" - # - id: properties - # type: KeyValueList # note: this is not a supported type in the current build tools - # stability: experimental - # brief: "The properties provided in the Azure Resource Log." - # examples: {
  "statusCode": "Created",
  "serviceRequestId": "50d5cddb-8ca0-47ad-9b80-6cde2207f97c"
}
- # - id: result.type - # type: string - # stability: experimental - # brief: "The status associated with the logged event." - # examples: - # - "Started" - # - "In Progress" - # - "Succeeded" - # - "Failed" - # - "Active" - # - "Resolved" - # - id: result.signature - # type: string - # stability: experimental - # brief: "The substatus of associated with the logged event. " - # examples: - # - "OK" - # - id: result.description - # type: string - # stability: experimental - # brief: "The description of the result." - # examples: - # - "The operation was successful" - # - "The operation failed" - # - id: tenant.id - # type: string - # stability: experimental - # brief: "The tenant ID of the Active Directory tenant that this event is tied to." - # requirement_level: - # conditionally_required: "if the event is tied to an Active Directory tenant." - # examples: - # - "00000000-0000-0000-0000-000000000000" + body: + id: az.resource.log + requirement_level: recommended + type: map + fields: + - id: category + type: string + stability: experimental + brief: "The Azure category of the log entry." + requirement_level: recommended + examples: ["AuditEvent", "GatewayLogs", "ApplicationGatewayAccessLog"] + - id: correlation.id + type: string + requirement_level: recommended + stability: experimental + brief: "The correlation ID of the log entry." + examples: ["607964b6-41a5-4e24-a5db-db7aab3b9b34"] + - id: duration + type: int + stability: experimental + requirement_level: recommended + brief: "The duration of the operations in milliseconds." + examples: [1000] + - id: identity + type: undefined + stability: experimental + brief: > + "A JSON blob that describes the identity of the user or application that performed the operation." + note: | + Typically, this field includes the authorization and claims or JWT token from Active Directory. + + > [!Warning] + > This field contains sensitive (PII) information. + requirement_level: opt_in + - id: operation.name + type: string + stability: experimental + requirement_level: recommended + brief: "The name of the operation." + examples: ["SecretGet", "Microsoft.ApiManagement/GatewayLogs", "ApplicationGatewayAccess"] + - id: operation.version + type: string + stability: experimental + requirement_level: recommended + brief: "The version of the operation." + examples: ["1.0"] + - id: properties + type: undefined + requirement_level: recommended + stability: experimental + brief: The properties provided in the Azure Resource Log. + - id: result.type + type: string + stability: experimental + requirement_level: recommended + brief: The status associated with the logged event. + examples: ["Succeeded", "Failed", "Started"] + - id: result.signature + type: string + stability: experimental + requirement_level: recommended + brief: The substatus of associated with the logged event. + examples: ["OK"] + - id: result.description + type: string + stability: experimental + requirement_level: recommended + brief: "The description of the result." + examples: ["The operation was successful", "The operation failed"] + - id: tenant.id + type: string + stability: experimental + brief: "The tenant ID of the Active Directory tenant that this event is tied to." + requirement_level: + conditionally_required: "if the event is tied to an Active Directory tenant." + examples: ["00000000-0000-0000-0000-000000000000"] diff --git a/model/device/events.yaml b/model/device/events.yaml index 6b980e033..36f4f9885 100644 --- a/model/device/events.yaml +++ b/model/device/events.yaml @@ -6,9 +6,16 @@ groups: brief: > This event represents an occurrence of a lifecycle transition on Android or iOS platform. note: > - This event identifies the fields that are common to all lifecycle events for android and iOS using - the `android.state` and `ios.state` fields. The `android.state` and `ios.state` attributes are - mutually exclusive. + The event body fields MUST be used to describe the state of the application + at the time of the event. + + This event is meant to be used in conjunction with `os.name` + [resource semantic convention](/docs/resource/os.md) to identify the + mobile operating system (e.g. Android, iOS). + + The `android.state` and `ios.state` fields are mutually exclusive and MUST + NOT be used together, each field MUST be used with its corresponding + `os.name` value. body: id: device_lifecycle_state type: map diff --git a/model/event/common.yaml b/model/event/common.yaml index b07e25a8d..8e68c5a90 100644 --- a/model/event/common.yaml +++ b/model/event/common.yaml @@ -1,6 +1,7 @@ groups: - id: event type: attribute_group + stability: experimental brief: > This document defines attributes for Events represented using Log Records. attributes: diff --git a/model/exceptions/events.yaml b/model/exceptions/events.yaml index 4078f0715..88ff5f387 100644 --- a/model/exceptions/events.yaml +++ b/model/exceptions/events.yaml @@ -1,10 +1,10 @@ groups: - id: trace-exception name: exception + stability: stable type: event brief: > - This document defines the attributes used to - report a single exception associated with a span. + This event describes a single exception. attributes: - ref: exception.type requirement_level: diff --git a/model/feature-flag/events.yaml b/model/feature-flag/events.yaml index 8aae49338..0248d07ce 100644 --- a/model/feature-flag/events.yaml +++ b/model/feature-flag/events.yaml @@ -1,10 +1,10 @@ groups: - id: feature_flag type: event + stability: experimental name: feature_flag brief: > - This semantic convention defines the attributes used to represent a - feature flag evaluation as an event. + This event describes feature flag evaluation. attributes: - ref: feature_flag.key requirement_level: required diff --git a/model/feature-flag/logs.yaml b/model/feature-flag/logs.yaml index 4530096de..030c0c11a 100644 --- a/model/feature-flag/logs.yaml +++ b/model/feature-flag/logs.yaml @@ -1,10 +1,10 @@ groups: - id: log-feature_flag type: event + stability: experimental name: feature_flag brief: > - This document defines attributes for feature flag evaluations - represented using Log Records. + This event describes feature flag evaluation representation on a Log Record. attributes: - ref: feature_flag.key - ref: feature_flag.provider_name diff --git a/model/feature-flag/registry.yaml b/model/feature-flag/registry.yaml index 1533d685e..380ef79d5 100644 --- a/model/feature-flag/registry.yaml +++ b/model/feature-flag/registry.yaml @@ -1,6 +1,7 @@ groups: - id: registry.feature_flag type: attribute_group + stability: experimental display_name: Feature Flag Attributes brief: > This document defines attributes for Feature Flags. diff --git a/model/rpc/spans.yaml b/model/rpc/spans.yaml index caf7a2747..07e418d06 100644 --- a/model/rpc/spans.yaml +++ b/model/rpc/spans.yaml @@ -100,8 +100,13 @@ groups: - id: rpc.message type: event + stability: experimental name: rpc.message - brief: "RPC received/sent message." + brief: Describes a message sent or received within the context of an RPC call. + note: > + In the lifetime of an RPC stream, an event for each message sent/received on + client and server spans SHOULD be created. In case of unary calls only one sent + and one received message will be recorded for both client and server spans. attributes: - ref: rpc.message.type requirement_level: recommended diff --git a/policies/yaml_schema.rego b/policies/yaml_schema.rego index 4d09e4d59..c8e40680c 100644 --- a/policies/yaml_schema.rego +++ b/policies/yaml_schema.rego @@ -48,6 +48,18 @@ deny[yaml_schema_violation(description, group.id, name)] { description := sprintf("Event name '%s' is invalid. Event name %s'", [name, invalid_name_helper]) } +# checks event.name is not referenced in event attributes +deny[yaml_schema_violation(description, group.id, name)] { + group := input.groups[_] + group.type == "event" + name := group.name + + attr := group.attributes[_] + attr.ref == "event.name" + + description := sprintf("Attribute 'event.name' is referenced on event group '%s'. Event name must be provided in 'name' property on the group", [name]) +} + # require resources have names deny[yaml_schema_violation(description, group.id, "")] { group := input.groups[_] diff --git a/policies/yaml_schema_test.rego b/policies/yaml_schema_test.rego index 4a506835e..a6d135f9d 100644 --- a/policies/yaml_schema_test.rego +++ b/policies/yaml_schema_test.rego @@ -24,6 +24,14 @@ test_fails_on_invalid_event_name if { } } +test_fails_on_referenced_event_name_on_event if { + event := [{ "id": "yaml_schema.test", + "type": "event", + "name": "foo", + "attributes": [{"ref": "event.name"}]}] + count(deny) == 1 with input as {"groups": event} +} + test_fails_on_invalid_resource_name if { every name in invalid_names { count(deny) >= 1 with input as {"groups": create_resource(name)} diff --git a/templates/registry/markdown/attribute_macros.j2 b/templates/registry/markdown/attribute_macros.j2 index 7ac1d474c..be39fd1a2 100644 --- a/templates/registry/markdown/attribute_macros.j2 +++ b/templates/registry/markdown/attribute_macros.j2 @@ -1,3 +1,4 @@ +{% import 'examples_macros.j2' as examples %} {% macro type(attribute) %}{%- if attribute.type is mapping %} {%- if attribute.type.members[0].value is string %}string{%- endif %} {%- if attribute.type.members[0].value is int %}int{%- endif %} @@ -19,22 +20,6 @@ {% macro name_with_link(attribute, attribute_registry_base_url, lineage_attributes) %}[{{name(attribute)}}]({{attribute_registry_base_url}}/{{ find_lineage(attribute.name, lineage_attributes) | split_id | list | reject("eq", "registry")| first | kebab_case }}.md){% endmacro %} -{% macro print_examples(examples) %}{%- for e in examples %}{%if loop.first == false %}; {% endif %}`{{ e | trim }}`{%- endfor %}{% endmacro %} - -{% macro examples(attribute) %}{%- if attribute.examples %} -{%- if "[]" in attribute.type and "template" not in attribute.type %} -{%- if attribute.examples is sequence %} -{%- if attribute.examples | select("sequence") | length == 0 %}`{{ attribute.examples | trim }}` -{%- else %}{{ print_examples(attribute.examples) }} -{%- endif %} -{%- else %}`[{{ attribute.examples | trim }}]` -{%- endif %} -{%- elif attribute.examples is sequence %}{{ print_examples(attribute.examples) }} -{%- else %}`{{ attribute.examples | trim }}` -{%- endif %}{%- elif attribute.type is mapping %} -{%- for e in attribute.type.members %}{% if loop.index0 < 3 %}{% if loop.first == false %}; {% endif %}`{{ e.value | trim }}`{% endif %}{%- endfor %} -{%- endif %}{% endmacro %} - {% macro display_name(group) %} {%- if 'display_name' in group %}{{ group.display_name }} {%- else %}{{ group.id | split_id | list | reject("eq", "registry") | join(" ") | title_case | acronym }} Attributes diff --git a/templates/registry/markdown/attribute_namespace.md.j2 b/templates/registry/markdown/attribute_namespace.md.j2 index ca2980520..19f862e35 100644 --- a/templates/registry/markdown/attribute_namespace.md.j2 +++ b/templates/registry/markdown/attribute_namespace.md.j2 @@ -7,6 +7,7 @@ {% import 'notes.j2' as notes %} {% import 'enum_macros.j2' as enums %} {% import 'attribute_macros.j2' as attrs %} +{% import 'examples_macros.j2' as examples %} {%- set my_file_name = ctx.id | lower | kebab_case ~ ".md" -%} {{- template.set_file_name(my_file_name) -}} {%- set groups = namespace(deprecated=[], non_deprecated=[]) -%} @@ -38,8 +39,8 @@ | Attribute | Type | Description | Examples | Stability | |---|---|---|---|---| -{%- for attribute in group.attributes | sort(attribute="name") %} -| {{ attrs.name(attribute) }} | {{ attrs.type(attribute) }} | {{ attribute.brief | trim }}{{ notes.add(attribute.note) }} | {{ attrs.examples(attribute) | trim }} | {{ stability.badge(attribute.stability, attribute.deprecated) | trim }} | +{%- for attribute in group.attributes | sort(attribute="name") %}{% set attr_anchor = attribute.name | kebab_case %} +| {{ attrs.name(attribute) }} | {{ attrs.type(attribute) }} | {{ attribute.brief | trim }}{{ notes.add(attribute.note) }} | {{ examples.format(attribute) | trim }} | {{ stability.badge(attribute.stability, attribute.deprecated) | trim }} | {%- endfor %} {{ notes.render() }} diff --git a/templates/registry/markdown/attribute_table.j2 b/templates/registry/markdown/attribute_table.j2 index e80c3669f..795512fdf 100644 --- a/templates/registry/markdown/attribute_table.j2 +++ b/templates/registry/markdown/attribute_table.j2 @@ -4,9 +4,10 @@ {% import 'attribute_macros.j2' as attrs %} {% import 'enum_macros.j2' as enums %} {% import 'sampling_macros.j2' as sampling %} +{% import 'examples_macros.j2' as examples %} {#- Macro for creating attribute table -#} {% macro generate(attributes, tag_filter, attribute_registry_base_url, lineage_attributes) %}{% if (tag_filter | length == 0) %}{% set filtered_attributes = attributes %}{% else %}{% set filtered_attributes = attributes | selectattr("tag", "in", tag_filter) %}{% endif %}{% if filtered_attributes | length > 0 %}| Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | |---|---|---|---|---|---| -{% for attribute in filtered_attributes | attribute_sort %}| {{ attrs.name_with_link(attribute, attribute_registry_base_url, lineage_attributes) }} | {{ attrs.type(attribute) }} | {{ attribute.brief | trim }}{{ notes.add(attribute.note) }} | {{ attrs.examples(attribute) | trim }} | {{ requirement.render(attribute.requirement_level, notes) | trim }} | {{ stability.badge(attribute.stability, attribute.deprecated) | trim }} | +{% for attribute in filtered_attributes | attribute_sort %}| {{ attrs.name_with_link(attribute, attribute_registry_base_url, lineage_attributes) }} | {{ attrs.type(attribute) }} | {{ attribute.brief | trim }}{{ notes.add(attribute.note) }} | {{ examples.format(attribute) | trim }} | {{ requirement.render(attribute.requirement_level, notes) | trim }} | {{ stability.badge(attribute.stability, attribute.deprecated) | trim }} | {% endfor %}{{ notes.render() }}{{ sampling.snippet(filtered_attributes, attribute_registry_base_url, lineage_attributes) }}{{ enums.tables(filtered_attributes | selectattr("type", "mapping"), notes) }} {% endif %}{% endmacro %} diff --git a/templates/registry/markdown/body_field_table.j2 b/templates/registry/markdown/body_field_table.j2 new file mode 100644 index 000000000..af1ec232c --- /dev/null +++ b/templates/registry/markdown/body_field_table.j2 @@ -0,0 +1,15 @@ +{% import 'requirement.j2' as requirement %} +{% import 'stability.j2' as stability %} +{% import 'notes.j2' as notes %} +{% import 'enum_macros.j2' as enums %} +{% import 'examples_macros.j2' as examples %} +{% macro flatten(fields, ns, depth) %}{% if fields %}{% for f in fields | sort(attribute="id") %} +{% set ns.flat = [ns.flat, [{'field':f,'depth':depth}]] | flatten %}{% if f.fields %}{% set _= flatten(f.fields, ns, depth + 1) %}{% endif %} +{% endfor %}{% endif %}{% endmacro %} +{% macro field_name(field, depth) %}{% set name= " " * 2 * depth ~ '`' ~ field.id ~ '`' %}{% if field.type == "map" %}{{ name ~ ":"}}{% else %}{{ name }}{% endif %}{% endmacro %} +{#- Macro for creating body table -#} +{% macro generate(fields) %}{% if (fields | length > 0) %}{% set ns = namespace(flat=[])%}{% set _ = flatten(fields, ns, 0) %}| Body Field | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +{% for f in ns.flat %}| {{ field_name(f.field, f.depth) }} | {{ f.field.type }} | {{ f.field.brief | trim }}{{ notes.add(f.field.note) }} | {{ examples.format(f.field) | trim }} | {{ requirement.render(f.field.requirement_level, notes) | trim }} | {{ stability.badge(f.field.stability, f.field.deprecated) | trim }} | +{% endfor %}{{ notes.render() }}{{ enums.field_tables(ns.flat | map(attribute="field") | selectattr("type", "eq", "enum"), notes) -}} +{%- endif %}{% endmacro %} diff --git a/templates/registry/markdown/enum_macros.j2 b/templates/registry/markdown/enum_macros.j2 index 4ffc10cbf..6ad5288a7 100644 --- a/templates/registry/markdown/enum_macros.j2 +++ b/templates/registry/markdown/enum_macros.j2 @@ -13,3 +13,16 @@ {% for enum in enums | sort(attribute="name") -%} {{ table(enum, notes) -}} {% endfor %}{% endmacro %} +{% macro field_table(enum, notes) %} +`{{enum.id}}` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used. + +| Value | Description | Stability | +|---|---|---| +{% for espec in enum.members | sort(attribute='value') %} +{%- if filter(espec) == "True" -%} +| `{{ espec.value }}` | {{ (espec.brief or espec.id) | trim }}{{ notes.add(espec.note) }} | {{ stability.badge(espec.stability, espec.deprecated) }} | +{% endif %}{% endfor %}{{ notes.render() }}{% endmacro %} +{% macro field_tables(enums, notes) -%} +{% for enum in enums | sort(attribute="id") -%} +{{ field_table(enum, notes) -}} +{% endfor %}{% endmacro %} \ No newline at end of file diff --git a/templates/registry/markdown/event_macros.j2 b/templates/registry/markdown/event_macros.j2 index 1b03019e0..c6ef8ef6f 100644 --- a/templates/registry/markdown/event_macros.j2 +++ b/templates/registry/markdown/event_macros.j2 @@ -1,4 +1,16 @@ {#- Macros for simplifying creating "Event" documentation. -#} -{% macro header(event) %}{% if event.name %}The event name MUST be `{{ event.name }}`. +{% import 'stability.j2' as stability %} +{% import 'body_field_table.j2' as body_table %} +{% macro header(event) %}**Status:** {{ stability.badge(event.stability, event.deprecated) }} +The event name MUST be `{{ event.name }}`. + +{{ event.brief | trim }} +{%if event.note %} +{{ event.note | trim }} +{% endif %} +{% endmacro %} +{% macro body(body) %}{% if body %}**Body fields:** + +{{ body_table.generate(body.fields) }} {% endif %}{% endmacro %} diff --git a/templates/registry/markdown/examples_macros.j2 b/templates/registry/markdown/examples_macros.j2 new file mode 100644 index 000000000..bbd840d67 --- /dev/null +++ b/templates/registry/markdown/examples_macros.j2 @@ -0,0 +1,17 @@ +{% macro print_examples(examples) %}{%- for e in examples %}{%if loop.first == false %}; {% endif %}`{{ e | trim }}`{%- endfor %}{% endmacro %} + +{% macro format(item) %}{%- if item.examples %} +{%- if "[]" in item.type and "template" not in item.type %} +{%- if item.examples is sequence %} +{%- if item.examples | select("sequence") | length == 0 %}`{{ item.examples | trim }}` +{%- else %}{{ print_examples(item.examples) }} +{%- endif %} +{%- else %}`[{{ item.examples | trim }}]` +{%- endif %} +{%- elif item.examples is sequence %}{{ print_examples(item.examples) }} +{%- else %}`{{ item.examples | trim }}` +{%- endif %}{%- elif item.type is mapping %} +{%- for e in item.type.members %}{% if loop.index0 < 3 %}{% if loop.first == false %}; {% endif %}`{{ e.value | trim }}`{% endif %}{%- endfor %} +{%- elif item.type == "enum" -%} +{%- for e in item.members %}{% if loop.index0 < 3 %}{% if loop.first == false %}; {% endif %}`{{ e.value | trim }}`{% endif %}{%- endfor %} +{%- endif %}{% endmacro %} diff --git a/templates/registry/markdown/snippet.md.j2 b/templates/registry/markdown/snippet.md.j2 index 4751cc65b..6b225c811 100644 --- a/templates/registry/markdown/snippet.md.j2 +++ b/templates/registry/markdown/snippet.md.j2 @@ -8,14 +8,25 @@ {%- import 'event_macros.j2' as event -%} {%- import 'resource_macros.j2' as resource %} -{% if group.type == "event" -%} -{{ event.header(group) }} -{%- elif group.type == "resource" -%} -{{ resource.header(group) }} -{%- elif group.type=="metric" -%} +{% macro generate_event(group) -%} +{{ event.header(group) }}{{ generate_attributes(group) }}{{ event.body(group.body) }}{% endmacro -%} +{%- macro generate_resource(group) -%} +{{ resource.header(group) }}{{ generate_attributes(group) }}{% endmacro -%} +{%- macro generate_metric(group) -%} {{ mt.generate(group) }} +{{ generate_attributes(group) }}{% endmacro -%} +{%- macro generate_attributes(group) -%} +{{ at.generate(group.attributes, tag_filter, attribute_registry_base_url, group.lineage.attributes) }}{% endmacro -%} + +{% if group.type == "event" -%} +{{ generate_event(group) -}} +{%- elif group.type == "resource" -%} +{{ generate_resource(group) }} +{%- elif group.type == "metric" -%} +{{ generate_metric(group) }} +{%- else -%} +{{ generate_attributes(group) -}} {% endif -%} -{{ at.generate(group.attributes, tag_filter, attribute_registry_base_url, group.lineage.attributes) -}}