diff --git a/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/ActivityTracer.kt b/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/ActivityTracer.kt index 0e109bfa7..bcc99b65d 100644 --- a/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/ActivityTracer.kt +++ b/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/ActivityTracer.kt @@ -11,6 +11,7 @@ import io.opentelemetry.android.instrumentation.activity.startup.AppStartupTimer import io.opentelemetry.android.instrumentation.common.ActiveSpan import io.opentelemetry.android.semconv.ActivityAttributes.ACTIVITY_NAME_KEY import io.opentelemetry.android.semconv.StartAttributes.START_TYPE_KEY +import io.opentelemetry.android.semconv.StartAttributes.StartTypeValues import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.Tracer import io.opentelemetry.context.Context @@ -51,7 +52,7 @@ internal class ActivityTracer( return createSpanWithParent("Created", appStartupTimer.startupSpan) } if (activityName == initialAppActivity) { - return createAppStartSpan("warm") + return createAppStartSpan(StartTypeValues.WARM.value) } return createSpan("Created") } @@ -69,7 +70,7 @@ internal class ActivityTracer( // Note: in a multi-activity application, navigating back to the first activity can trigger // this, so it would not be ideal to call it an AppStart. if (!multiActivityApp && activityName == initialAppActivity) { - return createAppStartSpan("hot") + return createAppStartSpan(StartTypeValues.HOT.value) } return createSpan("Restarted") } diff --git a/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/startup/AppStartupTimer.kt b/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/startup/AppStartupTimer.kt index 1a8a88191..937be8f16 100644 --- a/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/startup/AppStartupTimer.kt +++ b/instrumentation/activity/src/main/java/io/opentelemetry/android/instrumentation/activity/startup/AppStartupTimer.kt @@ -12,6 +12,7 @@ import android.util.Log import io.opentelemetry.android.common.RumConstants import io.opentelemetry.android.internal.services.visiblescreen.activities.DefaultingActivityLifecycleCallbacks import io.opentelemetry.android.semconv.StartAttributes.START_TYPE_KEY +import io.opentelemetry.android.semconv.StartAttributes.StartTypeValues import io.opentelemetry.api.trace.Span import io.opentelemetry.api.trace.Tracer import io.opentelemetry.sdk.common.Clock @@ -47,7 +48,7 @@ internal class AppStartupTimer { tracer .spanBuilder("AppStart") .setStartTimestamp(firstPossibleTimestamp, TimeUnit.NANOSECONDS) - .setAttribute(START_TYPE_KEY, "cold") + .setAttribute(START_TYPE_KEY, StartTypeValues.COLD.value) .startSpan() this.startupSpan = appStart return appStart diff --git a/instrumentation/okhttp3-websocket/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/websocket/internal/WebsocketListenerWrapper.kt b/instrumentation/okhttp3-websocket/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/websocket/internal/WebsocketListenerWrapper.kt index 78bbb1119..772034f55 100644 --- a/instrumentation/okhttp3-websocket/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/websocket/internal/WebsocketListenerWrapper.kt +++ b/instrumentation/okhttp3-websocket/library/src/main/java/io/opentelemetry/instrumentation/library/okhttp/websocket/internal/WebsocketListenerWrapper.kt @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.library.okhttp.websocket.internal +import io.opentelemetry.android.semconv.WebsocketAttributes.WebsocketMessageTypeValues import io.opentelemetry.android.semconv.events.WebsocketCloseEvent import io.opentelemetry.android.semconv.events.WebsocketErrorEvent import io.opentelemetry.android.semconv.events.WebsocketMessageEvent @@ -49,7 +50,7 @@ class WebsocketListenerWrapper( val attributes = extractAttributes(webSocket) WebsocketMessageEvent( websocketMessageSize = text.length.toLong(), - websocketMessageType = "text", + websocketMessageType = WebsocketMessageTypeValues.TEXT.value, ).emit(logger, attributes) delegate.onMessage(webSocket, text) } @@ -61,7 +62,7 @@ class WebsocketListenerWrapper( val attributes = extractAttributes(webSocket) WebsocketMessageEvent( websocketMessageSize = bytes.size.toLong(), - websocketMessageType = "bytes", + websocketMessageType = WebsocketMessageTypeValues.BYTES.value, ).emit(logger, attributes) delegate.onMessage(webSocket, bytes) } diff --git a/instrumentation/screen-orientation/src/main/java/io/opentelemetry/android/instrumentation/screenorientation/ScreenOrientationDetector.kt b/instrumentation/screen-orientation/src/main/java/io/opentelemetry/android/instrumentation/screenorientation/ScreenOrientationDetector.kt index 9856b1e88..d6e273b31 100644 --- a/instrumentation/screen-orientation/src/main/java/io/opentelemetry/android/instrumentation/screenorientation/ScreenOrientationDetector.kt +++ b/instrumentation/screen-orientation/src/main/java/io/opentelemetry/android/instrumentation/screenorientation/ScreenOrientationDetector.kt @@ -10,6 +10,7 @@ import android.content.Context import android.content.res.Configuration import android.content.res.Configuration.ORIENTATION_LANDSCAPE import android.content.res.Configuration.ORIENTATION_PORTRAIT +import io.opentelemetry.android.semconv.ScreenAttributes.ScreenOrientationValues import io.opentelemetry.android.semconv.events.DeviceScreenOrientationEvent import io.opentelemetry.api.logs.Logger @@ -36,9 +37,9 @@ internal class ScreenOrientationDetector( private val Int.name: String get() { return when (this) { - ORIENTATION_LANDSCAPE -> "landscape" - ORIENTATION_PORTRAIT -> "portrait" - else -> "undefined" + ORIENTATION_LANDSCAPE -> ScreenOrientationValues.LANDSCAPE.value + ORIENTATION_PORTRAIT -> ScreenOrientationValues.PORTRAIT.value + else -> ScreenOrientationValues.UNDEFINED.value } } diff --git a/semconv/model/android/registry.yaml b/semconv/model/android/registry.yaml index 4a6637587..8ed39cb42 100644 --- a/semconv/model/android/registry.yaml +++ b/semconv/model/android/registry.yaml @@ -109,11 +109,23 @@ groups: The current application screen name used by the Android SDK. examples: ["user.login", "Cart"] - id: screen.orientation - type: string + type: + members: + - id: portrait + value: "portrait" + stability: development + brief: The screen is in portrait orientation. + - id: landscape + value: "landscape" + stability: development + brief: The screen is in landscape orientation. + - id: undefined + value: "undefined" + stability: development + brief: The screen orientation could not be determined. stability: development brief: > The Android screen orientation. - examples: ["portrait", "landscape", "undefined"] - id: span.exporter type: string stability: development @@ -121,11 +133,23 @@ groups: A string representation of the span exporter configured during SDK initialization. examples: ["OtlpHttpSpanExporter"] - id: start.type - type: string + type: + members: + - id: cold + value: "cold" + stability: development + brief: The application started from scratch, with no process or activity reused. + - id: warm + value: "warm" + stability: development + brief: The application process was reused but the activity had to be recreated. + - id: hot + value: "hot" + stability: development + brief: The application and its activity were already in memory and brought to the foreground. stability: development brief: > The application start type. - examples: ["cold", "warm", "hot"] - id: storage.free type: int stability: development @@ -139,8 +163,16 @@ groups: The size of a WebSocket message payload. examples: [128] - id: websocket.message.type - type: string + type: + members: + - id: text + value: "text" + stability: development + brief: A text message. + - id: bytes + value: "bytes" + stability: development + brief: A binary message. stability: development brief: > The WebSocket message payload type. - examples: ["text", "bytes"] diff --git a/semconv/templates/registry/kotlin/kotlin_event_template.kt.j2 b/semconv/templates/registry/kotlin/kotlin_event_template.kt.j2 index e1e2fb7a7..96c7a2213 100644 --- a/semconv/templates/registry/kotlin/kotlin_event_template.kt.j2 +++ b/semconv/templates/registry/kotlin/kotlin_event_template.kt.j2 @@ -6,10 +6,14 @@ package io.opentelemetry.android.semconv.events +{%- set has_array_attributes = (ctx.attributes | selectattr("type", "in", ["string[]", "boolean[]", "int[]", "double[]"]) | list | length) > 0 -%} +{%- if has_array_attributes %} +import io.opentelemetry.api.common.AttributeKey +{%- endif %} import io.opentelemetry.api.common.Attributes import io.opentelemetry.api.logs.Logger -// Autogenerated OpenTelemetry Android semantic convention event for '{{ctx.name}}'. +// Autogenerated OpenTelemetry Android semantic convention event for '{{ ctx.name }}'. // // DO NOT EDIT - AUTOGENERATED FILE {{ [ctx.brief, concat_if("\n\nNotes:\n\n", ctx.note)] | comment }} @@ -30,7 +34,14 @@ class {{ my_class_name }}( val attributes = Attributes.builder() {%- for attribute in ctx.attributes %} {%- set required = attribute.requirement_level == "required" %} + {%- if attribute.type in ["string[]", "boolean[]", "int[]", "double[]"] %} + {%- set key = 'AttributeKey.' ~ (attribute.type | map_text("kotlin_key_factory")) ~ '("' ~ attribute.name ~ '")' %} {%- if required %} + attributes.put({{ key }}, {{ attribute.name | camel_case }}) + {%- else %} + {{ attribute.name | camel_case }}?.let { attributes.put({{ key }}, it) } + {%- endif %} + {%- elif required %} attributes.put("{{ attribute.name }}", {{ attribute.name | camel_case }}) {%- else %} {{ attribute.name | camel_case }}?.let { attributes.put("{{ attribute.name }}", it) } diff --git a/semconv/templates/registry/kotlin/kotlin_semconv_template.kt.j2 b/semconv/templates/registry/kotlin/kotlin_semconv_template.kt.j2 index 77fa552a3..f781cac4b 100644 --- a/semconv/templates/registry/kotlin/kotlin_semconv_template.kt.j2 +++ b/semconv/templates/registry/kotlin/kotlin_semconv_template.kt.j2 @@ -21,19 +21,30 @@ object {{ my_class_name }} { @Deprecated("{{ attribute.deprecated.note | replace('\n', ' ') | trim }}") {%- endif %} const val {{ attribute.name | screaming_snake_case }}: String = "{{ attribute.name }}" +{%- if attribute.type != "any" %} +{%- set itype = attribute.type | instantiated_type %} +{%- if itype in ["string", "boolean", "int", "double", "string[]", "boolean[]", "int[]", "double[]"] %} @JvmField -{%- if attribute.type == "string" %} - val {{ attribute.name | screaming_snake_case }}_KEY: AttributeKey = - AttributeKey.stringKey({{ attribute.name | screaming_snake_case }}) -{%- elif attribute.type == "boolean" %} - val {{ attribute.name | screaming_snake_case }}_KEY: AttributeKey = - AttributeKey.booleanKey({{ attribute.name | screaming_snake_case }}) -{%- elif attribute.type == "int" %} - val {{ attribute.name | screaming_snake_case }}_KEY: AttributeKey = - AttributeKey.longKey({{ attribute.name | screaming_snake_case }}) -{%- elif attribute.type == "double" %} - val {{ attribute.name | screaming_snake_case }}_KEY: AttributeKey = - AttributeKey.doubleKey({{ attribute.name | screaming_snake_case }}) + val {{ attribute.name | screaming_snake_case }}_KEY: AttributeKey<{{ itype | map_text("kotlin_key_generic") }}> = + AttributeKey.{{ itype | map_text("kotlin_key_factory") }}({{ attribute.name | screaming_snake_case }}) +{%- endif %} +{%- endif %} +{%- endfor %} +{%- for attribute in ctx.attributes | rejectattr("name", "in", ctx.excluded_attributes) %} +{%- if attribute is enum %} + + {{ ("Values for " ~ (attribute.name | screaming_snake_case) ~ ".") | comment | trim | indent(4) -}} +{%- if attribute is deprecated %} + @Deprecated("{{ attribute.deprecated.note | replace('\n', ' ') | trim }}") +{%- endif %} + enum class {{ attribute.name | pascal_case }}Values( + val value: {{ attribute.type | instantiated_type | map_text("kotlin_enum_type") }}, + ) { +{%- for member in attribute.type.members %} +{{ "\n" if not loop.first }} {{ [member.brief or (member.id ~ '.')] | comment | trim | indent(8) }} + {{ member.id | screaming_snake_case }}({{ member.value | print_member_value }}), +{%- endfor %} + } {%- endif %} {%- endfor %} } diff --git a/semconv/templates/registry/kotlin/weaver.yaml b/semconv/templates/registry/kotlin/weaver.yaml index 1812fdde7..4e1613185 100644 --- a/semconv/templates/registry/kotlin/weaver.yaml +++ b/semconv/templates/registry/kotlin/weaver.yaml @@ -31,7 +31,9 @@ templates: brief: .brief, note: .note, deprecated: .deprecated, - attributes: ((.attributes // []) | unique_by(.name)) + attributes: ((.attributes // []) + | map(select(.type != "any")) + | unique_by(.name)) }) application_mode: each file_name: "io/opentelemetry/android/semconv/events/{{ctx.name | pascal_case}}Event.kt" @@ -39,6 +41,24 @@ text_maps: kotlin_enum_type: int: Long string: String + kotlin_key_generic: + string: String + boolean: Boolean + int: Long + double: Double + "string[]": List + "boolean[]": List + "int[]": List + "double[]": List + kotlin_key_factory: + string: stringKey + boolean: booleanKey + int: longKey + double: doubleKey + "string[]": stringArrayKey + "boolean[]": booleanArrayKey + "int[]": longArrayKey + "double[]": doubleArrayKey kotlin_event_type: int: Long double: Double